pub struct Logger(_);
Expand description

Middleware for logging request and response summaries to the terminal.

Actually it’s a copy & paste from the official Logger middleware (original source code), but it allows to choose the logging level depending on the HTTP status code responded (see Logger::custom_level() and Logger::custom_error_resp_level()), and by default server errors are logged with ERROR level.

Moreover, error in response log are also configurable, and by default logged as ERROR in server side failures.

This middleware uses the log crate to output information. Enable log’s output for the “http_logger” scope using env_logger or similar crate.

Default Format

The default Logger uses the following format:

%a "%r" %s %b "%{Referer}i" "%{User-Agent}i" %T

Example Output:
127.0.0.1:54278 "GET /test HTTP/1.1" 404 20 "-" "HTTPie/2.2.0" 0.001074

Examples

use actix_web::App;
use actix_contrib_logger::middleware::Logger;
use env_logger::Env;

// access logs are printed with the INFO level so ensure it is enabled by default
env_logger::init_from_env(Env::new().default_filter_or("info"));

let app = App::new()
    // .wrap(Logger::default())
    .wrap(Logger::new("%a %{User-Agent}i"));

Format

VariableDescription
%%The percent sign
%aPeer IP address (or IP address of reverse proxy if used)
%tTime when the request started processing (in RFC 3339 format)
%rFirst line of request (Example: GET /test HTTP/1.1)
%sResponse status code
%bSize of response in bytes, including HTTP headers
%TTime taken to serve the request, in seconds to 6 decimal places
%DTime taken to serve the request, in milliseconds
%URequest URL
%{r}a“Real IP” remote address *
%{FOO}irequest.headers["FOO"]
%{FOO}oresponse.headers["FOO"]
%{FOO}eenv_var["FOO"]
%{FOO}xiCustom request replacement labelled “FOO”
%{FOO}xoCustom response replacement labelled “FOO”

Security

* “Real IP” remote address is calculated using ConnectionInfo::realip_remote_addr()

If you use this value, ensure that all requests come from trusted hosts. Otherwise, it is trivial for the remote client to falsify their source IP address.

Implementations§

source§

impl Logger

source

pub fn new(format: &str) -> Logger

Create Logger middleware with the specified format.

source

pub fn exclude<T: Into<String>>(self, path: T) -> Self

Ignore and do not log access info for specified path.

source

pub fn exclude_regex<T: Into<String>>(self, path: T) -> Self

Ignore and do not log access info for paths that match regex.

source

