Originally from @eriko.eurosky.social with some changes
+15
Cargo.lock
+15
Cargo.lock
···
2812
2812
"tracing-subscriber",
2813
2813
"url",
2814
2814
"urlencoding",
2815
+
"valuable",
2815
2816
]
2816
2817
2817
2818
[[package]]
···
4457
4458
"tower",
4458
4459
"tower-layer",
4459
4460
"tower-service",
4461
+
"tracing",
4460
4462
]
4461
4463
4462
4464
[[package]]
···
4533
4535
]
4534
4536
4535
4537
[[package]]
4538
+
name = "tracing-serde"
4539
+
version = "0.2.0"
4540
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4541
+
checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1"
4542
+
dependencies = [
4543
+
"serde",
4544
+
"tracing-core",
4545
+
]
4546
+
4547
+
[[package]]
4536
4548
name = "tracing-subscriber"
4537
4549
version = "0.3.22"
4538
4550
source = "registry+https://github.com/rust-lang/crates.io-index"
···
4542
4554
"nu-ansi-term",
4543
4555
"once_cell",
4544
4556
"regex-automata",
4557
+
"serde",
4558
+
"serde_json",
4545
4559
"sharded-slab",
4546
4560
"smallvec",
4547
4561
"thread_local",
4548
4562
"tracing",
4549
4563
"tracing-core",
4550
4564
"tracing-log",
4565
+
"tracing-serde",
4551
4566
]
4552
4567
4553
4568
[[package]]
+4
-3
Cargo.toml
+4
-3
Cargo.toml
···
11
11
dotenvy = "0.15.7"
12
12
serde = { version = "1.0", features = ["derive"] }
13
13
serde_json = "1.0"
14
-
tracing = "0.1"
15
-
tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] }
14
+
tracing = { version = "0.1.44" }
15
+
tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt", "json", "serde", ] }
16
16
hyper-util = { version = "0.1.19", features = ["client", "client-legacy"] }
17
-
tower-http = { version = "0.6", features = ["cors", "compression-zstd"] }
17
+
tower-http = { version = "0.6", features = ["cors", "compression-zstd", "trace"] }
18
18
tower_governor = { version = "0.8.0", features = ["axum", "tracing"] }
19
19
hex = "0.4"
20
20
jwt-compact = { version = "0.8.0", features = ["es256k"] }
···
40
40
josekit = "0.10.3"
41
41
dashmap = "6.1"
42
42
tower = "0.5"
43
+
valuable = "0.1.1"
+56
-7
src/main.rs
+56
-7
src/main.rs
···
31
31
use tower_governor::{
32
32
GovernorLayer, governor::GovernorConfigBuilder, key_extractor::SmartIpKeyExtractor,
33
33
};
34
+
use tower_http::cors::AllowHeaders;
35
+
use tower_http::trace::{DefaultOnRequest, HttpMakeClassifier};
34
36
use tower_http::{
35
37
compression::CompressionLayer,
36
-
cors::{AllowHeaders, Any, CorsLayer},
38
+
cors::{Any, CorsLayer},
39
+
trace::TraceLayer,
37
40
};
38
-
use tracing::log;
41
+
use tracing::{Span, log};
39
42
use tracing_subscriber::{EnvFilter, fmt, prelude::*};
40
43
41
44
mod auth;
···
199
202
200
203
#[tokio::main]
201
204
async fn main() -> Result<(), Box<dyn std::error::Error>> {
202
-
setup_tracing();
203
205
let pds_env_location =
204
206
env::var("PDS_ENV_LOCATION").unwrap_or_else(|_| "/pds/pds.env".to_string());
205
207
···
209
211
"Error loading pds.env file (ignore if you loaded your variables in the environment somehow else): {e}"
210
212
);
211
213
}
214
+
// Sets up after the pds.env file is loaded
215
+
setup_tracing();
212
216
213
217
let pds_root =
214
218
env::var("PDS_DATA_DIRECTORY").expect("PDS_DATA_DIRECTORY is not set in your pds.env file");
···
385
389
);
386
390
}
387
391
392
+
let request_logging = env::var("GATEKEEPER_REQUEST_LOGGING")
393
+
.map(|v| v.eq_ignore_ascii_case("true") || v == "1")
394
+
.unwrap_or(false);
395
+
396
+
if request_logging {
397
+
app = app.layer(request_trace_layer());
398
+
}
399
+
388
400
let app = app
389
401
.layer(CompressionLayer::new())
390
402
.layer(cors)
···
416
428
417
429
fn setup_tracing() {
418
430
let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"));
419
-
tracing_subscriber::registry()
420
-
.with(env_filter)
421
-
.with(fmt::layer())
422
-
.init();
431
+
let json = env::var("GATEKEEPER_LOG_FORMAT")
432
+
.map(|v| v.eq_ignore_ascii_case("json"))
433
+
.unwrap_or(false);
434
+
435
+
if json {
436
+
tracing_subscriber::registry()
437
+
.with(env_filter)
438
+
.with(fmt::layer().json())
439
+
.init();
440
+
} else {
441
+
tracing_subscriber::registry()
442
+
.with(env_filter)
443
+
.with(fmt::layer())
444
+
.init();
445
+
}
423
446
}
424
447
425
448
async fn shutdown_signal() {
···
447
470
_ = terminate => {},
448
471
}
449
472
}
473
+
474
+
fn request_trace_layer() -> TraceLayer<
475
+
HttpMakeClassifier,
476
+
impl Fn(&axum::http::Request<Body>) -> Span + Clone,
477
+
DefaultOnRequest,
478
+
impl Fn(&axum::http::Response<Body>, Duration, &Span) + Clone,
479
+
> {
480
+
TraceLayer::new_for_http()
481
+
.make_span_with(|req: &axum::http::Request<Body>| {
482
+
let headers = req.headers();
483
+
tracing::info_span!("request",
484
+
method = %req.method(),
485
+
path = %req.uri().path(),
486
+
headers = %format!("{:?}", headers),
487
+
)
488
+
})
489
+
.on_response(
490
+
|resp: &axum::http::Response<Body>, latency: Duration, _span: &tracing::Span| {
491
+
tracing::info!(
492
+
status = resp.status().as_u16(),
493
+
latency_ms = latency.as_millis() as u64,
494
+
"response"
495
+
);
496
+
},
497
+
)
498
+
}
History
2 rounds
0 comments
baileytownsend.dev
submitted
#1
2 commits
expand
collapse
feat: add opt-in per-request logging
Add GATEKEEPER_REQUEST_LOGGING=true to enable per-request tracing
with method, path, client_ip, status, and latency_ms. Disabled by
default — existing behavior unchanged.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
light changes to logging changes
expand 0 comments
pull request successfully merged
baileytownsend.dev
submitted
#0
2 commits
expand
collapse
feat: add opt-in per-request logging
Add GATEKEEPER_REQUEST_LOGGING=true to enable per-request tracing
with method, path, client_ip, status, and latency_ms. Disabled by
default — existing behavior unchanged.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
light changes to logging changes