Skip to content

Commit 9dbf400

Browse files
committed
Auto merge of #379 - pietroalbini:logs, r=pietroalbini
Refactor log handling This PR refactors log handling to use the `log` and `env_logger` crates instead of `slog`. They provide more compatibility with the rest of the ecosystem, and they're more flexible to work with. The refactor also prepends the logs generated during the prepare step to a crate's build log, so it's easier to investigate failures in that step.
2 parents 24e9ce0 + a0415eb commit 9dbf400

File tree

20 files changed

+372
-238
lines changed

20 files changed

+372
-238
lines changed

Cargo.lock

Lines changed: 44 additions & 62 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,6 @@ serde = "1.0"
4949
serde_derive = "1.0"
5050
serde_json = "1.0"
5151
serde_regex = "0.3.0"
52-
slog = "2.4.1"
53-
slog-scope = "4"
54-
slog-term = "2"
5552
structopt = "0.2"
5653
structopt-derive = "0.2"
5754
tar = "0.4.0"
@@ -64,6 +61,8 @@ url = "1.1"
6461
walkdir = "2"
6562
warp = "0.1.9"
6663
winapi = "0.3"
64+
log = "0.4.6"
65+
env_logger = "0.6.0"
6766

6867
[dev-dependencies]
6968
assert_cmd = "0.10.1"

src/agent/results.rs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
use crate::agent::api::AgentApi;
22
use crate::crates::{Crate, GitHubRepo};
33
use crate::experiments::Experiment;
4-
use crate::log;
4+
use crate::logs::{self, LogStorage};
55
use crate::prelude::*;
66
use crate::results::{TestResult, WriteResults};
77
use crate::toolchain::Toolchain;
8-
use std::io::Read;
8+
use log::LevelFilter;
99
use std::ops::DerefMut;
1010
use std::sync::{Arc, Mutex};
1111

@@ -48,22 +48,21 @@ impl<'a> WriteResults for ResultsUploader<'a> {
4848
_ex: &Experiment,
4949
toolchain: &Toolchain,
5050
krate: &Crate,
51+
existing_logs: Option<LogStorage>,
5152
f: F,
5253
) -> Fallible<TestResult>
5354
where
5455
F: FnOnce() -> Fallible<TestResult>,
5556
{
56-
let mut log_file = ::tempfile::NamedTempFile::new()?;
57-
let result = log::redirect(log_file.path(), f)?;
58-
59-
let mut buffer = Vec::new();
60-
log_file.read_to_end(&mut buffer)?;
57+
let storage = existing_logs.unwrap_or_else(|| LogStorage::new(LevelFilter::Info));
58+
let result = logs::capture(&storage, f)?;
59+
let output = storage.to_string();
6160

6261
let shas = ::std::mem::replace(self.shas.lock().unwrap().deref_mut(), Vec::new());
6362

6463
info!("sending results to the crater server...");
6564
self.api
66-
.record_progress(krate, toolchain, &buffer, result, &shas)?;
65+
.record_progress(krate, toolchain, output.as_bytes(), result, &shas)?;
6766

6867
Ok(result)
6968
}

src/db/mod.rs

Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use r2d2_sqlite::SqliteConnectionManager;
77
use rusqlite::types::ToSql;
88
use rusqlite::{Connection, Row, Transaction};
99
use std::sync::Arc;
10+
use std::time::Instant;
1011
use tempfile::NamedTempFile;
1112

1213
static LEGACY_DATABASE_PATHS: &[&str] = &["server.db"];
@@ -50,6 +51,7 @@ impl Database {
5051
}
5152

5253
let path = WORK_DIR.join(DATABASE_PATH);
54+
std::fs::create_dir_all(&*WORK_DIR)?;
5355
Database::new(SqliteConnectionManager::file(path), None)
5456
}
5557

@@ -118,16 +120,20 @@ pub trait QueryUtils {
118120

119121
fn exists(&self, sql: &str, params: &[&ToSql]) -> Fallible<bool> {
120122
self.with_conn(|conn| {
121-
let mut prepared = conn.prepare(sql)?;
122-
Ok(prepared.exists(params)?)
123+
self.trace(sql, || {
124+
let mut prepared = conn.prepare(sql)?;
125+
Ok(prepared.exists(params)?)
126+
})
123127
})
124128
}
125129

126130
fn execute(&self, sql: &str, params: &[&ToSql]) -> Fallible<usize> {
127131
self.with_conn(|conn| {
128-
let mut prepared = conn.prepare(sql)?;
129-
let changes = prepared.execute(params)?;
130-
Ok(changes)
132+
self.trace(sql, || {
133+
let mut prepared = conn.prepare(sql)?;
134+
let changes = prepared.execute(params)?;
135+
Ok(changes)
136+
})
131137
})
132138
}
133139

@@ -138,14 +144,16 @@ pub trait QueryUtils {
138144
func: F,
139145
) -> Fallible<Option<T>> {
140146
self.with_conn(|conn| {
141-
let mut prepared = conn.prepare(sql)?;
142-
let mut iter = prepared.query_map(params, func)?;
143-
144-
if let Some(item) = iter.next() {
145-
Ok(Some(item?))
146-
} else {
147-
Ok(None)
148-
}
147+
self.trace(sql, || {
148+
let mut prepared = conn.prepare(sql)?;
149+
let mut iter = prepared.query_map(params, func)?;
150+
151+
if let Some(item) = iter.next() {
152+
Ok(Some(item?))
153+
} else {
154+
Ok(None)
155+
}
156+
})
149157
})
150158
}
151159

@@ -156,17 +164,26 @@ pub trait QueryUtils {
156164
func: F,
157165
) -> Fallible<Vec<T>> {
158166
self.with_conn(|conn| {
159-
let mut prepared = conn.prepare(sql)?;
160-
let rows = prepared.query_map(params, func)?;
167+
self.trace(sql, || {
168+
let mut prepared = conn.prepare(sql)?;
169+
let rows = prepared.query_map(params, func)?;
161170

162-
let mut results = Vec::new();
163-
for row in rows {
164-
results.push(row?);
165-
}
171+
let mut results = Vec::new();
172+
for row in rows {
173+
results.push(row?);
174+
}
166175

167-
Ok(results)
176+
Ok(results)
177+
})
168178
})
169179
}
180+
181+
fn trace<T, F: FnOnce() -> T>(&self, sql: &str, f: F) -> T {
182+
let start = Instant::now();
183+
let res = f();
184+
trace!("sql query \"{}\" executed in {:?}", sql, start.elapsed());
185+
res
186+
}
170187
}
171188

