Add start server command and logging setup for CLI
This commit is contained in:
142
apps/api/src/cmd/start_server.rs
Normal file
142
apps/api/src/cmd/start_server.rs
Normal file
@@ -0,0 +1,142 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use axum::Router;
|
||||
use clap::Command;
|
||||
use database::get_connection;
|
||||
use sea_orm::ConnectOptions;
|
||||
use tracing::{debug, info};
|
||||
use tracing_subscriber::fmt::format::{DefaultFields, Format};
|
||||
|
||||
use crate::{
|
||||
cmd::CliCommand,
|
||||
configs::{ProgramSettings, get_program_settings, logging::LoggingSettings},
|
||||
log,
|
||||
routes::{self, AppService, AppState},
|
||||
services::settings::SettingsService,
|
||||
tasks,
|
||||
};
|
||||
|
||||
pub fn get_cli_command() -> CliCommand {
|
||||
CliCommand {
|
||||
command: command(),
|
||||
action,
|
||||
}
|
||||
}
|
||||
|
||||
fn command() -> Command {
|
||||
Command::new("start").about("Start the server")
|
||||
}
|
||||
|
||||
fn action(
|
||||
_matches: &clap::ArgMatches,
|
||||
) -> std::pin::Pin<Box<dyn std::future::Future<Output = ()> + Send>> {
|
||||
Box::pin(async move {})
|
||||
}
|
||||
|
||||
pub async fn start_server() {
|
||||
let settings = tracing::subscriber::with_default(
|
||||
log::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 = Arc::new(
|
||||
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(Arc::new(get_app_state(&db_connection)));
|
||||
|
||||
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(),
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
fn get_app_state(db_connection: &Arc<sea_orm::DatabaseConnection>) -> AppState {
|
||||
AppState {
|
||||
database_connection: db_connection.clone(),
|
||||
service: Arc::new(AppService {
|
||||
settings: Arc::new(SettingsService::new(db_connection.clone())),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user