cargo/util/
log_message.rs

1//! Messages for logging.
2
3use std::borrow::Cow;
4use std::io::Write;
5use std::path::PathBuf;
6
7use cargo_util_schemas::core::PackageIdSpec;
8use jiff::Timestamp;
9use serde::Deserialize;
10use serde::Serialize;
11
12use crate::core::compiler::CompileMode;
13use crate::core::compiler::fingerprint::DirtyReason;
14
15/// A log message.
16///
17/// Each variant represents a different type of event.
18#[derive(Serialize, Deserialize)]
19#[serde(tag = "reason", rename_all = "kebab-case")]
20pub enum LogMessage {
21    /// Emitted when a build starts.
22    BuildStarted {
23        /// Current working directory.
24        cwd: PathBuf,
25        /// Host triple.
26        host: String,
27        /// Number of parallel jobs.
28        jobs: u32,
29        /// Available parallelism of the compilation environment.
30        num_cpus: Option<u64>,
31        /// Build profile name (e.g., "dev", "release").
32        profile: String,
33        /// The rustc version (`1.23.4-beta.2`).
34        rustc_version: String,
35        /// The rustc verbose version information (the output of `rustc -vV`).
36        rustc_version_verbose: String,
37        /// Target directory for build artifacts.
38        target_dir: PathBuf,
39        /// Workspace root directory.
40        workspace_root: PathBuf,
41    },
42    /// Emitted when resolving dependencies starts.
43    ResolutionStarted {
44        /// Seconds elapsed from build start.
45        elapsed: f64,
46    },
47    /// Emitted when resolving dependencies finishes.
48    ResolutionFinished {
49        /// Seconds elapsed from build start.
50        elapsed: f64,
51    },
52    /// Emitted when unit graph generation starts.
53    UnitGraphStarted {
54        /// Seconds elapsed from build start.
55        elapsed: f64,
56    },
57    /// Emitted when unit graph generation finishes.
58    UnitGraphFinished {
59        /// Seconds elapsed from build start.
60        elapsed: f64,
61    },
62    /// Emitted when a compilation unit is registered in the unit graph,
63    /// right before [`LogMessage::UnitGraphFinished`] that Cargo finalizes
64    /// the unit graph.
65    UnitRegistered {
66        /// Package ID specification.
67        package_id: PackageIdSpec,
68        /// Cargo target (lib, bin, example, etc.).
69        target: Target,
70        /// The compilation action this unit is for (check, build, test, etc.).
71        mode: CompileMode,
72        /// The target platform this unit builds for.
73        ///
74        /// It is either a [target triple] the compiler accepts,
75        /// or a file name with the `json` extension for a [custom target].
76        ///
77        /// [target triple]: https://doc.rust-lang.org/nightly/rustc/platform-support.html
78        /// [custom target]: https://doc.rust-lang.org/nightly/rustc/targets/custom.html
79        platform: String,
80        /// Unit index for compact reference in subsequent events.
81        index: u64,
82        /// Enabled features.
83        #[serde(default, skip_serializing_if = "Vec::is_empty")]
84        features: Vec<String>,
85        /// Whether this is requested to build by user directly,
86        /// like via the `-p` flag or the default workspace members.
87        #[serde(default, skip_serializing_if = "std::ops::Not::not")]
88        requested: bool,
89    },
90    /// Emitted when a compilation unit starts.
91    UnitStarted {
92        /// Unit index from the associated unit-registered event.
93        index: u64,
94        /// Seconds elapsed from build start.
95        elapsed: f64,
96    },
97    /// Emitted when a section (e.g., rmeta, link) of the compilation unit finishes.
98    UnitRmetaFinished {
99        /// Unit index from the associated unit-registered event.
100        index: u64,
101        /// Seconds elapsed from build start.
102        elapsed: f64,
103        /// Unit indices that were unblocked by this rmeta completion.
104        #[serde(default, skip_serializing_if = "Vec::is_empty")]
105        unblocked: Vec<u64>,
106    },
107    /// Emitted when a section (e.g., rmeta, link) of the compilation unit starts.
108    ///
109    /// Requires `-Zsection-timings` to be enabled.
110    UnitSectionStarted {
111        /// Unit index from the associated unit-registered event.
112        index: u64,
113        /// Seconds elapsed from build start.
114        elapsed: f64,
115        /// Section name from rustc's `-Zjson=timings` (e.g., "codegen", "link").
116        section: String,
117    },
118    /// Emitted when a section (e.g., rmeta, link) of the compilation unit finishes.
119    ///
120    /// Requires `-Zsection-timings` to be enabled.
121    UnitSectionFinished {
122        /// Unit index from the associated unit-registered event.
123        index: u64,
124        /// Seconds elapsed from build start.
125        elapsed: f64,
126        /// Section name from rustc's `-Zjson=timings` (e.g., "codegen", "link").
127        section: String,
128    },
129    /// Emitted when a compilation unit finishes.
130    UnitFinished {
131        /// Unit index from the associated unit-registered event.
132        index: u64,
133        /// Seconds elapsed from build start.
134        elapsed: f64,
135        /// Unit indices that were unblocked by this completion.
136        #[serde(default, skip_serializing_if = "Vec::is_empty")]
137        unblocked: Vec<u64>,
138    },
139    /// Emitted when rebuild fingerprint information is determined for a unit.
140    UnitFingerprint {
141        /// Unit index from the associated unit-registered event.
142        index: u64,
143        /// Status of the rebuild detection fingerprint of this unit
144        status: FingerprintStatus,
145        /// Reason why the unit is dirty and needs rebuilding.
146        #[serde(default, skip_deserializing, skip_serializing_if = "Option::is_none")]
147        cause: Option<DirtyReason>,
148    },
149}
150
151/// Cargo target information.
152#[derive(Serialize, Deserialize)]
153pub struct Target {
154    /// Target name.
155    pub name: String,
156    /// Target kind (lib, bin, test, bench, example, build-script).
157    pub kind: Cow<'static, str>,
158}
159
160/// Status of the rebuild detection fingerprint.
161#[derive(Serialize, Deserialize)]
162#[serde(rename_all = "kebab-case")]
163pub enum FingerprintStatus {
164    /// There is no previous fingerprints for this unit.
165    /// Might be a brand-new build.
166    New,
167    /// The current fingerprint doesn't match the previous fingerprints.
168    /// Rebuild needed.
169    Dirty,
170    /// The current fingerprint matches the previous fingerprints.
171    /// No rebuild needed.
172    Fresh,
173}
174
175impl From<&crate::core::Target> for Target {
176    fn from(target: &crate::core::Target) -> Self {
177        use crate::core::TargetKind;
178        Self {
179            name: target.name().to_string(),
180            kind: match target.kind() {
181                TargetKind::Lib(..) => "lib",
182                TargetKind::Bin => "bin",
183                TargetKind::Test => "test",
184                TargetKind::Bench => "bench",
185                TargetKind::ExampleLib(..) | TargetKind::ExampleBin => "example",
186                TargetKind::CustomBuild => "build-script",
187            }
188            .into(),
189        }
190    }
191}
192
193impl LogMessage {
194    /// Serializes this message as a JSON log line directly to the writer.
195    pub fn write_json_log<W: Write>(&self, writer: &mut W, run_id: &str) -> std::io::Result<()> {
196        #[derive(Serialize)]
197        struct LogEntry<'a> {
198            run_id: &'a str,
199            timestamp: Timestamp,
200            #[serde(flatten)]
201            msg: &'a LogMessage,
202        }
203
204        let entry = LogEntry {
205            run_id,
206            timestamp: Timestamp::now(),
207            msg: self,
208        };
209
210        serde_json::to_writer(&mut *writer, &entry)?;
211        writer.write_all(b"\n")?;
212        Ok(())
213    }
214}