Skip to main content

better_fetch/plugins/
logger.rs

1use async_trait::async_trait;
2use tracing::{error, info, info_span, warn};
3
4use crate::hooks::{ErrorContext, Hooks};
5use crate::plugin::Plugin;
6
7/// Tracing-based logger plugin (request, response, retry, error).
8#[derive(Debug, Clone)]
9pub struct LoggerPlugin {
10    /// When `false`, hooks are registered but do not log.
11    pub enabled: bool,
12    /// When `true`, includes method and URL on each line.
13    pub verbose: bool,
14}
15
16impl LoggerPlugin {
17    /// Creates a plugin with logging enabled.
18    pub fn new() -> Self {
19        Self {
20            enabled: true,
21            verbose: false,
22        }
23    }
24
25    /// Enables or disables log output.
26    pub fn enabled(mut self, enabled: bool) -> Self {
27        self.enabled = enabled;
28        self
29    }
30
31    /// Enables verbose log fields.
32    pub fn verbose(mut self, verbose: bool) -> Self {
33        self.verbose = verbose;
34        self
35    }
36}
37
38impl Default for LoggerPlugin {
39    fn default() -> Self {
40        Self::new()
41    }
42}
43
44#[async_trait]
45impl Plugin for LoggerPlugin {
46    /// Plugin id: `"logger"`.
47    fn id(&self) -> &'static str {
48        "logger"
49    }
50
51    fn hooks(&self) -> Hooks {
52        let enabled = self.enabled;
53        let verbose = self.verbose;
54
55        Hooks::new()
56            .on_request(move |ctx| {
57                let enabled = enabled;
58                let verbose = verbose;
59                async move {
60                    if enabled {
61                        let span = info_span!(
62                            "http.request",
63                            method = %ctx.method,
64                            url = %ctx.url,
65                            retry_attempt = ctx.retry_attempt,
66                        );
67                        let _guard = span.enter();
68                        if verbose {
69                            info!("better-fetch request");
70                        } else {
71                            info!("better-fetch request");
72                        }
73                    }
74                    Ok(ctx)
75                }
76            })
77            .on_response_stream({
78                let enabled = self.enabled;
79                let verbose = self.verbose;
80                move |ctx| {
81                    let enabled = enabled;
82                    let verbose = verbose;
83                    async move {
84                        if enabled {
85                            let span = info_span!(
86                                "http.response",
87                                status = %ctx.status,
88                                url = %ctx.request.url,
89                                streaming = true,
90                            );
91                            let _guard = span.enter();
92                            if verbose {
93                                info!("better-fetch stream response");
94                            } else {
95                                info!("better-fetch stream response");
96                            }
97                        }
98                        Ok(crate::hooks::StreamingResponseMeta {
99                            status: ctx.status,
100                            headers: ctx.headers,
101                        })
102                    }
103                }
104            })
105            .on_response({
106                let enabled = self.enabled;
107                let verbose = self.verbose;
108                move |ctx| {
109                    let enabled = enabled;
110                    let verbose = verbose;
111                    async move {
112                        if enabled {
113                            let status = ctx.response.status();
114                            let span = info_span!(
115                                "http.response",
116                                status = %status,
117                                url = %ctx.request.url,
118                                streaming = false,
119                            );
120                            let _guard = span.enter();
121                            if verbose {
122                                info!("better-fetch response");
123                            } else {
124                                info!("better-fetch response");
125                            }
126                        }
127                        Ok(ctx.response)
128                    }
129                }
130            })
131            .on_retry({
132                let enabled = self.enabled;
133                move |ctx| {
134                    let enabled = enabled;
135                    async move {
136                        if enabled {
137                            warn!(
138                                retry_attempt = ctx.request.retry_attempt,
139                                next_attempt = ctx.request.retry_attempt + 1,
140                                status = %ctx.response.status(),
141                                url = %ctx.request.url,
142                                "better-fetch retry"
143                            );
144                        }
145                    }
146                }
147            })
148            .on_error({
149                let enabled = self.enabled;
150                move |ctx: ErrorContext| {
151                    let enabled = enabled;
152                    async move {
153                        if enabled {
154                            let status = ctx.response.as_ref().map(|r| r.status().as_u16());
155                            let body_preview = ctx.response_body_preview(256);
156                            error!(
157                                error = %ctx.error,
158                                url = %ctx.request.url,
159                                ?status,
160                                body_preview = body_preview.as_deref(),
161                                retry_attempt = ctx.request.retry_attempt,
162                                "better-fetch error"
163                            );
164                        }
165                    }
166                }
167            })
168    }
169}