120 lines
4.0 KiB
Rust
120 lines
4.0 KiB
Rust
mod configs;
|
|
mod middlewares;
|
|
mod routes;
|
|
mod tasks;
|
|
|
|
use axum::Router;
|
|
use database::{ConnectOptions, get_connection};
|
|
use tracing::{debug, info};
|
|
use tracing_subscriber::fmt::format::{DefaultFields, Format};
|
|
|
|
use crate::configs::{ProgramSettings, get_program_settings, logging::LoggingSettings};
|
|
|
|
#[tokio::main]
|
|
async fn main() {
|
|
// Temporary subscriber for initial logging during configuration reading
|
|
let make_temporary_subscriber = || {
|
|
tracing_subscriber::fmt()
|
|
.with_max_level(tracing::Level::DEBUG)
|
|
.with_target(false)
|
|
.with_level(true)
|
|
.finish()
|
|
};
|
|
|
|
let settings =
|
|
tracing::subscriber::with_default(make_temporary_subscriber(), || -> ProgramSettings {
|
|
debug!("Temporary subscriber installed.");
|
|
info!("Reading configuration...");
|
|
let settings = get_program_settings();
|
|
info!("Configuration read successfully.");
|
|
debug!("Resetting global subscriber...");
|
|
|
|
let subscriber = get_global_tracing_subscriber_builder(&settings.logging).finish();
|
|
tracing::subscriber::set_global_default(subscriber)
|
|
.expect("Failed to set global default subscriber");
|
|
|
|
debug!(
|
|
"Global subscriber set with logging level: {:?}",
|
|
settings.logging.level
|
|
);
|
|
|
|
settings
|
|
});
|
|
|
|
tasks::startup::run_startup_tasks(&settings)
|
|
.await
|
|
.expect("Failed to run startup tasks");
|
|
|
|
// setup database connection pool
|
|
info!("Establishing database connection...");
|
|
debug!("Database URL: {}", settings.database.url);
|
|
|
|
let db_options = |options: &mut ConnectOptions| {
|
|
options.max_connections(settings.database.max_connections);
|
|
};
|
|
|
|
let db_connection = get_connection(&settings.database.url, Some(db_options))
|
|
.await
|
|
.expect("Failed to establish database connection");
|
|
|
|
info!("Database connection established.");
|
|
|
|
// build the axum app and run the server...
|
|
info!("Starting application...");
|
|
let app: Router = routes::get_root_router(routes::AppState {
|
|
database_connection: db_connection,
|
|
service: std::sync::Arc::new(routes::AppService {}),
|
|
});
|
|
|
|
let address = format!("{}:{}", settings.server.address, settings.server.port);
|
|
info!("Starting server at http://{}", address);
|
|
|
|
let listener = tokio::net::TcpListener::bind(address)
|
|
.await
|
|
.expect("Failed to bind to address");
|
|
|
|
axum::serve(listener, app)
|
|
.await
|
|
.expect("Failed to run the server");
|
|
}
|
|
|
|
fn get_global_tracing_subscriber_builder(
|
|
settings: &LoggingSettings,
|
|
) -> tracing_subscriber::fmt::SubscriberBuilder<
|
|
DefaultFields,
|
|
Format<tracing_subscriber::fmt::format::Full, BoxedTimer>,
|
|
> {
|
|
// After configuration is read, install the global subscriber
|
|
let builder = tracing_subscriber::fmt()
|
|
.with_max_level(settings.level)
|
|
.with_target(false)
|
|
.with_level(true);
|
|
|
|
if settings.utc {
|
|
builder.with_timer(BoxedTimer(Box::new(
|
|
tracing_subscriber::fmt::time::UtcTime::rfc_3339(),
|
|
)))
|
|
} else {
|
|
builder.with_timer(BoxedTimer(Box::new(
|
|
tracing_subscriber::fmt::time::ChronoLocal::rfc_3339(),
|
|
)))
|
|
}
|
|
}
|
|
|
|
// A small wrapper that holds a boxed `FormatTime` trait object and itself
|
|
// implements `FormatTime`, allowing us to use it as a concrete type with
|
|
// `builder.with_timer` while still picking the concrete timer implementation
|
|
// at runtime.
|
|
// wrapper type to hold boxed timers and implement the `FormatTime` trait for
|
|
// a concrete type so `with_timer` may be called once outside the conditional.
|
|
struct BoxedTimer(Box<dyn tracing_subscriber::fmt::time::FormatTime + Send + Sync + 'static>);
|
|
|
|
impl tracing_subscriber::fmt::time::FormatTime for BoxedTimer {
|
|
fn format_time(
|
|
&self,
|
|
w: &mut tracing_subscriber::fmt::format::Writer<'_>,
|
|
) -> std::result::Result<(), std::fmt::Error> {
|
|
self.0.format_time(w)
|
|
}
|
|
}
|