guinea_trace/point.rs
1use std::fmt;
2
3/// Which bus carried a publication.
4#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
5pub enum Bus {
6 /// Every window hears it.
7 Global,
8 /// One window's own.
9 Window,
10}
11
12/// How a stored value changed.
13#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
14pub enum StoreOp {
15 Set,
16 Delete,
17 /// Everything under the path went at once.
18 DeletePrefix,
19}
20
21/// An observable point, with what identifies it.
22///
23/// Names are short type names: `processes::Kill`, not a crate path.
24///
25/// New kinds of point come in patch releases, so a match on it outside this
26/// crate needs an arm for the ones it doesn't know yet:
27///
28/// ```compile_fail
29/// use guinea_trace::Point;
30///
31/// fn known(point: &Point) -> bool {
32/// match point {
33/// Point::Action { .. }
34/// | Point::Send { .. }
35/// | Point::Handle { .. }
36/// | Point::Spawn { .. }
37/// | Point::Settled { .. }
38/// | Point::Cancelled { .. }
39/// | Point::Source { .. }
40/// | Point::Arrived { .. }
41/// | Point::Pull { .. }
42/// | Point::Closed { .. }
43/// | Point::Publish { .. }
44/// | Point::Deliver { .. }
45/// | Point::Push { .. }
46/// | Point::Navigate { .. }
47/// | Point::Tick { .. }
48/// | Point::Store { .. }
49/// | Point::Render { .. }
50/// | Point::Log { .. }
51/// | Point::Span { .. }
52/// | Point::Note(_) => true,
53/// }
54/// }
55/// ```
56#[derive(Clone, Debug, PartialEq)]
57#[non_exhaustive]
58pub enum Point {
59 /// The UI asked a feature for something.
60 Action { message: &'static str },
61 /// A message was queued for an actor.
62 Send {
63 actor: &'static str,
64 message: &'static str,
65 },
66 /// An actor handled a message.
67 Handle {
68 actor: &'static str,
69 message: &'static str,
70 },
71 /// An actor started background work whose result will come back as
72 /// `output`. `actor` is the address it will come back to, by the id the
73 /// snapshot lists it under: what a task belongs to is what that actor
74 /// belongs to.
75 Spawn {
76 actor: &'static str,
77 actor_id: u64,
78 output: &'static str,
79 },
80 /// Background work finished, and its result is on its way to the actor.
81 Settled {
82 actor: &'static str,
83 actor_id: u64,
84 output: &'static str,
85 took_us: u64,
86 },
87 /// Background work was dropped where it last awaited, because the actor
88 /// that started it is gone. Nothing comes back.
89 Cancelled {
90 actor: &'static str,
91 actor_id: u64,
92 output: &'static str,
93 took_us: u64,
94 },
95 /// An actor opened a source whose items will come to it as `output`.
96 /// Recorded under what opened it, which is done with it once it is open:
97 /// the items are not its work.
98 Source {
99 actor: &'static str,
100 actor_id: u64,
101 output: &'static str,
102 },
103 /// An item came from a source, as `output`. A root, as a timer's tick
104 /// is; `source` is the id of the [`Point::Source`] it came from.
105 Arrived {
106 actor: &'static str,
107 actor_id: u64,
108 output: &'static str,
109 source: u64,
110 },
111 /// A source is making its next item, which will come as `output`: what
112 /// its stream does meanwhile is recorded under this. A root, as
113 /// [`Point::Arrived`] is; `source` is the id of the [`Point::Source`].
114 /// Open from when the source is asked for the item until it has it, runs
115 /// dry or is dropped.
116 Pull {
117 actor: &'static str,
118 actor_id: u64,
119 output: &'static str,
120 source: u64,
121 },
122 /// A source ended: it ran dry, or `gone` - the actor that opened it is
123 /// gone and the source was dropped where it last awaited.
124 Closed {
125 actor: &'static str,
126 actor_id: u64,
127 output: &'static str,
128 took_us: u64,
129 gone: bool,
130 },
131 /// An event went out.
132 Publish {
133 event: &'static str,
134 bus: Bus,
135 subscribers: usize,
136 },
137 /// An event reached a subscriber that is not an actor.
138 Deliver { event: &'static str, bus: Bus },
139 /// A reducer was changed.
140 Push { reducer: &'static str },
141 /// A router moved.
142 Navigate { root: String, to: String },
143 /// A timer fired; `timer` is its id, as the application's timers list
144 /// it. The rest says whose it is, so the record reads on its own once the
145 /// timer is gone: the name it was given, if any, and where it was set up.
146 Tick {
147 timer: u64,
148 name: Option<&'static str>,
149 file: &'static str,
150 line: u32,
151 },
152 /// A persisted value changed.
153 Store {
154 op: StoreOp,
155 path: String,
156 /// The declared field the path belongs to, `Settings.theme`, when
157 /// the store knows it.
158 field: Option<String>,
159 /// Whether the change came from outside the process, an edited file.
160 outside: bool,
161 },
162 /// A page or layout drew itself, and how long that took. Recorded only
163 /// for the frames worth looking at - see `observability::Rendering`.
164 Render {
165 segment: &'static str,
166 took_us: u64,
167 },
168 /// An ordinary `tracing` event the application wrote.
169 Log {
170 level: tracing::Level,
171 target: &'static str,
172 /// Where it was written, as the compiler named the file: relative to
173 /// the workspace root for the application's own crates.
174 file: Option<&'static str>,
175 line: Option<u32>,
176 module: Option<&'static str>,
177 /// The message, then the other fields as `name=value`.
178 text: String,
179 },
180 /// A `tracing` span the application opened - an `#[instrument]`ed
181 /// function, say. Open until the span closes, and current while it is
182 /// entered; what it took is the time it was entered, not the time it
183 /// waited in between.
184 Span {
185 name: &'static str,
186 /// The level it was opened at, as [`Point::Log`]'s.
187 level: tracing::Level,
188 target: &'static str,
189 /// Where it was written; see [`Point::Log`].
190 file: Option<&'static str>,
191 line: Option<u32>,
192 module: Option<&'static str>,
193 /// Its fields as `name=value`, as they were when it opened.
194 fields: String,
195 },
196 /// Anything else worth a line.
197 Note(String),
198}
199
200impl Point {
201 /// A short name for the kind of point, for filtering.
202 pub fn kind(&self) -> &'static str {
203 match self {
204 Point::Action { .. } => "action",
205 Point::Send { .. } => "send",
206 Point::Handle { .. } => "handle",
207 Point::Spawn { .. } => "spawn",
208 Point::Settled { .. } => "settled",
209 Point::Cancelled { .. } => "cancelled",
210 Point::Source { .. } => "source",
211 Point::Arrived { .. } => "arrived",
212 Point::Pull { .. } => "pull",
213 Point::Closed { .. } => "closed",
214 Point::Publish { .. } => "publish",
215 Point::Deliver { .. } => "deliver",
216 Point::Push { .. } => "push",
217 Point::Navigate { .. } => "navigate",
218 Point::Tick { .. } => "tick",
219 Point::Store { .. } => "store",
220 Point::Render { .. } => "render",
221 Point::Log { .. } => "log",
222 Point::Span { .. } => "span",
223 Point::Note(_) => "note",
224 }
225 }
226}
227
228impl fmt::Display for Bus {
229 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
230 f.write_str(match self {
231 Bus::Global => "global",
232 Bus::Window => "window",
233 })
234 }
235}
236
237impl fmt::Display for StoreOp {
238 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
239 f.write_str(match self {
240 StoreOp::Set => "set",
241 StoreOp::Delete => "delete",
242 StoreOp::DeletePrefix => "delete_prefix",
243 })
244 }
245}
246
247impl fmt::Display for Point {
248 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
249 match self {
250 Point::Action { message } => write!(f, "action {message}"),
251 Point::Send { actor, message } => write!(f, "send {message} → {actor}"),
252 Point::Handle { actor, message, .. } => write!(f, "{actor} handles {message}"),
253 Point::Spawn { actor, output, .. } => write!(f, "{actor} starts work for {output}"),
254 Point::Settled {
255 actor,
256 output,
257 took_us,
258 ..
259 } => write!(
260 f,
261 "{actor} has its {output} after {:.1} ms",
262 *took_us as f64 / 1000.0
263 ),
264 Point::Cancelled {
265 actor,
266 output,
267 took_us,
268 ..
269 } => write!(
270 f,
271 "{actor} is gone: {output} cancelled after {:.1} ms",
272 *took_us as f64 / 1000.0
273 ),
274 Point::Source { actor, output, .. } => write!(f, "{actor} opens a source of {output}"),
275 Point::Arrived { actor, output, .. } => write!(f, "{output} arrives at {actor}"),
276 Point::Pull { actor, output, .. } => {
277 write!(f, "{actor}'s source makes the next {output}")
278 }
279 Point::Closed {
280 actor,
281 output,
282 took_us,
283 gone: true,
284 ..
285 } => write!(
286 f,
287 "{actor} is gone: its source of {output} closed after {:.1} ms",
288 *took_us as f64 / 1000.0
289 ),
290 Point::Closed {
291 actor,
292 output,
293 took_us,
294 ..
295 } => write!(
296 f,
297 "{actor}'s source of {output} ran dry after {:.1} ms",
298 *took_us as f64 / 1000.0
299 ),
300 Point::Publish {
301 event,
302 bus,
303 subscribers,
304 } => write!(f, "publish {event} on the {bus} bus to {subscribers}"),
305 Point::Deliver { event, bus } => write!(f, "deliver {event} from the {bus} bus"),
306 Point::Push { reducer } => write!(f, "push into {reducer}"),
307 Point::Navigate { root, to } => write!(f, "{root} navigates to {to}"),
308 Point::Tick { name: Some(name), .. } => write!(f, "timer {name}"),
309 Point::Tick { timer, .. } => write!(f, "timer #{timer}"),
310 Point::Render { segment, took_us } => {
311 write!(f, "{segment} drew itself in {:.1} ms", *took_us as f64 / 1000.0)
312 }
313 Point::Store {
314 op,
315 path,
316 field,
317 outside,
318 } => {
319 let who = if *outside { "disk" } else { "store" };
320 let verb = match op {
321 StoreOp::Set => "sets",
322 StoreOp::Delete => "deletes",
323 StoreOp::DeletePrefix => "clears",
324 };
325 write!(f, "{who} {verb} {path}")?;
326 match field {
327 Some(field) => write!(f, " ({field})"),
328 None => Ok(()),
329 }
330 }
331 Point::Log {
332 level,
333 target,
334 text,
335 ..
336 } => write!(f, "{level} {target}: {text}"),
337 Point::Span { name, fields, .. } if fields.is_empty() => f.write_str(name),
338 Point::Span { name, fields, .. } => write!(f, "{name} {fields}"),
339 Point::Note(text) => f.write_str(text),
340 }
341 }
342}