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