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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
// Copyright 2021 Twitter, Inc.
// Licensed under the Apache License, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0

//! This crate provides an asynchronous logging backend that can direct logs to
//! one or more outputs.
//!
//! The core of this crate is the `RingLog` type, which is constructed using a
//! builder that is specific to your logging needs. After building the
//! `RingLog`, it can be registered as the global logger using the `start`
//! method. You will be left with a `Box<dyn Drain>` which should be
//! periodically flushed outside of any critical path. For example, in an admin
//! thread or dedicated logging thread.
//!
//! For logging to a single file, the `LogBuilder` type can be used to construct
//! an `RingLog` which has low overhead, but directs log messages to a single
//! `Output`.
//!
//! A `SamplingLogBuilder` can be used to construct an `RingLog` which will
//! filter the log messages using sampling before directing the log messages to
//! a single `Output`.
//!
//! A `MultiLogBuilder` can be used to construct an `RingLog` which routes log
//! messages based on the `target` metadata of the log `Record`. If there is an
//! `RingLog` registered for that specific `target`, then the log message will
//! be routed to that instance of `RingLog`. Log messages that do not match any
//! specific target will be routed to the default `RingLog` that has been added
//! to the `MultiLogBuilder`. If there is no default, messages that do not match
//! any specific target will be simply dropped.
//!
//! This combination of logging types allows us to compose a logging backend
//! which meets the application's needs. For example, you can use a local log
//! macro to set the target to some specific category and log those messages to
//! a file, while letting all other log messages pass to standard out. This
//! could allow splitting command/access/audit logs from the normal logging.

pub use log::*;

mod format;
mod multi;
mod nop;
mod outputs;
mod sampling;
mod single;
mod traits;

pub use format::*;
pub use multi::*;
pub use nop::*;
pub use outputs::*;
pub use sampling::*;
pub use single::*;
pub use traits::*;

use clocksource::datetime::DateTime;
use mpmc::Queue;

pub(crate) type LogBuffer = Vec<u8>;

use metriken::{metric, Counter, Gauge};

#[metric(name = "log_create", description = "logging targets initialized")]
pub static LOG_CREATE: Counter = Counter::new();

#[metric(
    name = "log_create_ex",
    description = "number of exceptions while initializing logging targets"
)]
pub static LOG_CREATE_EX: Counter = Counter::new();

#[metric(name = "log_destroy", description = "logging targets destroyed")]
pub static LOG_DESTROY: Counter = Counter::new();

#[metric(name = "log_curr", description = "current number of logging targets")]
pub static LOG_CURR: Gauge = Gauge::new();

#[metric(
    name = "log_open",
    description = "number of logging destinations which have been opened"
)]
pub static LOG_OPEN: Counter = Counter::new();

#[metric(
    name = "log_open_ex",
    description = "number of exceptions while opening logging destinations"
)]
pub static LOG_OPEN_EX: Counter = Counter::new();

#[metric(
    name = "log_write",
    description = "number of writes to all logging destinations"
)]
pub static LOG_WRITE: Counter = Counter::new();

#[metric(
    name = "log_write_byte",
    description = "number of bytes written to all logging destinations"
)]
pub static LOG_WRITE_BYTE: Counter = Counter::new();

#[metric(
    name = "log_write_ex",
    description = "number of exceptions while writing to logging destinations"
)]
pub static LOG_WRITE_EX: Counter = Counter::new();

#[metric(
    name = "log_skip",
    description = "number of log messages skipped due to sampling policy"
)]
pub static LOG_SKIP: Counter = Counter::new();

#[metric(
    name = "log_drop",
    description = "number of log messages dropped due to full queues"
)]
pub static LOG_DROP: Counter = Counter::new();

#[metric(
    name = "log_drop_byte",
    description = "number of bytes dropped due to full queues"
)]
pub static LOG_DROP_BYTE: Counter = Counter::new();

#[metric(
    name = "log_flush",
    description = "number of times logging destinations have been flushed"
)]
pub static LOG_FLUSH: Counter = Counter::new();

#[metric(
    name = "log_flush_ex",
    description = "number of times logging destinations have been flushed"
)]
pub static LOG_FLUSH_EX: Counter = Counter::new();

/// A type which implements an asynchronous logging backend.
pub struct RingLog {
    pub(crate) logger: Box<dyn Log>,
    pub(crate) drain: Box<dyn Drain>,
    pub(crate) level_filter: LevelFilter,
}

impl RingLog {
    /// Register the logger and return a type which implements `Drain`. It is
    /// up to the user to periodically call flush on the resulting drain.
    pub fn start(self) -> Box<dyn Drain> {
        let level_filter = self.level_filter;
        log::set_boxed_logger(self.logger)
            .map(|()| log::set_max_level(level_filter))
            .expect("failed to start logger");
        self.drain
    }
}

#[macro_export]
macro_rules! fatal {
    () => (
        error!();
        std::process::exit(1);
        );
    ($fmt:expr) => (
        error!($fmt);
        std::process::exit(1);
        );
    ($fmt:expr, $($arg:tt)*) => (
        error!($fmt, $($arg)*);
        std::process::exit(1);
        );
}