use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub(crate) struct LogEvent {
#[serde(rename = "timestamp")]
pub timestamp: DateTime<Utc>,
pub level: String,
pub message: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub target: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub location: Option<LogLocation>,
#[serde(skip_serializing_if = "HashMap::is_empty")]
pub fields: HashMap<String, LogValue>,
#[serde(skip_serializing_if = "Option::is_none")]
pub span: Option<SpanInfo>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub(crate) struct LogLocation {
pub file: Option<String>,
pub line: Option<u32>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub(crate) struct SpanInfo {
pub name: String,
pub fields: HashMap<String, LogValue>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub(crate) enum LogValue {
String(String),
Number(i64),
Float(f64),
Bool(bool),
Array(Vec<LogValue>),
Object(HashMap<String, LogValue>),
}
impl From<String> for LogValue {
fn from(s: String) -> Self {
LogValue::String(s)
}
}
impl From<&str> for LogValue {
fn from(s: &str) -> Self {
LogValue::String(s.to_string())
}
}
impl From<i64> for LogValue {
fn from(n: i64) -> Self {
LogValue::Number(n)
}
}
impl From<f64> for LogValue {
fn from(f: f64) -> Self {
LogValue::Float(f)
}
}
impl From<bool> for LogValue {
fn from(b: bool) -> Self {
LogValue::Bool(b)
}
}
impl LogEvent {
pub fn new(level: impl Into<String>, message: impl Into<String>) -> Self {
Self {
timestamp: Utc::now(),
level: level.into(),
message: message.into(),
target: None,
location: None,
fields: HashMap::new(),
span: None,
}
}
pub fn with_target(mut self, target: impl Into<String>) -> Self {
self.target = Some(target.into());
self
}
pub fn with_location(mut self, file: Option<String>, line: Option<u32>) -> Self {
if file.is_some() || line.is_some() {
self.location = Some(LogLocation { file, line });
}
self
}
pub fn add_field(&mut self, key: impl Into<String>, value: impl Into<LogValue>) {
self.fields.insert(key.into(), value.into());
}
pub fn with_span(mut self, name: String, fields: HashMap<String, LogValue>) -> Self {
self.span = Some(SpanInfo { name, fields });
self
}
}