1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
use tracing::{warn, Level};
use xitca_http::util::middleware;

use crate::service::Service;

/// builder for tracing log middleware.
///
/// # Examples
/// ```rust
/// # use xitca_web::{handler::handler_service, middleware::Logger, route::get, App, WebContext};
/// App::new()
///     .at("/", get(handler_service(|| async { "hello,world!" })))
///     # .at("/infer", handler_service(|_: &WebContext<'_>| async{ "infer type" }))
///     // log http request and error with default setting.
///     .enclosed(Logger::new());
/// ```
pub struct Logger {
    logger: middleware::Logger,
}

impl Default for Logger {
    fn default() -> Self {
        Self::new()
    }
}

impl Logger {
    /// construct a new logger middleware builder with [`Level::INFO`] of verbosity it generate and captures.
    /// would try to initialize global trace dispatcher.
    pub fn new() -> Self {
        Self::with_level(Level::INFO)
    }

    /// construct a new logger middleware builder with given [Level] of verbosity it generate and captures.
    /// would try to initialize global trace dispatcher.
    pub fn with_level(level: Level) -> Self {
        if let Err(e) = tracing_subscriber::fmt().with_max_level(level).try_init() {
            // the most likely case is trace dispatcher has already been set by user. log the warning and move on.
            warn!("failed to initialize global trace dispatcher: {}", e);
        }

        Self {
            logger: middleware::Logger::with_level(level),
        }
    }
}

impl<Arg> Service<Arg> for Logger
where
    middleware::Logger: Service<Arg>,
{
    type Response = <middleware::Logger as Service<Arg>>::Response;
    type Error = <middleware::Logger as Service<Arg>>::Error;

    async fn call(&self, arg: Arg) -> Result<Self::Response, Self::Error> {
        self.logger.call(arg).await
    }
}