-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Summary: Add a small Scuba wrapper module which logs to the supertd_events table. If the environment variable `SUPERTD_SCUBA_LOGFILE` is set, the samples are logged to that file instead of Scuba. Includes a convenience macro `scuba!`. Instead of writing: ``` td_utils::supertd_events::sample_builder() .add("event", "COMMAND_SUCCESS") .add("count", foos_run + bars_launched) .add("data", serde_json::to_string(&serde_json::json!({ "foos_run": foos_run, "bars_launched": bars_launched, })).unwrap()) .log(); ``` the macro allows us to write: ``` td_util::scuba!( event: "COMMAND_SUCCESS", count: foos_run + bars_launched, data: json!({ "foos_run": foos_run, "bars_launched": bars_launched, }) ); ``` The `scuba!` macro brings the `serde_json::json!` macro into scope for the `data` column, and passes the provided value through `serde_json::to_string(...)`. This only kicks in for the `data` column, which is intended to be event-specific properties (so that we don't bloat up the table with columns that are only populated by one event). This data can be used in Scuba queries or derived columns using `JSON_EXTRACT`. Each sample automatically includes [several useful server and build data fields](https://www.internalfb.com/code/fbsource/[ec6f25826e22a953d23af7d59165e8002ff2ab55]/fbcode/common/rust/scuba/src/builder.rs?lines=188%2C191%2C194%2C197%2C202%2C206%2C210%2C214%2C218%2C222%2C231%2C238-239) via [`ScubaSampleBuilder::add_common_server_data`](https://www.internalfb.com/code/fbsource/[ec6f25826e22a953d23af7d59165e8002ff2ab55]/fbcode/common/rust/scuba/src/builder.rs?lines=245) and several Sandcastle variables (see `add_sandcastle_columns` in this diff). See {D53788472} for more examples of how this might be used. Reviewed By: Acesine Differential Revision: D53689274 fbshipit-source-id: 2de6fec92d7e834c13e1ea7995a44e9900022ebd
- Loading branch information
1 parent
f5bac14
commit ed847af
Showing
9 changed files
with
184 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
/* | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
* This source code is licensed under both the MIT license found in the | ||
* LICENSE-MIT file in the root directory of this source tree and the Apache | ||
* License, Version 2.0 found in the LICENSE-APACHE file in the root directory | ||
* of this source tree. | ||
*/ | ||
|
||
//! Simple interface for logging to the `supertd_events` dataset. | ||
|
||
use std::sync::OnceLock; | ||
|
||
use scuba::ScubaSampleBuilder; | ||
pub use serde_json; | ||
|
||
const SCUBA_DATASET: &str = "supertd_events"; | ||
|
||
static BUILDER: OnceLock<ScubaSampleBuilder> = OnceLock::new(); | ||
|
||
/// Initialize the Scuba client for the `supertd_events` dataset. | ||
/// | ||
/// Returns a guard that flushes the Scuba client when dropped. | ||
/// | ||
/// Expects `tracing` to be initialized. | ||
/// | ||
/// If the environment variable `SUPERTD_SCUBA_LOGFILE` is set, then log to that | ||
/// filename instead of Scuba (useful for testing). | ||
/// | ||
/// # Panics | ||
/// | ||
/// Panics if `SUPERTD_SCUBA_LOGFILE` is set and the log file cannot be opened | ||
/// for writing. | ||
pub fn init(fb: fbinit::FacebookInit) -> ScubaClientGuard { | ||
let mut builder = match std::env::var_os("SUPERTD_SCUBA_LOGFILE") { | ||
None => ScubaSampleBuilder::new(fb, SCUBA_DATASET), | ||
Some(path) => ScubaSampleBuilder::with_discard() | ||
.with_log_file(path) | ||
.unwrap(), | ||
}; | ||
builder.add_common_server_data(); | ||
add_sandcastle_columns(&mut builder); | ||
if BUILDER.set(builder).is_err() { | ||
tracing::error!("supertd_events Scuba client initialized twice"); | ||
} | ||
ScubaClientGuard(()) | ||
} | ||
|
||
/// Log a sample to the `supertd_events` dataset. | ||
/// | ||
/// The `event` column should be a distinct string for each source location | ||
/// logging an event. | ||
/// | ||
/// The `data` column contains JSON-encoded data specific to that event (so that | ||
/// we do not inflate the number of columns in the Scuba table with properties | ||
/// populated by only one event). Use this data in derived columns or queries | ||
/// using `JSON_EXTRACT`. | ||
/// | ||
/// If [`init`] has not been invoked, the sample will not be logged. | ||
/// | ||
/// # Panics | ||
/// | ||
/// Panics if [`serde_json::to_string`] fails for `data`. | ||
/// | ||
/// # Examples | ||
/// | ||
/// ``` | ||
/// # use serde_json::json; | ||
/// # let foos_run = 10; | ||
/// # let bars_launched = 2; | ||
/// td_util::scuba!( | ||
/// event: "COMMAND_SUCCESS", | ||
/// count: foos_run + bars_launched, | ||
/// data: json!({ | ||
/// "arbitrary": ["JSON", "object"], | ||
/// "foos_run": foos_run, | ||
/// "bars_launched": bars_launched, | ||
/// }) | ||
/// ); | ||
/// ``` | ||
#[macro_export] | ||
macro_rules! scuba { | ||
( $($key:ident : $value:expr),* $(,)? ) => { | ||
let mut builder = $crate::supertd_events::sample_builder(); | ||
$($crate::scuba! { @SET_FIELD(builder, $key, $value) })* | ||
builder.log(); | ||
}; | ||
( @SET_FIELD ( $builder:ident, data, $value:expr ) ) => { | ||
// This unwrap should only fail for map keys with invalid UTF-8. | ||
$builder.add("data", $crate::supertd_events::serde_json::to_string(&$value).unwrap()); | ||
}; | ||
( @SET_FIELD ( $builder:ident, $key:ident, $value:expr ) ) => { | ||
$builder.add(stringify!($key), $value); | ||
}; | ||
} | ||
|
||
/// Get the sample builder for the `supertd_events` dataset. | ||
/// | ||
/// Most use cases should use the [`scuba!`] macro instead of this function, but | ||
/// this function can be used to access the underlying [`ScubaSampleBuilder`] | ||
/// (in order to do things like set the sampling rate). | ||
pub fn sample_builder() -> ScubaSampleBuilder { | ||
BUILDER | ||
.get() | ||
.cloned() | ||
.unwrap_or_else(ScubaSampleBuilder::with_discard) | ||
} | ||
|
||
fn add_sandcastle_columns(sample: &mut ScubaSampleBuilder) { | ||
let Some(nexus_path) = std::env::var_os("SANDCASTLE_NEXUS") else { | ||
return; | ||
}; | ||
let nexus_path = std::path::Path::new(&nexus_path); | ||
if !nexus_path.exists() { | ||
return; | ||
} | ||
let variables_path = nexus_path.join("variables"); | ||
let variables = [ | ||
"SANDCASTLE_ALIAS_NAME", | ||
"SANDCASTLE_ALIAS", | ||
"SANDCASTLE_COMMAND_NAME", | ||
"SANDCASTLE_INSTANCE_ID", | ||
"SANDCASTLE_IS_DRY_RUN", | ||
"SANDCASTLE_JOB_OWNER", | ||
"SANDCASTLE_NONCE", | ||
"SANDCASTLE_PHABRICATOR_DIFF_ID", | ||
"SANDCASTLE_SCHEDULE_TYPE", | ||
"SANDCASTLE_TYPE", | ||
"SANDCASTLE_URL", | ||
"SKYCASTLE_ACTION_ID", | ||
"SKYCASTLE_JOB_ID", | ||
"SKYCASTLE_WORKFLOW_RUN_ID", | ||
"STEP_IDX", | ||
]; | ||
for var in variables { | ||
let var_lowercase = var.to_ascii_lowercase(); | ||
if let Ok(value) = std::fs::read_to_string(variables_path.join(var)) { | ||
sample.add(var_lowercase, value); | ||
} else if let Ok(value) = std::env::var(var) { | ||
sample.add(var_lowercase, value); | ||
} | ||
} | ||
} | ||
|
||
/// Flushes the `supertd_events` Scuba client when dropped. | ||
/// | ||
/// Make sure this value is in scope for the duration of the program so that we | ||
/// flush the client upon program exit. | ||
#[must_use] | ||
pub struct ScubaClientGuard(()); | ||
|
||
impl Drop for ScubaClientGuard { | ||
fn drop(&mut self) { | ||
if let Some(builder) = BUILDER.get() { | ||
builder.flush(std::time::Duration::from_secs(5)); | ||
} | ||
} | ||
} |