172189
impl QueryUtils for Database {

src/lib.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,13 @@
11
#![recursion_limit = "256"]
22
#![allow(clippy::needless_pass_by_value, clippy::new_ret_no_self)]
33

4-
#[macro_use(slog_o, slog_info, slog_error, slog_warn)]
5-
extern crate slog;
6-
#[macro_use]
7-
extern crate slog_scope;
84
#[cfg_attr(test, macro_use)]
95
extern crate toml;
106

117
pub mod actions;
128
pub mod agent;
139
mod assets;
14-
#[macro_use]
15-
pub mod log;
10+
pub mod logs;
1611
#[macro_use]
1712
pub mod utils;
1813
pub mod config;

src/log.rs

Lines changed: 0 additions & 87 deletions
This file was deleted.

src/logs/mod.rs

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
mod storage;
2+
3+
use crate::prelude::*;
4+
use log::{Log, Metadata, Record};
5+
use std::cell::RefCell;
6+
use std::sync::Once;
7+
use std::thread::LocalKey;
8+
9+
pub use self::storage::LogStorage;
10+
11+
static INIT_LOGS: Once = Once::new();
12+
13+
thread_local! {
14+
static SCOPED: RefCell<Vec<Box<Log>>> = RefCell::new(Vec::new());
15+
}
16+
17+
struct MultiLogger {
18+
global: Vec<Box<Log>>,
19+
scoped: &'static LocalKey<RefCell<Vec<Box<Log>>>>,
20+
}
21+
22+
impl MultiLogger {
23+
fn new(global: Vec<Box<Log>>, scoped: &'static LocalKey<RefCell<Vec<Box<Log>>>>) -> Self {
24+
MultiLogger { global, scoped }
25+
}
26+
27+
fn each<F: FnMut(&Log)>(&self, mut f: F) {
28+
for logger in &self.global {
29+
f(logger.as_ref());
30+
}
31+
self.scoped.with(|scoped| {
32+
for logger in &*scoped.borrow() {
33+
f(logger.as_ref());
34+
}
35+
});
36+
}
37+
}
38+
39+
impl Log for MultiLogger {
40+
fn enabled(&self, metadata: &Metadata) -> bool {
41+
let mut result = false;
42+
self.each(|logger| {
43+
if logger.enabled(metadata) {
44+
result = true;
45+
}
46+
});
47+
result
48+
}
49+
50+
fn log(&self, record: &Record) {
51+
self.each(|logger| {
52+
logger.log(record);
53+
});
54+
}
55+
56+
fn flush(&self) {
57+
self.each(|logger| {
58+
logger.flush();
59+
});
60+
}
61+
}
62+
63+
pub fn capture<F, R, L>(storage: &L, f: F) -> Fallible<R>
64+
where
65+
F: FnOnce() -> Fallible<R>,
66+
L: Log + Clone + 'static,
67+
{
68+
let storage = Box::new(storage.clone());
69+
SCOPED.with(|scoped| scoped.borrow_mut().push(storage));
70+
let result = f();
71+
SCOPED.with(|scoped| {
72+
let _ = scoped.borrow_mut().pop();
73+
});
74+
result
75+
}
76+
77+
pub fn init() {
78+
INIT_LOGS.call_once(|| {
79+
// Initialize env_logger
80+
// This doesn't use from_default_env() because it doesn't allow to override filter_module()
81+
// with the RUST_LOG environment variable
82+
let mut env = env_logger::Builder::new();
83+
env.filter_module("crater", log::LevelFilter::Info);
84+
if let Ok(content) = std::env::var("RUST_LOG") {
85+
env.parse(&content);
86+
}
87+
88+
let multi = MultiLogger::new(vec![Box::new(env.build())], &SCOPED);
89+
log::set_boxed_logger(Box::new(multi)).unwrap();
90+
log::set_max_level(log::LevelFilter::Trace);
91+
});
92+
}
93+
94+
#[cfg(test)]
95+
pub(crate) fn init_test() {
96+
INIT_LOGS.call_once(|| {
97+
// Avoid setting up ENV_LOGGER inside tests
98+
let multi = MultiLogger::new(vec![], &SCOPED);
99+
log::set_boxed_logger(Box::new(multi)).unwrap();
100+
log::set_max_level(log::LevelFilter::Trace);
101+
})
102+
}

0 commit comments

Comments
 (0)