1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
use chrono::{DateTime,UTC};
use std::collections;
use std::collections::btree_map::Entry;
use LogLevel;
use serde;
use serde_json;
use templates;
use std::fmt;
use std::convert::Into;

/// Currently just used as a marker; plan is to
/// adopt the same scheme as serde_json's Value: https://github.com/serde-rs/json/blob/master/json/src/value.rs
#[derive(Clone, PartialEq)]
pub enum Value {
    Json(String)
}

impl Value {
    // JSON serialization belongs elsewhere, but keeps
    // the distinction between regular string and JSON data clear.
    pub fn to_json<'a>(&'a self) -> &'a str {
        match self {
            &Value::Json(ref s) => &s
        }
    }
    
    /// Converts an arbitrary serializable object into the internal property format
    /// carried on events (currently JSON values...)
    pub fn capture<T: serde::ser::Serialize>(v: &T) -> Value {
        Value::Json(serde_json::to_string(v).unwrap())
    }
}

impl fmt::Debug for Value {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &Value::Json(ref s) => write!(f, "Value({})", s)
        }
    }
}

impl fmt::Display for Value {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &Value::Json(ref s) => {                    
                let bytes = s.as_bytes();            
                if bytes.len() > 1 && bytes[0] == b'"' {
                    write!(f, "{}", &s[1..s.len() - 1])
                } else {
                    write!(f, "{}", s)
                }
            }
        }
    }
}

impl Into<Value> for String {
    fn into(self) -> Value {
        Value::Json(self)
    }
}

impl<'a> Into<Value> for &'a str {
    fn into(self) -> Value {
        Value::Json(self.into())
    }
}

pub struct Event<'a> {
    timestamp: DateTime<UTC>,
    level: LogLevel,
    message_template: templates::MessageTemplate,
    properties: collections::BTreeMap<&'a str, Value>
}

impl<'a> Event<'a> {
    pub fn new(timestamp: DateTime<UTC>, level: LogLevel, message_template: templates::MessageTemplate, properties: collections::BTreeMap<&'a str, Value>) -> Event<'a> {
        Event {
            timestamp: timestamp,
            level: level,
            message_template: message_template,
            properties: properties
        }
    }
    
    pub fn new_now(level: LogLevel, message_template: templates::MessageTemplate, properties: collections::BTreeMap<&'a str, Value>) -> Event<'a> {
        Self::new(UTC::now(), level, message_template, properties)
    }
    
    pub fn timestamp(&self) -> DateTime<UTC> {
        self.timestamp
    }
    
    pub fn level(&self) -> LogLevel {
        self.level
    }
    
    pub fn message_template(&self) -> &templates::MessageTemplate {
        &self.message_template
    }
    
    pub fn properties(&self) -> &collections::BTreeMap<&'a str, Value> {
        &self.properties
    }
    
    pub fn add_or_update_property(&mut self, name: &'a str, value: Value) {
        match self.properties.entry(name) {
            Entry::Vacant(v) => {v.insert(value);},
            Entry::Occupied(mut o) => {o.insert(value);}
        }
    }
    
    pub fn add_property_if_absent(&mut self, name: &'a str, value: Value) {
        if !self.properties.contains_key(name) {
            self.properties.insert(name, value);
        }
    }
}