Skip to main content

tonic_debug/
layer.rs

1//! Tower Layer implementation for the gRPC debug middleware.
2//!
3//! [`DebugLayer`] is the primary entry point for users of this crate. It wraps
4//! any tonic/tower service with request/response inspection and logging.
5
6use std::collections::HashSet;
7
8use http::HeaderName;
9use tower_layer::Layer;
10
11use crate::service::DebugService;
12
13/// Configuration for the gRPC debug middleware.
14#[derive(Debug, Clone)]
15pub struct DebugLayer {
16    config: DebugConfig,
17}
18
19/// Controls what the debug middleware captures and logs.
20#[derive(Debug, Clone)]
21pub struct DebugConfig {
22    /// Log request headers.
23    pub log_headers: bool,
24    /// Log request and response bodies (protobuf inspection).
25    pub log_bodies: bool,
26    /// Log individual response body frames as they stream.
27    pub log_response_frames: bool,
28    /// Maximum number of bytes to capture for body inspection.
29    pub max_body_bytes: usize,
30    /// Include a hex dump of raw bytes in logs.
31    pub hex_dump: bool,
32    /// Headers that will be redacted in logs (default: ["authorization"])
33    pub sensitive_headers: HashSet<HeaderName>,
34    /// If true, sensitive headers are logged in full (overrides redaction)
35    pub reveal_sensitive_headers: bool,
36}
37
38impl Default for DebugConfig {
39    fn default() -> Self {
40        Self {
41            log_headers: true,
42            log_bodies: true,
43            log_response_frames: true,
44            max_body_bytes: 4096,
45            hex_dump: false,
46            sensitive_headers: HashSet::new(),
47            reveal_sensitive_headers: false,
48        }
49    }
50}
51
52impl DebugLayer {
53    /// Create a new `DebugLayer` with default configuration.
54    ///
55    /// By default, headers and bodies are logged, hex dumps are off.
56    pub fn new() -> Self {
57        Self {
58            config: DebugConfig::default(),
59        }
60    }
61
62    /// Create a new `DebugLayer` with the given configuration.
63    pub fn with_config(config: DebugConfig) -> Self {
64        Self { config }
65    }
66
67    /// Set whether to log request/response headers.
68    pub fn log_headers(mut self, enabled: bool) -> Self {
69        self.config.log_headers = enabled;
70        self
71    }
72
73    /// Set whether to log request/response bodies.
74    pub fn log_bodies(mut self, enabled: bool) -> Self {
75        self.config.log_bodies = enabled;
76        self
77    }
78
79    /// Set whether to log individual response body frames.
80    pub fn log_response_frames(mut self, enabled: bool) -> Self {
81        self.config.log_response_frames = enabled;
82        self
83    }
84
85    /// Set the maximum number of bytes to capture for body inspection.
86    pub fn max_body_bytes(mut self, max: usize) -> Self {
87        self.config.max_body_bytes = max;
88        self
89    }
90
91    /// Set whether to include hex dumps in log output.
92    pub fn hex_dump(mut self, enabled: bool) -> Self {
93        self.config.hex_dump = enabled;
94        self
95    }
96}
97
98impl Default for DebugLayer {
99    fn default() -> Self {
100        Self::new()
101    }
102}
103
104impl<S> Layer<S> for DebugLayer {
105    type Service = DebugService<S>;
106
107    fn layer(&self, inner: S) -> Self::Service {
108        DebugService::new(inner, self.config.clone())
109    }
110}