Factor out instrumentation code. Add metrics and logging to otel.

This commit is contained in:
Daniel Lundin 2024-11-27 16:31:04 +01:00
parent 421bed1611
commit 2da3a239cd
Signed by: dln
SSH key fingerprint: SHA256:dQy1Xj3UiqJYpKR5ggQ2bxgz4jCH8IF+k3AB8o0kmdI
6 changed files with 200 additions and 50 deletions

View file

@ -1,5 +1,6 @@
[package]
name = "patagia-controller"
description = "Patagia controller server"
version = "0.1.0"
edition = "2021"
license = "MPL-2.0"
@ -23,6 +24,10 @@ opentelemetry_sdk = { version = "0.27.0", features = ["rt-tokio"] }
opentelemetry-otlp = { version = "0.27.0", features = ["http-proto", "hyper-client", "opentelemetry-http", "trace"] }
opentelemetry = "0.27.0"
opentelemetry-semantic-conventions = "0.27.0"
tracing-core = "0.1.33"
opentelemetry-appender-tracing = { version = "0.27.0", features = ["log", "experimental_metadata_attributes"] }
opentelemetry-stdout = "0.27.0"
once_cell = "1.20.2"
[[bin]]
name = "patagia-controller"

View file

@ -4,27 +4,17 @@ use dropshot::{
endpoint, ApiDescription, ConfigDropshot, HttpError, HttpResponseOk, RequestContext,
ServerBuilder,
};
use opentelemetry::{trace::TracerProvider as _, KeyValue};
use opentelemetry_otlp::WithExportConfig;
use opentelemetry_sdk::{
trace::{RandomIdGenerator, Sampler},
Resource,
};
use opentelemetry_semantic_conventions::{
attribute::{SERVICE_NAME, SERVICE_VERSION},
SCHEMA_URL,
};
use schemars::JsonSchema;
use serde::Serialize;
use slog::Drain;
use tracing_opentelemetry::OpenTelemetryLayer;
use tracing_slog::TracingSlogDrain;
use tracing_subscriber::prelude::*;
use std::net::SocketAddr;
use std::str::FromStr;
use std::sync::Arc;
use patagia_controller::instrumentation;
#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
struct Cli {}
@ -66,44 +56,16 @@ async fn api_version(
#[tokio::main]
async fn main() -> Result<()> {
let _args = Cli::parse();
let fmt_layer = tracing_subscriber::fmt::layer();
let otlp_exporter = opentelemetry_otlp::SpanExporter::builder()
.with_tonic()
.with_endpoint("https://localhost:4317")
.build()
.map_err(|e| anyhow!("Error creating OTLP exporter: {:?}", e))?;
let resource = Resource::from_schema_url(
[
KeyValue::new(SERVICE_NAME, env!("CARGO_PKG_NAME")),
KeyValue::new(SERVICE_VERSION, env!("CARGO_PKG_VERSION")),
],
SCHEMA_URL,
);
let tracer_provider = opentelemetry_sdk::trace::TracerProvider::builder()
.with_config(
opentelemetry_sdk::trace::Config::default()
.with_sampler(Sampler::ParentBased(Box::new(Sampler::TraceIdRatioBased(
1.0,
))))
.with_id_generator(RandomIdGenerator::default())
.with_resource(resource),
)
.with_batch_exporter(otlp_exporter, opentelemetry_sdk::runtime::Tokio)
.build();
let tracer = tracer_provider.tracer("patagia-controller");
tracing_subscriber::registry()
.with(tracing_subscriber::EnvFilter::from_default_env())
.with(fmt_layer)
.with(OpenTelemetryLayer::new(tracer))
.init();
let _tracing = instrumentation::init_tracing_subscriber()?;
tracing::info!("Patagia Controller");
tracing::debug!("Starting server");
tracing::error!(name: "my-event-name", target: "my-system", event_id = 20, user_name = "otel", user_email = "otel@opentelemetry.io", message = "This is an example message");
foo().await;
let mut api = ApiDescription::new();
api.register(api_version).unwrap();
@ -126,3 +88,15 @@ async fn main() -> Result<()> {
.await
.map_err(|e| anyhow!(e))
}
#[tracing::instrument]
async fn foo() {
tracing::info!(
monotonic_counter.foo = 1_u64,
key_1 = "bar",
key_2 = 10,
"handle foo",
);
tracing::info!(histogram.baz = 10, "histogram example",);
}

View file

@ -0,0 +1,126 @@
use anyhow::{anyhow, Result};
use once_cell::sync::Lazy;
use opentelemetry::{trace::TracerProvider as _, KeyValue};
use opentelemetry_appender_tracing::layer::OpenTelemetryTracingBridge;
use opentelemetry_sdk::{
logs::LoggerProvider,
metrics::{MeterProviderBuilder, PeriodicReader, SdkMeterProvider},
runtime,
trace::{RandomIdGenerator, Sampler, TracerProvider},
Resource,
};
use tracing_opentelemetry::{MetricsLayer, OpenTelemetryLayer};
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
static RESOURCE: Lazy<Resource> = Lazy::new(|| {
Resource::new(vec![
KeyValue::new(
opentelemetry_semantic_conventions::resource::SERVICE_NAME,
env!("CARGO_PKG_NAME"),
),
KeyValue::new(
opentelemetry_semantic_conventions::resource::SERVICE_VERSION,
env!("CARGO_PKG_VERSION"),
),
])
});
// Construct LoggingProdiver for LoggingLayer
fn init_logging_provider() -> Result<LoggerProvider> {
let exporter = opentelemetry_otlp::LogExporter::builder()
.with_tonic()
.build()?;
let logging_provider: LoggerProvider = LoggerProvider::builder()
.with_resource(RESOURCE.clone())
.with_batch_exporter(exporter, runtime::Tokio)
.build();
Ok(logging_provider)
}
// Construct MeterProvider for MetricsLayer
fn init_meter_provider() -> Result<SdkMeterProvider> {
let exporter = opentelemetry_otlp::MetricExporter::builder()
.with_tonic()
.with_temporality(opentelemetry_sdk::metrics::Temporality::default())
.build()
.map_err(|e| anyhow!("Error creating OTLP metric exporter: {:?}", e))?;
let meter_provider = MeterProviderBuilder::default()
.with_resource(RESOURCE.clone())
.with_reader(
PeriodicReader::builder(exporter, runtime::Tokio)
.with_interval(std::time::Duration::from_secs(10))
.build(),
)
.build();
opentelemetry::global::set_meter_provider(meter_provider.clone());
Ok(meter_provider)
}
// Construct TracerProvider for OpenTelemetryLayer
fn init_tracer_provider() -> Result<TracerProvider> {
let exporter = opentelemetry_otlp::SpanExporter::builder()
.with_tonic()
.build()
.map_err(|e| anyhow!("Error creating OTLP span exporter: {:?}", e))?;
let tracer_provider = opentelemetry_sdk::trace::TracerProvider::builder()
.with_config(
opentelemetry_sdk::trace::Config::default()
.with_sampler(Sampler::ParentBased(Box::new(Sampler::TraceIdRatioBased(
1.0,
))))
.with_id_generator(RandomIdGenerator::default())
.with_resource(RESOURCE.clone()),
)
.with_batch_exporter(exporter, opentelemetry_sdk::runtime::Tokio)
.build();
Ok(tracer_provider)
}
// Initialize tracing-subscriber and return OtelGuard for opentelemetry-related termination processing
pub fn init_tracing_subscriber() -> Result<OtelGuard> {
let logging_provider = init_logging_provider()?;
let meter_provider = init_meter_provider()?;
let tracer_provider = init_tracer_provider()?;
let tracer = tracer_provider.tracer("tracing-otel-subscriber");
tracing_subscriber::registry()
.with(tracing_subscriber::EnvFilter::from_default_env())
.with(tracing_subscriber::fmt::layer())
.with(MetricsLayer::new(meter_provider.clone()))
.with(OpenTelemetryLayer::new(tracer))
.with(OpenTelemetryTracingBridge::new(&logging_provider))
.init();
Ok(OtelGuard {
logging_provider,
meter_provider,
tracer_provider,
})
}
pub struct OtelGuard {
logging_provider: LoggerProvider,
meter_provider: SdkMeterProvider,
tracer_provider: TracerProvider,
}
impl Drop for OtelGuard {
fn drop(&mut self) {
if let Err(err) = self.tracer_provider.shutdown() {
eprintln!("{err:?}");
}
if let Err(err) = self.logging_provider.shutdown() {
eprintln!("{err:?}");
}
if let Err(err) = self.meter_provider.shutdown() {
eprintln!("{err:?}");
}
}
}

1
controller/src/lib.rs Normal file
View file

@ -0,0 +1 @@
pub mod instrumentation;