pub fn log_target(self, target: impl Into<Cow<'static, str>>) -> Self

Sets the logging target to target.

By default, the log target is http_logger.

Examples

Using .log_target("http") would have this effect on request logs:

- [2015-10-21T07:28:00Z INFO  http_logger] 127.0.0.1 "GET / HTTP/1.1" 200 88 "-" "dmc/1.0" 0.001985
+ [2015-10-21T07:28:00Z INFO  http] 127.0.0.1 "GET / HTTP/1.1" 200 88 "-" "dmc/1.0" 0.001985
                              ^^^^
source

pub fn custom_request_replace( self, label: &str, f: impl Fn(&ServiceRequest) -> String + 'static ) -> Self

Register a function that receives a ServiceRequest and returns a String for use in the log line. The label passed as the first argument should match a replacement substring in the logger format like %{label}xi.

It is convention to print “-” to indicate no output instead of an empty string.

Examples
Logger::new("example %{JWT_ID}xi")
    .custom_request_replace("JWT_ID", |req| parse_jwt_id(req.headers().get("Authorization")));
source

pub fn custom_response_replace( self, label: &str, f: impl Fn(&ServiceResponse) -> String + 'static ) -> Self

Register a function that receives a ServiceResponse and returns a string for use in the log line.

The label passed as the first argument should match a replacement substring in the logger format like %{label}xo.

It is convention to print “-” to indicate no output instead of an empty string.

The replacement function does not have access to the response body.

Examples
fn log_if_error(res: &ServiceResponse) -> String {
    if res.status().as_u16() >= 400 {
        "ERROR".to_string()
    } else {
        "-".to_string()
    }
}

Logger::new("example %{ERROR_STATUS}xo")
    .custom_response_replace("ERROR_STATUS", |res| log_if_error(res) );
source

pub fn custom_level(self, f: fn(_: StatusCode) -> Level) -> Self

Register a function that receives a StatusCode to define what Level to use to log an HTTP event. By default all HTTP requests are logged with INFO severity except the ones ended with server errors (600 > status code >= 500) that are logged with ERROR severity. With this function the level used is customized.

Examples

In the following example ERROR level is used for server errors, WARN for HTTP 404 responses (Not Found), and for the rest INFO level:

use actix_contrib_logger::middleware::Logger;
use http::StatusCode;
use log::Level;

let logger = Logger::default()
    .custom_level(|status| {
        if status.is_server_error() {
            Level::Error
        } else if status == StatusCode::NOT_FOUND {
            Level::Warn
        } else {
            Level::Info
        }
    });

Requests logs will look like:

[2023-08-13T07:28:00Z INFO  http_logger] 127.0.0.1 "GET / HTTP/1.1" 200 802 "-" "Mozilla/5.0 ..." 0.001985
[2023-08-13T07:29:10Z ERROR http_logger] 127.0.0.1 "POST /users HTTP/1.1" 500 86 "-" "curl/7.68.0" 0.002023
[2023-08-13T07:29:10Z WARN  http_logger] 127.0.0.1 "PUT /users HTTP/1.1" 404 55 "-" "HTTPie/3.2.1" 0.002023
source

pub fn custom_error_resp_level(self, f: fn(_: StatusCode) -> Level) -> Self

Register a function that receives a StatusCode to define what Level to use to log an error in the response. By default all error in responses are logged with DEBUG severity except the ones ended with server errors (600 > status code >= 500) that are logged with ERROR severity. With this function the level used is customized.

Examples

In the following example ERROR level is used for server errors, INFO level instead of DEBUG for the rest:

use actix_contrib_logger::middleware::Logger;
use http::StatusCode;
use log::Level;

let logger = Logger::default()
    .custom_error_resp_level(|status| {
        if status.is_server_error() {
            Level::Error
        } else {
            Level::Info
        }
    });

Requests logs with errors will look like (the error in response logs are the first and the third lines):

[2023-08-13T21:51:02Z INFO  http_logger] Error in "400 Bad Request" response: Validation("Tenant already exists.")
[2023-08-13T21:51:02Z INFO  http_logger] 127.0.0.1 "POST /tenants HTTP/1.1" 400 56 "-" "HTTPie/3.2.1" 0.002368
[2023-08-13T20:59:53Z ERROR http_logger] Error in "500 Internal Server Error" response: DB(PoolTimedOut)
[2023-08-13T07:59:53Z ERROR http_logger] 127.0.0.1 "POST /users HTTP/1.1" 500 86 "-" "curl/7.68.0" 0.002023

Trait Implementations§

source§

impl Debug for Logger

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for Logger

source§

fn default() -> Logger

Create Logger middleware with format:

%a "%r" %s %b "%{Referer}i" "%{User-Agent}i" %T
source§

impl<S, B> Transform<S, ServiceRequest> for Loggerwhere S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>, B: MessageBody,

§

type Response = ServiceResponse<StreamLog<B>>

Responses produced by the service.
§

type Error = Error

Errors produced by the service.
§

type Transform = LoggerMiddleware<S>

The TransformService value created by this factory
§

type InitError = ()

Errors produced while building a transform service.
§

type Future = Ready<Result<<Logger as Transform<S, ServiceRequest>>::Transform, <Logger as Transform<S, ServiceRequest>>::InitError>>

The future response value.
source§

fn new_transform(&self, service: S) -> Self::Future

Creates and returns a new Transform component, asynchronously

Auto Trait Implementations§

§

impl !RefUnwindSafe for Logger

§

impl !Send for Logger

§

impl !Sync for Logger

§

impl Unpin for Logger

§

impl !UnwindSafe for Logger

Blanket Implementations§

source§

impl<T> Any for Twhere T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for Twhere U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T> Same<T> for T

§

type Output = T

Should always be Self
source§

impl<T, U> TryFrom<U> for Twhere U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere V: MultiLane<T>,

§

fn vzip(self) -> V

source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more