blob: 16b6a0cb2422c8e1f7f2e3a72e80c17eabb73bc1 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
pub mod buffer;
pub mod debuglog;
pub mod error;
pub mod interest;
pub mod listener;
pub mod message;
pub mod redact;
pub mod socket;
pub mod stats;
#[cfg(test)]
pub mod testing;
pub use debuglog::{convert_debuglog_to_log_message, KernelDebugLog};
pub use message::Message;
use crate::logs::error::{EventError, LogsError};
use fidl::endpoints::{ServerEnd, ServiceMarker};
use fidl_fuchsia_logger::{LogSinkMarker, LogSinkRequestStream};
use fidl_fuchsia_sys2 as fsys;
use fidl_fuchsia_sys_internal::SourceIdentity;
use std::sync::Arc;
/// Extract the SourceIdentity from a components v2 event.
pub fn source_identity_from_event(event: &fsys::Event) -> Result<Arc<SourceIdentity>, LogsError> {
let target_moniker = event
.header
.as_ref()
.and_then(|header| header.moniker.clone())
.ok_or(EventError::MissingField("moniker"))?;
let component_url = event
.header
.as_ref()
.and_then(|header| header.component_url.clone())
.ok_or(EventError::MissingField("component_url"))?;
let mut source = SourceIdentity::EMPTY;
source.component_url = Some(component_url.clone());
source.component_name = Some(target_moniker.clone());
Ok(Arc::new(source))
}
/// Extract the LogSinkRequestStream from a CapabilityRequested v2 event.
pub fn log_sink_request_stream_from_event(
event: fsys::Event,
) -> Result<LogSinkRequestStream, LogsError> {
let payload =
event.event_result.ok_or(EventError::MissingField("event_result")).and_then(|result| {
match result {
fsys::EventResult::Payload(fsys::EventPayload::CapabilityRequested(payload)) => {
Ok(payload)
}
fsys::EventResult::Error(fsys::EventError {
description: Some(description),
..
}) => Err(EventError::ReceivedError { description }),
_ => Err(EventError::InvalidEventType),
}
})?;
let capability_name = payload.name.ok_or(EventError::MissingField("name"))?;
if &capability_name != LogSinkMarker::NAME {
Err(EventError::IncorrectName {
received: capability_name,
expected: LogSinkMarker::NAME,
})?;
}
let capability = payload.capability.ok_or(EventError::MissingField("capability"))?;
let server_end = ServerEnd::<LogSinkMarker>::new(capability)
.into_stream()
.map_err(|source| EventError::InvalidServerEnd { source })?;
Ok(server_end)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::logs::{message::LegacySeverity, testing::*};
use diagnostics_data::{DROPPED_LABEL, MESSAGE_LABEL, PID_LABEL, TAG_LABEL, TID_LABEL};
use diagnostics_stream::{Argument, Record, Severity as StreamSeverity, Value};
use fidl_fuchsia_logger::{LogFilterOptions, LogLevelFilter, LogMessage, LogSinkMarker};
use fidl_fuchsia_sys_internal::SourceIdentity;
use fuchsia_async as fasync;
use fuchsia_inspect::assert_inspect_tree;
use fuchsia_zircon as zx;
use matches::assert_matches;
use std::sync::Arc;
#[fasync::run_singlethreaded(test)]
async fn test_log_manager_simple() {
TestHarness::new().manager_test(false).await;
}
#[fasync::run_singlethreaded(test)]
async fn test_log_manager_dump() {
TestHarness::new().manager_test(true).await;
}
#[fasync::run_singlethreaded(test)]
async fn unfiltered_stats() {
let first_packet = setup_default_packet();
let first_message = LogMessage {
pid: first_packet.metadata.pid,
tid: first_packet.metadata.tid,
time: first_packet.metadata.time,
dropped_logs: first_packet.metadata.dropped_logs,
severity: first_packet.metadata.severity,
msg: String::from("BBBBB"),
tags: vec![String::from("AAAAA")],
};
let (mut second_packet, mut second_message) = (first_packet.clone(), first_message.clone());
second_packet.metadata.pid = 0;
second_message.pid = second_packet.metadata.pid;
let (mut third_packet, mut third_message) = (second_packet.clone(), second_message.clone());
third_packet.metadata.severity = LogLevelFilter::Info.into_primitive().into();
third_message.severity = third_packet.metadata.severity;
let (fourth_packet, fourth_message) = (third_packet.clone(), third_message.clone());
let (mut fifth_packet, mut fifth_message) = (fourth_packet.clone(), fourth_message.clone());
fifth_packet.metadata.severity = LogLevelFilter::Error.into_primitive().into();
fifth_message.severity = fifth_packet.metadata.severity;
let mut harness = TestHarness::new();
let mut stream = harness.create_stream(Arc::new(SourceIdentity::EMPTY));
stream.write_packets(vec![
first_packet,
second_packet,
third_packet,
fourth_packet,
fifth_packet,
]);
drop(stream);
let log_stats_tree = harness
.filter_test(
vec![first_message, second_message, third_message, fourth_message, fifth_message],
None,
)
.await;
assert_inspect_tree!(
log_stats_tree,
root: {
log_stats: {
total_logs: 5u64,
kernel_logs: 0u64,
logsink_logs: 5u64,
trace_logs: 0u64,
debug_logs: 0u64,
info_logs: 2u64,
warning_logs: 2u64,
error_logs: 1u64,
fatal_logs: 0u64,
closed_streams: 0u64,
unattributed_log_sinks: 1u64,
by_component: { "(unattributed)": contains {
total_logs: 5u64,
trace_logs: 0u64,
debug_logs: 0u64,
info_logs: 2u64,
warning_logs: 2u64,
error_logs: 1u64,
fatal_logs: 0u64,
} },
buffer_stats: {
rolled_out_entries: 0u64,
},
},
}
);
}
async fn attributed_inspect_two_streams_different_identities_by_reader(
mut harness: TestHarness,
log_reader1: Arc<dyn LogReader>,
log_reader2: Arc<dyn LogReader>,
) {
let mut packet = setup_default_packet();
let message = LogMessage {
pid: packet.metadata.pid,
tid: packet.metadata.tid,
time: packet.metadata.time,
dropped_logs: packet.metadata.dropped_logs,
severity: packet.metadata.severity,
msg: String::from("BBBBB"),
tags: vec![String::from("AAAAA")],
};
let mut packet2 = packet.clone();
packet2.metadata.severity = LogLevelFilter::Error.into_primitive().into();
let mut message2 = message.clone();
message2.severity = packet2.metadata.severity;
let mut foo_stream = harness.create_stream_from_log_reader(log_reader1.clone());
foo_stream.write_packet(&mut packet);
let mut bar_stream = harness.create_stream_from_log_reader(log_reader2.clone());
bar_stream.write_packet(&mut packet2);
drop((foo_stream, bar_stream));
let log_stats_tree = harness.filter_test(vec![message, message2], None).await;
assert_inspect_tree!(
log_stats_tree,
root: {
log_stats: {
total_logs: 2u64,
kernel_logs: 0u64,
logsink_logs: 2u64,
trace_logs: 0u64,
debug_logs: 0u64,
info_logs: 0u64,
warning_logs: 1u64,
error_logs: 1u64,
fatal_logs: 0u64,
closed_streams: 0u64,
unattributed_log_sinks: 0u64,
by_component: {
"http://foo.com": contains {
total_logs: 1u64,
trace_logs: 0u64,
debug_logs: 0u64,
info_logs: 0u64,
warning_logs: 1u64,
error_logs: 0u64,
fatal_logs: 0u64,
},
"http://bar.com": contains {
total_logs: 1u64,
trace_logs: 0u64,
debug_logs: 0u64,
info_logs: 0u64,
warning_logs: 0u64,
error_logs: 1u64,
fatal_logs: 0u64,
}
},
buffer_stats: {
rolled_out_entries: 0u64,
}
},
}
);
}
#[fasync::run_singlethreaded(test)]
async fn attributed_inspect_two_streams_different_identities() {
let harness = TestHarness::with_retained_sinks();
let log_reader1 = harness.create_default_reader(SourceIdentity {
component_name: Some("foo".into()),
component_url: Some("http://foo.com".into()),
instance_id: None,
realm_path: None,
..SourceIdentity::EMPTY
});
let log_reader2 = harness.create_default_reader(SourceIdentity {
component_name: Some("bar".into()),
component_url: Some("http://bar.com".into()),
instance_id: None,
realm_path: None,
..SourceIdentity::EMPTY
});
attributed_inspect_two_streams_different_identities_by_reader(
harness,
log_reader1,
log_reader2,
)
.await;
}
#[fasync::run_singlethreaded(test)]
async fn attributed_inspect_two_v2_streams_different_identities() {
let harness = TestHarness::with_retained_sinks();
let log_reader1 = harness.create_event_stream_reader("foo", "http://foo.com");
let log_reader2 = harness.create_event_stream_reader("bar", "http://bar.com");
attributed_inspect_two_streams_different_identities_by_reader(
harness,
log_reader1,
log_reader2,
)
.await;
}
#[fasync::run_singlethreaded(test)]
async fn attributed_inspect_two_mixed_streams_different_identities() {
let harness = TestHarness::with_retained_sinks();
let log_reader1 = harness.create_event_stream_reader("foo", "http://foo.com");
let log_reader2 = harness.create_default_reader(SourceIdentity {
component_name: Some("bar".into()),
component_url: Some("http://bar.com".into()),
instance_id: None,
realm_path: None,
..SourceIdentity::EMPTY
});
attributed_inspect_two_streams_different_identities_by_reader(
harness,
log_reader1,
log_reader2,
)
.await;
}
#[fasync::run_singlethreaded(test)]
async fn source_identity_from_v2_event() {
let target_moniker = "foo".to_string();
let target_url = "http://foo.com".to_string();
let (_log_sink_proxy, log_sink_server_end) =
fidl::endpoints::create_proxy::<LogSinkMarker>().unwrap();
let event = create_capability_requested_event(
target_moniker.clone(),
target_url.clone(),
log_sink_server_end.into_channel(),
);
let identity = source_identity_from_event(&event).unwrap();
assert_matches!(&identity.component_name,
Some(component_name) if *component_name == target_moniker);
assert_matches!(&identity.component_url,
Some(component_url) if *component_url == target_url);
}
#[fasync::run_singlethreaded(test)]
async fn log_sink_request_stream_from_v2_event() {
let target_moniker = "foo".to_string();
let target_url = "http://foo.com".to_string();
let (_log_sink_proxy, log_sink_server_end) =
fidl::endpoints::create_proxy::<LogSinkMarker>().unwrap();
let event = create_capability_requested_event(
target_moniker.clone(),
target_url.clone(),
log_sink_server_end.into_channel(),
);
log_sink_request_stream_from_event(event).unwrap();
}
async fn attributed_inspect_two_streams_same_identity_by_reader(
mut harness: TestHarness,
log_reader1: Arc<dyn LogReader>,
log_reader2: Arc<dyn LogReader>,
) {
let mut packet = setup_default_packet();
let message = LogMessage {
pid: packet.metadata.pid,
tid: packet.metadata.tid,
time: packet.metadata.time,
dropped_logs: packet.metadata.dropped_logs,
severity: packet.metadata.severity,
msg: String::from("BBBBB"),
tags: vec![String::from("AAAAA")],
};
let mut packet2 = packet.clone();
packet2.metadata.severity = LogLevelFilter::Error.into_primitive().into();
let mut message2 = message.clone();
message2.severity = packet2.metadata.severity;
let mut foo_stream = harness.create_stream_from_log_reader(log_reader1.clone());
foo_stream.write_packet(&mut packet);
let mut bar_stream = harness.create_stream_from_log_reader(log_reader2.clone());
bar_stream.write_packet(&mut packet2);
drop((foo_stream, bar_stream));
let log_stats_tree = harness.filter_test(vec![message, message2], None).await;
assert_inspect_tree!(
log_stats_tree,
root: {
log_stats: {
total_logs: 2u64,
kernel_logs: 0u64,
logsink_logs: 2u64,
trace_logs: 0u64,
debug_logs: 0u64,
info_logs: 0u64,
warning_logs: 1u64,
error_logs: 1u64,
fatal_logs: 0u64,
closed_streams: 0u64,
unattributed_log_sinks: 0u64,
by_component: {
"http://foo.com": contains {
total_logs: 2u64,
trace_logs: 0u64,
debug_logs: 0u64,
info_logs: 0u64,
warning_logs: 1u64,
error_logs: 1u64,
fatal_logs: 0u64,
},
},
buffer_stats: {
rolled_out_entries: 0u64,
},
},
}
);
}
#[fasync::run_singlethreaded(test)]
async fn attributed_inspect_two_streams_same_identity() {
let harness = TestHarness::with_retained_sinks();
let log_reader = harness.create_default_reader(SourceIdentity {
component_name: Some("foo".into()),
component_url: Some("http://foo.com".into()),
instance_id: None,
realm_path: None,
..SourceIdentity::EMPTY
});
attributed_inspect_two_streams_same_identity_by_reader(
harness,
log_reader.clone(),
log_reader.clone(),
)
.await;
}
#[fasync::run_singlethreaded(test)]
async fn attributed_inspect_two_v2_streams_same_identity() {
let harness = TestHarness::with_retained_sinks();
let log_reader = harness.create_event_stream_reader("foo", "http://foo.com");
attributed_inspect_two_streams_same_identity_by_reader(
harness,
log_reader.clone(),
log_reader.clone(),
)
.await;
}
#[fasync::run_singlethreaded(test)]
async fn attributed_inspect_two_mixed_streams_same_identity() {
let harness = TestHarness::with_retained_sinks();
let log_reader1 = harness.create_event_stream_reader("foo", "http://foo.com");
let log_reader2 = harness.create_default_reader(SourceIdentity {
component_name: Some("foo".into()),
component_url: Some("http://foo.com".into()),
instance_id: None,
realm_path: None,
..SourceIdentity::EMPTY
});
attributed_inspect_two_streams_same_identity_by_reader(harness, log_reader1, log_reader2)
.await;
}
#[fasync::run_singlethreaded(test)]
async fn test_filter_by_pid() {
let p = setup_default_packet();
let mut p2 = p.clone();
p2.metadata.pid = 0;
let lm = LogMessage {
pid: p.metadata.pid,
tid: p.metadata.tid,
time: p.metadata.time,
dropped_logs: p.metadata.dropped_logs,
severity: p.metadata.severity,
msg: String::from("BBBBB"),
tags: vec![String::from("AAAAA")],
};
let options = LogFilterOptions {
filter_by_pid: true,
pid: 1,
filter_by_tid: false,
tid: 0,
min_severity: LogLevelFilter::None,
verbosity: 0,
tags: vec![],
};
let mut harness = TestHarness::new();
let mut stream = harness.create_stream(Arc::new(SourceIdentity::EMPTY));
stream.write_packets(vec![p, p2]);
drop(stream);
harness.filter_test(vec![lm], Some(options)).await;
}
#[fasync::run_singlethreaded(test)]
async fn test_filter_by_tid() {
let mut p = setup_default_packet();
p.metadata.pid = 0;
let mut p2 = p.clone();
p2.metadata.tid = 0;
let lm = LogMessage {
pid: p.metadata.pid,
tid: p.metadata.tid,
time: p.metadata.time,
dropped_logs: p.metadata.dropped_logs,
severity: p.metadata.severity,
msg: String::from("BBBBB"),
tags: vec![String::from("AAAAA")],
};
let options = LogFilterOptions {
filter_by_pid: false,
pid: 1,
filter_by_tid: true,
tid: 1,
min_severity: LogLevelFilter::None,
verbosity: 0,
tags: vec![],
};
let mut harness = TestHarness::new();
let mut stream = harness.create_stream(Arc::new(SourceIdentity::EMPTY));
stream.write_packets(vec![p, p2]);
drop(stream);
harness.filter_test(vec![lm], Some(options)).await;
}
#[fasync::run_singlethreaded(test)]
async fn test_filter_by_min_severity() {
let p = setup_default_packet();
let mut p2 = p.clone();
p2.metadata.pid = 0;
p2.metadata.tid = 0;
p2.metadata.severity = LogLevelFilter::Error.into_primitive().into();
let mut p3 = p.clone();
p3.metadata.severity = LogLevelFilter::Info.into_primitive().into();
let mut p4 = p.clone();
p4.metadata.severity = 0x70; // custom
let mut p5 = p.clone();
p5.metadata.severity = LogLevelFilter::Fatal.into_primitive().into();
let lm = LogMessage {
pid: p2.metadata.pid,
tid: p2.metadata.tid,
time: p2.metadata.time,
dropped_logs: p2.metadata.dropped_logs,
severity: p2.metadata.severity,
msg: String::from("BBBBB"),
tags: vec![String::from("AAAAA")],
};
let options = LogFilterOptions {
filter_by_pid: false,
pid: 1,
filter_by_tid: false,
tid: 1,
min_severity: LogLevelFilter::Error,
verbosity: 0,
tags: vec![],
};
let mut harness = TestHarness::new();
let mut stream = harness.create_stream(Arc::new(SourceIdentity::EMPTY));
stream.write_packets(vec![p, p2, p3, p4, p5]);
drop(stream);
harness.filter_test(vec![lm], Some(options)).await;
}
#[fasync::run_singlethreaded(test)]
async fn test_filter_by_combination() {
let mut p = setup_default_packet();
p.metadata.pid = 0;
p.metadata.tid = 0;
let mut p2 = p.clone();
p2.metadata.severity = LogLevelFilter::Error.into_primitive().into();
let mut p3 = p.clone();
p3.metadata.pid = 1;
let lm = LogMessage {
pid: p2.metadata.pid,
tid: p2.metadata.tid,
time: p2.metadata.time,
dropped_logs: p2.metadata.dropped_logs,
severity: p2.metadata.severity,
msg: String::from("BBBBB"),
tags: vec![String::from("AAAAA")],
};
let options = LogFilterOptions {
filter_by_pid: true,
pid: 0,
filter_by_tid: false,
tid: 1,
min_severity: LogLevelFilter::Error,
verbosity: 0,
tags: vec![],
};
let mut harness = TestHarness::new();
let mut stream = harness.create_stream(Arc::new(SourceIdentity::EMPTY));
stream.write_packets(vec![p, p2, p3]);
drop(stream);
harness.filter_test(vec![lm], Some(options)).await;
}
#[fasync::run_singlethreaded(test)]
async fn test_filter_by_tags() {
let mut p = setup_default_packet();
let mut p2 = p.clone();
// p tags - "DDDDD"
p.fill_data(1..6, 68);
p2.metadata.pid = 0;
p2.metadata.tid = 0;
p2.data[6] = 5;
// p2 tag - "AAAAA", "BBBBB"
// p2 msg - "CCCCC"
p2.fill_data(13..18, 67);
let lm1 = LogMessage {
pid: p.metadata.pid,
tid: p.metadata.tid,
time: p.metadata.time,
dropped_logs: p.metadata.dropped_logs,
severity: p.metadata.severity,
msg: String::from("BBBBB"),
tags: vec![String::from("DDDDD")],
};
let lm2 = LogMessage {
pid: p2.metadata.pid,
tid: p2.metadata.tid,
time: p2.metadata.time,
dropped_logs: p2.metadata.dropped_logs,
severity: p2.metadata.severity,
msg: String::from("CCCCC"),
tags: vec![String::from("AAAAA"), String::from("BBBBB")],
};
let options = LogFilterOptions {
filter_by_pid: false,
pid: 1,
filter_by_tid: false,
tid: 1,
min_severity: LogLevelFilter::None,
verbosity: 0,
tags: vec![String::from("BBBBB"), String::from("DDDDD")],
};
let mut harness = TestHarness::new();
let mut stream = harness.create_stream(Arc::new(SourceIdentity::EMPTY));
stream.write_packets(vec![p, p2]);
drop(stream);
harness.filter_test(vec![lm1, lm2], Some(options)).await;
}
#[fasync::run_singlethreaded(test)]
async fn test_structured_log() {
let logs = vec![
Record {
timestamp: 6,
severity: StreamSeverity::Info,
arguments: vec![Argument {
name: MESSAGE_LABEL.into(),
value: Value::Text("hi".to_string()),
}],
},
Record { timestamp: 14, severity: StreamSeverity::Error, arguments: vec![] },
Record {
timestamp: 19,
severity: StreamSeverity::Warn,
arguments: vec![
Argument { name: PID_LABEL.into(), value: Value::UnsignedInt(0x1d1) },
Argument { name: TID_LABEL.into(), value: Value::UnsignedInt(0x1d2) },
Argument { name: DROPPED_LABEL.into(), value: Value::UnsignedInt(23) },
Argument { name: TAG_LABEL.into(), value: Value::Text(String::from("tag")) },
Argument {
name: MESSAGE_LABEL.into(),
value: Value::Text(String::from("message")),
},
],
},
Record {
timestamp: 21,
severity: StreamSeverity::Warn,
arguments: vec![
Argument { name: TAG_LABEL.into(), value: Value::Text(String::from("tag-1")) },
Argument { name: TAG_LABEL.into(), value: Value::Text(String::from("tag-2")) },
],
},
];
let expected_logs = vec![
LogMessage {
pid: zx::sys::ZX_KOID_INVALID,
tid: zx::sys::ZX_KOID_INVALID,
time: 6,
severity: LegacySeverity::Info.for_listener(),
dropped_logs: 0,
msg: String::from("hi"),
tags: vec!["UNKNOWN".to_owned()],
},
LogMessage {
pid: zx::sys::ZX_KOID_INVALID,
tid: zx::sys::ZX_KOID_INVALID,
time: 14,
severity: LegacySeverity::Error.for_listener(),
dropped_logs: 0,
msg: String::from(""),
tags: vec!["UNKNOWN".to_owned()],
},
LogMessage {
pid: 0x1d1,
tid: 0x1d2,
time: 19,
severity: LegacySeverity::Warn.for_listener(),
dropped_logs: 23,
msg: String::from("message"),
tags: vec![String::from("tag")],
},
LogMessage {
pid: zx::sys::ZX_KOID_INVALID,
tid: zx::sys::ZX_KOID_INVALID,
time: 21,
severity: LegacySeverity::Warn.for_listener(),
dropped_logs: 0,
msg: String::from(""),
tags: vec![String::from("tag-1"), String::from("tag-2")],
},
];
let mut harness = TestHarness::new();
let mut stream = harness.create_structured_stream(Arc::new(SourceIdentity::EMPTY));
stream.write_packets(logs);
drop(stream);
harness.filter_test(expected_logs, None).await;
}
#[fasync::run_singlethreaded(test)]
async fn test_debuglog_drainer() {
let log1 = TestDebugEntry::new("log1".as_bytes());
let log2 = TestDebugEntry::new("log2".as_bytes());
let log3 = TestDebugEntry::new("log3".as_bytes());
let klog_reader = TestDebugLog::new();
klog_reader.enqueue_read_entry(&log1);
klog_reader.enqueue_read_entry(&log2);
// logs recieved after kernel indicates no logs should be read
klog_reader.enqueue_read_fail(zx::Status::SHOULD_WAIT);
klog_reader.enqueue_read_entry(&log3);
klog_reader.enqueue_read_fail(zx::Status::SHOULD_WAIT);
let expected_logs = vec![
LogMessage {
pid: log1.pid,
tid: log1.tid,
time: log1.timestamp,
dropped_logs: 0,
severity: fidl_fuchsia_logger::LogLevelFilter::Info as i32,
msg: String::from("log1"),
tags: vec![String::from("klog")],
},
LogMessage {
pid: log2.pid,
tid: log2.tid,
time: log2.timestamp,
dropped_logs: 0,
severity: fidl_fuchsia_logger::LogLevelFilter::Info as i32,
msg: String::from("log2"),
tags: vec![String::from("klog")],
},
LogMessage {
pid: log3.pid,
tid: log3.tid,
time: log3.timestamp,
dropped_logs: 0,
severity: fidl_fuchsia_logger::LogLevelFilter::Info as i32,
msg: String::from("log3"),
tags: vec![String::from("klog")],
},
];
let klog_stats_tree = debuglog_test(expected_logs, klog_reader).await;
assert_inspect_tree!(
klog_stats_tree,
root: {
log_stats: contains {
total_logs: 3u64,
kernel_logs: 3u64,
logsink_logs: 0u64,
trace_logs: 0u64,
debug_logs: 0u64,
info_logs: 3u64,
warning_logs: 0u64,
error_logs: 0u64,
fatal_logs: 0u64,
closed_streams: 0u64,
unattributed_log_sinks: 0u64,
by_component: {
"fuchsia-boot://klog": contains {
total_logs: 3u64,
trace_logs: 0u64,
debug_logs: 0u64,
info_logs: 3u64,
warning_logs: 0u64,
error_logs: 0u64,
fatal_logs: 0u64,
},
}
}
}
);
}
}