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::from([HeaderName::from_static("authorization")]),
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 Headers that will be redacted
68    pub fn sensitive_headers(mut self, sensitive_headers: HashSet<HeaderName>) -> Self {
69        self.config.sensitive_headers = sensitive_headers;
70        self
71    }
72
73    /// Set whether to log request/response headers.
74    pub fn reveal_sensitive_headers(mut self, enabled: bool) -> Self {
75        self.config.reveal_sensitive_headers = enabled;
76        self
77    }
78
79    /// Set whether to log request/response headers.
80    pub fn log_headers(mut self, enabled: bool) -> Self {
81        self.config.log_headers = enabled;
82        self
83    }
84
85    /// Set whether to log request/response bodies.
86    pub fn log_bodies(mut self, enabled: bool) -> Self {
87        self.config.log_bodies = enabled;
88        self
89    }
90
91    /// Set whether to log individual response body frames.
92    pub fn log_response_frames(mut self, enabled: bool) -> Self {
93        self.config.log_response_frames = enabled;
94        self
95    }
96
97    /// Set the maximum number of bytes to capture for body inspection.
98    pub fn max_body_bytes(mut self, max: usize) -> Self {
99        self.config.max_body_bytes = max;
100        self
101    }
102
103    /// Set whether to include hex dumps in log output.
104    pub fn hex_dump(mut self, enabled: bool) -> Self {
105        self.config.hex_dump = enabled;
106        self
107    }
108}
109
110impl Default for DebugLayer {
111    fn default() -> Self {
112        Self::new()
113    }
114}
115
116impl<S> Layer<S> for DebugLayer {
117    type Service = DebugService<S>;
118
119    fn layer(&self, inner: S) -> Self::Service {
120        DebugService::new(inner, self.config.clone())
121    }
122}