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