Skip to content

Commit 0cc7cb0

Browse files
ref(normalization): Remove TransactionsProcessor (#2714)
This PR removes the transaction processor and moves its normalization to `NormalizeProcessor`, bringing us a step closer to having a single processor. There are no new normalization steps in this commit.
1 parent 4ccb5b7 commit 0cc7cb0

File tree

3 files changed

+356
-287
lines changed

3 files changed

+356
-287
lines changed

relay-event-normalization/src/normalize/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,14 @@ use crate::{
3030

3131
pub mod breakdowns;
3232
pub mod nel;
33+
pub(crate) mod processor;
3334
pub mod span;
3435
pub mod user_agent;
3536
pub mod utils;
3637

3738
mod contexts;
3839
mod logentry;
3940
mod mechanism;
40-
mod processor;
4141
mod request;
4242
mod stacktrace;
4343

relay-event-normalization/src/normalize/processor.rs

+102-19
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@ use std::collections::BTreeSet;
1111

1212
use std::hash::{Hash, Hasher};
1313
use std::mem;
14+
use std::ops::Range;
1415

1516
use chrono::{DateTime, Duration, Utc};
1617
use relay_base_schema::metrics::{is_valid_metric_name, DurationUnit, FractionUnit, MetricUnit};
18+
use relay_common::time::UnixTimestamp;
1719
use relay_event_schema::processor::{
1820
self, MaxChars, ProcessValue, ProcessingAction, ProcessingResult, ProcessingState, Processor,
1921
};
@@ -31,25 +33,42 @@ use crate::span::tag_extraction::{self, extract_span_tags};
3133
use crate::timestamp::TimestampProcessor;
3234
use crate::utils::{self, MAX_DURATION_MOBILE_MS};
3335
use crate::{
34-
breakdowns, schema, span, transactions, trimming, user_agent, BreakdownsConfig,
35-
ClockDriftProcessor, DynamicMeasurementsConfig, GeoIpLookup, PerformanceScoreConfig,
36-
RawUserAgentInfo,
36+
breakdowns, end_all_spans, normalize_transaction_name, schema, set_default_transaction_source,
37+
span, trimming, user_agent, validate_transaction, BreakdownsConfig, ClockDriftProcessor,
38+
DynamicMeasurementsConfig, GeoIpLookup, PerformanceScoreConfig, RawUserAgentInfo,
39+
TransactionNameConfig,
3740
};
3841

3942
use crate::LightNormalizationConfig;
4043

4144
/// Configuration for [`NormalizeProcessor`].
4245
#[derive(Clone, Debug, Default)]
43-
pub struct NormalizeProcessorConfig<'a> {
46+
pub(crate) struct NormalizeProcessorConfig<'a> {
4447
/// Light normalization config.
4548
// XXX(iker): we should split this config appropriately.
46-
light_config: LightNormalizationConfig<'a>,
49+
pub light_config: LightNormalizationConfig<'a>,
50+
51+
/// Configuration to apply to transaction names, especially around sanitizing.
52+
pub transaction_name_config: TransactionNameConfig<'a>,
53+
54+
/// Timestamp range in which a transaction must end.
55+
///
56+
/// Transactions that finish outside of this range are considered invalid.
57+
/// This check is skipped if no range is provided.
58+
pub transaction_range: Option<Range<UnixTimestamp>>,
4759
}
4860

4961
impl<'a> From<LightNormalizationConfig<'a>> for NormalizeProcessorConfig<'a> {
50-
fn from(config: LightNormalizationConfig<'a>) -> Self {
62+
fn from(mut config: LightNormalizationConfig<'a>) -> Self {
63+
// HACK(iker): workaround to avoid cloning of config items. We'll get
64+
// rid of this when we remove light normalization in the next step.
65+
let transaction_name_config = std::mem::take(&mut config.transaction_name_config);
66+
let transaction_range = config.transaction_range.take();
67+
5168
Self {
5269
light_config: config,
70+
transaction_name_config,
71+
transaction_range,
5372
}
5473
}
5574
}
@@ -62,7 +81,7 @@ impl<'a> From<LightNormalizationConfig<'a>> for NormalizeProcessorConfig<'a> {
6281
/// The returned [`ProcessingResult`] indicates whether the passed event should
6382
/// be ingested or dropped.
6483
#[derive(Debug, Default)]
65-
pub struct NormalizeProcessor<'a> {
84+
pub(crate) struct NormalizeProcessor<'a> {
6685
/// Configuration for the normalization steps.
6786
config: NormalizeProcessorConfig<'a>,
6887
}
@@ -80,23 +99,40 @@ impl<'a> Processor for NormalizeProcessor<'a> {
8099
meta: &mut Meta,
81100
state: &ProcessingState<'_>,
82101
) -> ProcessingResult {
102+
if event.ty.value() == Some(&EventType::Transaction) {
103+
// TODO: Parts of this processor should probably be a filter so we
104+
// can revert some changes to ProcessingAction)
105+
106+
validate_transaction(event, self.config.transaction_range.as_ref())?;
107+
108+
if let Some(trace_context) = event.context_mut::<TraceContext>() {
109+
trace_context.op.get_or_insert_with(|| "default".to_owned());
110+
}
111+
112+
// The transaction name is expected to be non-empty by downstream services (e.g. Snuba), but
113+
// Relay doesn't reject events missing the transaction name. Instead, a default transaction
114+
// name is given, similar to how Sentry gives an "<unlabeled event>" title to error events.
115+
// SDKs should avoid sending empty transaction names, setting a more contextual default
116+
// value when possible.
117+
if event.transaction.value().map_or(true, |s| s.is_empty()) {
118+
event
119+
.transaction
120+
.set_value(Some("<unlabeled transaction>".to_owned()))
121+
}
122+
set_default_transaction_source(event);
123+
normalize_transaction_name(event, &self.config.transaction_name_config)?;
124+
end_all_spans(event)?;
125+
}
126+
127+
// XXX(iker): processing child values should be the last step. The logic
128+
// below this call is being moved (WIP) to the processor appropriately.
83129
event.process_child_values(self, state)?;
84130

85131
let config = &self.config.light_config;
86132
if config.is_renormalize {
87133
return Ok(());
88134
}
89135

90-
// Validate and normalize transaction
91-
// (internally noops for non-transaction events).
92-
// TODO: Parts of this processor should probably be a filter so we
93-
// can revert some changes to ProcessingAction)
94-
let mut transactions_processor = transactions::TransactionsProcessor::new(
95-
config.transaction_name_config.clone(),
96-
config.transaction_range.clone(),
97-
);
98-
transactions_processor.process_event(event, meta, ProcessingState::root())?;
99-
100136
// Check for required and non-empty values
101137
schema::SchemaProcessor.process_event(event, meta, ProcessingState::root())?;
102138

@@ -973,7 +1009,7 @@ mod tests {
9731009
use insta::assert_debug_snapshot;
9741010
use relay_base_schema::events::EventType;
9751011
use relay_base_schema::metrics::{DurationUnit, MetricUnit};
976-
use relay_event_schema::processor::{process_value, ProcessingAction, ProcessingState};
1012+
use relay_event_schema::processor::{self, process_value, ProcessingAction, ProcessingState};
9771013
use relay_event_schema::protocol::{
9781014
Contexts, Csp, DeviceContext, Event, Headers, IpAddr, Measurement, Measurements, Request,
9791015
Span, SpanId, Tags, TraceContext, TraceId,
@@ -1755,7 +1791,7 @@ mod tests {
17551791
}
17561792

17571793
#[test]
1758-
fn test_renormalize_is_idempotent() {
1794+
fn test_renormalize_spans_is_idempotent() {
17591795
let json = r#"{
17601796
"start_timestamp": 1,
17611797
"timestamp": 2,
@@ -1782,6 +1818,53 @@ mod tests {
17821818
assert_eq!(reprocessed, reprocessed2);
17831819
}
17841820

1821+
#[test]
1822+
fn test_renormalize_transactions_is_idempotent() {
1823+
let json = r#"{
1824+
"event_id": "52df9022835246eeb317dbd739ccd059",
1825+
"type": "transaction",
1826+
"transaction": "test-transaction",
1827+
"start_timestamp": 1,
1828+
"timestamp": 2,
1829+
"contexts": {
1830+
"trace": {
1831+
"trace_id": "ff62a8b040f340bda5d830223def1d81",
1832+
"span_id": "bd429c44b67a3eb4"
1833+
}
1834+
}
1835+
}"#;
1836+
1837+
let mut processed = Annotated::<Event>::from_json(json).unwrap();
1838+
let processor_config = NormalizeProcessorConfig::default();
1839+
let mut processor = NormalizeProcessor::new(processor_config.clone());
1840+
process_value(&mut processed, &mut processor, ProcessingState::root()).unwrap();
1841+
remove_received_from_event(&mut processed);
1842+
let trace_context = get_value!(processed!).context::<TraceContext>().unwrap();
1843+
assert_eq!(trace_context.op.value().unwrap(), "default");
1844+
1845+
let mut reprocess_config = processor_config.clone();
1846+
reprocess_config.light_config.is_renormalize = true;
1847+
let mut processor = NormalizeProcessor::new(processor_config.clone());
1848+
1849+
let mut reprocessed = processed.clone();
1850+
process_value(&mut reprocessed, &mut processor, ProcessingState::root()).unwrap();
1851+
remove_received_from_event(&mut reprocessed);
1852+
assert_eq!(processed, reprocessed);
1853+
1854+
let mut reprocessed2 = reprocessed.clone();
1855+
process_value(&mut reprocessed2, &mut processor, ProcessingState::root()).unwrap();
1856+
remove_received_from_event(&mut reprocessed2);
1857+
assert_eq!(reprocessed, reprocessed2);
1858+
}
1859+
1860+
fn remove_received_from_event(event: &mut Annotated<Event>) {
1861+
processor::apply(event, |e, _| {
1862+
e.received = Annotated::empty();
1863+
Ok(())
1864+
})
1865+
.unwrap();
1866+
}
1867+
17851868
#[test]
17861869
fn test_computed_performance_score() {
17871870
let json = r#"

0 commit comments

Comments
 (0)