Skip to main content

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