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}