use anyhow::{anyhow, Result}; use clap::Parser; use dropshot::endpoint; use dropshot::ApiDescription; use dropshot::ConfigDropshot; use dropshot::HttpError; use dropshot::HttpResponseOk; use dropshot::RequestContext; use dropshot::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; #[derive(Parser, Debug)] #[command(version, about, long_about = None)] struct Cli {} /// Represents a project in our API. #[derive(Serialize, JsonSchema)] struct VersionInfo { name: String, } /// Fetch version info. #[endpoint { method = GET, path = "/version", }] #[tracing::instrument( skip(rqctx), fields( http.method=rqctx.request.method().as_str(), http.path=rqctx.request.uri().path(), http.remote_ip=rqctx.request.remote_addr().ip().to_string(), request_id = rqctx.request_id, ), err(Debug), )] async fn api_version( rqctx: RequestContext<Arc<()>>, ) -> Result<HttpResponseOk<VersionInfo>, HttpError> { let ver = VersionInfo { name: String::from("patagia-controller"), }; tracing::info!("Someone made a request to /version"); Ok(HttpResponseOk(ver)) } #[tokio::main] async fn main() -> Result<()> { let _args = Cli::parse(); let fmt_layer = tracing_subscriber::fmt::layer(); let mut config_dropshot = ConfigDropshot::default(); config_dropshot.bind_address = SocketAddr::from_str("0.0.0.0:9474").unwrap(); config_dropshot.request_body_max_bytes = 1024 * 1024; // Adapt the Dropshot logger to tracing let dropshot_logger = { let level_drain = slog::LevelFilter(TracingSlogDrain, slog::Level::Debug).fuse(); let async_drain = slog_async::Async::new(level_drain).build().fuse(); slog::Logger::root(async_drain, slog::o!()) }; 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(); tracing::info!("Patagia Controller"); let mut api = ApiDescription::new(); api.register(api_version).unwrap(); ServerBuilder::new(api, Arc::new(()), dropshot_logger) .config(config_dropshot) .start() .map_err(|e| anyhow!("Error starting server: {:?}", e))? .await .map_err(|e| anyhow!(e)) }