logforth_core/layout/plain_text.rs
1// Copyright 2024 FastLabs Developers
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::fmt::Write;
16use std::time::SystemTime;
17
18use crate::Diagnostic;
19use crate::Error;
20use crate::Layout;
21use crate::kv::Key;
22use crate::kv::Value;
23use crate::kv::Visitor;
24use crate::record::Record;
25
26/// A layout that formats log record as plain text.
27///
28/// Output format:
29///
30/// ```text
31/// 2024-08-11T22:44:57.172105+08:00 ERROR file: examples/file.rs:51 Hello error!
32/// 2024-08-11T22:44:57.172219+08:00 WARN file: examples/file.rs:52 Hello warn!
33/// 2024-08-11T22:44:57.172276+08:00 INFO file: examples/file.rs:53 Hello info!
34/// 2024-08-11T22:44:57.172329+08:00 DEBUG file: examples/file.rs:54 Hello debug!
35/// 2024-08-11T22:44:57.172382+08:00 TRACE file: examples/file.rs:55 Hello trace!
36/// ```
37///
38/// # Examples
39///
40/// ```
41/// use logforth_core::layout::PlainTextLayout;
42///
43/// let text_layout = PlainTextLayout::default();
44/// ```
45
46#[derive(Debug, Clone, Default)]
47#[non_exhaustive]
48pub struct PlainTextLayout {}
49
50struct KvWriter {
51 text: String,
52}
53
54impl Visitor for KvWriter {
55 fn visit(&mut self, key: Key, value: Value) -> Result<(), Error> {
56 // SAFETY: write to a string always succeeds
57 write!(&mut self.text, " {key}={value}").unwrap();
58 Ok(())
59 }
60}
61
62impl Layout for PlainTextLayout {
63 fn format(&self, record: &Record, diags: &[Box<dyn Diagnostic>]) -> Result<Vec<u8>, Error> {
64 let mut text = String::new();
65
66 let time = record.time();
67 match time.duration_since(SystemTime::UNIX_EPOCH) {
68 Ok(dur) => {
69 let time = dur.as_nanos();
70 write!(&mut text, "{time}").unwrap();
71 }
72 Err(err) => {
73 let time = err.duration().as_nanos();
74 write!(&mut text, "-{time}").unwrap();
75 }
76 }
77
78 let level = record.level().as_str();
79 let target = record.target();
80 let file = record.filename();
81 let line = record.line().unwrap_or_default();
82 let message = record.payload();
83 write!(&mut text, " {level:>5} {target}: {file}:{line} {message}").unwrap();
84
85 let mut visitor = KvWriter { text };
86 record.key_values().visit(&mut visitor)?;
87 for d in diags {
88 d.visit(&mut visitor)?;
89 }
90
91 Ok(visitor.text.into_bytes())
92 }
93}