Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Network Error Logging #2421

Merged
merged 69 commits into from
Nov 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
f863254
hack nel
oioki Aug 21, 2023
2f8a2d9
Fix Nel endpoint
olksdr Aug 21, 2023
a695391
moar hacking
oioki Aug 21, 2023
704592c
cleanup
oioki Aug 21, 2023
ae3c9cc
cleanup warnings
oioki Aug 21, 2023
6fd93e0
tiny cleanup
oioki Aug 21, 2023
2f376d2
add browser contexts from user-agent header
oioki Aug 21, 2023
a503eb3
add logger and placeholder for culprit
oioki Aug 21, 2023
78c2839
Add tags, user and make sure we have "type" in the type
olksdr Aug 22, 2023
8029b3a
fix tests
olksdr Aug 22, 2023
58ee827
Fix lint
olksdr Aug 22, 2023
0d2ffd9
Wrong user ip
olksdr Aug 22, 2023
b04fa10
Normalize NEL
olksdr Aug 22, 2023
1d8b7b6
Change the timestamp of the event
olksdr Aug 22, 2023
0c7eda5
removed culprit (it will be set on sentry side)
oioki Aug 22, 2023
fec72d9
split issues by HTTP status code
oioki Aug 22, 2023
a8d7ee1
merge master; fix conflics
oioki Sep 26, 2023
aa21e10
fixes
oioki Sep 26, 2023
be0726c
emit envelope for each NEL report item
oioki Sep 26, 2023
71f844b
Merge branch 'master' into hackweek/nel
oioki Oct 2, 2023
1c41705
remove unused vars
olksdr Oct 5, 2023
1d072c2
Merge branch 'master' into hackweek/nel
olksdr Oct 5, 2023
c8cf938
Hide JsonSchema behind the feature flag
olksdr Oct 5, 2023
7eaed71
add Nel event type
oioki Oct 5, 2023
90a8994
update test snapshot
oioki Oct 5, 2023
0686a91
add test for test_nel_basic
oioki Oct 5, 2023
d50c667
bit more docs, fix typo
oioki Oct 5, 2023
2297808
fix docs
oioki Oct 5, 2023
df467b8
do not deduplicate NEL reports
oioki Oct 5, 2023
efc6f64
add to changelog
oioki Oct 5, 2023
ebf6ee9
fix changelog
oioki Oct 5, 2023
925c42d
also add to py/CHANGELOG
oioki Oct 5, 2023
8a1f795
Use Annotated to deserialize into the data type from json
olksdr Oct 6, 2023
33e38bd
Apply suggestions from code review
olksdr Oct 6, 2023
c92aefb
Merge branch 'master' into hackweek/nel
olksdr Oct 6, 2023
d894f2a
Fix lint
olksdr Oct 6, 2023
9ca755f
Simplified
olksdr Oct 6, 2023
ed5b68f
Address review comments
olksdr Oct 6, 2023
be229ee
Merge branch 'master' into hackweek/nel
olksdr Oct 12, 2023
34f3aad
Nel is an error
olksdr Oct 12, 2023
29a4cb6
Minimal deserialization and split envelope while queueing
olksdr Oct 12, 2023
5010cad
Move code around
olksdr Oct 13, 2023
34014f0
Merge branch 'master' into hackweek/nel
olksdr Oct 13, 2023
f005272
Add snapshots
olksdr Oct 13, 2023
e89bc63
Move NEL into context
olksdr Oct 13, 2023
eabb20b
fix doc lint
olksdr Oct 13, 2023
a61d9a4
Docs and typos
olksdr Oct 16, 2023
f2cdee0
fix tests
olksdr Oct 16, 2023
bc3aafc
Merge remote-tracking branch 'origin/master' into hackweek/nel
olksdr Oct 17, 2023
282ff20
Address review comments
olksdr Oct 17, 2023
2ea8209
refactor repetitions in the code
olksdr Oct 17, 2023
ede374e
PR review. Fix typos
olksdr Oct 17, 2023
e860db5
Remove wrong doc comment
olksdr Oct 17, 2023
d9de7f9
Explicitly set the event id for the new item
olksdr Oct 17, 2023
8530410
Make sure that each item has differnet event id in the envelope
olksdr Oct 17, 2023
8b1de5a
set status code conditionaly, remove dbg
olksdr Oct 17, 2023
c5777db
Merge branch 'master' into hackweek/nel
olksdr Oct 18, 2023
5858d8c
Merge branch 'master' into hackweek/nel
olksdr Oct 19, 2023
ddae542
Update test "test_processing_quotas" to include "nel" quota checks
olksdr Oct 19, 2023
82b8932
Update relay-event-schema/src/protocol/contexts/nel.rs
oioki Oct 30, 2023
b1b3c68
Merge branch 'master' into hackweek/nel
olksdr Oct 31, 2023
13cc9e2
review comments
olksdr Oct 31, 2023
ecc9249
fix lint and tests
olksdr Oct 31, 2023
1d02da1
Merge branch 'master' into hackweek/nel
olksdr Oct 31, 2023
0c3773a
Move functionality to normalization crate
olksdr Oct 31, 2023
e6a4d27
Bump timeout
olksdr Oct 31, 2023
2d4aba3
Merge remote-tracking branch 'origin/master' into hackweek/nel
olksdr Oct 31, 2023
8bc4b23
Merge branch 'master' into hackweek/nel
olksdr Nov 6, 2023
61cd8c9
Use event timestamp when available for calculation
olksdr Nov 6, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
- Filter outliers (>180s) for mobile measurements. ([#2649](https://github.com/getsentry/relay/pull/2649))
- Allow access to more context fields in dynamic sampling and metric extraction. ([#2607](https://github.com/getsentry/relay/pull/2607), [#2640](https://github.com/getsentry/relay/pull/2640), [#2675](https://github.com/getsentry/relay/pull/2675))
- Allow advanced scrubbing expressions for datascrubbing safe fields. ([#2670](https://github.com/getsentry/relay/pull/2670))
- Add context for NEL (Network Error Logging) reports to the event schema. ([#2421](https://github.com/getsentry/relay/pull/2421))

**Bug Fixes**:

Expand Down
5 changes: 5 additions & 0 deletions py/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## Unreleased

- Add context for NEL (Network Error Logging) reports to the event schema. ([#2421](https://github.com/getsentry/relay/pull/2421))

## 0.8.33

- Drop events starting or ending before January 1, 1970 UTC. ([#2613](https://github.com/getsentry/relay/pull/2613))
Expand All @@ -12,6 +16,7 @@
## 0.8.32

- Add `scraping_attempts` field to the event schema. ([#2575](https://github.com/getsentry/relay/pull/2575))
- Drop events starting or ending before January 1, 1970 UTC. ([#2613](https://github.com/getsentry/relay/pull/2613))

## 0.8.31

Expand Down
2 changes: 1 addition & 1 deletion relay-base-schema/src/data_category.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ impl FromStr for DataCategory {
impl From<EventType> for DataCategory {
fn from(ty: EventType) -> Self {
match ty {
EventType::Default | EventType::Error => Self::Error,
EventType::Default | EventType::Error | EventType::Nel => Self::Error,
EventType::Transaction => Self::Transaction,
EventType::Csp | EventType::Hpkp | EventType::ExpectCt | EventType::ExpectStaple => {
Self::Security
Expand Down
4 changes: 4 additions & 0 deletions relay-base-schema/src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ pub enum EventType {
ExpectCt,
/// An ExpectStaple violation payload.
ExpectStaple,
/// Network Error Logging report.
Nel,
/// Performance monitoring transactions carrying spans.
Transaction,
/// User feedback payload.
Expand Down Expand Up @@ -74,6 +76,7 @@ impl FromStr for EventType {
"hpkp" => EventType::Hpkp,
"expectct" => EventType::ExpectCt,
"expectstaple" => EventType::ExpectStaple,
"nel" => EventType::Nel,
"transaction" => EventType::Transaction,
"feedback" => EventType::UserReportV2,
_ => return Err(ParseEventTypeError),
Expand All @@ -90,6 +93,7 @@ impl fmt::Display for EventType {
EventType::Hpkp => write!(f, "hpkp"),
EventType::ExpectCt => write!(f, "expectct"),
EventType::ExpectStaple => write!(f, "expectstaple"),
EventType::Nel => write!(f, "nel"),
EventType::Transaction => write!(f, "transaction"),
EventType::UserReportV2 => write!(f, "feedback"),
}
Expand Down
20 changes: 19 additions & 1 deletion relay-event-normalization/src/normalize/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use relay_event_schema::processor::{
use relay_event_schema::protocol::{
AsPair, Breadcrumb, ClientSdkInfo, Context, ContextInner, Contexts, DebugImage, DeviceClass,
Event, EventId, EventType, Exception, Frame, Headers, IpAddr, Level, LogEntry, Measurement,
Measurements, ReplayContext, Request, SpanAttribute, SpanStatus, Stacktrace, Tags,
Measurements, NelContext, ReplayContext, Request, SpanAttribute, SpanStatus, Stacktrace, Tags,
TraceContext, User, VALID_PLATFORMS,
};
use relay_protocol::{
Expand All @@ -36,6 +36,7 @@ use crate::{
};

pub mod breakdowns;
pub mod nel;
pub mod span;
pub mod user_agent;
pub mod utils;
Expand Down Expand Up @@ -270,6 +271,8 @@ impl<'a> NormalizeProcessor<'a> {
EventType::ExpectCt
} else if event.expectstaple.value().is_some() {
EventType::ExpectStaple
} else if event.context::<NelContext>().is_some() {
EventType::Nel
} else {
EventType::Default
}
Expand Down Expand Up @@ -774,6 +777,18 @@ fn is_security_report(event: &Event) -> bool {
|| event.hpkp.value().is_some()
}

/// Backfills the client IP address on for the NEL reports.
fn normalize_nel_report(event: &mut Event, client_ip: Option<&IpAddr>) {
if event.context::<NelContext>().is_none() {
return;
}

if let Some(client_ip) = client_ip {
let user = event.user.value_mut().get_or_insert_with(User::default);
user.ip_address = Annotated::new(client_ip.to_owned());
}
}

/// Backfills common security report attributes.
fn normalize_security_report(
event: &mut Event,
Expand Down Expand Up @@ -1172,6 +1187,9 @@ pub fn light_normalize_event(
// Process security reports first to ensure all props.
normalize_security_report(event, config.client_ip, &config.user_agent);

// Process NEL reports to ensure all props.
normalize_nel_report(event, config.client_ip);

// Insert IP addrs before recursing, since geo lookup depends on it.
normalize_ip_addresses(
&mut event.request,
Expand Down
90 changes: 90 additions & 0 deletions relay-event-normalization/src/normalize/nel.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
//! Contains helper function for NEL reports.

use chrono::{Duration, Utc};
use relay_event_schema::protocol::{
Contexts, Event, HeaderName, HeaderValue, Headers, LogEntry, NelContext, NetworkReportRaw,
Request, ResponseContext, Timestamp,
};
use relay_protocol::Annotated;

/// Enriches the event with new values using the provided [`NetworkReportRaw`].
pub fn enrich_event(event: &mut Event, nel: Annotated<NetworkReportRaw>) {
// If the incoming NEL report is empty or it contains an empty body, just exit.
let Some(nel) = nel.into_value() else {
return;
};
let Some(body) = nel.body.into_value() else {
return;
};

event.logger = Annotated::from("nel".to_string());

event.logentry = Annotated::new(LogEntry::from({
if nel.ty.value().map_or("<unknown-type>", |v| v.as_str()) == "http.error" {
format!(
"{} / {} ({})",
body.phase.as_str().unwrap_or("<unknown-phase>"),
body.ty.as_str().unwrap_or("<unknown-type>"),
body.status_code.value().unwrap_or(&0)
)
} else {
format!(
"{} / {}",
body.phase.as_str().unwrap_or("<unknown-phase>"),
body.ty.as_str().unwrap_or("<unknown-type>"),
)
}
}));

let request = event.request.get_or_insert_with(Request::default);
request.url = nel.url;
request.method = body.method;
request.protocol = body.protocol;

let headers = request.headers.get_or_insert_with(Headers::default);

if let Some(ref user_agent) = nel.user_agent.value() {
if !user_agent.is_empty() {
headers.insert(
HeaderName::new("user-agent"),
HeaderValue::new(user_agent).into(),
);
}
}

if let Some(referrer) = body.referrer.value() {
headers.insert(
HeaderName::new("referer"),
HeaderValue::new(referrer).into(),
);
}

let contexts = event.contexts.get_or_insert_with(Contexts::new);

let nel_context = contexts.get_or_default::<NelContext>();
nel_context.server_ip = body.server_ip;
nel_context.elapsed_time = body.elapsed_time;
nel_context.error_type = body.ty;
nel_context.phase = body.phase;
nel_context.sampling_fraction = body.sampling_fraction;

// Set response status code only if it's bigger than zero.
let status_code = body
.status_code
.map_value(|v| u64::try_from(v).unwrap_or(0));
if status_code.value().unwrap_or(&0) > &0 {
let response_context = contexts.get_or_default::<ResponseContext>();
response_context.status_code = status_code;
}

// Set the timestamp on the event when it actually occurred.
let event_time = event
.timestamp
.value_mut()
.map_or(Utc::now(), |timestamp| timestamp.into_inner());
if let Some(event_time) =
event_time.checked_sub_signed(Duration::milliseconds(*nel.age.value().unwrap_or(&0)))
{
event.timestamp = Annotated::new(Timestamp::from(event_time))
}
}
4 changes: 4 additions & 0 deletions relay-event-schema/src/protocol/contexts/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ mod cloud_resource;
mod device;
mod gpu;
mod monitor;
mod nel;
mod os;
mod otel;
mod profile;
Expand All @@ -19,6 +20,7 @@ pub use cloud_resource::*;
pub use device::*;
pub use gpu::*;
pub use monitor::*;
pub use nel::*;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here wrt star exports.

pub use os::*;
pub use otel::*;
pub use profile::*;
Expand Down Expand Up @@ -82,6 +84,8 @@ pub enum Context {
Otel(Box<OtelContext>),
/// Cloud resource information.
CloudResource(Box<CloudResourceContext>),
/// Nel information.
Nel(Box<NelContext>),
/// Additional arbitrary fields for forwards compatibility.
#[metastructure(fallback_variant)]
Other(#[metastructure(pii = "true")] Object<Value>),
Expand Down
63 changes: 63 additions & 0 deletions relay-event-schema/src/protocol/contexts/nel.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#[cfg(feature = "jsonschema")]
use relay_jsonschema_derive::JsonSchema;
use relay_protocol::{Annotated, Empty, FromValue, IntoValue, Object, Value};

use crate::processor::ProcessValue;
use crate::protocol::{IpAddr, NetworkReportPhases};

/// Contains NEL report information.
///
/// Network Error Logging (NEL) is a browser feature that allows reporting of failed network
/// requests from the client side. See the following resources for more information:
///
/// - [W3C Editor's Draft](https://w3c.github.io/network-error-logging/)
/// - [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Network_Error_Logging)
#[derive(Clone, Debug, Default, PartialEq, Empty, FromValue, IntoValue, ProcessValue)]
#[cfg_attr(feature = "jsonschema", derive(JsonSchema))]
pub struct NelContext {
/// If request failed, the type of its network error. If request succeeded, "ok".
pub error_type: Annotated<String>,
/// Server IP where the requests was sent to.
#[metastructure(pii = "maybe")]
pub server_ip: Annotated<IpAddr>,
/// The number of milliseconds between the start of the resource fetch and when it was aborted by the user agent.
pub elapsed_time: Annotated<u64>,
/// If request failed, the phase of its network error. If request succeeded, "application".
pub phase: Annotated<NetworkReportPhases>,
/// The sampling rate.
pub sampling_fraction: Annotated<f64>,
/// For forward compatibility.
#[metastructure(additional_properties, pii = "maybe")]
pub other: Object<Value>,
}

impl super::DefaultContext for NelContext {
fn default_key() -> &'static str {
"nel"
}

fn from_context(context: super::Context) -> Option<Self> {
match context {
super::Context::Nel(c) => Some(*c),
_ => None,
}
}

fn cast(context: &super::Context) -> Option<&Self> {
match context {
super::Context::Nel(c) => Some(c),
_ => None,
}
}

fn cast_mut(context: &mut super::Context) -> Option<&mut Self> {
match context {
super::Context::Nel(c) => Some(c),
_ => None,
}
}

fn into_context(self) -> super::Context {
super::Context::Nel(Box::new(self))
}
}
2 changes: 2 additions & 0 deletions relay-event-schema/src/protocol/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ mod logentry;
mod measurements;
mod mechanism;
mod metrics;
mod nel;
mod relay_info;
mod replay;
mod request;
Expand Down Expand Up @@ -52,6 +53,7 @@ pub use self::logentry::*;
pub use self::measurements::*;
pub use self::mechanism::*;
pub use self::metrics::*;
pub use self::nel::*;
pub use self::relay_info::*;
pub use self::replay::*;
pub use self::request::*;
Expand Down
Loading