#[macro_use] extern crate log; extern crate chrono; #[cfg(feature = "profile")] extern crate cpuprofiler; extern crate rand; extern crate rtiow; extern crate stderrlog; extern crate structopt; #[macro_use] #[cfg(feature = "prom")] extern crate lazy_static; #[macro_use] #[cfg(feature = "prom")] extern crate prometheus; use std::fs; use chrono::DateTime; use chrono::Utc; #[cfg(feature = "profile")] use cpuprofiler::PROFILER; #[cfg(feature = "prom")] use prometheus::Encoder; use structopt::StructOpt; use rtiow::renderer::opt_hash; use rtiow::renderer::render; use rtiow::renderer::Opt; #[cfg(feature = "prom")] lazy_static! { static ref RUNTIME_COUNTER: prometheus::Gauge = register_gauge!("render_time_seconds", "Wall clock time for render").unwrap(); } #[cfg(not(feature = "prom"))] fn push_metrics(_push_gateway_addr: &str, _instance: String, start_time: &DateTime) { let runtime = (Utc::now() - *start_time).to_std().unwrap(); info!( "Render time {}.{} seconds", runtime.as_secs(), runtime.subsec_millis() ); } #[cfg(feature = "prom")] fn push_metrics(push_gateway_addr: &str, instance: String, start_time: &DateTime) { let runtime = (Utc::now() - *start_time).to_std().unwrap(); info!( "Render time {}.{} seconds", runtime.as_secs(), runtime.subsec_millis() ); RUNTIME_COUNTER.set(runtime.as_secs() as f64 + f64::from(runtime.subsec_nanos()) * 1e-9); let metric_families = prometheus::gather(); let encoder = prometheus::TextEncoder::new(); let stdout = std::io::stdout(); println!("Prometheus metrics:"); encoder .encode(&metric_families, &mut stdout.lock()) .unwrap(); if push_gateway_addr.is_empty() { info!("Logging stats to push gateway disabled"); return; } if let Err(err) = prometheus::push_metrics( "tracer", labels! { "instance".to_owned() => instance, "start_time".to_owned() => start_time.to_rfc3339(), "timestamp".to_owned() => start_time.timestamp().to_string(), }, push_gateway_addr, metric_families, None, // TODO(wathiede): auth? // Some(prometheus::BasicAuthentication { // username: "user".to_owned(), // password: "pass".to_owned(), // }), ) { error!("Failed to push to prometheus gateway: {}", err); } } #[cfg(not(feature = "profile"))] struct MockTimer; #[cfg(not(feature = "profile"))] impl MockTimer { fn start>>(&self, _: T) -> Result<(), ()> { Ok(()) } fn stop(&self) -> Result<(), ()> { Ok(()) } } #[cfg(not(feature = "profile"))] struct MockProfiler; #[cfg(not(feature = "profile"))] impl MockProfiler { fn lock(&self) -> Option { Some(MockTimer {}) } } #[cfg(not(feature = "profile"))] static PROFILER: MockProfiler = MockProfiler {}; fn main() -> Result<(), std::io::Error> { stderrlog::new() .verbosity(3) .timestamp(stderrlog::Timestamp::Millisecond) .init() .unwrap(); let start_time: DateTime = Utc::now(); let opt = Opt::from_args(); let scene = opt.model.scene(&opt); fs::create_dir_all(&opt.output)?; if opt.pprof.is_some() && !cfg!(feature = "profile") { panic!("profiling disabled at compile time, but -pprof specified"); } if let Some(ref pprof_path) = opt.pprof { PROFILER .lock() .unwrap() .start(pprof_path.to_str().unwrap().as_bytes()) .unwrap(); } let res = render(scene, &opt.output); if let Some(pprof_path) = &opt.pprof { info!("Saving pprof to {}", pprof_path.to_string_lossy()); PROFILER.lock().unwrap().stop().unwrap(); } push_metrics(&opt.push_gateway, opt_hash(&opt), &start_time); res }