main.rs
1 mod ambient_weather; 2 mod args; 3 mod conditions; 4 pub mod metrics; 5 mod push_metrics; 6 mod root; 7 mod server; 8 mod sun; 9 10 use std::{io::IsTerminal, sync::OnceLock}; 11 12 pub use crate::{ 13 args::Args, conditions::Conditions, metrics::Metrics, push_metrics::PushMetrics, root::Root, 14 sun::Sun, 15 }; 16 use clap::Parser; 17 use opentelemetry::{ 18 KeyValue, 19 global::{set_text_map_propagator, set_tracer_provider}, 20 trace::TracerProvider, 21 }; 22 use opentelemetry_appender_tracing::layer::OpenTelemetryTracingBridge; 23 use opentelemetry_otlp::{LogExporter, Protocol, WithExportConfig}; 24 use opentelemetry_sdk::{ 25 Resource, 26 logs::SdkLoggerProvider, 27 propagation::TraceContextPropagator, 28 trace::{RandomIdGenerator, Sampler, SdkTracerProvider}, 29 }; 30 use opentelemetry_semantic_conventions::{SCHEMA_URL, resource::SERVICE_VERSION}; 31 use server::Server; 32 use tokio::signal::unix::SignalKind; 33 use tracing::{error, info, level_filters::LevelFilter}; 34 use tracing_opentelemetry::OpenTelemetryLayer; 35 use tracing_subscriber::{EnvFilter, Layer, Registry, prelude::*}; 36 37 #[tokio::main] 38 async fn main() -> std::io::Result<()> { 39 let metrics = Metrics::new(); 40 let (logger, tracer) = tracing(); 41 42 let args = Args::parse(); 43 44 info!(address = ?args.address, latitude = ?args.latitude, longitude = ?args.longitude, push_url = %args.push_url(), "arguments"); 45 46 let mut int = tokio::signal::unix::signal(SignalKind::interrupt())?; 47 let mut term = tokio::signal::unix::signal(SignalKind::terminate())?; 48 49 let server = Server::new(&args, &metrics).start(); 50 51 tokio::select! { 52 biased; 53 _ = int.recv() => { 54 info!("SIGINT"); 55 }, 56 _ = term.recv() => { 57 info!("SIGTERM"); 58 } 59 result = server => { 60 error!(?result, "server exited"); 61 } 62 } 63 64 logger.shutdown().ok(); 65 tracer.shutdown().ok(); 66 67 Ok(()) 68 } 69 70 fn get_resource() -> Resource { 71 static RESOURCE: OnceLock<Resource> = OnceLock::new(); 72 RESOURCE 73 .get_or_init(|| { 74 Resource::builder() 75 .with_service_name(env!("CARGO_PKG_NAME")) 76 .with_schema_url( 77 [KeyValue::new(SERVICE_VERSION, env!("CARGO_PKG_VERSION"))], 78 SCHEMA_URL, 79 ) 80 .build() 81 }) 82 .clone() 83 } 84 85 fn init_otel_logs() -> SdkLoggerProvider { 86 let exporter = LogExporter::builder() 87 .with_http() 88 .with_protocol(Protocol::HttpBinary) 89 .build() 90 .expect("Failed to create log exporter"); 91 92 SdkLoggerProvider::builder() 93 .with_batch_exporter(exporter) 94 .with_resource(get_resource()) 95 .build() 96 } 97 98 fn tracing() -> (SdkLoggerProvider, SdkTracerProvider) { 99 let logger_provider = init_otel_logs(); 100 101 let otel_layer = OpenTelemetryTracingBridge::new(&logger_provider); 102 103 let filter_otel = EnvFilter::new("info"); 104 // .add_directive("hyper=off".parse().unwrap()) 105 // .add_directive("tonic=off".parse().unwrap()) 106 // .add_directive("h2=off".parse().unwrap()) 107 // .add_directive("reqwest=off".parse().unwrap()); 108 let otel_logs = otel_layer.with_filter(filter_otel); 109 110 let env_filter = tracing_subscriber::EnvFilter::builder() 111 .with_default_directive(LevelFilter::INFO.into()) 112 .from_env_lossy(); 113 114 let stdout: Box<dyn Layer<Registry> + Send + Sync> = tracing_subscriber::fmt::layer() 115 .with_ansi(std::io::stdout().is_terminal()) 116 .with_timer( 117 tracing_subscriber::fmt::time::OffsetTime::local_rfc_3339() 118 .expect("could not get local offset!"), 119 ) 120 .with_filter(env_filter) 121 .boxed(); 122 123 let tracer_provider = init_tracer_provider(); 124 let tracer = tracer_provider.tracer(env!("CARGO_PKG_NAME")); 125 set_tracer_provider(tracer_provider.clone()); 126 127 let registry = tracing_subscriber::registry(); 128 129 registry 130 .with(stdout) 131 .with(otel_logs) 132 .with(OpenTelemetryLayer::new(tracer)) 133 .init(); 134 135 (logger_provider, tracer_provider) 136 } 137 138 fn init_tracer_provider() -> SdkTracerProvider { 139 set_text_map_propagator(TraceContextPropagator::new()); 140 141 let resource = Resource::builder() 142 .with_service_name(env!("CARGO_PKG_NAME")) 143 .with_schema_url( 144 [KeyValue::new(SERVICE_VERSION, env!("CARGO_PKG_VERSION"))], 145 SCHEMA_URL, 146 ) 147 .build(); 148 149 let exporter = opentelemetry_otlp::SpanExporter::builder() 150 .with_http() 151 .build() 152 .unwrap(); 153 154 SdkTracerProvider::builder() 155 // Customize sampling strategy 156 .with_sampler(Sampler::ParentBased(Box::new(Sampler::TraceIdRatioBased( 157 1.0, 158 )))) 159 // If export trace to AWS X-Ray, you can use XrayIdGenerator 160 .with_id_generator(RandomIdGenerator::default()) 161 .with_resource(resource) 162 .with_batch_exporter(exporter) 163 .build() 164 }