Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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 Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions relay-dynamic-config/src/feature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ pub enum Feature {
/// Serialized as `projects:relay-otel-endpoint`.
#[serde(rename = "projects:relay-otel-endpoint")]
OtelEndpoint,
/// Enable standalone log ingestion via the `/logs` OTel endpoint.
///
/// Serialized as `projects:relay-otel-logs-endpoint`
#[serde(rename = "projects:relay-otel-logs-endpoint")]
OtelLogsEndpoint,
/// Enable playstation crash dump ingestion via the `/playstation/` endpoint.
///
/// Serialized as `organizations:relay-playstation-ingestion`.
Expand Down
2 changes: 1 addition & 1 deletion relay-server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ mime = { workspace = true }
minidump = { workspace = true, optional = true }
multer = { workspace = true }
once_cell = { workspace = true }
opentelemetry-proto = { workspace = true }
opentelemetry-proto = { workspace = true, features = ["trace", "logs"] }
papaya = { workspace = true }
pin-project-lite = { workspace = true }
priority-queue = { workspace = true }
Expand Down
7 changes: 6 additions & 1 deletion relay-server/src/endpoints/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ mod health_check;
mod minidump;
mod monitor;
mod nel;
mod ourlogs;
#[cfg(sentry)]
mod playstation;
mod project_configs;
Expand Down Expand Up @@ -81,7 +82,11 @@ pub fn routes(config: &Config) -> Router<ServiceState>{
// Because we initially released this endpoint with a trailing slash, keeping it for
// backwards compatibility.
.route("/api/{project_id}/otlp/v1/traces", traces::route(config))
.route("/api/{project_id}/otlp/v1/traces/", traces::route(config));
.route("/api/{project_id}/otlp/v1/traces/", traces::route(config))
// The OTLP/HTTP transport defaults to a request suffix of /v1/logs (no trailing slash):
// https://opentelemetry.io/docs/specs/otlp/#otlphttp-request
.route("/api/{project_id}/otlp/v1/logs", ourlogs::route(config))
.route("/api/{project_id}/otlp/v1/logs/", ourlogs::route(config));
// NOTE: If you add a new (non-experimental) route here, please also list it in
// https://github.com/getsentry/sentry-docs/blob/master/docs/product/relay/operating-guidelines.mdx

Expand Down
43 changes: 43 additions & 0 deletions relay-server/src/endpoints/ourlogs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use axum::RequestExt;
use axum::extract::{DefaultBodyLimit, Request};
use axum::http::StatusCode;
use axum::response::IntoResponse;
use axum::routing::{MethodRouter, post};
use bytes::Bytes;
use relay_config::Config;
use relay_dynamic_config::Feature;

use crate::endpoints::common;
use crate::envelope::{ContentType, Envelope, Item, ItemType};
use crate::extractors::{RawContentType, RequestMeta};
use crate::service::ServiceState;

async fn handle(
state: ServiceState,
content_type: RawContentType,
meta: RequestMeta,
request: Request,
) -> axum::response::Result<impl IntoResponse> {
let content_type @ (ContentType::Json | ContentType::Protobuf) =
ContentType::from(content_type.as_ref())
else {
return Ok(StatusCode::UNSUPPORTED_MEDIA_TYPE);
};
let payload: Bytes = request.extract().await?;
let mut envelope = Envelope::from_request(None, meta);
envelope.require_feature(Feature::OtelLogsEndpoint);

envelope.add_item({
let mut item = Item::new(ItemType::OtelLogsData);
item.set_payload(content_type, payload);
item
});

common::handle_envelope(&state, envelope).await?;

Ok(StatusCode::ACCEPTED)
}

pub fn route(config: &Config) -> MethodRouter<ServiceState> {
post(handle).route_layer(DefaultBodyLimit::max(config.max_envelope_size()))
}
12 changes: 12 additions & 0 deletions relay-server/src/envelope/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,11 @@ impl Item {
ItemType::Span | ItemType::OtelSpan => smallvec![(DataCategory::Span, item_count)],
// NOTE: semantically wrong, but too expensive to parse.
ItemType::OtelTracesData => smallvec![(DataCategory::Span, item_count)],
// NOTE: OtelLogsData is mapped to Log data category.
ItemType::OtelLogsData => smallvec![
(DataCategory::LogByte, self.len().max(1)),
(DataCategory::LogItem, item_count)
],
ItemType::ProfileChunk => match self.profile_type() {
Some(ProfileType::Backend) => smallvec![(DataCategory::ProfileChunk, item_count)],
Some(ProfileType::Ui) => smallvec![(DataCategory::ProfileChunkUi, item_count)],
Expand Down Expand Up @@ -418,6 +423,7 @@ impl Item {
| ItemType::Log
| ItemType::OtelSpan
| ItemType::OtelTracesData
| ItemType::OtelLogsData
| ItemType::ProfileChunk => false,

// The unknown item type can observe any behavior, most likely there are going to be no
Expand Down Expand Up @@ -453,6 +459,7 @@ impl Item {
ItemType::Log => false,
ItemType::OtelSpan => false,
ItemType::OtelTracesData => false,
ItemType::OtelLogsData => false,
ItemType::ProfileChunk => false,

// Since this Relay cannot interpret the semantics of this item, it does not know
Expand Down Expand Up @@ -530,6 +537,8 @@ pub enum ItemType {
OtelSpan,
/// An OTLP TracesData container.
OtelTracesData,
/// An OTLP LogsData container.
OtelLogsData,
/// UserReport as an Event
UserReportV2,
/// ProfileChunk is a chunk of a profiling session.
Expand Down Expand Up @@ -585,6 +594,7 @@ impl ItemType {
Self::Span => "span",
Self::OtelSpan => "otel_span",
Self::OtelTracesData => "otel_traces_data",
Self::OtelLogsData => "otel_logs_data",
Self::ProfileChunk => "profile_chunk",
Self::Unknown(_) => "unknown",
}
Expand Down Expand Up @@ -636,6 +646,7 @@ impl ItemType {
ItemType::Span => true,
ItemType::OtelSpan => true,
ItemType::OtelTracesData => true,
ItemType::OtelLogsData => true,
ItemType::UserReportV2 => false,
ItemType::ProfileChunk => true,
ItemType::Unknown(_) => true,
Expand Down Expand Up @@ -678,6 +689,7 @@ impl std::str::FromStr for ItemType {
"span" => Self::Span,
"otel_span" => Self::OtelSpan,
"otel_traces_data" => Self::OtelTracesData,
"otel_logs_data" => Self::OtelLogsData,
"profile_chunk" => Self::ProfileChunk,
// "profile_chunk_ui" is to be treated as an alias for `ProfileChunk`
// because Android 8.10.0 and 8.11.0 is sending it as the item type.
Expand Down
Loading
Loading