brainwires_proxy/middleware/
logging.rs1use crate::error::ProxyResult;
4use crate::middleware::{LayerAction, ProxyLayer};
5use crate::types::{ProxyRequest, ProxyResponse};
6
7const DEFAULT_MAX_BODY_LOG_BYTES: usize = 4096;
8
9pub struct LoggingLayer {
11 pub log_bodies: bool,
13 pub max_body_log_bytes: usize,
15}
16
17impl LoggingLayer {
18 pub fn new() -> Self {
19 Self {
20 log_bodies: false,
21 max_body_log_bytes: DEFAULT_MAX_BODY_LOG_BYTES,
22 }
23 }
24
25 pub fn with_bodies(mut self, enabled: bool) -> Self {
26 self.log_bodies = enabled;
27 self
28 }
29
30 pub fn with_max_body_bytes(mut self, max: usize) -> Self {
31 self.max_body_log_bytes = max;
32 self
33 }
34}
35
36impl Default for LoggingLayer {
37 fn default() -> Self {
38 Self::new()
39 }
40}
41
42#[async_trait::async_trait]
43impl ProxyLayer for LoggingLayer {
44 async fn on_request(&self, request: ProxyRequest) -> ProxyResult<LayerAction> {
45 let body_preview = if self.log_bodies {
46 let bytes = request.body.as_bytes();
47 let len = bytes.len().min(self.max_body_log_bytes);
48 String::from_utf8_lossy(&bytes[..len]).into_owned()
49 } else {
50 format!("[{} bytes]", request.body.len())
51 };
52
53 tracing::info!(
54 request_id = %request.id,
55 method = %request.method,
56 uri = %request.uri,
57 transport = ?request.transport,
58 body = %body_preview,
59 "proxy request"
60 );
61
62 Ok(LayerAction::Forward(request))
63 }
64
65 async fn on_response(&self, response: ProxyResponse) -> ProxyResult<ProxyResponse> {
66 let body_preview = if self.log_bodies {
67 let bytes = response.body.as_bytes();
68 let len = bytes.len().min(self.max_body_log_bytes);
69 String::from_utf8_lossy(&bytes[..len]).into_owned()
70 } else {
71 format!("[{} bytes]", response.body.len())
72 };
73
74 tracing::info!(
75 request_id = %response.id,
76 status = %response.status,
77 body = %body_preview,
78 "proxy response"
79 );
80
81 Ok(response)
82 }
83
84 fn name(&self) -> &str {
85 "logging"
86 }
87}