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}