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 diem_infallible::{duration_since_epoch, Mutex};
9use once_cell::sync::Lazy;
10use serde::{Deserialize, Serialize};
11use serde_json::{self, value as json};
12use std::{collections::VecDeque, convert::TryInto};
13
14#[derive(Serialize, Deserialize, Clone)]
15pub struct JsonLogEntry {
16    pub name: String,
17    pub timestamp: u64,
18    pub json: json::Value,
19}
20
21const MAX_EVENTS_IN_QUEUE: usize = 10_000;
22
23/// Writes event to event stream
24/// Example:
25///   event!("committed", block="b");
26// TODO: ideally we want to unify it with existing logger
27#[macro_export]
28macro_rules! event {
29    ($name:expr, $($json:tt)*) => {
30        $crate::json_log::send_json_log($crate::json_log::JsonLogEntry::new(
31            $name,
32            serde_json::json!({$($json)+}),
33        ));
34    };
35}
36
37// This queue maintains last MAX_EVENTS_IN_QUEUE events
38// This is very efficiently implemented with circular buffer with fixed capacity
39static JSON_LOG_ENTRY_QUEUE: Lazy<Mutex<VecDeque<JsonLogEntry>>> =
40    Lazy::new(|| Mutex::new(VecDeque::with_capacity(MAX_EVENTS_IN_QUEUE)));
41
42impl JsonLogEntry {
43    pub fn new(name: &'static str, json: json::Value) -> Self {
44        let timestamp = duration_since_epoch()
45            .as_millis()
46            .try_into()
47            .expect("Unable to convert u128 into u64");
48        JsonLogEntry {
49            name: name.into(),
50            timestamp,
51            json,
52        }
53    }
54}
55
56/// Sends event to event stream.
57///
58/// Note that this method acquires global lock for brief moment.
59/// This means that very hot threads can not use this method concurrently,
60/// otherwise they will contend for same lock.
61// TODO: if we use events more often we should rewrite it to be non-blocking
62pub fn send_json_log(entry: JsonLogEntry) {
63    let mut queue = JSON_LOG_ENTRY_QUEUE.lock();
64    if queue.len() >= MAX_EVENTS_IN_QUEUE {
65        queue.pop_front();
66    }
67    queue.push_back(entry);
68}
69
70/// Get up to MAX_EVENTS_IN_QUEUE last events and clears the queue
71pub fn pop_last_entries() -> Vec<JsonLogEntry> {
72    let mut queue = JSON_LOG_ENTRY_QUEUE.lock();
73    queue.drain(..).collect()
74}