use crate::{
trace::{SpanContext, SpanId, TraceContextExt, TraceFlags, TraceId},
Array, Key, OrderMap, StringValue, Value,
};
use std::{borrow::Cow, time::SystemTime};
#[derive(Debug, Clone, Default)]
#[non_exhaustive]
pub struct LogRecord {
pub timestamp: Option<SystemTime>,
pub observed_timestamp: Option<SystemTime>,
pub trace_context: Option<TraceContext>,
pub severity_text: Option<Cow<'static, str>>,
pub severity_number: Option<Severity>,
pub body: Option<AnyValue>,
pub attributes: Option<Vec<(Key, AnyValue)>>,
}
impl LogRecord {
pub fn builder() -> LogRecordBuilder {
LogRecordBuilder::new()
}
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct TraceContext {
pub trace_id: TraceId,
pub span_id: SpanId,
pub trace_flags: Option<TraceFlags>,
}
impl From<&SpanContext> for TraceContext {
fn from(span_context: &SpanContext) -> Self {
TraceContext {
trace_id: span_context.trace_id(),
span_id: span_context.span_id(),
trace_flags: Some(span_context.trace_flags()),
}
}
}
#[derive(Debug, Clone)]
pub enum AnyValue {
Int(i64),
Double(f64),
String(StringValue),
Boolean(bool),
Bytes(Vec<u8>),
ListAny(Vec<AnyValue>),
Map(OrderMap<Key, AnyValue>),
}
macro_rules! impl_trivial_from {
($t:ty, $variant:path) => {
impl From<$t> for AnyValue {
fn from(val: $t) -> AnyValue {
$variant(val.into())
}
}
};
}
impl_trivial_from!(i8, AnyValue::Int);
impl_trivial_from!(i16, AnyValue::Int);
impl_trivial_from!(i32, AnyValue::Int);
impl_trivial_from!(i64, AnyValue::Int);
impl_trivial_from!(u8, AnyValue::Int);
impl_trivial_from!(u16, AnyValue::Int);
impl_trivial_from!(u32, AnyValue::Int);
impl_trivial_from!(f64, AnyValue::Double);
impl_trivial_from!(f32, AnyValue::Double);
impl_trivial_from!(String, AnyValue::String);
impl_trivial_from!(Cow<'static, str>, AnyValue::String);
impl_trivial_from!(&'static str, AnyValue::String);
impl_trivial_from!(StringValue, AnyValue::String);
impl_trivial_from!(bool, AnyValue::Boolean);
impl<T: Into<AnyValue>> FromIterator<T> for AnyValue {
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
AnyValue::ListAny(iter.into_iter().map(Into::into).collect())
}
}
impl<K: Into<Key>, V: Into<AnyValue>> FromIterator<(K, V)> for AnyValue {
fn from_iter<I: IntoIterator<Item = (K, V)>>(iter: I) -> Self {
AnyValue::Map(OrderMap::from_iter(
iter.into_iter().map(|(k, v)| (k.into(), v.into())),
))
}
}
impl From<Value> for AnyValue {
fn from(value: Value) -> Self {
match value {
Value::Bool(b) => b.into(),
Value::I64(i) => i.into(),
Value::F64(f) => f.into(),
Value::String(s) => s.into(),
Value::Array(a) => match a {
Array::Bool(b) => AnyValue::from_iter(b),
Array::F64(f) => AnyValue::from_iter(f),
Array::I64(i) => AnyValue::from_iter(i),
Array::String(s) => AnyValue::from_iter(s),
},
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd)]
pub enum Severity {
Trace = 1,
Trace2 = 2,
Trace3 = 3,
Trace4 = 4,
Debug = 5,
Debug2 = 6,
Debug3 = 7,
Debug4 = 8,
Info = 9,
Info2 = 10,
Info3 = 11,
Info4 = 12,
Warn = 13,
Warn2 = 14,
Warn3 = 15,
Warn4 = 16,
Error = 17,
Error2 = 18,
Error3 = 19,
Error4 = 20,
Fatal = 21,
Fatal2 = 22,
Fatal3 = 23,
Fatal4 = 24,
}
impl Severity {
pub const fn name(&self) -> &'static str {
match &self {
Severity::Trace => "TRACE",
Severity::Trace2 => "TRACE2",
Severity::Trace3 => "TRACE3",
Severity::Trace4 => "TRACE4",
Severity::Debug => "DEBUG",
Severity::Debug2 => "DEBUG2",
Severity::Debug3 => "DEBUG3",
Severity::Debug4 => "DEBUG4",
Severity::Info => "INFO",
Severity::Info2 => "INFO2",
Severity::Info3 => "INFO3",
Severity::Info4 => "INFO4",
Severity::Warn => "WARN",
Severity::Warn2 => "WARN2",
Severity::Warn3 => "WARN3",
Severity::Warn4 => "WARN4",
Severity::Error => "ERROR",
Severity::Error2 => "ERROR2",
Severity::Error3 => "ERROR3",
Severity::Error4 => "ERROR4",
Severity::Fatal => "FATAL",
Severity::Fatal2 => "FATAL2",
Severity::Fatal3 => "FATAL3",
Severity::Fatal4 => "FATAL4",
}
}
}
#[derive(Debug, Clone)]
pub struct LogRecordBuilder {
record: LogRecord,
}
impl LogRecordBuilder {
pub fn new() -> Self {
Self {
record: Default::default(),
}
}
pub fn with_timestamp(self, timestamp: SystemTime) -> Self {
Self {
record: LogRecord {
timestamp: Some(timestamp),
..self.record
},
}
}
pub fn with_observed_timestamp(self, timestamp: SystemTime) -> Self {
Self {
record: LogRecord {
observed_timestamp: Some(timestamp),
..self.record
},
}
}
pub fn with_span_context(self, span_context: &SpanContext) -> Self {
Self {
record: LogRecord {
trace_context: Some(TraceContext {
span_id: span_context.span_id(),
trace_id: span_context.trace_id(),
trace_flags: Some(span_context.trace_flags()),
}),
..self.record
},
}
}
pub fn with_context<T>(self, context: &T) -> Self
where
T: TraceContextExt,
{
if context.has_active_span() {
self.with_span_context(context.span().span_context())
} else {
self
}
}
pub fn with_severity_text<T>(self, severity: T) -> Self
where
T: Into<Cow<'static, str>>,
{
Self {
record: LogRecord {
severity_text: Some(severity.into()),
..self.record
},
}
}
pub fn with_severity_number(self, severity: Severity) -> Self {
Self {
record: LogRecord {
severity_number: Some(severity),
..self.record
},
}
}
pub fn with_body(self, body: AnyValue) -> Self {
Self {
record: LogRecord {
body: Some(body),
..self.record
},
}
}
pub fn with_attributes(self, attributes: Vec<(Key, AnyValue)>) -> Self {
Self {
record: LogRecord {
attributes: Some(attributes),
..self.record
},
}
}
pub fn with_attribute<K, V>(mut self, key: K, value: V) -> Self
where
K: Into<Key>,
V: Into<AnyValue>,
{
if let Some(ref mut vec) = self.record.attributes {
vec.push((key.into(), value.into()));
} else {
let vec = vec![(key.into(), value.into())];
self.record.attributes = Some(vec);
}
self
}
pub fn build(self) -> LogRecord {
self.record
}
}
impl Default for LogRecordBuilder {
fn default() -> Self {
Self::new()
}
}