rust_observer 0.2.2

Express telemetry rust SDK
Documentation
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(", "))
        }
    }
}