Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(196)

Side by Side Diff: appengine_module/gae_ts_mon/config.py

Issue 1797103003: gae_ts_mon: instrument Cloud Endpoint methods (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: Add docs Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 # Copyright 2015 The Chromium Authors. All rights reserved. 1 # Copyright 2015 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 import copy 5 import copy
6 import datetime 6 import datetime
7 import functools
7 import logging 8 import logging
8 import os 9 import os
9 import sys 10 import sys
10 import time 11 import time
11 import threading 12 import threading
12 13
14 import endpoints
13 import webapp2 15 import webapp2
14 16
15 from google.appengine.api import modules 17 from google.appengine.api import modules
16 from google.appengine.api.app_identity import app_identity 18 from google.appengine.api.app_identity import app_identity
17 from google.appengine.api import runtime 19 from google.appengine.api import runtime
18 from google.appengine.ext import ndb 20 from google.appengine.ext import ndb
19 21
20 from infra_libs.ts_mon import handlers 22 from infra_libs.ts_mon import handlers
21 from infra_libs.ts_mon import shared 23 from infra_libs.ts_mon import shared
22 from infra_libs.ts_mon.common import http_metrics 24 from infra_libs.ts_mon.common import http_metrics
(...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after
160 shared.PUBSUB_TOPIC) 162 shared.PUBSUB_TOPIC)
161 163
162 shared.register_global_metrics([shared.appengine_default_version]) 164 shared.register_global_metrics([shared.appengine_default_version])
163 shared.register_global_metrics_callback( 165 shared.register_global_metrics_callback(
164 shared.INTERNAL_CALLBACK_NAME, _internal_callback) 166 shared.INTERNAL_CALLBACK_NAME, _internal_callback)
165 167
166 logging.info('Initialized ts_mon with service_name=%s, job_name=%s, ' 168 logging.info('Initialized ts_mon with service_name=%s, job_name=%s, '
167 'hostname=%s', service_name, job_name, hostname) 169 'hostname=%s', service_name, job_name, hostname)
168 170
169 171
172 def update_metrics(name, response_status, elapsed_ms,
dsansome 2016/03/15 02:01:20 This name is quite generic. Maybe update_http_ser
Sergey Berezin 2016/03/17 22:14:04 Good idea, done.
173 request_size=None, response_size=None, user_agent=None):
174 fields = {'status': response_status, 'name': name, 'is_robot': False}
175 if user_agent is not None:
176 # We must not log user agents, but we can store whether or not the
177 # user agent string indicates that the requester was a Google bot.
178 fields['is_robot'] = (
179 'GoogleBot' in user_agent or 'GoogleSecurityScanner' in user_agent)
180
181 http_metrics.server_durations.add(elapsed_ms, fields=fields)
182 http_metrics.server_response_status.increment(fields=fields)
183 if request_size is not None:
184 http_metrics.server_request_bytes.add(request_size, fields=fields)
185 if response_size is not None: # pragma: no cover
186 http_metrics.server_response_bytes.add(response_size, fields=fields)
187
188
170 def _instrumented_dispatcher(dispatcher, request, response, time_fn=time.time): 189 def _instrumented_dispatcher(dispatcher, request, response, time_fn=time.time):
171 start_time = time_fn() 190 start_time = time_fn()
172 response_status = 0 191 response_status = 0
173 interface.state.store.initialize_context() 192 interface.state.store.initialize_context()
174 flush_thread = threading.Thread(target=flush_metrics_if_needed) 193 flush_thread = threading.Thread(target=flush_metrics_if_needed)
175 flush_thread.start() 194 flush_thread.start()
176 try: 195 try:
177 ret = dispatcher(request, response) 196 ret = dispatcher(request, response)
178 except webapp2.HTTPException as ex: 197 except webapp2.HTTPException as ex:
179 response_status = ex.code 198 response_status = ex.code
180 raise 199 raise
181 except Exception: 200 except Exception:
182 response_status = 500 201 response_status = 500
183 raise 202 raise
184 else: 203 else:
185 if isinstance(ret, webapp2.Response): 204 if isinstance(ret, webapp2.Response):
186 response = ret 205 response = ret
187 response_status = response.status_int 206 response_status = response.status_int
188 finally: 207 finally:
189 flush_thread.join() 208 flush_thread.join()
190 elapsed_ms = int((time_fn() - start_time) * 1000) 209 elapsed_ms = int((time_fn() - start_time) * 1000)
191 210
192 fields = {'status': response_status, 'name': '', 'is_robot': False} 211 # Use the route template regex, not the request path, to prevent an
193 if request.route is not None: 212 # explosion in possible field values.
194 # Use the route template regex, not the request path, to prevent an 213 name = request.route.template if request.route is not None else ''
195 # explosion in possible field values.
196 fields['name'] = request.route.template
197 if request.user_agent is not None:
198 # We must not log user agents, but we can store whether or not the
199 # user agent string indicates that the requester was a Google bot.
200 fields['is_robot'] = (
201 'GoogleBot' in request.user_agent or
202 'GoogleSecurityScanner' in request.user_agent)
203 214
204 http_metrics.server_durations.add(elapsed_ms, fields=fields) 215 update_metrics(name, response_status, elapsed_ms,
205 http_metrics.server_response_status.increment(fields=fields) 216 request_size=request.content_length,
206 if request.content_length is not None: 217 response_size=response.content_length,
207 http_metrics.server_request_bytes.add(request.content_length, 218 user_agent=request.user_agent)
208 fields=fields) 219
209 if response.content_length is not None: # pragma: no cover
210 http_metrics.server_response_bytes.add(response.content_length,
211 fields=fields)
212 return ret 220 return ret
213 221
214 222
215 def instrument_wsgi_application(app, time_fn=time.time): 223 def instrument_wsgi_application(app, time_fn=time.time):
216 # Don't instrument the same router twice. 224 # Don't instrument the same router twice.
217 if hasattr(app.router, '__instrumented_by_ts_mon'): 225 if hasattr(app.router, '__instrumented_by_ts_mon'):
218 return 226 return
219 227
220 old_dispatcher = app.router.dispatch 228 old_dispatcher = app.router.dispatch
221 229
222 def dispatch(router, request, response): 230 def dispatch(router, request, response):
223 return _instrumented_dispatcher(old_dispatcher, request, response, 231 return _instrumented_dispatcher(old_dispatcher, request, response,
224 time_fn=time_fn) 232 time_fn=time_fn)
225 233
226 app.router.set_dispatcher(dispatch) 234 app.router.set_dispatcher(dispatch)
227 app.router.__instrumented_by_ts_mon = True 235 app.router.__instrumented_by_ts_mon = True
228 236
229 237
238 def instrument_endpoint(time_fn=time.time):
239 """Decorator to instrument Cloud Endpoint methods."""
240 def decorator(fn):
241 method_name = fn.__name__
242 assert method_name
243 @functools.wraps(fn)
244 def decorated(*args, **kwargs):
245 start_time = time_fn()
246 response_status = 0
247 interface.state.store.initialize_context()
248 flush_thread = threading.Thread(target=flush_metrics_if_needed)
249 flush_thread.start()
nodir 2016/03/15 18:59:49 check if it is needed in this thread, do not creat
Sergey Berezin 2016/03/17 22:14:03 Done here and in the regular endpoint instrumentat
250 try:
251 ret = fn(*args, **kwargs)
252 response_status = 200
253 return ret
254 except endpoints.ServiceException as e:
255 response_status = e.http_status
256 raise
257 except Exception:
258 response_status = 500
259 raise
260 finally:
261 flush_thread.join()
262 elapsed_ms = int((time_fn() - start_time) * 1000)
263 update_metrics(method_name, response_status, elapsed_ms)
nodir 2016/03/15 18:59:49 there may be different endpoint services with clas
Sergey Berezin 2016/03/17 22:14:03 Good catch, done.
264 return decorated
265 return decorator
266
267
230 def reset_for_unittest(): 268 def reset_for_unittest():
231 shared.reset_for_unittest() 269 shared.reset_for_unittest()
232 interface.reset_for_unittest() 270 interface.reset_for_unittest()
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698