1mod chunked;
2mod level;
3mod message;
4mod udp;
5
6extern crate chrono;
7extern crate flate2;
8extern crate rand;
9extern crate serde;
10extern crate serde_json;
11extern crate slog;
12
13use flate2::{write::GzEncoder, Compression};
14use slog::{Drain, Key, OwnedKVList, Record, KV};
15use std::io;
16use std::io::prelude::*;
17
18use chunked::ChunkSize;
19use message::Message;
20use udp::UdpDestination;
21
22static VERSION: &'static str = "1.1";
23
24pub struct Gelf {
25 source: String,
26 destination: UdpDestination,
27}
28
29impl Gelf {
30 pub fn new(source: &str, destination: &str) -> Result<Self, io::Error> {
31 let destination = UdpDestination::new(destination, ChunkSize::LAN)?;
32
33 Ok(Gelf {
34 source: source.to_owned(),
35 destination,
36 })
37 }
38}
39
40pub struct KeyValueList(pub Vec<(Key, String)>);
41
42impl slog::Serializer for KeyValueList {
43 fn emit_arguments(&mut self, key: Key, val: &std::fmt::Arguments) -> slog::Result {
44 self.0.push((key, format!("{}", val)));
45 Ok(())
46 }
47}
48
49impl Drain for Gelf {
50 type Ok = ();
51 type Err = io::Error;
52
53 fn log(&self, record: &Record, values: &OwnedKVList) -> Result<Self::Ok, Self::Err> {
54 let mut additional = KeyValueList(Vec::with_capacity(16));
55 record.kv().serialize(record, &mut additional)?;
56 values.serialize(record, &mut additional)?;
57
58 let message = Message {
59 version: VERSION,
60 host: &self.source,
61 short_message: record.msg().to_string(),
62 full_message: None,
63 timestamp: Some(timestamp()),
64 level: Some(record.level().into()),
65 module: Some(record.location().module),
66 file: Some(record.location().file),
67 line: Some(record.location().line),
68 column: None,
69 additional: additional.0,
70 };
71
72 let serialized = serde_json::to_vec(&message)?;
73
74 let mut e = GzEncoder::new(Vec::new(), Compression::default());
75 e.write_all(&serialized)?;
76 let compressed = e.finish()?;
77 let _ = self.destination.log(compressed);
78
79 Ok(())
80 }
81}
82
83fn timestamp() -> f64 {
84 let now = chrono::Utc::now();
85 let milliseconds = (now.timestamp() as f64) + (now.timestamp_subsec_millis() as f64) / 1E3;
86 milliseconds
87}