#![doc(
html_logo_url = "https://raw.githubusercontent.com/getsentry/relay/master/artwork/relay-icon.png",
html_favicon_url = "https://raw.githubusercontent.com/getsentry/relay/master/artwork/relay-icon.png"
)]
use std::fs;
use std::io::{self, Read};
use std::path::PathBuf;
use anyhow::{format_err, Context, Result};
use clap::Parser;
use relay_event_normalization::{
normalize_event, validate_event, EventValidationConfig, NormalizationConfig,
};
use relay_event_schema::processor::{process_value, ProcessingState};
use relay_event_schema::protocol::Event;
use relay_pii::{PiiConfig, PiiProcessor};
use relay_protocol::Annotated;
#[derive(Debug, Parser)]
#[structopt(verbatim_doc_comment)]
struct Cli {
#[arg(short = 'c', long)]
pii_config: Option<PathBuf>,
#[arg(short, long)]
event: Option<PathBuf>,
#[arg(long)]
store: bool,
#[arg(long, conflicts_with = "debug")]
pretty: bool,
#[arg(long)]
debug: bool,
}
impl Cli {
fn load_pii_config(&self) -> Result<Option<PiiConfig>> {
let path = match self.pii_config {
Some(ref path) => path,
None => return Ok(None),
};
let json = fs::read_to_string(path).with_context(|| "failed to read PII config")?;
let config = serde_json::from_str(&json).with_context(|| "failed to parse PII config")?;
Ok(Some(config))
}
fn load_event(&self) -> Result<Annotated<Event>> {
let json = match self.event {
Some(ref path) => fs::read_to_string(path).with_context(|| "failed to read event")?,
None => {
let mut json = String::new();
io::stdin()
.read_to_string(&mut json)
.with_context(|| "failed to read event")?;
json
}
};
let event = Annotated::from_json(&json).with_context(|| "failed to parse event")?;
Ok(event)
}
pub fn run(self) -> Result<()> {
let mut event = self.load_event()?;
if let Some(pii_config) = self.load_pii_config()? {
let mut processor = PiiProcessor::new(pii_config.compiled());
process_value(&mut event, &mut processor, ProcessingState::root())
.map_err(|e| format_err!("{e}"))?;
}
if self.store {
validate_event(&mut event, &EventValidationConfig::default())
.map_err(|e| format_err!("{e}"))?;
normalize_event(&mut event, &NormalizationConfig::default());
}
if self.debug {
println!("{event:#?}");
} else if self.pretty {
println!("{}", event.to_json_pretty()?);
} else {
println!("{}", event.to_json()?);
}
Ok(())
}
}
fn print_error(error: &anyhow::Error) {
eprintln!("Error: {error}");
let mut cause = error.source();
while let Some(ref e) = cause {
eprintln!(" caused by: {e}");
cause = e.source();
}
}
fn main() {
let cli = Cli::parse();
match cli.run() {
Ok(()) => (),
Err(error) => {
print_error(&error);
std::process::exit(1);
}
}
}