5715f86dc6
When LOGD_JSON=1 is set in the environment, logd formats all log lines as JSON objects with timestamp, source, and message fields. Also fixes indentation issues in P51 logd rotation patch.
116 lines
4.0 KiB
Diff
116 lines
4.0 KiB
Diff
--- a/logd/src/scheme.rs
|
|
+++ b/logd/src/scheme.rs
|
|
@@ -5,6 +5,7 @@
|
|
use std::os::fd::{FromRawFd, RawFd};
|
|
use std::path::PathBuf;
|
|
use std::sync::mpsc::{self, Sender};
|
|
+use std::time::{SystemTime, UNIX_EPOCH};
|
|
|
|
use redox_scheme::scheme::SchemeSync;
|
|
use redox_scheme::{CallerCtx, OpenResult, SendFdRequest, Socket};
|
|
@@ -38,6 +39,50 @@
|
|
AddSink(usize),
|
|
}
|
|
|
|
+fn json_escape(s: &str) -> String {
|
|
+ let mut out = String::with_capacity(s.len());
|
|
+ for c in s.chars() {
|
|
+ match c {
|
|
+ '\\' => out.push_str("\\\\"),
|
|
+ '"' => out.push_str("\\\""),
|
|
+ '\n' => out.push_str("\\n"),
|
|
+ '\r' => out.push_str("\\r"),
|
|
+ '\t' => out.push_str("\\t"),
|
|
+ c if c.is_control() => out.push_str(&format!("\\u{:04x}", c as u32)),
|
|
+ c => out.push(c),
|
|
+ }
|
|
+ }
|
|
+ out
|
|
+}
|
|
+
|
|
+fn format_json_line(context: &str, line: &[u8]) -> Vec<u8> {
|
|
+ let now = SystemTime::now()
|
|
+ .duration_since(UNIX_EPOCH)
|
|
+ .unwrap_or_default();
|
|
+ let secs = now.as_secs();
|
|
+ let timestamp = format!(
|
|
+ "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}Z",
|
|
+ 1970 + secs / 31_557_600,
|
|
+ (secs % 31_557_600) / 2_592_000 + 1,
|
|
+ (secs % 2_592_000) / 86_400 + 1,
|
|
+ (secs % 86_400) / 3600,
|
|
+ (secs % 3600) / 60,
|
|
+ secs % 60
|
|
+ );
|
|
+ let text = String::from_utf8_lossy(line).trim_end_matches('\n').to_string();
|
|
+ let (source, message) = match text.split_once(": ") {
|
|
+ Some((s, m)) => (s, m),
|
|
+ None => (context, text.as_str()),
|
|
+ };
|
|
+ let json = format!(
|
|
+ "{{\"timestamp\":\"{}\",\"source\":\"{}\",\"message\":\"{}\"}}\n",
|
|
+ json_escape(×tamp),
|
|
+ json_escape(source),
|
|
+ json_escape(message)
|
|
+ );
|
|
+ json.into_bytes()
|
|
+}
|
|
+
|
|
struct LogFile {
|
|
file: File,
|
|
path: PathBuf,
|
|
@@ -110,6 +155,8 @@
|
|
let _ = std::fs::create_dir_all(LOG_DIR);
|
|
|
|
|
|
+ let json_format = std::env::var("LOGD_JSON").map_or(false, |v| v == "1");
|
|
+
|
|
let (output_tx, output_rx) = mpsc::channel::<OutputCmd>();
|
|
|
|
std::thread::spawn(move || {
|
|
@@ -123,9 +170,15 @@
|
|
for cmd in output_rx {
|
|
match cmd {
|
|
OutputCmd::Log { context, line } => {
|
|
+ let out_line = if json_format {
|
|
+ format_json_line(&context, &line)
|
|
+ } else {
|
|
+ line.clone()
|
|
+ };
|
|
if let Some(ref mut f) = persistent {
|
|
- let _ = f.write(&line);
|
|
+ let _ = f.write(&out_line);
|
|
let _ = f.flush();
|
|
+ }
|
|
|
|
let service_name = context.split(':').next().unwrap_or("unknown");
|
|
if !service_name.is_empty() {
|
|
@@ -135,7 +188,7 @@
|
|
LogFile::open(PathBuf::from("/dev/null")).unwrap()
|
|
})
|
|
});
|
|
- let _ = entry.write(&line);
|
|
+ let _ = entry.write(&out_line);
|
|
let _ = entry.maybe_rotate();
|
|
}
|
|
|
|
@@ -145,15 +198,14 @@
|
|
LogFile::open(PathBuf::from("/dev/null")).unwrap()
|
|
})
|
|
});
|
|
- let _ = system_entry.write(&line);
|
|
+ let _ = system_entry.write(&out_line);
|
|
let _ = system_entry.maybe_rotate();
|
|
|
|
- }
|
|
for file in &mut files {
|
|
- let _ = file.write(&line);
|
|
+ let _ = file.write(&out_line);
|
|
let _ = file.flush();
|
|
}
|
|
- logs.push_back(line);
|
|
+ logs.push_back(out_line);
|
|
while logs.len() > MEMORY_LOG_LIMIT {
|
|
logs.pop_front();
|
|
}
|