Add OpenTelemetry tracing and log export via OTLP
Shared telemetry init module in compliance-core (behind `telemetry` feature) sets up OTLP/gRPC export for traces and logs when OTEL_EXPORTER_OTLP_ENDPOINT is set. Falls back to console-only output when unset. Both agent and dashboard now use the shared init. Docker Compose includes an OTel Collector service with a config template for SigNoz, Grafana Tempo/Loki, Jaeger, etc. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
205
Cargo.lock
generated
205
Cargo.lock
generated
@@ -167,7 +167,7 @@ dependencies = [
|
|||||||
"sync_wrapper",
|
"sync_wrapper",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-tungstenite 0.28.0",
|
"tokio-tungstenite 0.28.0",
|
||||||
"tower",
|
"tower 0.5.3",
|
||||||
"tower-layer",
|
"tower-layer",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
"tracing",
|
"tracing",
|
||||||
@@ -582,11 +582,18 @@ dependencies = [
|
|||||||
"chrono",
|
"chrono",
|
||||||
"hex",
|
"hex",
|
||||||
"mongodb",
|
"mongodb",
|
||||||
|
"opentelemetry",
|
||||||
|
"opentelemetry-appender-tracing",
|
||||||
|
"opentelemetry-otlp",
|
||||||
|
"opentelemetry_sdk",
|
||||||
"secrecy",
|
"secrecy",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha2",
|
"sha2",
|
||||||
"thiserror 2.0.18",
|
"thiserror 2.0.18",
|
||||||
|
"tracing",
|
||||||
|
"tracing-opentelemetry",
|
||||||
|
"tracing-subscriber",
|
||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1316,7 +1323,7 @@ dependencies = [
|
|||||||
"tokio-stream",
|
"tokio-stream",
|
||||||
"tokio-tungstenite 0.27.0",
|
"tokio-tungstenite 0.27.0",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
"tower",
|
"tower 0.5.3",
|
||||||
"tower-http",
|
"tower-http",
|
||||||
"tower-layer",
|
"tower-layer",
|
||||||
"tracing",
|
"tracing",
|
||||||
@@ -1607,7 +1614,7 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
"tokio-tungstenite 0.27.0",
|
"tokio-tungstenite 0.27.0",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
"tower",
|
"tower 0.5.3",
|
||||||
"tower-http",
|
"tower-http",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-futures",
|
"tracing-futures",
|
||||||
@@ -2104,6 +2111,12 @@ dependencies = [
|
|||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "glob"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gloo-net"
|
name = "gloo-net"
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
@@ -3472,7 +3485,7 @@ dependencies = [
|
|||||||
"serde_urlencoded",
|
"serde_urlencoded",
|
||||||
"snafu",
|
"snafu",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tower",
|
"tower 0.5.3",
|
||||||
"tower-http",
|
"tower-http",
|
||||||
"tracing",
|
"tracing",
|
||||||
"url",
|
"url",
|
||||||
@@ -3519,6 +3532,98 @@ dependencies = [
|
|||||||
"vcpkg",
|
"vcpkg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "opentelemetry"
|
||||||
|
version = "0.29.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9e87237e2775f74896f9ad219d26a2081751187eb7c9f5c58dde20a23b95d16c"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"futures-sink",
|
||||||
|
"js-sys",
|
||||||
|
"pin-project-lite",
|
||||||
|
"thiserror 2.0.18",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "opentelemetry-appender-tracing"
|
||||||
|
version = "0.29.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e716f864eb23007bdd9dc4aec381e188a1cee28eecf22066772b5fd822b9727d"
|
||||||
|
dependencies = [
|
||||||
|
"opentelemetry",
|
||||||
|
"tracing",
|
||||||
|
"tracing-core",
|
||||||
|
"tracing-subscriber",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "opentelemetry-http"
|
||||||
|
version = "0.29.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "46d7ab32b827b5b495bd90fa95a6cb65ccc293555dcc3199ae2937d2d237c8ed"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"bytes",
|
||||||
|
"http",
|
||||||
|
"opentelemetry",
|
||||||
|
"reqwest",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "opentelemetry-otlp"
|
||||||
|
version = "0.29.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d899720fe06916ccba71c01d04ecd77312734e2de3467fd30d9d580c8ce85656"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"http",
|
||||||
|
"opentelemetry",
|
||||||
|
"opentelemetry-http",
|
||||||
|
"opentelemetry-proto",
|
||||||
|
"opentelemetry_sdk",
|
||||||
|
"prost",
|
||||||
|
"reqwest",
|
||||||
|
"thiserror 2.0.18",
|
||||||
|
"tokio",
|
||||||
|
"tonic",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "opentelemetry-proto"
|
||||||
|
version = "0.29.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8c40da242381435e18570d5b9d50aca2a4f4f4d8e146231adb4e7768023309b3"
|
||||||
|
dependencies = [
|
||||||
|
"opentelemetry",
|
||||||
|
"opentelemetry_sdk",
|
||||||
|
"prost",
|
||||||
|
"tonic",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "opentelemetry_sdk"
|
||||||
|
version = "0.29.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "afdefb21d1d47394abc1ba6c57363ab141be19e27cc70d0e422b7f303e4d290b"
|
||||||
|
dependencies = [
|
||||||
|
"futures-channel",
|
||||||
|
"futures-executor",
|
||||||
|
"futures-util",
|
||||||
|
"glob",
|
||||||
|
"opentelemetry",
|
||||||
|
"percent-encoding",
|
||||||
|
"rand 0.9.2",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror 2.0.18",
|
||||||
|
"tokio",
|
||||||
|
"tokio-stream",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ownedbytes"
|
name = "ownedbytes"
|
||||||
version = "0.7.0"
|
version = "0.7.0"
|
||||||
@@ -3752,6 +3857,29 @@ dependencies = [
|
|||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "prost"
|
||||||
|
version = "0.13.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"prost-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "prost-derive"
|
||||||
|
version = "0.13.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"itertools",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "psl-types"
|
name = "psl-types"
|
||||||
version = "2.0.11"
|
version = "2.0.11"
|
||||||
@@ -4007,6 +4135,7 @@ dependencies = [
|
|||||||
"bytes",
|
"bytes",
|
||||||
"cookie",
|
"cookie",
|
||||||
"cookie_store",
|
"cookie_store",
|
||||||
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"http",
|
"http",
|
||||||
@@ -4030,7 +4159,7 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
"tokio-rustls",
|
"tokio-rustls",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
"tower",
|
"tower 0.5.3",
|
||||||
"tower-http",
|
"tower-http",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
"url",
|
"url",
|
||||||
@@ -5211,6 +5340,52 @@ dependencies = [
|
|||||||
"winnow",
|
"winnow",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tonic"
|
||||||
|
version = "0.12.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"base64",
|
||||||
|
"bytes",
|
||||||
|
"http",
|
||||||
|
"http-body",
|
||||||
|
"http-body-util",
|
||||||
|
"hyper",
|
||||||
|
"hyper-timeout",
|
||||||
|
"hyper-util",
|
||||||
|
"percent-encoding",
|
||||||
|
"pin-project",
|
||||||
|
"prost",
|
||||||
|
"tokio",
|
||||||
|
"tokio-stream",
|
||||||
|
"tower 0.4.13",
|
||||||
|
"tower-layer",
|
||||||
|
"tower-service",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tower"
|
||||||
|
version = "0.4.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"futures-util",
|
||||||
|
"indexmap 1.9.3",
|
||||||
|
"pin-project",
|
||||||
|
"pin-project-lite",
|
||||||
|
"rand 0.8.5",
|
||||||
|
"slab",
|
||||||
|
"tokio",
|
||||||
|
"tokio-util",
|
||||||
|
"tower-layer",
|
||||||
|
"tower-service",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tower"
|
name = "tower"
|
||||||
version = "0.5.3"
|
version = "0.5.3"
|
||||||
@@ -5250,7 +5425,7 @@ dependencies = [
|
|||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
"tower",
|
"tower 0.5.3",
|
||||||
"tower-layer",
|
"tower-layer",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
"tracing",
|
"tracing",
|
||||||
@@ -5322,6 +5497,24 @@ dependencies = [
|
|||||||
"tracing-core",
|
"tracing-core",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-opentelemetry"
|
||||||
|
version = "0.30.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fd8e764bd6f5813fd8bebc3117875190c5b0415be8f7f8059bffb6ecd979c444"
|
||||||
|
dependencies = [
|
||||||
|
"js-sys",
|
||||||
|
"once_cell",
|
||||||
|
"opentelemetry",
|
||||||
|
"opentelemetry_sdk",
|
||||||
|
"smallvec",
|
||||||
|
"tracing",
|
||||||
|
"tracing-core",
|
||||||
|
"tracing-log",
|
||||||
|
"tracing-subscriber",
|
||||||
|
"web-time",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing-subscriber"
|
name = "tracing-subscriber"
|
||||||
version = "0.3.22"
|
version = "0.3.22"
|
||||||
|
|||||||
@@ -2,10 +2,9 @@
|
|||||||
|
|
||||||
#[allow(clippy::expect_used)]
|
#[allow(clippy::expect_used)]
|
||||||
fn main() {
|
fn main() {
|
||||||
dioxus_logger::init(tracing::Level::DEBUG).expect("Failed to init logger");
|
|
||||||
|
|
||||||
#[cfg(feature = "web")]
|
#[cfg(feature = "web")]
|
||||||
{
|
{
|
||||||
|
dioxus_logger::init(tracing::Level::DEBUG).expect("Failed to init logger");
|
||||||
dioxus::web::launch::launch_cfg(
|
dioxus::web::launch::launch_cfg(
|
||||||
compliance_dashboard::App,
|
compliance_dashboard::App,
|
||||||
dioxus::web::Config::new().hydrate(true),
|
dioxus::web::Config::new().hydrate(true),
|
||||||
@@ -14,6 +13,10 @@ fn main() {
|
|||||||
|
|
||||||
#[cfg(feature = "server")]
|
#[cfg(feature = "server")]
|
||||||
{
|
{
|
||||||
|
dotenvy::dotenv().ok();
|
||||||
|
let _telemetry_guard =
|
||||||
|
compliance_core::telemetry::init_telemetry("compliance-dashboard");
|
||||||
|
|
||||||
compliance_dashboard::infrastructure::server_start(compliance_dashboard::App)
|
compliance_dashboard::infrastructure::server_start(compliance_dashboard::App)
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
tracing::error!("Unable to start server: {e}");
|
tracing::error!("Unable to start server: {e}");
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ edition = "2021"
|
|||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
compliance-core = { workspace = true, features = ["mongodb"] }
|
compliance-core = { workspace = true, features = ["mongodb", "telemetry"] }
|
||||||
compliance-graph = { path = "../compliance-graph" }
|
compliance-graph = { path = "../compliance-graph" }
|
||||||
compliance-dast = { path = "../compliance-dast" }
|
compliance-dast = { path = "../compliance-dast" }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
use tracing_subscriber::EnvFilter;
|
|
||||||
|
|
||||||
mod agent;
|
mod agent;
|
||||||
mod api;
|
mod api;
|
||||||
mod config;
|
mod config;
|
||||||
@@ -15,14 +13,10 @@ mod webhooks;
|
|||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
tracing_subscriber::fmt()
|
|
||||||
.with_env_filter(
|
|
||||||
EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")),
|
|
||||||
)
|
|
||||||
.init();
|
|
||||||
|
|
||||||
dotenvy::dotenv().ok();
|
dotenvy::dotenv().ok();
|
||||||
|
|
||||||
|
let _telemetry_guard = compliance_core::telemetry::init_telemetry("compliance-agent");
|
||||||
|
|
||||||
tracing::info!("Loading configuration...");
|
tracing::info!("Loading configuration...");
|
||||||
let config = config::load_config()?;
|
let config = config::load_config()?;
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,15 @@ workspace = true
|
|||||||
[features]
|
[features]
|
||||||
default = ["mongodb"]
|
default = ["mongodb"]
|
||||||
mongodb = ["dep:mongodb"]
|
mongodb = ["dep:mongodb"]
|
||||||
|
telemetry = [
|
||||||
|
"dep:opentelemetry",
|
||||||
|
"dep:opentelemetry_sdk",
|
||||||
|
"dep:opentelemetry-otlp",
|
||||||
|
"dep:opentelemetry-appender-tracing",
|
||||||
|
"dep:tracing-opentelemetry",
|
||||||
|
"dep:tracing-subscriber",
|
||||||
|
"dep:tracing",
|
||||||
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
@@ -21,3 +30,10 @@ uuid = { workspace = true }
|
|||||||
secrecy = { workspace = true }
|
secrecy = { workspace = true }
|
||||||
bson = { version = "2", features = ["chrono-0_4"] }
|
bson = { version = "2", features = ["chrono-0_4"] }
|
||||||
mongodb = { workspace = true, optional = true }
|
mongodb = { workspace = true, optional = true }
|
||||||
|
opentelemetry = { version = "0.29", optional = true }
|
||||||
|
opentelemetry_sdk = { version = "0.29", features = ["rt-tokio"], optional = true }
|
||||||
|
opentelemetry-otlp = { version = "0.29", features = ["grpc-tonic"], optional = true }
|
||||||
|
opentelemetry-appender-tracing = { version = "0.29", optional = true }
|
||||||
|
tracing-opentelemetry = { version = "0.30", optional = true }
|
||||||
|
tracing-subscriber = { workspace = true, optional = true }
|
||||||
|
tracing = { workspace = true, optional = true }
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod models;
|
pub mod models;
|
||||||
|
#[cfg(feature = "telemetry")]
|
||||||
|
pub mod telemetry;
|
||||||
pub mod traits;
|
pub mod traits;
|
||||||
|
|
||||||
pub use config::{AgentConfig, DashboardConfig};
|
pub use config::{AgentConfig, DashboardConfig};
|
||||||
|
|||||||
159
compliance-core/src/telemetry.rs
Normal file
159
compliance-core/src/telemetry.rs
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
//! OpenTelemetry initialization for traces and logs.
|
||||||
|
//!
|
||||||
|
//! Exports traces and logs via OTLP (gRPC) when `OTEL_EXPORTER_OTLP_ENDPOINT`
|
||||||
|
//! is set. Always includes a `tracing_subscriber::fmt` layer for console output.
|
||||||
|
//!
|
||||||
|
//! Compatible with SigNoz, Grafana Tempo/Loki, Jaeger, and any OTLP-compatible
|
||||||
|
//! collector.
|
||||||
|
//!
|
||||||
|
//! # Environment Variables
|
||||||
|
//!
|
||||||
|
//! | Variable | Description | Default |
|
||||||
|
//! |---|---|---|
|
||||||
|
//! | `OTEL_EXPORTER_OTLP_ENDPOINT` | OTLP collector endpoint (e.g. `http://localhost:4317`) | *(disabled)* |
|
||||||
|
//! | `OTEL_SERVICE_NAME` | Service name for resource | `service_name` param |
|
||||||
|
//! | `RUST_LOG` / standard `EnvFilter` | Log level filter | `info` |
|
||||||
|
|
||||||
|
use opentelemetry::global;
|
||||||
|
use opentelemetry::trace::TracerProvider as _;
|
||||||
|
use opentelemetry::KeyValue;
|
||||||
|
use opentelemetry_appender_tracing::layer::OpenTelemetryTracingBridge;
|
||||||
|
use opentelemetry_otlp::{LogExporter, SpanExporter, WithExportConfig};
|
||||||
|
use opentelemetry_sdk::{
|
||||||
|
logs::SdkLoggerProvider,
|
||||||
|
trace::SdkTracerProvider,
|
||||||
|
Resource,
|
||||||
|
};
|
||||||
|
use tracing_opentelemetry::OpenTelemetryLayer;
|
||||||
|
use tracing_subscriber::{
|
||||||
|
layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Layer as _,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Guard that shuts down OTel providers on drop.
|
||||||
|
///
|
||||||
|
/// Must be held for the lifetime of the application. When dropped,
|
||||||
|
/// flushes and shuts down the tracer and logger providers.
|
||||||
|
pub struct TelemetryGuard {
|
||||||
|
tracer_provider: Option<SdkTracerProvider>,
|
||||||
|
logger_provider: Option<SdkLoggerProvider>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for TelemetryGuard {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if let Some(tp) = self.tracer_provider.take() {
|
||||||
|
if let Err(e) = tp.shutdown() {
|
||||||
|
eprintln!("Failed to shutdown tracer provider: {e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(lp) = self.logger_provider.take() {
|
||||||
|
if let Err(e) = lp.shutdown() {
|
||||||
|
eprintln!("Failed to shutdown logger provider: {e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_resource(service_name: &str) -> Resource {
|
||||||
|
let name = std::env::var("OTEL_SERVICE_NAME").unwrap_or_else(|_| service_name.to_string());
|
||||||
|
Resource::builder()
|
||||||
|
.with_service_name(name)
|
||||||
|
.with_attributes([KeyValue::new(
|
||||||
|
"service.version",
|
||||||
|
env!("CARGO_PKG_VERSION"),
|
||||||
|
)])
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialize telemetry (tracing + logging).
|
||||||
|
///
|
||||||
|
/// If `OTEL_EXPORTER_OTLP_ENDPOINT` is set, traces and logs are exported
|
||||||
|
/// via OTLP/gRPC. Console fmt output is always enabled.
|
||||||
|
///
|
||||||
|
/// Returns a [`TelemetryGuard`] that must be held alive for the application
|
||||||
|
/// lifetime. Dropping it triggers a graceful shutdown of OTel providers.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the tracing subscriber cannot be initialized (e.g. called twice).
|
||||||
|
pub fn init_telemetry(service_name: &str) -> TelemetryGuard {
|
||||||
|
let otel_endpoint = std::env::var("OTEL_EXPORTER_OTLP_ENDPOINT").ok();
|
||||||
|
|
||||||
|
let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"));
|
||||||
|
let fmt_layer = tracing_subscriber::fmt::layer();
|
||||||
|
|
||||||
|
match otel_endpoint {
|
||||||
|
Some(ref endpoint) => {
|
||||||
|
let resource = build_resource(service_name);
|
||||||
|
|
||||||
|
// Traces
|
||||||
|
#[allow(clippy::expect_used)]
|
||||||
|
let span_exporter = SpanExporter::builder()
|
||||||
|
.with_tonic()
|
||||||
|
.with_endpoint(endpoint)
|
||||||
|
.build()
|
||||||
|
.expect("failed to create OTLP span exporter");
|
||||||
|
|
||||||
|
let tracer_provider = SdkTracerProvider::builder()
|
||||||
|
.with_batch_exporter(span_exporter)
|
||||||
|
.with_resource(resource.clone())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
global::set_tracer_provider(tracer_provider.clone());
|
||||||
|
let tracer = tracer_provider.tracer(service_name.to_string());
|
||||||
|
let otel_trace_layer = OpenTelemetryLayer::new(tracer);
|
||||||
|
|
||||||
|
// Logs
|
||||||
|
#[allow(clippy::expect_used)]
|
||||||
|
let log_exporter = LogExporter::builder()
|
||||||
|
.with_tonic()
|
||||||
|
.with_endpoint(endpoint)
|
||||||
|
.build()
|
||||||
|
.expect("failed to create OTLP log exporter");
|
||||||
|
|
||||||
|
let logger_provider = SdkLoggerProvider::builder()
|
||||||
|
.with_batch_exporter(log_exporter)
|
||||||
|
.with_resource(resource)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let otel_log_layer = OpenTelemetryTracingBridge::new(&logger_provider);
|
||||||
|
|
||||||
|
// Filter to prevent telemetry-induced-telemetry loops
|
||||||
|
let otel_filter = EnvFilter::new("info")
|
||||||
|
.add_directive("hyper=off".parse().unwrap_or_default())
|
||||||
|
.add_directive("tonic=off".parse().unwrap_or_default())
|
||||||
|
.add_directive("h2=off".parse().unwrap_or_default())
|
||||||
|
.add_directive("reqwest=off".parse().unwrap_or_default());
|
||||||
|
|
||||||
|
tracing_subscriber::registry()
|
||||||
|
.with(env_filter)
|
||||||
|
.with(fmt_layer)
|
||||||
|
.with(otel_trace_layer)
|
||||||
|
.with(otel_log_layer.with_filter(otel_filter))
|
||||||
|
.init();
|
||||||
|
|
||||||
|
tracing::info!(
|
||||||
|
endpoint = endpoint.as_str(),
|
||||||
|
service = service_name,
|
||||||
|
"OpenTelemetry OTLP export enabled"
|
||||||
|
);
|
||||||
|
|
||||||
|
TelemetryGuard {
|
||||||
|
tracer_provider: Some(tracer_provider),
|
||||||
|
logger_provider: Some(logger_provider),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
tracing_subscriber::registry()
|
||||||
|
.with(env_filter)
|
||||||
|
.with(fmt_layer)
|
||||||
|
.init();
|
||||||
|
|
||||||
|
tracing::info!("OpenTelemetry disabled (set OTEL_EXPORTER_OTLP_ENDPOINT to enable)");
|
||||||
|
|
||||||
|
TelemetryGuard {
|
||||||
|
tracer_provider: None,
|
||||||
|
logger_provider: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,6 +18,7 @@ server = [
|
|||||||
"dioxus/router",
|
"dioxus/router",
|
||||||
"dioxus/fullstack",
|
"dioxus/fullstack",
|
||||||
"compliance-core/mongodb",
|
"compliance-core/mongodb",
|
||||||
|
"compliance-core/telemetry",
|
||||||
"dep:axum",
|
"dep:axum",
|
||||||
"dep:mongodb",
|
"dep:mongodb",
|
||||||
"dep:reqwest",
|
"dep:reqwest",
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ services:
|
|||||||
- "3001:3001"
|
- "3001:3001"
|
||||||
- "3002:3002"
|
- "3002:3002"
|
||||||
env_file: .env
|
env_file: .env
|
||||||
|
environment:
|
||||||
|
OTEL_EXPORTER_OTLP_ENDPOINT: http://otel-collector:4317
|
||||||
|
OTEL_SERVICE_NAME: compliance-agent
|
||||||
depends_on:
|
depends_on:
|
||||||
- mongo
|
- mongo
|
||||||
volumes:
|
volumes:
|
||||||
@@ -29,6 +32,9 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "8080:8080"
|
- "8080:8080"
|
||||||
env_file: .env
|
env_file: .env
|
||||||
|
environment:
|
||||||
|
OTEL_EXPORTER_OTLP_ENDPOINT: http://otel-collector:4317
|
||||||
|
OTEL_SERVICE_NAME: compliance-dashboard
|
||||||
depends_on:
|
depends_on:
|
||||||
- mongo
|
- mongo
|
||||||
- agent
|
- agent
|
||||||
@@ -43,6 +49,15 @@ services:
|
|||||||
PREBOOT_CHROME: "true"
|
PREBOOT_CHROME: "true"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
|
otel-collector:
|
||||||
|
image: otel/opentelemetry-collector-contrib:latest
|
||||||
|
ports:
|
||||||
|
- "4317:4317"
|
||||||
|
- "4318:4318"
|
||||||
|
volumes:
|
||||||
|
- ./otel-collector-config.yaml:/etc/otelcol-contrib/config.yaml
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
mongo_data:
|
mongo_data:
|
||||||
repos_data:
|
repos_data:
|
||||||
|
|||||||
52
otel-collector-config.yaml
Normal file
52
otel-collector-config.yaml
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
receivers:
|
||||||
|
otlp:
|
||||||
|
protocols:
|
||||||
|
grpc:
|
||||||
|
endpoint: 0.0.0.0:4317
|
||||||
|
http:
|
||||||
|
endpoint: 0.0.0.0:4318
|
||||||
|
|
||||||
|
processors:
|
||||||
|
batch:
|
||||||
|
timeout: 5s
|
||||||
|
send_batch_size: 1024
|
||||||
|
|
||||||
|
exporters:
|
||||||
|
# Log to stdout for debugging
|
||||||
|
debug:
|
||||||
|
verbosity: basic
|
||||||
|
|
||||||
|
# Configure your backend below. Examples:
|
||||||
|
#
|
||||||
|
# SigNoz:
|
||||||
|
# otlp/signoz:
|
||||||
|
# endpoint: "signoz-otel-collector:4317"
|
||||||
|
# tls:
|
||||||
|
# insecure: true
|
||||||
|
#
|
||||||
|
# Grafana Tempo (traces):
|
||||||
|
# otlp/tempo:
|
||||||
|
# endpoint: "tempo:4317"
|
||||||
|
# tls:
|
||||||
|
# insecure: true
|
||||||
|
#
|
||||||
|
# Grafana Loki (logs):
|
||||||
|
# loki:
|
||||||
|
# endpoint: "http://loki:3100/loki/api/v1/push"
|
||||||
|
#
|
||||||
|
# Jaeger:
|
||||||
|
# otlp/jaeger:
|
||||||
|
# endpoint: "jaeger:4317"
|
||||||
|
# tls:
|
||||||
|
# insecure: true
|
||||||
|
|
||||||
|
service:
|
||||||
|
pipelines:
|
||||||
|
traces:
|
||||||
|
receivers: [otlp]
|
||||||
|
processors: [batch]
|
||||||
|
exporters: [debug]
|
||||||
|
logs:
|
||||||
|
receivers: [otlp]
|
||||||
|
processors: [batch]
|
||||||
|
exporters: [debug]
|
||||||
Reference in New Issue
Block a user