Files
RedBear-OS/local/patches/base/P55-logd-json-format.patch
T
vasilito 5715f86dc6 Add P55: JSON structured log format option to logd
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.
2026-05-21 00:48:13 +03:00

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(&timestamp),
+ 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();
}