axum-sqlx/src/main.rs

110 lines
3.6 KiB
Rust
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use std::sync::Arc;
use axum::Router;
use axum::routing::get;
use sqlx::mysql::MySqlPoolOptions;
use tokio::signal;
use tower_http::trace::{DefaultMakeSpan, DefaultOnResponse, TraceLayer};
use tracing::Level;
use tracing_appender::{non_blocking, rolling};
use tracing_appender::non_blocking::WorkerGuard;
use tracing_subscriber::{EnvFilter, fmt, layer::SubscriberExt, util::SubscriberInitExt};
use tracing_subscriber::fmt::time::LocalTime;
use axum_sqlx::{config::AppConfig, handler, model::state::AppState};
fn init_trace_logger_by_no_blocking() -> WorkerGuard {
// 设置日志级别 根据RUST_LOG
let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"));
let timer = LocalTime::rfc_3339();
// 输出到文件中(按天分割)
let file_appender = rolling::daily("logs", "app.log");
// 非阻塞 _guard释放会导致文件记录失效
let (non_blocking_appender, guard) = non_blocking(file_appender);
// 构建layer 文件输出
let file_layer = fmt::layer()
.with_timer(timer.clone())
.with_ansi(false)
.with_writer(non_blocking_appender);
// 构建layer 控制台打印
let console_layer = fmt::layer().with_timer(timer);
tracing_subscriber::registry()
.with(env_filter)
.with(console_layer)
.with(file_layer.json())
.init();
return guard;
}
#[tokio::main]
async fn main() {
// 解析 .env 文件
dotenvy::dotenv().expect("解析.env文件失败");
let _guard = init_trace_logger_by_no_blocking();
let cfg = AppConfig::new()
.map_err(|e| tracing::error!("初始化配置失败:{}", e.to_string()))
.unwrap();
let pool = MySqlPoolOptions::new()
.max_connections(cfg.mysql.max_cons)
.connect(&cfg.mysql.dsn)
.await
.map_err(|e| tracing::error!("数据库连接失败:{}", e.to_string()))
.unwrap();
// 默认TraceLayer的打印级别为DEBUG设置response的打印级别为INFO这样可以避免过多的打印日志
// 利用make_span_with来显示span中的其他信息
let trace_layer = TraceLayer::new_for_http()
.make_span_with(DefaultMakeSpan::new().level(Level::INFO))
.on_response(DefaultOnResponse::new().level(Level::INFO));
let app = Router::new()
.route("/", get(handler::index))
.route("/detail/:id", get(handler::detail))
.route("/add", get(handler::add_ui).post(handler::add))
.route("/edit/:id", get(handler::edit_ui).post(handler::edit))
.route("/del/:id", get(handler::del))
.route("/real_del/:id", get(handler::real_del))
.route("/tran", get(handler::tran_ui).post(handler::tran))
.layer(trace_layer)
.with_state(Arc::new(AppState {
pool: Arc::new(pool)
}));
let listener = tokio::net::TcpListener::bind(&cfg.web.addr).await.unwrap();
tracing::info!("服务器运行于: {}", listener.local_addr().unwrap());
axum::serve(listener, app).with_graceful_shutdown(shutdown_signal()).await.unwrap()
}
async fn shutdown_signal() {
let ctrl_c = async {
signal::ctrl_c()
.await
.expect("failed to install Ctrl+C handler");
};
#[cfg(unix)]
let terminate = async {
signal::unix::signal(signal::unix::SignalKind::terminate())
.expect("failed to install signal handler")
.recv()
.await;
};
#[cfg(not(unix))]
let terminate = std::future::pending::<()>();
tokio::select! {
_ = ctrl_c => {},
_ = terminate => {},
}
tracing::info!("signal received, starting graceful shutdown");
}