relay_event_normalization/normalize/
nel.rs1use chrono::{Duration, Utc};
4use relay_event_schema::protocol::{
5 Contexts, Event, HeaderName, HeaderValue, Headers, LogEntry, NelContext, NetworkReportRaw,
6 Request, ResponseContext, Timestamp,
7};
8use relay_protocol::Annotated;
9
10pub fn enrich_event(event: &mut Event, nel: Annotated<NetworkReportRaw>) {
12 let Some(nel) = nel.into_value() else {
14 return;
15 };
16 let Some(body) = nel.body.into_value() else {
17 return;
18 };
19
20 event.logger = Annotated::from("nel".to_string());
21
22 event.logentry = Annotated::new(LogEntry::from({
23 if nel.ty.value().map_or("<unknown-type>", |v| v.as_str()) == "http.error" {
24 format!(
25 "{} / {} ({})",
26 body.phase.as_str().unwrap_or("<unknown-phase>"),
27 body.ty.as_str().unwrap_or("<unknown-type>"),
28 body.status_code.value().unwrap_or(&0)
29 )
30 } else {
31 format!(
32 "{} / {}",
33 body.phase.as_str().unwrap_or("<unknown-phase>"),
34 body.ty.as_str().unwrap_or("<unknown-type>"),
35 )
36 }
37 }));
38
39 let request = event.request.get_or_insert_with(Request::default);
40 request.url = nel.url;
41 request.method = body.method;
42 request.protocol = body.protocol;
43
44 let headers = request.headers.get_or_insert_with(Headers::default);
45
46 if let Some(ref user_agent) = nel.user_agent.value() {
47 if !user_agent.is_empty() {
48 headers.insert(
49 HeaderName::new("user-agent"),
50 HeaderValue::new(user_agent).into(),
51 );
52 }
53 }
54
55 if let Some(referrer) = body.referrer.value() {
56 headers.insert(
57 HeaderName::new("referer"),
58 HeaderValue::new(referrer).into(),
59 );
60 }
61
62 let contexts = event.contexts.get_or_insert_with(Contexts::new);
63
64 let nel_context = contexts.get_or_default::<NelContext>();
65 nel_context.server_ip = body.server_ip;
66 nel_context.elapsed_time = body.elapsed_time;
67 nel_context.error_type = body.ty;
68 nel_context.phase = body.phase;
69 nel_context.sampling_fraction = body.sampling_fraction;
70
71 let status_code = body
73 .status_code
74 .map_value(|v| u64::try_from(v).unwrap_or(0));
75 if status_code.value().unwrap_or(&0) > &0 {
76 let response_context = contexts.get_or_default::<ResponseContext>();
77 response_context.status_code = status_code;
78 }
79
80 let event_time = event
82 .timestamp
83 .value_mut()
84 .map_or(Utc::now(), |timestamp| timestamp.into_inner());
85 if let Some(event_time) =
86 event_time.checked_sub_signed(Duration::milliseconds(*nel.age.value().unwrap_or(&0)))
87 {
88 event.timestamp = Annotated::new(Timestamp::from(event_time))
89 }
90}