/ src / main.rs
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  }