64
67
return ["FAKE APP"]
67
class FakeLogger(object):
68
def __init__(self, *args, **kwargs):
71
def info(self, string):
75
70
def start_response(*args):
79
74
class TestProxyLogging(unittest.TestCase):
76
def _log_parts(self, app):
77
info_calls = app.access_logger.log_dict['info']
78
self.assertEquals(1, len(info_calls))
79
return info_calls[0][0][0].split(' ')
81
def assertTiming(self, exp_metric, app, exp_timing=None):
82
timing_calls = app.access_logger.log_dict['timing']
83
self.assertEquals(1, len(timing_calls))
84
self.assertEquals({}, timing_calls[0][1])
85
self.assertEquals(2, len(timing_calls[0][0]))
86
self.assertEquals(exp_metric, timing_calls[0][0][0])
87
if exp_timing is not None:
88
self.assertEquals(exp_timing, timing_calls[0][0][1])
90
def assertUpdateStats(self, exp_metric, exp_bytes, app):
91
update_stats_calls = app.access_logger.log_dict['update_stats']
92
self.assertEquals(1, len(update_stats_calls))
93
self.assertEquals({}, update_stats_calls[0][1])
94
self.assertEquals((exp_metric, exp_bytes), update_stats_calls[0][0])
96
def test_log_request_statsd_invalid_stats_types(self):
97
app = proxy_logging.ProxyLoggingMiddleware(FakeApp(), {})
98
app.access_logger = FakeLogger()
99
for url in ['/', '/foo', '/foo/bar', '/v1']:
100
req = Request.blank(url, environ={'REQUEST_METHOD': 'GET'})
101
resp = app(req.environ, start_response)
102
resp_body = ''.join(resp)
103
self.assertEquals(0, len(app.access_logger.log_dict['timing']))
105
len(app.access_logger.log_dict['update_stats']))
107
def test_log_request_stat_type_bad(self):
108
for bad_path in ['', '/', '/bad', '/baddy/mc_badderson', '/v1',
110
app = proxy_logging.ProxyLoggingMiddleware(FakeApp(), {})
111
app.access_logger = FakeLogger()
112
req = Request.blank(bad_path, environ={'REQUEST_METHOD': 'GET'})
113
app.log_request(req.environ, 123, 7, 13, 2.71828182846, False)
114
self.assertEqual([], app.access_logger.log_dict['timing'])
115
self.assertEqual([], app.access_logger.log_dict['update_stats'])
117
def test_log_request_stat_type_good(self):
121
'/v1/a/c': 'container',
122
'/v1/a/c/': 'container',
123
'/v1/a/c/o': 'object',
124
'/v1/a/c/o/': 'object',
125
'/v1/a/c/o/p': 'object',
126
'/v1/a/c/o/p/': 'object',
127
'/v1/a/c/o/p/p2': 'object',
129
for path, exp_type in path_types.iteritems():
130
app = proxy_logging.ProxyLoggingMiddleware(FakeApp(), {})
131
app.access_logger = FakeLogger()
132
req = Request.blank(path, environ={'REQUEST_METHOD': 'GET'})
133
app.log_request(req.environ, 321, 7, 13, 2.71828182846, False)
134
self.assertTiming('%s.GET.321.timing' % exp_type, app,
135
exp_timing=2.71828182846 * 1000)
136
self.assertUpdateStats('%s.GET.321.xfer' % exp_type, 7 + 13, app)
138
def test_log_request_stat_method_filtering_default(self):
142
'PUTT': 'BAD_METHOD',
143
'SPECIAL': 'BAD_METHOD',
151
for method, exp_method in method_map.iteritems():
152
app = proxy_logging.ProxyLoggingMiddleware(FakeApp(), {})
153
app.access_logger = FakeLogger()
154
req = Request.blank('/v1/a/', environ={'REQUEST_METHOD': method})
155
app.log_request(req.environ, 299, 11, 3, 1.17, False)
156
self.assertTiming('account.%s.299.timing' % exp_method, app,
157
exp_timing=1.17 * 1000)
158
self.assertUpdateStats('account.%s.299.xfer' % exp_method,
161
def test_log_request_stat_method_filtering_custom(self):
165
'PUTT': 'BAD_METHOD',
166
'SPECIAL': 'SPECIAL', # will be configured
169
'COPY': 'BAD_METHOD', # prove no one's special
171
# this conf var supports optional leading access_
172
for conf_key in ['access_log_statsd_valid_http_methods',
173
'log_statsd_valid_http_methods']:
174
for method, exp_method in method_map.iteritems():
175
app = proxy_logging.ProxyLoggingMiddleware(FakeApp(), {
176
conf_key: 'SPECIAL, GET,PUT ', # crazy spaces ok
178
app.access_logger = FakeLogger()
179
req = Request.blank('/v1/a/c', environ={'REQUEST_METHOD': method})
180
app.log_request(req.environ, 911, 4, 43, 1.01, False)
181
self.assertTiming('container.%s.911.timing' % exp_method, app,
182
exp_timing=1.01 * 1000)
183
self.assertUpdateStats('container.%s.911.xfer' % exp_method,
81
186
def test_basic_req(self):
82
187
app = proxy_logging.ProxyLoggingMiddleware(FakeApp(), {})
83
188
app.access_logger = FakeLogger()
84
189
req = Request.blank('/', environ={'REQUEST_METHOD': 'GET'})
85
190
resp = app(req.environ, start_response)
86
191
resp_body = ''.join(resp)
87
log_parts = app.access_logger.msg.split()
192
log_parts = self._log_parts(app)
88
193
self.assertEquals(log_parts[3], 'GET')
89
194
self.assertEquals(log_parts[4], '/')
90
195
self.assertEquals(log_parts[5], 'HTTP/1.0')
96
201
app = proxy_logging.ProxyLoggingMiddleware(FakeApp(
97
202
['some', 'chunks', 'of data']), {})
98
203
app.access_logger = FakeLogger()
99
req = Request.blank('/', environ={'REQUEST_METHOD': 'GET'})
204
req = Request.blank('/', environ={'REQUEST_METHOD': 'GET',
205
'swift.source': 'SOS'})
100
206
resp = app(req.environ, start_response)
101
207
resp_body = ''.join(resp)
102
log_parts = app.access_logger.msg.split()
208
log_parts = self._log_parts(app)
103
209
self.assertEquals(log_parts[3], 'GET')
104
210
self.assertEquals(log_parts[4], '/')
105
211
self.assertEquals(log_parts[5], 'HTTP/1.0')
106
212
self.assertEquals(log_parts[6], '200')
107
213
self.assertEquals(resp_body, 'somechunksof data')
108
214
self.assertEquals(log_parts[11], str(len(resp_body)))
215
self.assertUpdateStats('SOS.GET.200.xfer', len(resp_body), app)
110
217
def test_log_headers(self):
111
218
app = proxy_logging.ProxyLoggingMiddleware(FakeApp(),
122
229
app = proxy_logging.ProxyLoggingMiddleware(FakeApp(),
123
230
{'log_headers': 'yes'})
124
231
app.access_logger = FakeLogger()
125
req = Request.blank('/', environ={'REQUEST_METHOD': 'GET',
232
req = Request.blank('/v1/a/c/o/foo', environ={'REQUEST_METHOD': 'PUT',
126
233
'wsgi.input': StringIO.StringIO('some stuff')})
127
234
resp = app(req.environ, start_response)
128
235
exhaust_generator = [x for x in resp]
129
log_parts = app.access_logger.msg.split()
236
log_parts = self._log_parts(app)
237
self.assertEquals(log_parts[11], str(len('FAKE APP')))
130
238
self.assertEquals(log_parts[10], str(len('some stuff')))
239
self.assertUpdateStats('object.PUT.200.xfer',
240
len('some stuff') + len('FAKE APP'),
132
243
def test_upload_line(self):
133
244
app = proxy_logging.ProxyLoggingMiddleware(FakeAppReadline(),
134
245
{'log_headers': 'yes'})
135
246
app.access_logger = FakeLogger()
136
req = Request.blank('/', environ={'REQUEST_METHOD': 'GET',
247
req = Request.blank('/v1/a/c', environ={'REQUEST_METHOD': 'POST',
137
248
'wsgi.input': StringIO.StringIO(
138
249
'some stuff\nsome other stuff\n')})
139
250
resp = app(req.environ, start_response)
140
exhaust_generator = ''.join(resp)
141
log_parts = app.access_logger.msg.split()
251
exhaust_generator = [x for x in resp]
252
log_parts = self._log_parts(app)
253
self.assertEquals(log_parts[11], str(len('FAKE APP')))
142
254
self.assertEquals(log_parts[10], str(len('some stuff\n')))
255
self.assertUpdateStats('container.POST.200.xfer',
256
len('some stuff\n') + len('FAKE APP'),
144
259
def test_log_query_string(self):
145
260
app = proxy_logging.ProxyLoggingMiddleware(FakeApp(), {})
158
273
'REMOTE_ADDR': '1.2.3.4'})
159
274
resp = app(req.environ, start_response)
160
275
exhaust_generator = [x for x in resp]
161
log_parts = app.access_logger.msg.split()
276
log_parts = self._log_parts(app)
162
277
self.assertEquals(log_parts[0], '1.2.3.4') # client ip
163
278
self.assertEquals(log_parts[1], '1.2.3.4') # remote addr
165
280
def test_proxy_client_logging(self):
166
281
app = proxy_logging.ProxyLoggingMiddleware(FakeApp(), {})
167
282
app.access_logger = FakeLogger()
168
req = Request.blank('/', environ={'REQUEST_METHOD': 'GET',
169
'REMOTE_ADDR': '1.2.3.4',
170
'HTTP_X_FORWARDED_FOR': '4.5.6.7,8.9.10.11'
283
req = Request.blank('/', environ={
284
'REQUEST_METHOD': 'GET',
285
'REMOTE_ADDR': '1.2.3.4',
286
'HTTP_X_FORWARDED_FOR': '4.5.6.7,8.9.10.11'})
172
287
resp = app(req.environ, start_response)
173
288
exhaust_generator = [x for x in resp]
174
log_parts = app.access_logger.msg.split()
289
log_parts = self._log_parts(app)
175
290
self.assertEquals(log_parts[0], '4.5.6.7') # client ip
176
291
self.assertEquals(log_parts[1], '1.2.3.4') # remote addr
178
293
app = proxy_logging.ProxyLoggingMiddleware(FakeApp(), {})
179
294
app.access_logger = FakeLogger()
180
req = Request.blank('/', environ={'REQUEST_METHOD': 'GET',
181
'REMOTE_ADDR': '1.2.3.4',
182
'HTTP_X_CLUSTER_CLIENT_IP': '4.5.6.7'
295
req = Request.blank('/', environ={
296
'REQUEST_METHOD': 'GET',
297
'REMOTE_ADDR': '1.2.3.4',
298
'HTTP_X_CLUSTER_CLIENT_IP': '4.5.6.7'})
184
299
resp = app(req.environ, start_response)
185
300
exhaust_generator = [x for x in resp]
186
log_parts = app.access_logger.msg.split()
301
log_parts = self._log_parts(app)
187
302
self.assertEquals(log_parts[0], '4.5.6.7') # client ip
188
303
self.assertEquals(log_parts[1], '1.2.3.4') # remote addr
190
305
def test_facility(self):
191
306
app = proxy_logging.ProxyLoggingMiddleware(FakeApp(),
192
{'log_headers': 'yes', 'access_log_facility': 'whatever'})
307
{'log_headers': 'yes',
308
'access_log_facility': 'LOG_LOCAL7'})
309
handler = get_logger.handler4logger[app.access_logger.logger]
310
self.assertEquals(SysLogHandler.LOG_LOCAL7, handler.facility)
194
312
def test_filter(self):
195
313
factory = proxy_logging.filter_factory({})
233
351
body = ''.join(resp)
234
352
except Exception:
236
log_parts = app.access_logger.msg.split()
354
log_parts = self._log_parts(app)
237
355
self.assertEquals(log_parts[6], '499')
238
356
self.assertEquals(log_parts[10], '-') # read length
240
def test_no_content_length_no_transfer_encoding(self):
241
app = proxy_logging.ProxyLoggingMiddleware(
242
FakeAppNoContentLengthNoTransferEncoding(), {})
243
app.access_logger = FakeLogger()
244
req = Request.blank('/', environ={'REQUEST_METHOD': 'GET'})
245
resp = app(req.environ, start_response)
358
def test_no_content_length_no_transfer_encoding_with_list_body(self):
359
app = proxy_logging.ProxyLoggingMiddleware(
360
FakeAppNoContentLengthNoTransferEncoding(
361
# test the "while not chunk: chunk = iterator.next()"
362
body=['', '', 'line1\n', 'line2\n'],
364
app.access_logger = FakeLogger()
365
req = Request.blank('/', environ={'REQUEST_METHOD': 'GET'})
366
resp = app(req.environ, start_response)
367
resp_body = ''.join(resp)
368
log_parts = self._log_parts(app)
369
self.assertEquals(log_parts[3], 'GET')
370
self.assertEquals(log_parts[4], '/')
371
self.assertEquals(log_parts[5], 'HTTP/1.0')
372
self.assertEquals(log_parts[6], '200')
373
self.assertEquals(resp_body, 'line1\nline2\n')
374
self.assertEquals(log_parts[11], str(len(resp_body)))
376
def test_no_content_length_no_transfer_encoding_with_empty_strings(self):
377
app = proxy_logging.ProxyLoggingMiddleware(
378
FakeAppNoContentLengthNoTransferEncoding(
379
# test the "while not chunk: chunk = iterator.next()"
382
app.access_logger = FakeLogger()
383
req = Request.blank('/', environ={'REQUEST_METHOD': 'GET'})
384
resp = app(req.environ, start_response)
385
resp_body = ''.join(resp)
386
log_parts = self._log_parts(app)
387
self.assertEquals(log_parts[3], 'GET')
388
self.assertEquals(log_parts[4], '/')
389
self.assertEquals(log_parts[5], 'HTTP/1.0')
390
self.assertEquals(log_parts[6], '200')
391
self.assertEquals(resp_body, '')
392
self.assertEquals(log_parts[11], '-')
394
def test_no_content_length_no_transfer_encoding_with_str_body(self):
395
app = proxy_logging.ProxyLoggingMiddleware(
396
FakeAppNoContentLengthNoTransferEncoding(
397
body='line1\nline2\n',
399
app.access_logger = FakeLogger()
400
req = Request.blank('/', environ={'REQUEST_METHOD': 'GET'})
401
resp = app(req.environ, start_response)
402
# Python 2.7 can have assertRaises act as a context manager, but python
403
# 2.6 can't. So there's this.
405
resp_body = ''.join(resp)
406
except Exception as e:
408
"WSGI [proxy-logging]: No content-length or transfer-encoding "
409
"header sent and there is content! 'l'", str(e))
248
413
def test_req_path_info_popping(self):
249
414
app = proxy_logging.ProxyLoggingMiddleware(FakeApp(), {})