feat(init): init service for testing log lib
This commit is contained in:
6
.cargo/config.toml
Normal file
6
.cargo/config.toml
Normal file
@@ -0,0 +1,6 @@
|
||||
[net]
|
||||
git-fetch-with-cli = true
|
||||
|
||||
|
||||
[registries.kellnr]
|
||||
index = "sparse+https://kellnr.shay7sev.site/api/v1/crates/"
|
||||
1
.env.example
Normal file
1
.env.example
Normal file
@@ -0,0 +1 @@
|
||||
DATABASE_URL=postgres://user:password@ip:port/db
|
||||
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
/target
|
||||
/logs
|
||||
|
||||
.env
|
||||
2161
Cargo.lock
generated
Normal file
2161
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
16
Cargo.toml
Normal file
16
Cargo.toml
Normal file
@@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "order-service"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
# rust_logger = { git = "ssh://git@gitea.shay7sev.site:2222/admin/rust_logger.git", tag = "v0.1.0" }
|
||||
rust_logger = { version = "0.1.0", registry = "kellnr" }
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
sqlx = { version = "0.8.6", features = [
|
||||
"postgres",
|
||||
"runtime-tokio-native-tls",
|
||||
] }
|
||||
dotenv = "0.15"
|
||||
uuid = { version = "1.0", features = ["v4"] }
|
||||
anyhow = "1.0.100"
|
||||
12
query.sql
Normal file
12
query.sql
Normal file
@@ -0,0 +1,12 @@
|
||||
-- 查找 Bob 在那个时间点的第一条日志,为了拿 TraceID
|
||||
SELECT trace_id, message, created_at
|
||||
FROM app_logs
|
||||
WHERE message LIKE '%Bob%'
|
||||
AND created_at BETWEEN '2026-01-22 10:00:00' AND '2026-01-22 10:05:00'
|
||||
LIMIT 1;
|
||||
|
||||
|
||||
SELECT created_at, log_level, module, message
|
||||
FROM app_logs
|
||||
WHERE trace_id = 'a1b2c3d4-ffff-...'
|
||||
ORDER BY created_at ASC;
|
||||
97
src/main.rs
Normal file
97
src/main.rs
Normal file
@@ -0,0 +1,97 @@
|
||||
use std::env;
|
||||
use std::time::Duration;
|
||||
|
||||
use dotenv::dotenv;
|
||||
use rust_logger::context::with_trace_id;
|
||||
use rust_logger::core::{Logger, LoggerConfig};
|
||||
use rust_logger::model::LogLevel;
|
||||
use rust_logger::outputs::console::ConsoleOutput;
|
||||
use rust_logger::outputs::file::FileOutput;
|
||||
use rust_logger::outputs::postgres::PostgresOutput;
|
||||
use rust_logger::{LogCleaner, log_error, log_info, set_global_logger}; // 引入宏
|
||||
use sqlx::postgres::PgPoolOptions;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
dotenv().ok();
|
||||
let db_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
|
||||
let pool = PgPoolOptions::new()
|
||||
.max_connections(5)
|
||||
.connect(&db_url)
|
||||
.await
|
||||
.unwrap();
|
||||
let log_dir = "logs";
|
||||
let file_prefix = "order-service";
|
||||
|
||||
let file_output = FileOutput::new(log_dir, file_prefix).await.unwrap();
|
||||
|
||||
LogCleaner::new(7)
|
||||
.with_db_cleanup(pool.clone())
|
||||
.with_file_cleanup(log_dir, file_prefix)
|
||||
.start();
|
||||
|
||||
let config = LoggerConfig {
|
||||
min_level: LogLevel::INFO,
|
||||
outputs: vec![
|
||||
Box::new(ConsoleOutput),
|
||||
Box::new(PostgresOutput::new(pool.clone())),
|
||||
Box::new(file_output),
|
||||
],
|
||||
};
|
||||
|
||||
// 注意:这里传入了 "order-service"
|
||||
let logger = Logger::init("order-service", config);
|
||||
set_global_logger(logger);
|
||||
|
||||
// log_info!("Order service is ready!Outputs ConsoleOutput,PostgresOutput and file_output");
|
||||
// // 数据库里会记录: service_name="order-service", message="Order service is ready!"
|
||||
|
||||
let request_a = tokio::spawn(mock_http_handler("Alice", "ITEM-001", true));
|
||||
let request_b = tokio::spawn(mock_http_handler("Bob", "ITEM-999", false)); // 模拟库存不足
|
||||
|
||||
// 等待请求处理完成
|
||||
let _ = tokio::join!(request_a, request_b);
|
||||
|
||||
tokio::time::sleep(Duration::from_secs(2)).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// 模拟 HTTP 请求入口 (Middleware 层)
|
||||
async fn mock_http_handler(user: &str, item_id: &str, is_success: bool) {
|
||||
// 1. 生成 TraceID (通常从 HTTP Header "X-Trace-ID" 获取,没有则生成)
|
||||
let trace_id = Uuid::new_v4().to_string();
|
||||
|
||||
// 2. 【关键】使用 with_trace_id 包裹整个业务逻辑
|
||||
// 只要在这个闭包里,无论调用多深层的函数,log_info! 都会自动带上 ID
|
||||
with_trace_id(trace_id.clone(), async move {
|
||||
log_info!("收到下单请求: User={}, Item={}", user, item_id);
|
||||
|
||||
// 进入业务逻辑层
|
||||
process_order(user, item_id, is_success).await;
|
||||
|
||||
log_info!("请求处理完毕,返回响应给 {}", user);
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
// 模拟业务逻辑层 (Service 层)
|
||||
async fn process_order(user: &str, item_id: &str, simulate_success: bool) {
|
||||
log_info!("正在检查库存: {}", item_id);
|
||||
tokio::time::sleep(Duration::from_millis(50)).await; // 模拟耗时
|
||||
|
||||
if simulate_success {
|
||||
log_info!("库存充足,开始扣款...");
|
||||
payment_service(user, 100).await;
|
||||
log_info!("下单成功!订单号: ORDER-{}", Uuid::new_v4());
|
||||
} else {
|
||||
log_error!("库存不足!商品 {} 缺货", item_id);
|
||||
log_info!("订单已取消");
|
||||
}
|
||||
}
|
||||
|
||||
// 模拟底层服务 (DAO/RPC 层)
|
||||
async fn payment_service(user: &str, amount: i32) {
|
||||
// 注意:这里我们没有传 trace_id 参数,但 log_info 依然能获取到它!
|
||||
log_info!("扣款成功: User={}, Amount=${}", user, amount);
|
||||
}
|
||||
Reference in New Issue
Block a user