Skip to content

Commit

Permalink
editoast: setup tracing with opentelemetry for TestApp
Browse files Browse the repository at this point in the history
Signed-off-by: hamz2a <[email protected]>
  • Loading branch information
hamz2a committed Dec 26, 2024
1 parent 6a105bf commit 72ae856
Show file tree
Hide file tree
Showing 9 changed files with 513 additions and 564 deletions.
831 changes: 347 additions & 484 deletions editoast/Cargo.lock

Large diffs are not rendered by default.

20 changes: 12 additions & 8 deletions editoast/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ openssl = "0.10.68"
opentelemetry-semantic-conventions = { version = "0.26", features = [
"semconv_experimental",
] }
opentelemetry_sdk = { version = "0.27.1", features = ["rt-tokio", "trace"] }
paste = "1.0.15"
postgis_diesel = { version = "2.4.1", features = ["serde"] }
postgres-openssl = "0.5.0"
Expand All @@ -65,6 +66,9 @@ rand = "0.8.5"
rangemap = "1.5.1"
# 0.12.0 to 0.12.4 have weird timeout issues https://github.com/seanmonstar/reqwest/issues/2283
# This bug was introduced between 0.12.0 and 0.12.3.
opentelemetry = { version = "0.27.1", default-features = false, features = [
"trace",
] }
reqwest = { version = "0.11.27", features = ["json"] }
rstest = { version = "0.19.0", default-features = false }
serde = { version = "1.0.216", features = ["derive"] }
Expand All @@ -78,6 +82,10 @@ tracing = { version = "0.1.41", default-features = false, features = [
"attributes",
"log",
] }
tracing-opentelemetry = { version = "0.28.0", default-features = false, features = [
"tracing-log",
] }
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
url = { version = "2.5.4", features = ["serde"] }
urlencoding = "2.1.3"
utoipa = { version = "4.2.3", features = ["chrono", "uuid"] }
Expand Down Expand Up @@ -140,15 +148,13 @@ json-patch = { version = "3.0.1", default-features = false, features = [
lapin = "2.5.0"
mime = "0.3.17"
mvt.workspace = true
opentelemetry = { version = "0.27.1", default-features = false, features = [
"trace",
] }
opentelemetry.workspace = true
opentelemetry-otlp = { version = "0.27.0", default-features = false, features = [
"grpc-tonic",
"trace",
] }
opentelemetry-semantic-conventions.workspace = true
opentelemetry_sdk = { version = "0.27.1", features = ["rt-tokio", "trace"] }
opentelemetry_sdk.workspace = true
ordered-float = { version = "4.6.0", features = ["serde"] }
osm_to_railjson = { path = "./osm_to_railjson" }
paste.workspace = true
Expand Down Expand Up @@ -182,10 +188,8 @@ tower-http = { version = "0.6.2", features = [
"trace",
] }
tracing.workspace = true
tracing-opentelemetry = { version = "0.28.0", default-features = false, features = [
"tracing-log",
] }
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
tracing-opentelemetry.workspace = true
tracing-subscriber.workspace = true
url.workspace = true
utoipa.workspace = true
uuid.workspace = true
Expand Down
8 changes: 8 additions & 0 deletions editoast/editoast_common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,18 @@ edition.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
derivative.workspace = true
opentelemetry.workspace = true
opentelemetry-semantic-conventions.workspace = true
opentelemetry_sdk.workspace = true
rangemap.workspace = true
serde = { workspace = true, features = ["derive"] }
serde_json.workspace = true
thiserror.workspace = true
tracing.workspace = true
tracing-opentelemetry.workspace = true
tracing-subscriber.workspace = true
url.workspace = true
utoipa.workspace = true

[lints]
Expand Down
1 change: 1 addition & 0 deletions editoast/editoast_common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ pub mod geometry;
mod hash_rounded_float;
pub mod rangemap_utils;
pub mod schemas;
pub mod tracing;

pub use hash_rounded_float::hash_float;
pub use hash_rounded_float::hash_float_slice;
Expand Down
93 changes: 93 additions & 0 deletions editoast/editoast_common/src/tracing.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
use std::time::Duration;

use derivative::Derivative;
use opentelemetry::trace::TracerProvider;
use opentelemetry::KeyValue;
use opentelemetry_sdk::export::trace::SpanExporter;
use opentelemetry_sdk::propagation::TraceContextPropagator;
use opentelemetry_sdk::resource::EnvResourceDetector;
use opentelemetry_sdk::resource::SdkProvidedResourceDetector;
use opentelemetry_sdk::resource::TelemetryResourceDetector;
use opentelemetry_sdk::Resource;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::Layer;
use url::Url;

#[derive(Debug, PartialEq)]
pub enum Stream {
Stderr,
Stdout,
}

#[derive(Debug, Derivative)]
#[derivative(Default)]
pub struct Telemetry {
#[derivative(Default(value = r#""osrd-editoast".into()"#))]
pub service_name: String,
#[derivative(Default(value = r#"Url::parse("http://localhost:4317").unwrap()"#))]
pub endpoint: Url,
}

pub struct TracingConfig {
pub stream: Stream,
pub telemetry: Option<Telemetry>,
}

impl Default for TracingConfig {
fn default() -> Self {
TracingConfig {
stream: Stream::Stdout,
telemetry: Some(Telemetry::default()),
}
}
}

pub fn create_tracing_subscriber<T: SpanExporter + 'static>(
tracing_config: TracingConfig,
exporter: T,
) -> impl tracing::Subscriber {
let env_filter_layer = tracing_subscriber::EnvFilter::builder()
// Set the default log level to 'info'
.with_default_directive(tracing_subscriber::filter::LevelFilter::INFO.into())
.from_env_lossy();
let fmt_layer = tracing_subscriber::fmt::layer()
.pretty()
.with_file(true)
.with_line_number(false);
let fmt_layer = if tracing_config.stream == Stream::Stderr {
fmt_layer.with_writer(std::io::stderr).boxed()
} else {
fmt_layer.boxed()
};
// https://docs.rs/tracing-subscriber/latest/tracing_subscriber/layer/index.html#runtime-configuration-with-layers
let telemetry_layer = match tracing_config.telemetry {
None => None,
Some(telemetry) => {
let resource = Resource::new(vec![KeyValue::new(
opentelemetry_semantic_conventions::resource::SERVICE_NAME,
telemetry.service_name.clone(),
)])
.merge(&Resource::from_detectors(
Duration::from_secs(10),
vec![
Box::new(SdkProvidedResourceDetector),
Box::new(TelemetryResourceDetector),
Box::new(EnvResourceDetector::new()),
],
));
let otlp_tracer = opentelemetry_sdk::trace::TracerProvider::builder()
.with_batch_exporter(exporter, opentelemetry_sdk::runtime::Tokio)
.with_resource(resource)
.build()
.tracer("osrd-editoast");
let layer = tracing_opentelemetry::OpenTelemetryLayer::new(otlp_tracer);
opentelemetry::global::set_text_map_propagator(TraceContextPropagator::new());
Some(layer)
}
};

tracing_subscriber::registry()
.with(telemetry_layer)
.with(env_filter_layer)
.with(fmt_layer)
}
9 changes: 9 additions & 0 deletions editoast/src/client/telemetry_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@ pub struct TelemetryConfig {
pub telemetry_endpoint: Url,
}

impl From<TelemetryConfig> for editoast_common::tracing::Telemetry {
fn from(telemetry_config: TelemetryConfig) -> Self {
Self {
service_name: telemetry_config.service_name,
endpoint: telemetry_config.telemetry_endpoint,
}
}
}

#[derive(Default, ValueEnum, Debug, Derivative, Clone, strum::Display)]
#[strum(serialize_all = "lowercase")]
pub enum TelemetryKind {
Expand Down
88 changes: 26 additions & 62 deletions editoast/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,29 +30,21 @@ use client::user::UserCommand;
use client::Client;
use client::Color;
use client::Commands;
use editoast_common::tracing::create_tracing_subscriber;
use editoast_common::tracing::TracingConfig;
use editoast_models::DbConnectionPoolV2;
use models::RollingStockModel;
use opentelemetry::trace::TracerProvider as _;
use opentelemetry_sdk::propagation::TraceContextPropagator;
use opentelemetry_sdk::resource::EnvResourceDetector;
use opentelemetry_sdk::resource::SdkProvidedResourceDetector;
use opentelemetry_sdk::resource::TelemetryResourceDetector;
use tracing_subscriber::util::SubscriberInitExt;
pub use views::AppState;

use models::prelude::*;
use opentelemetry::KeyValue;
use opentelemetry_otlp::WithExportConfig as _;
use opentelemetry_sdk::Resource;
use std::error::Error;
use std::io::IsTerminal;
use std::process::exit;
use std::sync::Arc;
use std::time::Duration;
use thiserror::Error;
use tracing::error;
use tracing_subscriber::layer::SubscriberExt as _;
use tracing_subscriber::util::SubscriberInitExt as _;
use tracing_subscriber::Layer as _;
pub use valkey_utils::ValkeyClient;
pub use valkey_utils::ValkeyConnection;

Expand All @@ -66,61 +58,18 @@ pub use valkey_utils::ValkeyConnection;
/// - we *expect* a webserver to output logging information, so since it's an expected
/// output (and not extra information), it should be on stdout
#[derive(Debug, PartialEq)]
enum EditoastMode {
pub enum EditoastMode {
Webservice,
Cli,
}

fn init_tracing(mode: EditoastMode, telemetry_config: &client::TelemetryConfig) {
let env_filter_layer = tracing_subscriber::EnvFilter::builder()
// Set the default log level to 'info'
.with_default_directive(tracing_subscriber::filter::LevelFilter::INFO.into())
.from_env_lossy();
let fmt_layer = tracing_subscriber::fmt::layer()
.pretty()
.with_file(true)
.with_line_number(false);
let fmt_layer = if mode == EditoastMode::Cli {
fmt_layer.with_writer(std::io::stderr).boxed()
} else {
fmt_layer.boxed()
};
// https://docs.rs/tracing-subscriber/latest/tracing_subscriber/layer/index.html#runtime-configuration-with-layers
let telemetry_layer = match telemetry_config.telemetry_kind {
client::TelemetryKind::None => None,
client::TelemetryKind::Opentelemetry => {
let exporter = opentelemetry_otlp::SpanExporter::builder()
.with_tonic()
.with_endpoint(telemetry_config.telemetry_endpoint.as_str())
.build()
.expect("failed to build a span exporter");
let resource = Resource::new(vec![KeyValue::new(
opentelemetry_semantic_conventions::resource::SERVICE_NAME,
telemetry_config.service_name.clone(),
)])
.merge(&Resource::from_detectors(
Duration::from_secs(10),
vec![
Box::new(SdkProvidedResourceDetector),
Box::new(TelemetryResourceDetector),
Box::new(EnvResourceDetector::new()),
],
));
let otlp_tracer = opentelemetry_sdk::trace::TracerProvider::builder()
.with_batch_exporter(exporter, opentelemetry_sdk::runtime::Tokio)
.with_resource(resource)
.build()
.tracer("osrd-editoast");
let layer = tracing_opentelemetry::OpenTelemetryLayer::new(otlp_tracer);
opentelemetry::global::set_text_map_propagator(TraceContextPropagator::new());
Some(layer)
impl From<EditoastMode> for editoast_common::tracing::Stream {
fn from(mode: EditoastMode) -> Self {
match mode {
EditoastMode::Webservice => Self::Stdout,
EditoastMode::Cli => Self::Stderr,
}
};
tracing_subscriber::registry()
.with(telemetry_layer)
.with(env_filter_layer)
.with(fmt_layer)
.init();
}
}

impl EditoastMode {
Expand Down Expand Up @@ -151,7 +100,22 @@ async fn main() {

async fn run() -> Result<(), Box<dyn Error + Send + Sync>> {
let client = Client::parse();
init_tracing(EditoastMode::from_client(&client), &client.telemetry_config);
let exporter = opentelemetry_otlp::SpanExporter::builder()
.with_tonic()
.with_endpoint(client.telemetry_config.telemetry_endpoint.as_str())
.build()
.expect("failed to build a span exporter");

let telemetry = match client.telemetry_config.telemetry_kind {
client::TelemetryKind::None => None,
client::TelemetryKind::Opentelemetry => Some(client.telemetry_config.clone().into()),
};

let tracing_config = TracingConfig {
stream: EditoastMode::from_client(&client).into(),
telemetry,
};
create_tracing_subscriber(tracing_config, exporter).init();

let pg_config = client.postgres_config;
let db_pool =
Expand Down
2 changes: 1 addition & 1 deletion editoast/src/views/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ pub mod train_schedule;
pub mod work_schedules;

#[cfg(test)]
mod test_app;
pub mod test_app;

use ::core::str;
use std::collections::HashSet;
Expand Down
25 changes: 16 additions & 9 deletions editoast/src/views/test_app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,14 @@ use std::sync::Arc;
use axum::Router;
use axum_tracing_opentelemetry::middleware::OtelAxumLayer;
use dashmap::DashMap;
use editoast_common::tracing::create_tracing_subscriber;
use editoast_common::tracing::TracingConfig;
use editoast_models::DbConnectionPoolV2;
use editoast_osrdyne_client::OsrdyneClient;
use futures::future::BoxFuture;
use opentelemetry_sdk::export::trace::ExportResult;
use opentelemetry_sdk::export::trace::SpanData;
use opentelemetry_sdk::export::trace::SpanExporter;
use serde::de::DeserializeOwned;
use tower_http::trace::TraceLayer;
use url::Url;
Expand All @@ -26,6 +32,15 @@ use axum_test::TestServer;

use super::{authentication_middleware, CoreConfig, OsrdyneConfig, PostgresConfig, ServerConfig};

#[derive(Debug)]
pub struct NoopSpanExporter;

impl SpanExporter for NoopSpanExporter {
fn export(&mut self, _: Vec<SpanData>) -> BoxFuture<'static, ExportResult> {
Box::pin(std::future::ready(Ok(())))
}
}

/// A builder interface for [TestApp]
///
/// It allows configuring some parameters for the app service.
Expand Down Expand Up @@ -106,15 +121,7 @@ impl TestAppBuilder {
};

// Setup tracing
let sub = tracing_subscriber::fmt()
.pretty()
.with_env_filter(
tracing_subscriber::EnvFilter::builder()
.with_default_directive(tracing_subscriber::filter::LevelFilter::DEBUG.into())
.from_env_lossy(),
)
.with_span_events(tracing_subscriber::fmt::format::FmtSpan::CLOSE)
.finish();
let sub = create_tracing_subscriber(TracingConfig::default(), NoopSpanExporter);
let tracing_guard = tracing::subscriber::set_default(sub);

// Config valkey
Expand Down

0 comments on commit 72ae856

Please sign in to comment.