use serde_json::Value;
use std::fmt::Write;
use crate::logging::constants::{RESET, TRACE_ID_COLOR};
use crate::logging::LogMessage;
use super::constants::{ATTRIBUTE_KEY_COLOR, ATTRIBUTE_VALUE_COLOR, SPAN_ID_COLOR};
pub fn prepare_stdout_message(log: &LogMessage<'_>) -> Result<String, Box<dyn std::error::Error>> {
let mut output = String::new();
writeln!(
output,
"[{}] │ {} │ {}.{} ({})",
log.timestamp.format("%Y-%m-%dT%H:%M:%S%.3fZ"),
format!("{}{:<5}{}", log.level.color(), log.level.as_str(), RESET),
log.app_name,
log.service_name,
log.replica_id
)?;
writeln!(output, "│ Target │ {}", log.target)?;
writeln!(output, "│ Message │ {}", log.message)?;
if !log.attributes.is_empty() {
writeln!(output, "│ Attributes │")?;
for (key, value) in &log.attributes {
writeln!(
output,
"│ │ {}{}{}: {}{}{}",
ATTRIBUTE_KEY_COLOR,
key,
RESET,
ATTRIBUTE_VALUE_COLOR,
format_value(value),
RESET
)?;
}
}
if let Some(ref ctx) = log.context {
writeln!(
output,
"│ Trace ID │ {}{}{}",
TRACE_ID_COLOR, ctx.trace_id, RESET
)?;
writeln!(
output,
"│ Span ID │ {}{}{}",
SPAN_ID_COLOR, ctx.span_id, RESET
)?;
}
writeln!(
output,
"└─────────────────────────────────────────────────────────────────────────────"
)?;
Ok(output)
}
fn format_value(value: &Value) -> String {
match value {
Value::String(s) => format!("\"{}\"", s),
Value::Number(n) => n.to_string(),
Value::Bool(b) => b.to_string(),
Value::Null => "null".to_string(),
Value::Array(arr) => {
let items: Vec<String> = arr.iter().map(format_value).collect();
format!("[{}]", items.join(", "))
}
Value::Object(obj) => {
let items: Vec<String> = obj
.iter()
.map(|(k, v)| format!("\"{}\": {}", k, format_value(v)))
.collect();
format!("{{{}}}", items.join(", "))
}
}
}