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