Skip to content

Commit 9241df1

Browse files
authored
Allow sending logs to stdout by using STDOUT_LOG env var (#334)
* Allow sending logs to stdout by using STDOUT_LOG env var * Increase stats buffer size
1 parent eb8cfdb commit 9241df1

File tree

3 files changed

+84
-2
lines changed

3 files changed

+84
-2
lines changed

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ pub mod config;
22
pub mod constants;
33
pub mod errors;
44
pub mod messages;
5+
pub mod multi_logger;
56
pub mod pool;
67
pub mod scram;
78
pub mod server;

src/main.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ mod config;
6666
mod constants;
6767
mod errors;
6868
mod messages;
69+
mod multi_logger;
6970
mod pool;
7071
mod prometheus;
7172
mod query_router;
@@ -81,7 +82,7 @@ use crate::prometheus::start_metric_server;
8182
use crate::stats::{Collector, Reporter, REPORTER};
8283

8384
fn main() -> Result<(), Box<dyn std::error::Error>> {
84-
env_logger::builder().format_timestamp_micros().init();
85+
multi_logger::MultiLogger::init().unwrap();
8586

8687
info!("Welcome to PgCat! Meow. (Version {})", VERSION);
8788

@@ -160,7 +161,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
160161
let client_server_map: ClientServerMap = Arc::new(Mutex::new(HashMap::new()));
161162

162163
// Statistics reporting.
163-
let (stats_tx, stats_rx) = mpsc::channel(100_000);
164+
let (stats_tx, stats_rx) = mpsc::channel(500_000);
164165
REPORTER.store(Arc::new(Reporter::new(stats_tx.clone())));
165166

166167
// Connection pool that allows to query all shards and replicas.

src/multi_logger.rs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
use log::{Level, Log, Metadata, Record, SetLoggerError};
2+
3+
// This is a special kind of logger that allows sending logs to different
4+
// targets depending on the log level.
5+
//
6+
// By default, if nothing is set, it acts as a regular env_log logger,
7+
// it sends everything to standard error.
8+
//
9+
// If the Env variable `STDOUT_LOG` is defined, it will be used for
10+
// configuring the standard out logger.
11+
//
12+
// The behavior is:
13+
// - If it is an error, the message is written to standard error.
14+
// - If it is not, and it matches the log level of the standard output logger (`STDOUT_LOG` env var), it will be send to standard output.
15+
// - If the above is not true, it is sent to the stderr logger that will log it or not depending on the value
16+
// of the RUST_LOG env var.
17+
//
18+
// So to summarize, if no `STDOUT_LOG` env var is present, the logger is the default logger. If `STDOUT_LOG` is set, everything
19+
// but errors, that matches the log level set in the `STDOUT_LOG` env var is sent to stdout. You can have also some esoteric configuration
20+
// where you set `RUST_LOG=debug` and `STDOUT_LOG=info`, in here, erros will go to stderr, warns and infos to stdout and debugs to stderr.
21+
//
22+
pub struct MultiLogger {
23+
stderr_logger: env_logger::Logger,
24+
stdout_logger: env_logger::Logger,
25+
}
26+
27+
impl MultiLogger {
28+
fn new() -> Self {
29+
let stderr_logger = env_logger::builder().format_timestamp_micros().build();
30+
let stdout_logger = env_logger::Builder::from_env("STDOUT_LOG")
31+
.format_timestamp_micros()
32+
.target(env_logger::Target::Stdout)
33+
.build();
34+
35+
Self {
36+
stderr_logger,
37+
stdout_logger,
38+
}
39+
}
40+
41+
pub fn init() -> Result<(), SetLoggerError> {
42+
let logger = Self::new();
43+
44+
log::set_max_level(logger.stderr_logger.filter());
45+
log::set_boxed_logger(Box::new(logger))
46+
}
47+
}
48+
49+
impl Log for MultiLogger {
50+
fn enabled(&self, metadata: &Metadata) -> bool {
51+
self.stderr_logger.enabled(metadata) && self.stdout_logger.enabled(metadata)
52+
}
53+
54+
fn log(&self, record: &Record) {
55+
if record.level() == Level::Error {
56+
self.stderr_logger.log(record);
57+
} else {
58+
if self.stdout_logger.matches(record) {
59+
self.stdout_logger.log(record);
60+
} else {
61+
self.stderr_logger.log(record);
62+
}
63+
}
64+
}
65+
66+
fn flush(&self) {
67+
self.stderr_logger.flush();
68+
self.stdout_logger.flush();
69+
}
70+
}
71+
72+
#[cfg(test)]
73+
mod test {
74+
use super::*;
75+
76+
#[test]
77+
fn test_init() {
78+
MultiLogger::init().unwrap();
79+
}
80+
}

0 commit comments

Comments
 (0)