1pub mod container;
6pub mod debuglog;
7pub mod error;
8pub mod listener;
9pub mod multiplex;
10pub mod repository;
11pub mod serial;
12pub mod servers;
13pub mod shared_buffer;
14pub mod stats;
15pub mod stored_message;
16#[cfg(test)]
17pub mod testing;
18
19#[cfg(test)]
20mod tests {
21 use crate::identity::ComponentIdentity;
22 use crate::logs::testing::*;
23 use diagnostics_assertions::{AnyProperty, assert_data_tree};
24 use diagnostics_log_encoding::{Argument, Record};
25 use diagnostics_log_types::Severity;
26 use fidl_fuchsia_logger::{LogFilterOptions, LogLevelFilter, LogMessage};
27 use fuchsia_async as fasync;
28 use moniker::ExtendedMoniker;
29 use std::sync::Arc;
30
31 #[fuchsia::test]
32 async fn test_log_manager() {
33 Box::pin(TestHarness::default().manager_test()).await;
34 }
35
36 #[fuchsia::test]
37 async fn unfiltered_stats() {
38 let first_message = LogMessage {
39 pid: 1,
40 tid: 2,
41 time: zx::BootInstant::get(),
42 dropped_logs: 3,
43 severity: Severity::Warn as i32,
44 msg: String::from("BBBBB"),
45 tags: vec![String::from("AAAAA")],
46 };
47 let first_packet = to_record(&first_message);
48
49 let mut second_message = first_message.clone();
50 second_message.pid = 0;
51 let second_packet = to_record(&second_message);
52
53 let mut third_message = second_message.clone();
54 third_message.severity = Severity::Info as i32;
55 let third_packet = to_record(&third_message);
56
57 let (fourth_packet, fourth_message) = (third_packet.clone(), third_message.clone());
58
59 let mut fifth_message = fourth_message.clone();
60 fifth_message.severity = Severity::Error as i32;
61 let fifth_packet = to_record(&fifth_message);
62
63 let mut harness = TestHarness::default();
64 let mut stream = harness.create_structured_stream(Arc::new(ComponentIdentity::unknown()));
65 stream.write_packets(vec![
66 first_packet,
67 second_packet,
68 third_packet,
69 fourth_packet,
70 fifth_packet,
71 ]);
72 drop(stream);
73
74 let log_stats_tree = harness
75 .filter_test(
76 vec![first_message, second_message, third_message, fourth_message, fifth_message],
77 None,
78 )
79 .await;
80
81 assert_data_tree!(
82 log_stats_tree,
83 root: contains {
84 log_sources: {
85 "UNKNOWN": {
86 url: "fuchsia-pkg://UNKNOWN",
87 last_timestamp: AnyProperty,
88 sockets_closed: 1u64,
89 sockets_opened: 1u64,
90 invalid: {
91 number: 0u64,
92 bytes: 0u64,
93 },
94 total: {
95 number: 5u64,
96 bytes: AnyProperty,
97 },
98 rolled_out: {
99 number: 0u64,
100 bytes: 0u64,
101 },
102 trace: {
103 number: 0u64,
104 bytes: 0u64,
105 },
106 debug: {
107 number: 0u64,
108 bytes: 0u64,
109 },
110 info: {
111 number: 2u64,
112 bytes: AnyProperty,
113 },
114 warn: {
115 number: 2u64,
116 bytes: AnyProperty,
117 },
118 error: {
119 number: 1u64,
120 bytes: AnyProperty,
121 },
122 fatal: {
123 number: 0u64,
124 bytes: 0u64,
125 },
126 },
127 }
128 }
129 );
130 }
131
132 macro_rules! attributed_inspect_two_streams_different_identities_by_reader {
133 (
134 $harness:ident,
135 $log_reader1:ident @ $foo_moniker:literal,
136 $log_reader2:ident @ $bar_moniker:literal,
137 ) => {{
138 let message = LogMessage {
139 pid: 1,
140 tid: 2,
141 time: zx::BootInstant::get(),
142 dropped_logs: 3,
143 severity: Severity::Warn as i32,
144 msg: String::from("BBBBB"),
145 tags: vec![String::from("AAAAA")],
146 };
147 let packet = to_record(&message);
148
149 let mut message2 = message.clone();
150 message2.severity = Severity::Error as i32;
151 let packet2 = to_record(&message2);
152
153 let mut foo_stream = $harness.create_stream_from_log_reader($log_reader1);
154 foo_stream.write_packet(packet);
155
156 let mut bar_stream = $harness.create_stream_from_log_reader($log_reader2);
157 bar_stream.write_packet(packet2);
158 drop((foo_stream, bar_stream));
159
160 let log_stats_tree = $harness.filter_test(vec![message, message2], None).await;
161
162 assert_data_tree!(
163 log_stats_tree,
164 root: contains {
165 log_sources: {
166 $foo_moniker: {
167 url: "http://foo.com",
168 last_timestamp: AnyProperty,
169 sockets_closed: 1u64,
170 sockets_opened: 1u64,
171 invalid: {
172 number: 0u64,
173 bytes: 0u64,
174 },
175 total: {
176 number: 1u64,
177 bytes: AnyProperty,
178 },
179 rolled_out: {
180 number: 0u64,
181 bytes: 0u64,
182 },
183 trace: {
184 number: 0u64,
185 bytes: 0u64,
186 },
187 debug: {
188 number: 0u64,
189 bytes: 0u64,
190 },
191 info: {
192 number: 0u64,
193 bytes: 0u64,
194 },
195 warn: {
196 number: 1u64,
197 bytes: AnyProperty,
198 },
199 error: {
200 number: 0u64,
201 bytes: 0u64,
202 },
203 fatal: {
204 number: 0u64,
205 bytes: 0u64,
206 },
207 },
208 $bar_moniker: {
209 url: "http://bar.com",
210 last_timestamp: AnyProperty,
211 sockets_closed: 1u64,
212 sockets_opened: 1u64,
213 invalid: {
214 number: 0u64,
215 bytes: 0u64,
216 },
217 total: {
218 number: 1u64,
219 bytes: AnyProperty,
220 },
221 rolled_out: {
222 number: 0u64,
223 bytes: 0u64,
224 },
225 trace: {
226 number: 0u64,
227 bytes: 0u64,
228 },
229 debug: {
230 number: 0u64,
231 bytes: 0u64,
232 },
233 info: {
234 number: 0u64,
235 bytes: 0u64,
236 },
237 warn: {
238 number: 0u64,
239 bytes: 0u64,
240 },
241 error: {
242 number: 1u64,
243 bytes: AnyProperty,
244 },
245 fatal: {
246 number: 0u64,
247 bytes: 0u64,
248 },
249 },
250 },
251 }
252 );
253 }}
254 }
255
256 #[fuchsia::test]
257 async fn attributed_inspect_two_streams_different_identities() {
258 let mut harness = TestHarness::with_retained_sinks();
259
260 let log_reader1 = harness.create_default_reader(ComponentIdentity::new(
261 ExtendedMoniker::parse_str("./foo").unwrap(),
262 "http://foo.com",
263 ));
264
265 let log_reader2 = harness.create_default_reader(ComponentIdentity::new(
266 ExtendedMoniker::parse_str("./bar").unwrap(),
267 "http://bar.com",
268 ));
269
270 attributed_inspect_two_streams_different_identities_by_reader!(
271 harness,
272 log_reader1 @ "foo",
273 log_reader2 @ "bar",
274 );
275 }
276
277 #[fuchsia::test]
278 async fn attributed_inspect_two_v2_streams_different_identities() {
279 let mut harness = TestHarness::with_retained_sinks();
280 let log_reader1 = harness.create_event_stream_reader("./foo", "http://foo.com");
281 let log_reader2 = harness.create_event_stream_reader("./bar", "http://bar.com");
282
283 attributed_inspect_two_streams_different_identities_by_reader!(
284 harness,
285 log_reader1 @ "foo",
286 log_reader2 @ "bar",
287 );
288 }
289
290 #[fuchsia::test]
291 async fn attributed_inspect_two_mixed_streams_different_identities() {
292 let mut harness = TestHarness::with_retained_sinks();
293 let log_reader1 = harness.create_event_stream_reader("./foo", "http://foo.com");
294 let log_reader2 = harness.create_default_reader(ComponentIdentity::new(
295 ExtendedMoniker::parse_str("./bar").unwrap(),
296 "http://bar.com",
297 ));
298
299 attributed_inspect_two_streams_different_identities_by_reader!(
300 harness,
301 log_reader1 @ "foo",
302 log_reader2 @ "bar",
303 );
304 }
305
306 #[fuchsia::test]
307 async fn test_filter_by_pid() {
308 let lm = LogMessage {
309 pid: 1,
310 tid: 2,
311 time: zx::BootInstant::get(),
312 dropped_logs: 3,
313 severity: Severity::Info as i32,
314 msg: String::from("BBBBB"),
315 tags: vec![String::from("AAAAA")],
316 };
317 let p = to_record(&lm);
318 let lm2 = LogMessage { pid: 11, ..lm.clone() };
319 let p2 = to_record(&lm2);
320 let options = LogFilterOptions {
321 filter_by_pid: true,
322 pid: 1,
323 filter_by_tid: false,
324 tid: 0,
325 min_severity: LogLevelFilter::None,
326 verbosity: 0,
327 tags: vec![],
328 };
329
330 let mut harness = TestHarness::default();
331 let mut stream = harness.create_structured_stream(Arc::new(ComponentIdentity::unknown()));
332 stream.write_packets(vec![p, p2]);
333 drop(stream);
334 harness.filter_test(vec![lm], Some(options)).await;
335 }
336
337 #[fuchsia::test]
338 async fn test_filter_by_tid() {
339 let lm = LogMessage {
340 pid: 1,
341 tid: 2,
342 time: zx::BootInstant::get(),
343 dropped_logs: 3,
344 severity: Severity::Info as i32,
345 msg: String::from("BBBBB"),
346 tags: vec![String::from("AAAAA")],
347 };
348 let p = to_record(&lm);
349 let lm2 = LogMessage { tid: 12, ..lm.clone() };
350 let p2 = to_record(&lm2);
351 let options = LogFilterOptions {
352 filter_by_pid: false,
353 pid: 1,
354 filter_by_tid: true,
355 tid: 2,
356 min_severity: LogLevelFilter::None,
357 verbosity: 0,
358 tags: vec![],
359 };
360
361 let mut harness = TestHarness::default();
362 let mut stream = harness.create_structured_stream(Arc::new(ComponentIdentity::unknown()));
363 stream.write_packets(vec![p, p2]);
364 drop(stream);
365 harness.filter_test(vec![lm], Some(options)).await;
366 }
367
368 #[fuchsia::test]
369 async fn test_filter_by_min_severity() {
370 let lm = LogMessage {
371 pid: 0,
372 tid: 0,
373 time: zx::BootInstant::get(),
374 dropped_logs: 2,
375 severity: Severity::Error as i32,
376 msg: String::from("BBBBB"),
377 tags: vec![String::from("AAAAA")],
378 };
379 let p = to_record(&LogMessage { severity: Severity::Warn as i32, ..lm.clone() });
380 let p2 = to_record(&lm);
381 let p3 = to_record(&LogMessage { severity: Severity::Info as i32, ..lm.clone() });
382 let p4 = to_record(&LogMessage { severity: 0x70, ..lm.clone() });
383 let p5 = to_record(&LogMessage { severity: Severity::Fatal as i32, ..lm.clone() });
384
385 let options = LogFilterOptions {
386 filter_by_pid: false,
387 pid: 1,
388 filter_by_tid: false,
389 tid: 1,
390 min_severity: LogLevelFilter::Error,
391 verbosity: 0,
392 tags: vec![],
393 };
394
395 let mut harness = TestHarness::default();
396 let mut stream = harness.create_structured_stream(Arc::new(ComponentIdentity::unknown()));
397 stream.write_packets(vec![p, p2, p3, p4, p5]);
398 drop(stream);
399 harness.filter_test(vec![lm], Some(options)).await;
400 }
401
402 #[fuchsia::test]
403 async fn test_filter_by_combination() {
404 let lm = LogMessage {
405 pid: 0,
406 tid: 0,
407 time: zx::BootInstant::get(),
408 dropped_logs: 2,
409 severity: Severity::Error as i32,
410 msg: String::from("BBBBB"),
411 tags: vec![String::from("AAAAA")],
412 };
413 let p = to_record(&LogMessage { severity: Severity::Warn as i32, ..lm.clone() });
414 let p2 = to_record(&lm);
415 let p3 = to_record(&LogMessage { pid: 1, ..lm.clone() });
416 let options = LogFilterOptions {
417 filter_by_pid: true,
418 pid: 0,
419 filter_by_tid: false,
420 tid: 1,
421 min_severity: LogLevelFilter::Error,
422 verbosity: 0,
423 tags: vec![],
424 };
425
426 let mut harness = TestHarness::default();
427 let mut stream = harness.create_structured_stream(Arc::new(ComponentIdentity::unknown()));
428 stream.write_packets(vec![p, p2, p3]);
429 drop(stream);
430 harness.filter_test(vec![lm], Some(options)).await;
431 }
432
433 #[fuchsia::test]
434 async fn test_filter_by_tags() {
435 let lm1 = LogMessage {
436 pid: 1,
437 tid: 1,
438 time: zx::BootInstant::get(),
439 dropped_logs: 2,
440 severity: Severity::Warn as i32,
441 msg: String::from("BBBBB"),
442 tags: vec![String::from("DDDDD")],
443 };
444 let p = to_record(&lm1);
445
446 let lm2 = LogMessage {
447 pid: 0,
448 tid: 0,
449 time: zx::BootInstant::get(),
450 dropped_logs: 2,
451 severity: Severity::Warn as i32,
452 msg: String::from("CCCCC"),
453 tags: vec![String::from("AAAAA"), String::from("BBBBB")],
454 };
455 let p2 = to_record(&lm2);
456
457 let options = LogFilterOptions {
458 filter_by_pid: false,
459 pid: 1,
460 filter_by_tid: false,
461 tid: 1,
462 min_severity: LogLevelFilter::None,
463 verbosity: 0,
464 tags: vec![String::from("BBBBB"), String::from("DDDDD")],
465 };
466
467 let mut harness = TestHarness::default();
468 let mut stream = harness.create_structured_stream(Arc::new(ComponentIdentity::unknown()));
469 stream.write_packets(vec![p, p2]);
470 drop(stream);
471 harness.filter_test(vec![lm1, lm2], Some(options)).await;
472 }
473
474 #[fuchsia::test]
475 async fn test_structured_log() {
476 let logs = vec![
477 Record {
478 timestamp: zx::BootInstant::from_nanos(6),
479 severity: Severity::Info as u8,
480 arguments: vec![Argument::message("hi")],
481 },
482 Record {
483 timestamp: zx::BootInstant::from_nanos(13),
484 severity: Severity::Error as u8,
485 arguments: vec![],
486 },
487 Record {
488 timestamp: zx::BootInstant::from_nanos(19),
489 severity: Severity::Warn as u8,
490 arguments: vec![
491 Argument::pid(zx::Koid::from_raw(0x1d1)),
492 Argument::tid(zx::Koid::from_raw(0x1d2)),
493 Argument::dropped(23),
494 Argument::tag("tag"),
495 Argument::message("message"),
496 ],
497 },
498 Record {
499 timestamp: zx::BootInstant::from_nanos(21),
500 severity: Severity::Warn as u8,
501 arguments: vec![Argument::tag("tag-1"), Argument::tag("tag-2")],
502 },
503 ];
504
505 let expected_logs = vec![
506 LogMessage {
507 pid: zx::sys::ZX_KOID_INVALID,
508 tid: zx::sys::ZX_KOID_INVALID,
509 time: zx::BootInstant::from_nanos(6),
510 severity: LogLevelFilter::Info as i32,
511 dropped_logs: 0,
512 msg: String::from("hi"),
513 tags: vec!["UNKNOWN".to_owned()],
514 },
515 LogMessage {
516 pid: zx::sys::ZX_KOID_INVALID,
517 tid: zx::sys::ZX_KOID_INVALID,
518 time: zx::BootInstant::from_nanos(14),
519 severity: LogLevelFilter::Error as i32,
520 dropped_logs: 0,
521 msg: String::from(""),
522 tags: vec!["UNKNOWN".to_owned()],
523 },
524 LogMessage {
525 pid: 0x1d1,
526 tid: 0x1d2,
527 time: zx::BootInstant::from_nanos(19),
528 severity: LogLevelFilter::Warn as i32,
529 dropped_logs: 23,
530 msg: String::from("message"),
531 tags: vec![String::from("tag")],
532 },
533 LogMessage {
534 pid: zx::sys::ZX_KOID_INVALID,
535 tid: zx::sys::ZX_KOID_INVALID,
536 time: zx::BootInstant::from_nanos(21),
537 severity: LogLevelFilter::Warn as i32,
538 dropped_logs: 0,
539 msg: String::from(""),
540 tags: vec![String::from("tag-1"), String::from("tag-2")],
541 },
542 ];
543 let mut harness = TestHarness::default();
544 let mut stream = harness.create_structured_stream(Arc::new(ComponentIdentity::unknown()));
545 stream.write_packets(logs);
546 drop(stream);
547 harness.filter_test(expected_logs, None).await;
548 }
549
550 #[fuchsia::test]
551 async fn test_debuglog_drainer() {
552 let log1 = TestDebugEntry::new("log1".as_bytes());
553 let log2 = TestDebugEntry::new("log2".as_bytes());
554 let log3 = TestDebugEntry::new("log3".as_bytes());
555
556 let klog_reader = TestDebugLog::default();
557 klog_reader.enqueue_read_entry(&log1);
558 klog_reader.enqueue_read_entry(&log2);
559 klog_reader.enqueue_read_fail(zx::Status::SHOULD_WAIT);
561 klog_reader.enqueue_read_entry(&log3);
562 klog_reader.enqueue_read_fail(zx::Status::SHOULD_WAIT);
563
564 let expected_logs = vec![
565 LogMessage {
566 pid: log1.record.pid.raw_koid(),
567 tid: log1.record.tid.raw_koid(),
568 time: log1.record.timestamp,
569 dropped_logs: 0,
570 severity: LogLevelFilter::Info as i32,
571 msg: String::from("log1"),
572 tags: vec![String::from("klog")],
573 },
574 LogMessage {
575 pid: log2.record.pid.raw_koid(),
576 tid: log2.record.tid.raw_koid(),
577 time: log2.record.timestamp,
578 dropped_logs: 0,
579 severity: LogLevelFilter::Info as i32,
580 msg: String::from("log2"),
581 tags: vec![String::from("klog")],
582 },
583 LogMessage {
584 pid: log3.record.pid.raw_koid(),
585 tid: log3.record.tid.raw_koid(),
586 time: log3.record.timestamp,
587 dropped_logs: 0,
588 severity: LogLevelFilter::Info as i32,
589 msg: String::from("log3"),
590 tags: vec![String::from("klog")],
591 },
592 ];
593
594 let scope = fasync::Scope::new();
595 let klog_stats_tree = debuglog_test(expected_logs, klog_reader, scope.new_child()).await;
596 assert_data_tree!(
597 klog_stats_tree,
598 root: contains {
599 log_sources: {
600 "klog": {
601 url: "fuchsia-boot://kernel",
602 last_timestamp: AnyProperty,
603 sockets_closed: 0u64,
604 sockets_opened: 0u64,
605 invalid: {
606 number: 0u64,
607 bytes: 0u64,
608 },
609 total: {
610 number: 3u64,
611 bytes: AnyProperty,
612 },
613 rolled_out: {
614 number: 0u64,
615 bytes: 0u64,
616 },
617 trace: {
618 number: 0u64,
619 bytes: 0u64,
620 },
621 debug: {
622 number: 0u64,
623 bytes: 0u64,
624 },
625 info: {
626 number: 3u64,
627 bytes: AnyProperty,
628 },
629 warn: {
630 number: 0u64,
631 bytes: 0u64,
632 },
633 error: {
634 number: 0u64,
635 bytes: 0u64,
636 },
637 fatal: {
638 number: 0u64,
639 bytes: 0u64,
640 },
641 },
642 }
643 }
644 );
645 }
646}