use anyhow::Result;
use opentelemetry::{global, KeyValue};
use pi_logger::observability::init_observability_from_optional_path;
use std::{
path::{Path, PathBuf},
thread,
time::{Duration, Instant},
};
fn local_only_log_test() {
tracing::info!(
target: "pi_logger_example::local_only",
module = "local_only",
test_kind = "local_log_test",
"本地日志测试:这条日志只应写入本地文件,不应上传到采集器"
);
}
fn log_compatibility_test() {
log::info!(
target: "pi_logger_example::log_compat",
"log 宏兼容测试:log::info! 已进入 observability"
);
log::debug!(
target: "pi_logger_example::log_compat",
"log 宏兼容测试:target override 开启后 log::debug! 也会输出"
);
}
fn remote_log_test() {
tracing::info!(
target: "pi_logger_example::remote_test",
id = 100_u64,
module = "remote_test",
test_kind = "remote_log_test",
status = "ok",
retry_count = 1_u64,
sampled = true,
"远端日志测试:全部 field_rules 匹配,这条日志应上传到采集器"
);
tracing::info!(
target: "pi_logger_example::remote_test",
id = 101_u64,
module = "remote_test",
test_kind = "remote_log_test",
status = "ok",
retry_count = 1_u64,
sampled = true,
"远端日志测试:id != 100,这条日志不应上传到采集器"
);
tracing::info!(
target: "pi_logger_example::remote_test",
id = 100_u64,
module = "remote_test",
test_kind = "local_log_test",
status = "ok",
retry_count = 1_u64,
sampled = true,
"远端日志测试:test_kind 不包含 remote,这条日志不应上传到采集器"
);
tracing::info!(
target: "pi_logger_example::remote_test",
id = 100_u64,
module = "local_test",
test_kind = "remote_log_test",
status = "ok",
retry_count = 1_u64,
sampled = true,
"远端日志测试:module 不是 remote 开头,这条日志不应上传到采集器"
);
tracing::info!(
target: "pi_logger_example::remote_test",
id = 100_u64,
module = "remote_test",
test_kind = "remote_log_test",
status = "failed",
retry_count = 1_u64,
sampled = true,
"远端日志测试:status 不在 [ok, retry] 中,这条日志不应上传到采集器"
);
tracing::info!(
target: "pi_logger_example::remote_test",
id = 100_u64,
module = "remote_test",
test_kind = "remote_log_test",
status = "retry",
retry_count = 4_u64,
sampled = true,
"远端日志测试:retry_count > 3,这条日志不应上传到采集器"
);
tracing::info!(
target: "pi_logger_example::remote_test",
id = 100_u64,
module = "remote_test",
test_kind = "remote_log_test",
status = "ok",
retry_count = 1_u64,
sampled = false,
"远端日志测试:sampled != true,这条日志不应上传到采集器"
);
}
fn metrics_report_test() {
let meter = global::meter("pi_logger_example");
let request_counter = meter
.u64_counter("pi_logger_demo_requests_total")
.with_description("指标上报测试:demo 请求总数")
.build();
let request_duration = meter
.f64_histogram("pi_logger_demo_request_duration_ms")
.with_description("指标上报测试:demo 请求耗时")
.with_unit("ms")
.build();
let in_flight = meter
.i64_up_down_counter("pi_logger_demo_requests_in_flight")
.with_description("指标上报测试:当前处理中请求数")
.build();
let attributes = vec![
KeyValue::new("module", "metrics_demo"),
KeyValue::new("protocol", "otlp"),
KeyValue::new("status", "ok"),
];
in_flight.add(1, &attributes);
let started = Instant::now();
thread::sleep(Duration::from_millis(15));
request_counter.add(1, &attributes);
request_duration.record(started.elapsed().as_secs_f64() * 1000.0, &attributes);
in_flight.add(-1, &attributes);
tracing::info!(
target: "pi_logger_example::local_only",
module = "metrics_demo",
test_kind = "metrics_report_test",
"指标上报测试:counter/histogram/up_down_counter 只通过 OTLP metrics 上传到采集器"
);
}
#[tracing::instrument(
name = "demo_request",
target = "pi_logger_example::trace_test",
level = "info",
skip_all,
fields(request_id = request_id, module = "trace_demo")
)]
fn demo_request_trace_test(request_id: u64) -> Result<()> {
tracing::info!(
target: "pi_logger_example::trace_test",
request_id = request_id,
"trace 跟踪测试:开始处理 demo_request 调用链"
);
decode_transaction_trace_test(request_id, b"trace payload")?;
persist_transaction_trace_test(request_id)?;
tracing::info!(
target: "pi_logger_example::trace_test",
request_id = request_id,
"trace 跟踪测试:完成 demo_request 调用链"
);
Ok(())
}
#[tracing::instrument(
name = "decode_transaction",
target = "pi_logger_example::trace_test",
level = "info",
skip_all,
fields(request_id = request_id, module = "decoder", protocol = "ton")
)]
fn decode_transaction_trace_test(request_id: u64, payload: &[u8]) -> Result<()> {
tracing::info!(
target: "pi_logger_example::trace_test",
request_id = request_id,
payload_len = payload.len(),
"trace 跟踪测试:进入 decode_transaction 子调用"
);
validate_payload_trace_test(request_id, payload)?;
parse_payload_trace_test(request_id, payload)?;
Ok(())
}
#[tracing::instrument(
name = "validate_payload",
target = "pi_logger_example::trace_test",
level = "info",
skip_all,
fields(request_id = request_id, module = "validator")
)]
fn validate_payload_trace_test(request_id: u64, payload: &[u8]) -> Result<()> {
tracing::info!(
target: "pi_logger_example::trace_test",
request_id = request_id,
payload_len = payload.len(),
"trace 跟踪测试:校验 payload"
);
Ok(())
}
#[tracing::instrument(
name = "parse_payload",
target = "pi_logger_example::trace_test",
level = "info",
skip_all,
fields(request_id = request_id, module = "parser")
)]
fn parse_payload_trace_test(request_id: u64, payload: &[u8]) -> Result<()> {
tracing::info!(
target: "pi_logger_example::trace_test",
request_id = request_id,
payload_len = payload.len(),
"trace 跟踪测试:解析 payload"
);
Ok(())
}
#[tracing::instrument(
name = "persist_transaction",
target = "pi_logger_example::trace_test",
level = "info",
skip_all,
fields(request_id = request_id, module = "storage")
)]
fn persist_transaction_trace_test(request_id: u64) -> Result<()> {
tracing::info!(
target: "pi_logger_example::trace_test",
request_id = request_id,
"trace 跟踪测试:持久化 transaction"
);
Ok(())
}
fn main() -> Result<()> {
let config_path = std::env::var_os("PI_LOGGER_OBSERVABILITY_CONFIG").map(PathBuf::from);
let (_reload, guards) = init_observability_from_optional_path(config_path.as_deref())?;
tracing::info!(target: "pi_logger_example", "pi_logger observability example started");
local_only_log_test();
log_compatibility_test();
remote_log_test();
demo_request_trace_test(100)?;
metrics_report_test();
tracing::info!(target: "pi_logger_example", "pi_logger observability example finished");
guards.shutdown();
println!(
"example finished. local log dir: {}",
Path::new("logs")
.canonicalize()
.unwrap_or_else(|_| "logs".into())
.display()
);
Ok(())
}