Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
84ed60e
first pass at adding request id to logs
glitch003 Dec 9, 2025
683d047
Update rust/lit-node/lit-node/src/main.rs
glitch003 Dec 9, 2025
34e4a57
change how we set the request id on the span
glitch003 Dec 10, 2025
848d70e
clippy
glitch003 Dec 10, 2025
8725876
Merge branch 'master' into feature/NODE-4860-correlation-id-in-logs
kapoorabhishek24 Dec 19, 2025
49d364c
Merge branch 'master' into feature/NODE-4860-correlation-id-in-logs
kapoorabhishek24 Dec 24, 2025
73300f3
Merge branch 'master' into feature/NODE-4860-correlation-id-in-logs
kapoorabhishek24 Dec 29, 2025
9412a91
Merge branch 'master' into feature/NODE-4860-correlation-id-in-logs
kapoorabhishek24 Dec 29, 2025
6c9afb8
Merge branch 'master' into feature/NODE-4860-correlation-id-in-logs
kapoorabhishek24 Dec 30, 2025
abd8dad
Refactor spans behaviour
kapoorabhishek24 Jan 2, 2026
f33d293
Fix fallback ID
kapoorabhishek24 Jan 5, 2026
3bd44a5
Add Rocket fairings
kapoorabhishek24 Jan 5, 2026
3b7489e
Remove redundant tracing
kapoorabhishek24 Jan 5, 2026
03aeb04
optimise tests
kapoorabhishek24 Jan 5, 2026
5de01eb
Address feedback
kapoorabhishek24 Jan 8, 2026
a2f2718
Remove fallback UUID
kapoorabhishek24 Jan 8, 2026
abeb19b
Merge branch 'master' into feature/NODE-4860-correlation-id-in-logs
kapoorabhishek24 Jan 8, 2026
6ebc205
Leverage Dashmap
kapoorabhishek24 Jan 9, 2026
a1a7d3e
Merge branch 'master' into feature/NODE-4860-correlation-id-in-logs
kapoorabhishek24 Jan 10, 2026
90e9b72
Merge branch 'master' into feature/NODE-4860-correlation-id-in-logs
kapoorabhishek24 Jan 12, 2026
f057a2c
build lit-os to rebuild cargo.lock
glitch003 Jan 13, 2026
c6f85f5
Merge branch 'master' into feature/NODE-4860-correlation-id-in-logs
glitch003 Jan 13, 2026
50b1bc2
rebuild lit actions and lit-node
glitch003 Jan 13, 2026
dadb8c1
Merge branch 'master' into feature/NODE-4860-correlation-id-in-logs
glitch003 Jan 13, 2026
7c4f5f9
remove correlation_id from /web/job_status/v2 instrumentation since i…
glitch003 Jan 14, 2026
809c636
Revert "remove correlation_id from /web/job_status/v2 instrumentation…
Garandor Jan 14, 2026
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
13 changes: 0 additions & 13 deletions rust/lit-core/Cargo.lock

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

51 changes: 42 additions & 9 deletions rust/lit-core/lit-api-core/src/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ use tracing::{Span, info_span};
use tracing_opentelemetry::OpenTelemetrySpanExt;
use uuid::Uuid;

use lit_observability::logging::set_request_context;

use crate::error::{EC, Error, Result, conversion_err_code, validation_err_code};

