1
0
Fork 0
mirror of https://gitlab.com/famedly/conduit.git synced 2025-06-27 16:35:59 +00:00

return ExitCode instead of using panic or exit

`panic!()` (and things that invoke it, such as `expect` and `unwrap`)
produces terrible looking error messages and `std::process::exit()`
doesn't run destructors. Instead, we'll make a `try_main` that can
return a `Result` with a structured error type, but for now I'm going to
be lazy and just use `Box<dyn Error>`. Then, `main` will call it and
return the appropriate `ExitCode` value based on `try_main`'s `Result`.
This gives us the opportunity to produce good error messages and doesn't
violently terminate the program.
This commit is contained in:
Charles Hall 2024-04-19 14:25:31 -07:00 committed by Matthias Ahouansou
parent 08485ea5e4
commit 2131c36eac
No known key found for this signature in database

View file

@ -1,4 +1,4 @@
use std::{future::Future, io, net::SocketAddr, sync::atomic, time::Duration}; use std::{future::Future, io, net::SocketAddr, process::ExitCode, sync::atomic, time::Duration};
use axum::{ use axum::{
extract::{DefaultBodyLimit, FromRequestParts, MatchedPath}, extract::{DefaultBodyLimit, FromRequestParts, MatchedPath},
@ -30,7 +30,7 @@ use tower_http::{
trace::TraceLayer, trace::TraceLayer,
ServiceBuilderExt as _, ServiceBuilderExt as _,
}; };
use tracing::{debug, error, info, warn}; use tracing::{debug, info, warn};
use tracing_subscriber::{prelude::*, EnvFilter}; use tracing_subscriber::{prelude::*, EnvFilter};
pub use conduit::*; // Re-export everything from the library crate pub use conduit::*; // Re-export everything from the library crate
@ -43,27 +43,32 @@ use tikv_jemallocator::Jemalloc;
static GLOBAL: Jemalloc = Jemalloc; static GLOBAL: Jemalloc = Jemalloc;
#[tokio::main] #[tokio::main]
async fn main() { async fn main() -> ExitCode {
let Err(e) = try_main().await else {
return ExitCode::SUCCESS;
};
eprintln!("error: {e}");
ExitCode::FAILURE
}
/// Fallible entrypoint
async fn try_main() -> Result<(), Box<dyn std::error::Error>> {
clap::parse(); clap::parse();
// Initialize config // Initialize config
let raw_config = let raw_config =
Figment::new() Figment::new()
.merge( .merge(
Toml::file(Env::var("CONDUIT_CONFIG").expect( Toml::file(Env::var("CONDUIT_CONFIG").ok_or(
"The CONDUIT_CONFIG env var needs to be set. Example: /etc/conduit.toml", "The CONDUIT_CONFIG env var needs to be set. Example: /etc/conduit.toml",
)) )?)
.nested(), .nested(),
) )
.merge(Env::prefixed("CONDUIT_").global()); .merge(Env::prefixed("CONDUIT_").global());
let config = match raw_config.extract::<Config>() { let config = raw_config.extract::<Config>()?;
Ok(s) => s,
Err(e) => {
eprintln!("It looks like your config is invalid. The following error occurred: {e}");
std::process::exit(1);
}
};
config.warn_deprecated(); config.warn_deprecated();
@ -72,47 +77,31 @@ async fn main() {
let tracer = opentelemetry_jaeger::new_agent_pipeline() let tracer = opentelemetry_jaeger::new_agent_pipeline()
.with_auto_split_batch(true) .with_auto_split_batch(true)
.with_service_name("conduit") .with_service_name("conduit")
.install_batch(opentelemetry::runtime::Tokio) .install_batch(opentelemetry::runtime::Tokio)?;
.unwrap();
let telemetry = tracing_opentelemetry::layer().with_tracer(tracer); let telemetry = tracing_opentelemetry::layer().with_tracer(tracer);
let filter_layer = match EnvFilter::try_new(&config.log) { let filter_layer = EnvFilter::try_new(&config.log)?;
Ok(s) => s,
Err(e) => {
eprintln!(
"It looks like your log config is invalid. The following error occurred: {e}"
);
EnvFilter::try_new("warn").unwrap()
}
};
let subscriber = tracing_subscriber::Registry::default() let subscriber = tracing_subscriber::Registry::default()
.with(filter_layer) .with(filter_layer)
.with(telemetry); .with(telemetry);
tracing::subscriber::set_global_default(subscriber).unwrap(); tracing::subscriber::set_global_default(subscriber)?;
} else if config.tracing_flame { } else if config.tracing_flame {
let registry = tracing_subscriber::Registry::default(); let registry = tracing_subscriber::Registry::default();
let (flame_layer, _guard) = let (flame_layer, _guard) = tracing_flame::FlameLayer::with_file("./tracing.folded")?;
tracing_flame::FlameLayer::with_file("./tracing.folded").unwrap();
let flame_layer = flame_layer.with_empty_samples(false); let flame_layer = flame_layer.with_empty_samples(false);
let filter_layer = EnvFilter::new("trace,h2=off"); let filter_layer = EnvFilter::new("trace,h2=off");
let subscriber = registry.with(filter_layer).with(flame_layer); let subscriber = registry.with(filter_layer).with(flame_layer);
tracing::subscriber::set_global_default(subscriber).unwrap(); tracing::subscriber::set_global_default(subscriber)?;
} else { } else {
let registry = tracing_subscriber::Registry::default(); let registry = tracing_subscriber::Registry::default();
let fmt_layer = tracing_subscriber::fmt::Layer::new(); let fmt_layer = tracing_subscriber::fmt::Layer::new();
let filter_layer = match EnvFilter::try_new(&config.log) { let filter_layer = EnvFilter::try_new(&config.log)?;
Ok(s) => s,
Err(e) => {
eprintln!("It looks like your config is invalid. The following error occured while parsing it: {e}");
EnvFilter::try_new("warn").unwrap()
}
};
let subscriber = registry.with(filter_layer).with(fmt_layer); let subscriber = registry.with(filter_layer).with(fmt_layer);
tracing::subscriber::set_global_default(subscriber).unwrap(); tracing::subscriber::set_global_default(subscriber)?;
} }
// This is needed for opening lots of file descriptors, which tends to // This is needed for opening lots of file descriptors, which tends to
@ -126,19 +115,17 @@ async fn main() {
maximize_fd_limit().expect("should be able to increase the soft limit to the hard limit"); maximize_fd_limit().expect("should be able to increase the soft limit to the hard limit");
info!("Loading database"); info!("Loading database");
if let Err(error) = KeyValueDatabase::load_or_create(config).await { KeyValueDatabase::load_or_create(config).await?;
error!(?error, "The database couldn't be loaded or created");
std::process::exit(1);
};
let config = &services().globals.config; let config = &services().globals.config;
info!("Starting server"); info!("Starting server");
run_server().await.unwrap(); run_server().await?;
if config.allow_jaeger { if config.allow_jaeger {
opentelemetry::global::shutdown_tracer_provider(); opentelemetry::global::shutdown_tracer_provider();
} }
Ok(())
} }
async fn run_server() -> io::Result<()> { async fn run_server() -> io::Result<()> {