diem_logger/
json_log.rs

1// Copyright (c) The Diem Core Contributors
2// SPDX-License-Identifier: Apache-2.0
3
4// Copyright 2021 Conflux Foundation. All rights reserved.
5// Conflux is free software and distributed under GNU General Public License.
6// See http://www.gnu.org/licenses/
7
8use once_cell::sync::Lazy;
9use parking_lot::Mutex;
10use serde::{Deserialize, Serialize};
11use serde_json::{self, value as json};
12use std::{
13    collections::VecDeque,
14    convert::TryInto,
15    time::{SystemTime, UNIX_EPOCH},
16};
17
18#[derive(Serialize, Deserialize, Clone)]
19pub struct JsonLogEntry {
20    pub name: String,
21    pub timestamp: u64,
22    pub json: json::Value,
23}
24
25const MAX_EVENTS_IN_QUEUE: usize = 10_000;
26
27/// Writes event to event stream
28/// Example:
29///   event!("committed", block="b");
30// TODO: ideally we want to unify it with existing logger
31#[macro_export]
32macro_rules! event {
33    ($name:expr, $($json:tt)*) => {
34        $crate::json_log::send_json_log($crate::json_log::JsonLogEntry::new(
35            $name,
36            serde_json::json!({$($json)+}),
37        ));
38    };
39}
40
41// This queue maintains last MAX_EVENTS_IN_QUEUE events
42// This is very efficiently implemented with circular buffer with fixed capacity
43static JSON_LOG_ENTRY_QUEUE: Lazy<Mutex<VecDeque<JsonLogEntry>>> =
44    Lazy::new(|| Mutex::new(VecDeque::with_capacity(MAX_EVENTS_IN_QUEUE)));
45
46impl JsonLogEntry {
47    pub fn new(name: &'static str, json: json::Value) -> Self {
48        let timestamp = SystemTime::now()
49            .duration_since(UNIX_EPOCH)
50            .expect("System time is before UNIX_EPOCH")
51            .as_millis()
52            .try_into()
53            .expect("Unable to convert u128 into u64");
54        JsonLogEntry {
55            name: name.into(),
56            timestamp,
57            json,
58        }
59    }
60}
61
62/// Sends event to event stream.
63///
64/// Note that this method acquires global lock for brief moment.
65/// This means that very hot threads can not use this method concurrently,
66/// otherwise they will contend for same lock.
67// TODO: if we use events more often we should rewrite it to be non-blocking
68pub fn send_json_log(entry: JsonLogEntry) {
69    let mut queue = JSON_LOG_ENTRY_QUEUE.lock();
70    if queue.len() >= MAX_EVENTS_IN_QUEUE {
71        queue.pop_front();
72    }
73    queue.push_back(entry);
74}
75
76/// Get up to MAX_EVENTS_IN_QUEUE last events and clears the queue
77pub fn pop_last_entries() -> Vec<JsonLogEntry> {
78    let mut queue = JSON_LOG_ENTRY_QUEUE.lock();
79    queue.drain(..).collect()
80}