pub const HEADER_KEY_X_CORRELATION_ID: &str = "X-Correlation-Id";
Expand Down Expand Up @@ -167,8 +169,19 @@ impl<'r> FromRequest<'r> for Tracing {
type Error = crate::error::Error;

async fn from_request(req: &'r Request<'_>) -> Outcome<Self, Self::Error> {
let correlation_id =
extract_correlation_id(req).unwrap_or_else(|| format!("LD-{}", Uuid::new_v4()));
let (request_id, correlation_id) = extract_request_and_correlation_ids(req);

// Generate fallback ID only if BOTH headers are missing
let fallback_id = || format!("LD-{}", Uuid::new_v4());
let correlation_id = correlation_id.unwrap_or_else(&fallback_id);
let request_id = request_id.unwrap_or_else(|| correlation_id.clone());

// Set request context on the current span. This centralizes context storage:
// 1. Span extensions (RequestContext) - for log injection in OTLP and stdout
// 2. OTel span attributes - for distributed tracing correlation
// This ensures other components (like set_request_id_on_span) can find
// this context via get_request_context() and avoid generating duplicate IDs.
set_request_context(Some(request_id), Some(correlation_id.clone()));

let mut tracing = Self::new(correlation_id);
apply_req_tracing_fields(req, &mut tracing);
Expand Down Expand Up @@ -227,7 +240,16 @@ impl<'r> FromRequest<'r> for TracingRequired {
type Error = crate::error::Error;

async fn from_request(req: &'r Request<'_>) -> Outcome<Self, Self::Error> {
if let Some(correlation_id) = extract_correlation_id(req) {
let (request_id, correlation_id) = extract_request_and_correlation_ids(req);

// TracingRequired requires at least one header
if let Some(correlation_id) = correlation_id {
// Preserve distinct values when both headers are present
let request_id = request_id.unwrap_or_else(|| correlation_id.clone());

// Set request context (span extensions + OTel attributes) for consistency
set_request_context(Some(request_id), Some(correlation_id.clone()));

let mut tracing = Self::new(correlation_id);
apply_req_tracing_fields(req, &mut tracing);

Expand Down Expand Up @@ -257,12 +279,23 @@ where
TRACING.scope(Box::new(tracing), f)
}

pub(crate) fn extract_correlation_id(req: &Request<'_>) -> Option<String> {
req.headers()
.get(HEADER_KEY_X_CORRELATION_ID)
.next()
.or_else(|| req.headers().get(HEADER_KEY_X_REQUEST_ID).next())
.map(|val| val.to_string())
/// Extracts both request_id and correlation_id from headers, preserving distinct values.
/// Returns (request_id, correlation_id) tuple.
/// - request_id: X-Request-Id header, falls back to X-Correlation-Id
/// - correlation_id: X-Correlation-Id header, falls back to X-Request-Id
pub(crate) fn extract_request_and_correlation_ids(
req: &Request<'_>,
) -> (Option<String>, Option<String>) {
let x_request_id = req.headers().get(HEADER_KEY_X_REQUEST_ID).next().map(|v| v.to_string());
let x_correlation_id =
req.headers().get(HEADER_KEY_X_CORRELATION_ID).next().map(|v| v.to_string());

// request_id: prefer X-Request-Id, fall back to X-Correlation-Id
let request_id = x_request_id.clone().or_else(|| x_correlation_id.clone());
// correlation_id: prefer X-Correlation-Id, fall back to X-Request-Id
let correlation_id = x_correlation_id.or(x_request_id);

(request_id, correlation_id)
}

pub(crate) fn apply_req_tracing_fields(req: &Request<'_>, tracing: &mut (impl Tracer + 'static)) {
Expand Down
1 change: 0 additions & 1 deletion rust/lit-core/lit-observability/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ flume = { version = "0.11", optional = true }
hyper-util.workspace = true
nu-ansi-term = { version = "0.50.1" }
opentelemetry.workspace = true
opentelemetry-appender-tracing = { version = "0.5.0", default-features = false }
opentelemetry-otlp = { workspace = true, features = ["logs"] }
opentelemetry-semantic-conventions.workspace = true
opentelemetry_sdk = { workspace = true, features = ["logs"] }
Expand Down
12 changes: 6 additions & 6 deletions rust/lit-core/lit-observability/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@ use std::str::FromStr;
pub use config::LitObservabilityConfig;
use error::unexpected_err;
use lit_core::config::LitConfig;
use logging::init_logger_provider;
use logging::{ContextAwareOtelLogLayer, CustomEventFormatter, init_logger_provider};
use metrics::init_metrics_provider;
use net::init_tonic_exporter_builder;
use opentelemetry::trace::TracerProvider;
use opentelemetry_appender_tracing::layer::OpenTelemetryTracingBridge;

use opentelemetry_sdk::logs::LoggerProvider;
use opentelemetry_sdk::metrics::SdkMeterProvider;
Expand Down Expand Up @@ -80,8 +79,7 @@ pub async fn create_providers(
};
let logger_provider = init_logger_provider(tonic_exporter_builder, resource.clone())?;

// Create a new OpenTelemetryTracingBridge using the above LoggerProvider.
let tracing_bridge_layer = OpenTelemetryTracingBridge::new(&logger_provider);
let context_aware_log_layer = ContextAwareOtelLogLayer::new(&logger_provider);

// Add a tracing filter to filter events from crates used by opentelemetry-otlp.
// The filter levels are set as follows:
Expand All @@ -101,10 +99,12 @@ pub async fn create_providers(
.add_directive("h2=error".parse().unwrap())
.add_directive("reqwest=error".parse().unwrap());

let custom_formatter = CustomEventFormatter::default();

let sub = tracing_subscriber::registry()
.with(level_filter)
.with(fmt::layer())
.with(tracing_bridge_layer)
.with(fmt::layer().event_format(custom_formatter))
.with(context_aware_log_layer)
.with(MetricsLayer::new(meter_provider.clone()))
.with(OpenTelemetryLayer::new(tracer));

Expand Down
Loading
Loading