Add configuration option to control request logging

This commit is contained in:
asonix 2024-06-03 22:15:49 -05:00
parent 6ef9dc404f
commit 4e75764110
5 changed files with 67 additions and 18 deletions

View file

@ -18,6 +18,7 @@ impl Args {
log_format,
log_targets,
log_spans,
log_requests,
no_log_ansi,
console_address,
console_buffer_capacity,
@ -40,6 +41,7 @@ impl Args {
targets: log_targets.map(Serde::new),
log_spans,
no_ansi: no_log_ansi,
log_requests,
},
console: Console {
address: console_address,
@ -584,6 +586,8 @@ struct Logging {
#[serde(skip_serializing_if = "std::ops::Not::not")]
log_spans: bool,
#[serde(skip_serializing_if = "std::ops::Not::not")]
log_requests: bool,
#[serde(skip_serializing_if = "std::ops::Not::not")]
no_ansi: bool,
}
@ -928,6 +932,9 @@ pub(super) struct Args {
/// Whether to log openning and closing of tracing spans to stdout
#[arg(long)]
log_spans: bool,
/// Whether to log request completions at an INFO level
#[arg(long)]
log_requests: bool,
#[arg(long)]
/// Whether to disable color-codes in log output

View file

@ -55,6 +55,7 @@ struct LoggingDefaults {
format: LogFormat,
targets: Serde<Targets>,
log_spans: bool,
log_requests: bool,
no_ansi: bool,
}
@ -236,6 +237,7 @@ impl Default for LoggingDefaults {
format: LogFormat::Normal,
targets: "info".parse().expect("Valid targets string"),
log_spans: false,
log_requests: false,
no_ansi: false,
}
}

View file

@ -165,6 +165,8 @@ pub(crate) struct Logging {
pub(crate) log_spans: bool,
pub(crate) no_ansi: bool,
pub(crate) log_requests: bool,
}
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]

View file

@ -1741,7 +1741,7 @@ async fn launch<
spawn_workers(state.clone());
App::new()
.wrap(Log)
.wrap(Log::new(state.config.tracing.logging.log_requests))
.wrap(TracingLogger::default())
.wrap(Deadline)
.wrap(Metrics)

View file

@ -7,16 +7,30 @@ use actix_web::{
ResponseError,
};
pub(crate) struct Log;
pub(crate) struct Log {
info: bool,
}
pub(crate) struct LogMiddleware<S> {
info: bool,
inner: S,
}
impl Log {
pub(crate) fn new(info: bool) -> Self {
Self { info }
}
}
#[derive(Debug)]
pub(crate) struct LogError(actix_web::Error);
pub(crate) struct LogError {
info: bool,
error: actix_web::Error,
}
pin_project_lite::pin_project! {
pub(crate) struct LogFuture<F> {
info: bool,
#[pin]
inner: F,
}
@ -24,6 +38,8 @@ pin_project_lite::pin_project! {
pin_project_lite::pin_project! {
pub(crate) struct LogBody<B> {
info: bool,
status: Option<StatusCode>,
#[pin]
@ -45,7 +61,10 @@ where
type Future = Ready<Result<Self::Transform, Self::InitError>>;
fn new_transform(&self, service: S) -> Self::Future {
ready(Ok(LogMiddleware { inner: service }))
ready(Ok(LogMiddleware {
info: self.info,
inner: service,
}))
}
}
@ -64,13 +83,20 @@ where
&self,
ctx: &mut core::task::Context<'_>,
) -> std::task::Poll<Result<(), Self::Error>> {
self.inner
.poll_ready(ctx)
.map(|res| res.map_err(|e| LogError(e.into()).into()))
self.inner.poll_ready(ctx).map(|res| {
res.map_err(|e| {
LogError {
info: self.info,
error: e.into(),
}
.into()
})
})
}
fn call(&self, req: ServiceRequest) -> Self::Future {
LogFuture {
info: self.info,
inner: self.inner.call(req),
}
}
@ -88,6 +114,7 @@ where
self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Self::Output> {
let info = self.info;
let this = self.project();
std::task::Poll::Ready(match std::task::ready!(this.inner.poll(cx)) {
@ -95,15 +122,23 @@ where
let status = response.status();
let status = if response.response().body().size().is_eof() {
emit(status);
emit(status, info);
None
} else {
Some(status)
};
Ok(response.map_body(|_, inner| LogBody { status, inner }))
Ok(response.map_body(|_, inner| LogBody {
info,
status,
inner,
}))
}
Err(e) => Err(LogError(e.into()).into()),
Err(e) => Err(LogError {
info,
error: e.into(),
}
.into()),
})
}
}
@ -128,7 +163,7 @@ where
if opt.is_none() {
if let Some(status) = this.status.take() {
emit(status);
emit(status, *this.info);
}
}
@ -138,31 +173,32 @@ where
impl std::fmt::Display for LogError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
self.error.fmt(f)
}
}
impl std::error::Error for LogError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.0.source()
self.error.source()
}
}
impl ResponseError for LogError {
fn status_code(&self) -> actix_web::http::StatusCode {
self.0.as_response_error().status_code()
self.error.as_response_error().status_code()
}
fn error_response(&self) -> actix_web::HttpResponse<actix_web::body::BoxBody> {
let response = self.0.error_response();
let response = self.error.error_response();
let status = response.status();
if response.body().size().is_eof() {
emit(status);
emit(status, self.info);
response
} else {
response.map_body(|_, inner| {
LogBody {
info: self.info,
status: Some(status),
inner,
}
@ -172,14 +208,16 @@ impl ResponseError for LogError {
}
}
fn emit(status: StatusCode) {
fn emit(status: StatusCode, info: bool) {
if status.is_server_error() {
tracing::error!("server error");
} else if status.is_client_error() {
tracing::warn!("client error");
} else if status.is_redirection() {
tracing::info!("redirected");
} else {
} else if info {
tracing::info!("completed");
} else {
tracing::debug!("completed");
}
}