0
# Bound Logger Classes
1
2
Immutable context-carrying logger classes that provide the core structlog API. Bound loggers maintain context data and delegate logging operations to wrapped loggers while supporting context binding, unbinding, and transformation operations.
3
4
## Capabilities
5
6
### Base Bound Logger
7
8
Base class for all bound loggers providing core context manipulation and event processing functionality.
9
10
```python { .api }
11
class BoundLoggerBase:
12
"""
13
Base class for immutable context carriers.
14
15
Provides core functionality for context binding, unbinding, and event processing.
16
All bound logger implementations inherit from this class.
17
"""
18
19
def __init__(self, logger, processors, context): ...
20
21
def bind(self, **new_values) -> Self:
22
"""
23
Return new bound logger with additional context values.
24
25
Args:
26
**new_values: Key-value pairs to add to the context
27
28
Returns:
29
New bound logger instance with merged context
30
"""
31
32
def unbind(self, *keys) -> Self:
33
"""
34
Return new bound logger with specified keys removed from context.
35
36
Args:
37
*keys: Context keys to remove
38
39
Returns:
40
New bound logger instance with keys removed
41
42
Raises:
43
KeyError: If any key is not found in context
44
"""
45
46
def try_unbind(self, *keys) -> Self:
47
"""
48
Return new bound logger with specified keys removed, ignoring missing keys.
49
50
Args:
51
*keys: Context keys to remove
52
53
Returns:
54
New bound logger instance with existing keys removed
55
"""
56
57
def new(self, **new_values) -> Self:
58
"""
59
Return new bound logger with cleared context and new values.
60
61
Args:
62
**new_values: Key-value pairs for the new context
63
64
Returns:
65
New bound logger instance with only the specified context
66
"""
67
68
def _process_event(self, method_name, event, event_kw) -> tuple[Sequence[Any], Mapping[str, Any]]:
69
"""Process event through processor chain (protected method)."""
70
71
def _proxy_to_logger(self, method_name, event=None, **event_kw) -> Any:
72
"""Proxy method call to wrapped logger (protected method)."""
73
```
74
75
### Generic Bound Logger
76
77
Generic bound logger that can wrap any logger and provides dynamic method delegation.
78
79
```python { .api }
80
class BoundLogger(BoundLoggerBase):
81
"""
82
Generic bound logger that can wrap anything.
83
84
Uses dynamic method delegation to forward all method calls
85
to the wrapped logger after processing through the processor chain.
86
"""
87
```
88
89
### Context Utilities
90
91
Functions for working with bound logger contexts.
92
93
```python { .api }
94
def get_context(bound_logger) -> Context:
95
"""
96
Return the context dictionary from a bound logger.
97
98
Args:
99
bound_logger: Bound logger instance
100
101
Returns:
102
dict: Copy of the logger's context
103
"""
104
```
105
106
## Usage Examples
107
108
### Basic Context Binding
109
110
```python
111
import structlog
112
113
structlog.configure(
114
processors=[structlog.processors.JSONRenderer()],
115
wrapper_class=structlog.BoundLogger,
116
)
117
118
logger = structlog.get_logger()
119
120
# Bind context values
121
user_logger = logger.bind(user_id=123, username="alice")
122
user_logger.info("User logged in")
123
124
# Bind additional context
125
session_logger = user_logger.bind(session_id="abc-def", ip="192.168.1.1")
126
session_logger.info("Session created")
127
128
# Original logger is unchanged
129
logger.info("System message") # No user context
130
```
131
132
### Context Unbinding
133
134
```python
135
import structlog
136
137
logger = structlog.get_logger()
138
rich_logger = logger.bind(
139
user_id=123,
140
session_id="abc-def",
141
request_id="req-456",
142
debug_info="temp"
143
)
144
145
# Remove specific keys
146
clean_logger = rich_logger.unbind("debug_info")
147
clean_logger.info("Cleaned up") # No debug_info
148
149
# Remove multiple keys
150
minimal_logger = rich_logger.unbind("debug_info", "request_id")
151
minimal_logger.info("Minimal context") # Only user_id and session_id
152
153
# try_unbind ignores missing keys
154
safe_logger = rich_logger.try_unbind("missing_key", "debug_info")
155
safe_logger.info("Safe unbind") # No error for missing_key
156
```
157
158
### Context Replacement
159
160
```python
161
import structlog
162
163
logger = structlog.get_logger()
164
request_logger = logger.bind(
165
user_id=123,
166
session_id="old-session",
167
request_id="req-1"
168
)
169
170
# Clear all context and start fresh
171
new_request_logger = request_logger.new(
172
user_id=456,
173
request_id="req-2",
174
endpoint="/api/orders"
175
)
176
177
new_request_logger.info("New request started") # Only new context
178
```
179
180
### Working with Context
181
182
```python
183
import structlog
184
185
logger = structlog.get_logger()
186
context_logger = logger.bind(service="auth", version="1.2.3")
187
188
# Get the current context
189
context = structlog.get_context(context_logger)
190
print(context) # {'service': 'auth', 'version': '1.2.3'}
191
192
# Use context for conditional logic
193
if context.get("service") == "auth":
194
enhanced_logger = context_logger.bind(auth_enabled=True)
195
enhanced_logger.info("Authentication service ready")
196
```
197
198
### Chaining Context Operations
199
200
```python
201
import structlog
202
203
logger = structlog.get_logger()
204
205
# Chain multiple context operations
206
request_logger = (logger
207
.bind(request_id="req-789")
208
.bind(user_id=123, ip="10.0.0.1")
209
.unbind("ip") # Remove IP for privacy
210
.bind(anonymized=True)
211
)
212
213
request_logger.info("Request processed")
214
```
215
216
### Custom Context Classes
217
218
```python
219
import structlog
220
from collections import UserDict
221
222
class CustomContext(UserDict):
223
def __init__(self, *args, **kwargs):
224
super().__init__(*args, **kwargs)
225
self.access_count = 0
226
227
def __getitem__(self, key):
228
self.access_count += 1
229
return super().__getitem__(key)
230
231
structlog.configure(
232
processors=[structlog.processors.JSONRenderer()],
233
wrapper_class=structlog.BoundLogger,
234
context_class=CustomContext,
235
)
236
237
logger = structlog.get_logger()
238
tracked_logger = logger.bind(user_id=123)
239
tracked_logger.info("Tracking access")
240
```
241
242
### Error Handling in Context Operations
243
244
```python
245
import structlog
246
247
logger = structlog.get_logger()
248
context_logger = logger.bind(user_id=123, session_id="abc")
249
250
try:
251
# This will raise KeyError
252
broken_logger = context_logger.unbind("missing_key")
253
except KeyError:
254
# Use try_unbind instead
255
safe_logger = context_logger.try_unbind("missing_key")
256
safe_logger.info("Handled missing key gracefully")
257
258
# Alternative: check context first
259
context = structlog.get_context(context_logger)
260
if "session_id" in context:
261
clean_logger = context_logger.unbind("session_id")
262
clean_logger.info("Session cleaned up")
263
```