Tracing with axum/tower_http via layer
This commit is contained in:
parent
d5a3d1582d
commit
27dd80830f
5 changed files with 82 additions and 22 deletions
11
Cargo.lock
generated
11
Cargo.lock
generated
|
@ -814,11 +814,9 @@ dependencies = [
|
||||||
"minijinja",
|
"minijinja",
|
||||||
"notify",
|
"notify",
|
||||||
"pathdiff",
|
"pathdiff",
|
||||||
"redact",
|
|
||||||
"serde",
|
"serde",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tower",
|
|
||||||
"tower-http",
|
"tower-http",
|
||||||
"tower-livereload",
|
"tower-livereload",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
@ -1123,15 +1121,6 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "redact"
|
|
||||||
version = "0.1.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b7301863b5e5486c9f18320ccc1aedce9a5fdf3056ae89fa021933d6f054430f"
|
|
||||||
dependencies = [
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
|
|
|
@ -19,20 +19,18 @@ panic = "abort"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
axum = { version = "0.7.5", features = ["macros", "tokio"] }
|
axum = { version = "0.7.5", features = ["macros", "tokio", "tracing"] }
|
||||||
clap = { version = "4.5.4", features = ["derive"] }
|
clap = { version = "4.5.4", features = ["derive", "env"] }
|
||||||
color-eyre = "0.6.3"
|
color-eyre = "0.6.3"
|
||||||
config = "0.14.0"
|
config = "0.14.0"
|
||||||
futures = "0.3.30"
|
futures = "0.3.30"
|
||||||
minijinja = { version = "2.0.1", features = ["loader"] }
|
minijinja = { version = "2.0.1", features = ["loader"] }
|
||||||
notify = "6.1.1"
|
notify = "6.1.1"
|
||||||
pathdiff = "0.2.1"
|
pathdiff = "0.2.1"
|
||||||
redact = { version = "0.1.9", features = ["serde"] }
|
|
||||||
serde = "1.0.201"
|
serde = "1.0.201"
|
||||||
thiserror = "1.0.60"
|
thiserror = "1.0.60"
|
||||||
tokio = { version = "1.37.0", features = ["full"] }
|
tokio = { version = "1.37.0", features = ["full"] }
|
||||||
tower = "0.4.13"
|
tower-http = { version = "0.5.2", features = ["fs", "trace"] }
|
||||||
tower-http = { version = "0.5.2", features = ["fs"] }
|
|
||||||
tower-livereload = "0.9.2"
|
tower-livereload = "0.9.2"
|
||||||
tracing = "0.1.40"
|
tracing = "0.1.40"
|
||||||
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::{prelude::*, router::NewRouterError};
|
use crate::{observe, prelude::*, router::NewRouterError};
|
||||||
use prelude::*;
|
use prelude::*;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
@ -13,6 +13,9 @@ mod prelude {
|
||||||
pub struct Cli {
|
pub struct Cli {
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
command: Commands,
|
command: Commands,
|
||||||
|
|
||||||
|
#[arg(global = true, long, value_enum, default_value = "info,lyrs=trace")]
|
||||||
|
log_env_filter: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Subcommand)]
|
#[derive(Subcommand)]
|
||||||
|
@ -64,10 +67,14 @@ pub fn cli() -> Cli {
|
||||||
pub enum ExecError {
|
pub enum ExecError {
|
||||||
#[error("run error: {0}")]
|
#[error("run error: {0}")]
|
||||||
Run(#[from] RunError),
|
Run(#[from] RunError),
|
||||||
|
|
||||||
|
#[error("{0}")]
|
||||||
|
Eyre(#[from] color_eyre::Report),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Cli {
|
impl Cli {
|
||||||
pub async fn exec(self) -> Result<(), ExecError> {
|
pub async fn exec(self) -> Result<(), ExecError> {
|
||||||
|
observe::setup_logging(&self.log_env_filter)?;
|
||||||
match self.command {
|
match self.command {
|
||||||
Commands::Run(args) => Ok(args.run().await?),
|
Commands::Run(args) => Ok(args.run().await?),
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,23 @@
|
||||||
pub fn setup_logging() {
|
use crate::prelude::*;
|
||||||
color_eyre::install().expect("Failed to install color_eyre");
|
|
||||||
|
pub fn setup_logging(env_filter: &str) -> Result<(), color_eyre::Report> {
|
||||||
|
color_eyre::install()?;
|
||||||
|
|
||||||
let filter = tracing_subscriber::EnvFilter::builder()
|
let filter = tracing_subscriber::EnvFilter::builder()
|
||||||
.with_default_directive(<tracing_subscriber::filter::Directive>::from(
|
.with_default_directive(<tracing_subscriber::filter::Directive>::from(
|
||||||
tracing::level_filters::LevelFilter::TRACE,
|
tracing::level_filters::LevelFilter::TRACE,
|
||||||
))
|
))
|
||||||
.parse_lossy("info,lyrs=trace");
|
.parse_lossy(env_filter);
|
||||||
|
|
||||||
tracing_subscriber::fmt().with_env_filter(filter).init();
|
tracing_subscriber::fmt().with_env_filter(filter).init();
|
||||||
|
|
||||||
|
let filter = tracing_subscriber::EnvFilter::builder()
|
||||||
|
.with_default_directive(<tracing_subscriber::filter::Directive>::from(
|
||||||
|
tracing::level_filters::LevelFilter::TRACE,
|
||||||
|
))
|
||||||
|
.parse_lossy(env_filter);
|
||||||
|
|
||||||
|
info!("{filter}");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
file_watcher::FileWatcher,
|
file_watcher::FileWatcher,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
|
@ -5,16 +7,19 @@ use crate::{
|
||||||
static_files,
|
static_files,
|
||||||
};
|
};
|
||||||
use axum::{
|
use axum::{
|
||||||
extract::State,
|
body::Bytes,
|
||||||
http::StatusCode,
|
extract::{MatchedPath, State},
|
||||||
|
http::{HeaderMap, Request, Response, StatusCode},
|
||||||
response::{Html, IntoResponse},
|
response::{Html, IntoResponse},
|
||||||
routing::get,
|
routing::get,
|
||||||
Router,
|
Router,
|
||||||
};
|
};
|
||||||
use minijinja::context;
|
use minijinja::context;
|
||||||
|
use tower_http::{classify::ServerErrorsFailureClass, trace::TraceLayer};
|
||||||
use tower_livereload::LiveReloadLayer;
|
use tower_livereload::LiveReloadLayer;
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
use tracing::{info_span, Span};
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum NewRouterError {
|
pub enum NewRouterError {
|
||||||
|
@ -71,6 +76,52 @@ pub async fn router(
|
||||||
.route("/", get(index))
|
.route("/", get(index))
|
||||||
.route("/about", get(about))
|
.route("/about", get(about))
|
||||||
.nest_service("/static", static_file_service)
|
.nest_service("/static", static_file_service)
|
||||||
|
// `TraceLayer` is provided by tower-http so you have to add that as a dependency.
|
||||||
|
// It provides good defaults but is also very customizable.
|
||||||
|
//
|
||||||
|
// See https://docs.rs/tower-http/0.1.1/tower_http/trace/index.html for more details.
|
||||||
|
//
|
||||||
|
// If you want to customize the behavior using closures here is how.
|
||||||
|
.layer(
|
||||||
|
TraceLayer::new_for_http()
|
||||||
|
.make_span_with(|request: &Request<_>| {
|
||||||
|
// Log the matched route's path (with placeholders not filled in).
|
||||||
|
// Use request.uri() or OriginalUri if you want the real path.
|
||||||
|
let matched_path = request
|
||||||
|
.extensions()
|
||||||
|
.get::<MatchedPath>()
|
||||||
|
.map(MatchedPath::as_str);
|
||||||
|
|
||||||
|
info_span!(
|
||||||
|
"http_request",
|
||||||
|
method = ?request.method(),
|
||||||
|
matched_path,
|
||||||
|
some_other_field = tracing::field::Empty,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.on_request(|_request: &Request<_>, _span: &Span| {
|
||||||
|
// You can use `_span.record("some_other_field", value)` in one of these
|
||||||
|
// closures to attach a value to the initially empty field in the info_span
|
||||||
|
// created above.
|
||||||
|
})
|
||||||
|
.on_response(|response: &Response<_>, latency: Duration, _span: &Span| {
|
||||||
|
trace!("on_response: {response:?} in {latency:?}");
|
||||||
|
})
|
||||||
|
.on_body_chunk(|_chunk: &Bytes, _latency: Duration, _span: &Span| {
|
||||||
|
// ...
|
||||||
|
})
|
||||||
|
.on_eos(
|
||||||
|
|_trailers: Option<&HeaderMap>, _stream_duration: Duration, _span: &Span| {
|
||||||
|
// ...
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.on_failure(
|
||||||
|
|error: ServerErrorsFailureClass, latency: Duration, _span: &Span| {
|
||||||
|
error!("on_failure: {error:?} in {latency:?}");
|
||||||
|
// ...
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
.with_state(state.clone());
|
.with_state(state.clone());
|
||||||
|
|
||||||
if let Some(lr) = live_reload_layer {
|
if let Some(lr) = live_reload_layer {
|
||||||
|
@ -82,6 +133,7 @@ pub async fn router(
|
||||||
Ok((result, watchers))
|
Ok((result, watchers))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(skip(state))]
|
||||||
async fn index(State(state): State<AppState>) -> ReqResult<Html<String>> {
|
async fn index(State(state): State<AppState>) -> ReqResult<Html<String>> {
|
||||||
Ok(Html(
|
Ok(Html(
|
||||||
state
|
state
|
||||||
|
@ -90,6 +142,8 @@ async fn index(State(state): State<AppState>) -> ReqResult<Html<String>> {
|
||||||
.await?,
|
.await?,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(skip(state))]
|
||||||
async fn about(State(state): State<AppState>) -> ReqResult<Html<String>> {
|
async fn about(State(state): State<AppState>) -> ReqResult<Html<String>> {
|
||||||
Ok(Html(
|
Ok(Html(
|
||||||
state
|
state
|
||||||
|
|
Loading…
Reference in a new issue