blackbox: buffer logs before initialization

Summary:
This allows us to preserve logs before the blackbox initialization.

In the edenscm usecase, we might want to log a "process start" event before
knowing the location of the blackbox.

Reviewed By: xavierd

Differential Revision: D16044038

fbshipit-source-id: 7f41994989bb3a83e9ded3014e5afeae00f3350c
This commit is contained in:
Jun Wu 2019-07-18 15:04:30 -07:00 committed by Facebook Github Bot
parent 17ee0df47c
commit 56cf4cef7d
2 changed files with 50 additions and 6 deletions

View File

@ -21,7 +21,8 @@ use std::time::SystemTime;
/// Local, rotated log consists of events tagged with "Invocation ID" and
/// timestamps.
pub struct Blackbox {
log: RotateLog,
// Used by singleton.
pub(crate) log: RotateLog,
opts: BlackboxOptions,
// An ID that can be "grouped by" to figure everything about a session.
@ -380,7 +381,7 @@ fn new_session_id() -> u64 {
}
#[cfg(test)]
mod tests {
pub(crate) mod tests {
use super::*;
use serde_derive::{Deserialize, Serialize};
use std::collections::HashSet;
@ -391,7 +392,7 @@ mod tests {
use tempfile::tempdir;
#[derive(Serialize, Deserialize, Debug, PartialEq)]
enum Event {
pub(crate) enum Event {
A(u64),
B(String),
}

View File

@ -8,8 +8,9 @@
//! Useful for cases where it's inconvenient to pass [`Blackbox`] around.
use crate::{Blackbox, BlackboxOptions};
use indexedlog::rotate::RotateLowLevelExt;
use lazy_static::lazy_static;
use std::ops::DerefMut;
use std::ops::{Deref, DerefMut};
use std::sync::Mutex;
lazy_static! {
@ -18,14 +19,28 @@ lazy_static! {
}
/// Replace the global [`Blackbox`] instance.
pub fn init(blackbox: Blackbox) {
///
/// If [`log`] was called, their side effects will be re-applied to the
/// specified blackbox.
pub fn init(mut blackbox: Blackbox) {
let mut singleton = SINGLETON.lock().unwrap();
// Insert dirty entries to the new blackbox.
let old_blackbox = singleton.deref();
for log in old_blackbox.log.logs().iter() {
for entry in log.iter_dirty() {
if let Ok(entry) = entry {
let _ = blackbox.log.append(entry);
}
}
}
*singleton.deref_mut() = blackbox;
}
/// Log to the global [`Blackbox`] instance.
///
/// Do nothing if [`init`] was not called.
/// If [`init`] was not called, log requests will be buffered in memory.
pub fn log(data: &impl serde::Serialize) {
if let Ok(mut singleton) = SINGLETON.lock() {
let blackbox = singleton.deref_mut();
@ -40,3 +55,31 @@ pub fn sync() {
blackbox.sync();
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::blackbox::{tests::Event, IndexFilter};
use tempfile::tempdir;
#[test]
fn test_buffered_writes() {
let events = vec![Event::A(42), Event::B("foo".to_string())];
for e in &events {
log(e);
}
let dir = tempdir().unwrap();
let blackbox = BlackboxOptions::new().open(&dir).unwrap();
init(blackbox);
for e in &events[1..2] {
log(e);
}
let mut singleton = SINGLETON.lock().unwrap();
let blackbox = singleton.deref_mut();
assert_eq!(blackbox.filter::<Event>(IndexFilter::Nop, None).len(), 3);
}
}