@@ -121,7 +121,15 @@ def formatMessage(self, record: logging.LogRecord) -> str:
121
121
122
122
123
123
class GunicornSafeAtoms (abc .Mapping ):
124
- """Implement atoms necessary for gunicorn log."""
124
+ """Implement atoms necessary for gunicorn log.
125
+
126
+ This class does a few things:
127
+ - provide all atoms necessary for gunicorn log formatter
128
+ - collect response body size for reporting from ASGI messages
129
+ - provide mapping interface that returns '-' for missing atoms
130
+ - escapes double quotes found in atom strings
131
+ """
132
+
125
133
def __init__ (self , scope ):
126
134
self .scope = scope
127
135
self .status_code = None
@@ -134,25 +142,23 @@ def __init__(self, scope):
134
142
def request_headers (self ):
135
143
if self ._request_headers is None :
136
144
self ._request_headers = {
137
- k .decode ('ascii' ): v .decode ('ascii' )
138
- for k , v in self .scope ['headers' ]
145
+ k .decode ("ascii" ): v .decode ("ascii" ) for k , v in self .scope ["headers" ]
139
146
}
140
147
return self ._request_headers
141
148
142
149
@property
143
150
def duration (self ):
144
- d = self .scope [' response_end_time' ] - self .scope [' request_start_time' ]
151
+ d = self .scope [" response_end_time" ] - self .scope [" request_start_time" ]
145
152
return d
146
153
147
154
def on_asgi_message (self , message ):
148
- if message [' type' ] == ' http.response.start' :
149
- self .status_code = message [' status' ]
155
+ if message [" type" ] == " http.response.start" :
156
+ self .status_code = message [" status" ]
150
157
self .response_headers = {
151
- k .decode ('ascii' ): v .decode ('ascii' )
152
- for k , v in message ['headers' ]
158
+ k .decode ("ascii" ): v .decode ("ascii" ) for k , v in message ["headers" ]
153
159
}
154
- elif message [' type' ] == ' http.response.body' :
155
- self .response_length += len (message .get (' body' , '' ))
160
+ elif message [" type" ] == " http.response.body" :
161
+ self .response_length += len (message .get (" body" , "" ))
156
162
157
163
def _request_header (self , key ):
158
164
return self .request_headers .get (key .lower ())
@@ -168,20 +174,20 @@ def _wsgi_environ_variable(self, key):
168
174
def __getitem__ (self , key ):
169
175
if key in self .HANDLERS :
170
176
retval = self .HANDLERS [key ](self )
171
- elif key .startswith ('{' ):
172
- if key .endswith ('}i' ):
177
+ elif key .startswith ("{" ):
178
+ if key .endswith ("}i" ):
173
179
retval = self ._request_header (key [1 :- 2 ])
174
- elif key .endswith ('}o' ):
180
+ elif key .endswith ("}o" ):
175
181
retval = self ._response_header (key [1 :- 2 ])
176
- elif key .endswith ('}e' ):
182
+ elif key .endswith ("}e" ):
177
183
retval = self ._wsgi_environ_variable (key [1 :- 2 ])
178
184
else :
179
185
retval = None
180
186
else :
181
187
retval = None
182
188
183
189
if retval is None :
184
- return '-'
190
+ return "-"
185
191
if isinstance (retval , str ):
186
192
return retval .replace ('"' , '\\ "' )
187
193
return retval
@@ -192,103 +198,104 @@ def _register_handler(key, handlers=HANDLERS):
192
198
def decorator (fn ):
193
199
handlers [key ] = fn
194
200
return fn
201
+
195
202
return decorator
196
203
197
- @_register_handler ('h' )
204
+ @_register_handler ("h" )
198
205
def _remote_address (self , * args , ** kwargs ):
199
- return self .scope [' client' ][0 ]
206
+ return self .scope [" client" ][0 ]
200
207
201
- @_register_handler ('l' )
208
+ @_register_handler ("l" )
202
209
def _dash (self , * args , ** kwargs ):
203
- return '-'
210
+ return "-"
204
211
205
- @_register_handler ('u' )
212
+ @_register_handler ("u" )
206
213
def _user_name (self , * args , ** kwargs ):
207
214
pass
208
215
209
- @_register_handler ('t' )
216
+ @_register_handler ("t" )
210
217
def date_of_the_request (self , * args , ** kwargs ):
211
218
"""Date and time in Apache Common Log Format"""
212
- return time .strftime (' [%d/%b/%Y:%H:%M:%S %z]' )
219
+ return time .strftime (" [%d/%b/%Y:%H:%M:%S %z]" )
213
220
214
- @_register_handler ('r' )
221
+ @_register_handler ("r" )
215
222
def status_line (self , * args , ** kwargs ):
216
- full_raw_path = ( self .scope [' raw_path' ] + self .scope [' query_string' ])
217
- full_path = full_raw_path .decode (' ascii' )
218
- return ' {method} {full_path} HTTP/{http_version}' .format (
223
+ full_raw_path = self .scope [" raw_path" ] + self .scope [" query_string" ]
224
+ full_path = full_raw_path .decode (" ascii" )
225
+ return " {method} {full_path} HTTP/{http_version}" .format (
219
226
full_path = full_path , ** self .scope
220
227
)
221
228
222
- @_register_handler ('m' )
229
+ @_register_handler ("m" )
223
230
def request_method (self , * args , ** kwargs ):
224
- return self .scope [' method' ]
231
+ return self .scope [" method" ]
225
232
226
- @_register_handler ('U' )
233
+ @_register_handler ("U" )
227
234
def url_path (self , * args , ** kwargs ):
228
- return self .scope [' raw_path' ].decode (' ascii' )
235
+ return self .scope [" raw_path" ].decode (" ascii" )
229
236
230
- @_register_handler ('q' )
237
+ @_register_handler ("q" )
231
238
def query_string (self , * args , ** kwargs ):
232
- return self .scope [' query_string' ].decode (' ascii' )
239
+ return self .scope [" query_string" ].decode (" ascii" )
233
240
234
- @_register_handler ('H' )
241
+ @_register_handler ("H" )
235
242
def protocol (self , * args , ** kwargs ):
236
- return ' HTTP/%s' % self .scope [' http_version' ]
243
+ return " HTTP/%s" % self .scope [" http_version" ]
237
244
238
- @_register_handler ('s' )
245
+ @_register_handler ("s" )
239
246
def status (self , * args , ** kwargs ):
240
- return self .status_code or '-'
247
+ return self .status_code or "-"
241
248
242
- @_register_handler ('B' )
249
+ @_register_handler ("B" )
243
250
def response_length (self , * args , ** kwargs ):
244
251
return self .response_length
245
252
246
- @_register_handler ('b' )
253
+ @_register_handler ("b" )
247
254
def response_length_or_dash (self , * args , ** kwargs ):
248
- return self .response_length or '-'
255
+ return self .response_length or "-"
249
256
250
- @_register_handler ('f' )
257
+ @_register_handler ("f" )
251
258
def referer (self , * args , ** kwargs ):
252
- val = self .request_headers .get (b' referer' )
259
+ val = self .request_headers .get (b" referer" )
253
260
if val is None :
254
261
return None
255
- return val .decode (' ascii' )
262
+ return val .decode (" ascii" )
256
263
257
- @_register_handler ('a' )
264
+ @_register_handler ("a" )
258
265
def user_agent (self , * args , ** kwargs ):
259
- val = self .request_headers .get (b' user-agent' )
266
+ val = self .request_headers .get (b" user-agent" )
260
267
if val is None :
261
268
return None
262
- return val .decode (' ascii' )
269
+ return val .decode (" ascii" )
263
270
264
- @_register_handler ('T' )
271
+ @_register_handler ("T" )
265
272
def request_time_seconds (self , * args , ** kwargs ):
266
273
return int (self .duration )
267
274
268
- @_register_handler ('D' )
275
+ @_register_handler ("D" )
269
276
def request_time_microseconds (self , * args , ** kwargs ):
270
277
return int (self .duration * 1_000_000 )
271
278
272
- @_register_handler ('L' )
279
+ @_register_handler ("L" )
273
280
def request_time_decimal_seconds (self , * args , ** kwargs ):
274
281
return "%.6f" % self .duration
275
282
276
- @_register_handler ('p' )
283
+ @_register_handler ("p" )
277
284
def process_id (self , * args , ** kwargs ):
278
285
return "<%s>" % getpid ()
279
286
280
287
def __iter__ (self ):
281
288
# FIXME: add WSGI environ
282
289
yield from self .HANDLERS
283
- for k , _ in self .scope [' headers' ]:
284
- yield ' {%s}i' % k .lower ()
290
+ for k , _ in self .scope [" headers" ]:
291
+ yield " {%s}i" % k .lower ()
285
292
for k in self .response_headers :
286
- yield ' {%s}o' % k .lower ()
293
+ yield " {%s}o" % k .lower ()
287
294
288
295
def __len__ (self ):
289
296
# FIXME: add WSGI environ
290
297
return (
291
298
len (self .HANDLERS )
292
- + len (self .scope [' headers' ] or ())
299
+ + len (self .scope [" headers" ] or ())
293
300
+ len (self .response_headers )
294
301
)
0 commit comments