build: add api server tracing bootstrap
This commit is contained in:
@@ -7,3 +7,6 @@ license.workspace = true
|
||||
[dependencies]
|
||||
axum = "0.8"
|
||||
tokio = { version = "1", features = ["macros", "rt-multi-thread", "net"] }
|
||||
tower-http = { version = "0.6", features = ["trace"] }
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] }
|
||||
|
||||
@@ -22,14 +22,22 @@
|
||||
4. `src/app.rs`
|
||||
5. `src/state.rs`
|
||||
6. `src/config.rs`
|
||||
7. `src/logging.rs`
|
||||
8. 基础 `TraceLayer` 挂载与 `tracing subscriber` 初始化
|
||||
|
||||
后续与本 package 直接相关的任务包括:
|
||||
|
||||
1. 接入统一日志与 tracing
|
||||
2. 接入 `request_id`
|
||||
3. 接入统一错误处理中间件
|
||||
4. 接入 response envelope
|
||||
5. 接入 `/healthz`
|
||||
1. [x] 接入统一日志与 tracing
|
||||
2. [ ] 接入 `request_id`
|
||||
3. [ ] 接入统一错误处理中间件
|
||||
4. [ ] 接入 response envelope
|
||||
5. [ ] 接入 `/healthz`
|
||||
|
||||
当前 tracing 约定:
|
||||
|
||||
1. 进程启动时统一初始化 `tracing subscriber`。
|
||||
2. 默认日志过滤器来自 `GENARRATIVE_API_LOG`,未提供时回落到 `info,tower_http=info`。
|
||||
3. HTTP 访问日志统一通过 Axum 路由层的 `TraceLayer` 输出,后续 `request_id`、响应头与错误中间件继续在同一层扩展。
|
||||
|
||||
## 3. 边界约束
|
||||
|
||||
|
||||
@@ -1,8 +1,19 @@
|
||||
use axum::Router;
|
||||
use tower_http::trace::{DefaultMakeSpan, DefaultOnFailure, DefaultOnRequest, DefaultOnResponse, TraceLayer};
|
||||
use tracing::Level;
|
||||
|
||||
use crate::state::AppState;
|
||||
|
||||
// 统一由这里构造 Axum 路由树,后续再逐项挂接中间件与业务路由。
|
||||
pub fn build_router(state: AppState) -> Router {
|
||||
Router::new().with_state(state)
|
||||
Router::new()
|
||||
// 当前阶段先统一挂接 HTTP tracing,后续 request_id、响应头与错误中间件继续在这里扩展。
|
||||
.layer(
|
||||
TraceLayer::new_for_http()
|
||||
.make_span_with(DefaultMakeSpan::new().level(Level::INFO))
|
||||
.on_request(DefaultOnRequest::new().level(Level::INFO))
|
||||
.on_response(DefaultOnResponse::new().level(Level::INFO))
|
||||
.on_failure(DefaultOnFailure::new().level(Level::ERROR)),
|
||||
)
|
||||
.with_state(state)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ use std::{env, net::SocketAddr};
|
||||
pub struct AppConfig {
|
||||
pub bind_host: String,
|
||||
pub bind_port: u16,
|
||||
pub log_filter: String,
|
||||
}
|
||||
|
||||
impl Default for AppConfig {
|
||||
@@ -12,6 +13,7 @@ impl Default for AppConfig {
|
||||
Self {
|
||||
bind_host: "127.0.0.1".to_string(),
|
||||
bind_port: 3000,
|
||||
log_filter: "info,tower_http=info".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,6 +34,12 @@ impl AppConfig {
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(log_filter) = env::var("GENARRATIVE_API_LOG") {
|
||||
if !log_filter.trim().is_empty() {
|
||||
config.log_filter = log_filter;
|
||||
}
|
||||
}
|
||||
|
||||
config
|
||||
}
|
||||
|
||||
|
||||
19
server-rs/apps/api-server/src/logging.rs
Normal file
19
server-rs/apps/api-server/src/logging.rs
Normal file
@@ -0,0 +1,19 @@
|
||||
use std::io;
|
||||
|
||||
use tracing_subscriber::{fmt, EnvFilter};
|
||||
|
||||
use crate::config::AppConfig;
|
||||
|
||||
// 统一在独立模块初始化 tracing,避免入口层和后续测试入口重复散落 subscriber 配置。
|
||||
pub fn init_tracing(config: &AppConfig) -> Result<(), io::Error> {
|
||||
let env_filter = EnvFilter::try_from_default_env()
|
||||
.or_else(|_| EnvFilter::try_new(config.log_filter.as_str()))
|
||||
.unwrap_or_else(|_| EnvFilter::new("info"));
|
||||
|
||||
fmt()
|
||||
.with_env_filter(env_filter)
|
||||
.with_target(true)
|
||||
.compact()
|
||||
.try_init()
|
||||
.map_err(|error| io::Error::other(format!("初始化 tracing subscriber 失败:{error}")))
|
||||
}
|
||||
@@ -1,20 +1,26 @@
|
||||
mod app;
|
||||
mod config;
|
||||
mod logging;
|
||||
mod state;
|
||||
|
||||
use tokio::net::TcpListener;
|
||||
use tracing::info;
|
||||
|
||||
use crate::{app::build_router, config::AppConfig, state::AppState};
|
||||
use crate::{app::build_router, config::AppConfig, logging::init_tracing, state::AppState};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), std::io::Error> {
|
||||
// 统一先从配置对象读取监听地址,避免后续把环境变量读取散落到入口和路由层。
|
||||
let config = AppConfig::from_env();
|
||||
init_tracing(&config)?;
|
||||
|
||||
let bind_address = config.bind_socket_addr();
|
||||
let listener = TcpListener::bind(bind_address).await?;
|
||||
|
||||
let state = AppState::new(config);
|
||||
let router = build_router(state);
|
||||
|
||||
info!(%bind_address, "api-server 已完成 tracing 初始化并开始监听");
|
||||
|
||||
axum::serve(listener, router).await
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user