1pub mod format;
12mod json_logger;
13mod stdout_logger;
14
15use std::fmt;
16
17use log::{Level, LevelFilter, Log, Metadata, Record as LogRecord};
18use serde::{Deserialize, Serialize};
19
20use self::{
21 format::{Formatter, HostFormatter},
22 json_logger::JsonLogger,
23 stdout_logger::StdoutLogger,
24};
25use crate::Frame;
26
27const DEFMT_TARGET_MARKER: &str = "defmt@";
28
29pub fn log_defmt(
31 frame: &Frame<'_>,
32 file: Option<&str>,
33 line: Option<u32>,
34 module_path: Option<&str>,
35) {
36 let (timestamp, level) = timestamp_and_level_from_frame(frame);
37
38 let target = format!(
39 "{}{}",
40 DEFMT_TARGET_MARKER,
41 serde_json::to_value(Payload { timestamp, level }).unwrap()
42 );
43
44 log::logger().log(
45 &LogRecord::builder()
46 .args(format_args!("{}", frame.display_message()))
47 .target(&target)
49 .module_path(module_path)
50 .file(file)
51 .line(line)
52 .build(),
53 );
54}
55
56pub fn is_defmt_frame(metadata: &Metadata) -> bool {
58 metadata.target().starts_with(DEFMT_TARGET_MARKER)
59}
60
61struct DefmtRecord<'a> {
63 log_record: &'a LogRecord<'a>,
64 payload: Payload,
65}
66
67#[derive(Clone, Copy, Debug)]
68pub enum DefmtLoggerType {
69 Stdout,
70 Json,
71}
72
73pub struct DefmtLoggerConfig {
74 pub formatter: Formatter,
75 pub host_formatter: HostFormatter,
76 pub logger_type: DefmtLoggerType,
77}
78
79#[derive(Deserialize, Serialize)]
80struct Payload {
81 level: Option<Level>,
82 timestamp: String,
83}
84
85impl<'a> DefmtRecord<'a> {
86 pub fn new(log_record: &'a LogRecord<'a>) -> Option<Self> {
88 let target = log_record.metadata().target();
89 target
90 .strip_prefix(DEFMT_TARGET_MARKER)
91 .map(|payload| Self {
92 log_record,
93 payload: serde_json::from_str(payload).expect("malformed 'payload'"),
94 })
95 }
96
97 pub fn timestamp(&self) -> &str {
99 self.payload.timestamp.as_str()
100 }
101
102 pub fn level(&self) -> Option<Level> {
103 self.payload.level
104 }
105
106 pub fn args(&self) -> &fmt::Arguments<'a> {
107 self.log_record.args()
108 }
109
110 pub fn module_path(&self) -> Option<&'a str> {
111 self.log_record.module_path()
112 }
113
114 pub fn file(&self) -> Option<&'a str> {
115 self.log_record.file()
116 }
117
118 pub fn line(&self) -> Option<u32> {
119 self.log_record.line()
120 }
121}
122
123pub fn init_logger(
130 formatter: Formatter,
131 host_formatter: HostFormatter,
132 logger_type: DefmtLoggerType,
133 should_log: impl Fn(&Metadata) -> bool + Sync + Send + 'static,
134) {
135 let logger: Box<dyn Log> = match logger_type {
136 DefmtLoggerType::Stdout => StdoutLogger::new(formatter, host_formatter, should_log),
137 DefmtLoggerType::Json => {
138 JsonLogger::print_schema_version();
139 JsonLogger::new(formatter, host_formatter, should_log)
140 }
141 };
142 alterable_logger::set_boxed_logger(logger);
143 alterable_logger::set_max_level(LevelFilter::Trace);
144}
145
146fn timestamp_and_level_from_frame(frame: &Frame<'_>) -> (String, Option<Level>) {
147 let timestamp = frame
148 .display_timestamp()
149 .map(|ts| ts.to_string())
150 .unwrap_or_default();
151 let level = frame.level().map(|level| match level {
152 crate::Level::Trace => Level::Trace,
153 crate::Level::Debug => Level::Debug,
154 crate::Level::Info => Level::Info,
155 crate::Level::Warn => Level::Warn,
156 crate::Level::Error => Level::Error,
157 });
158 (timestamp, level)
159}