1# Copyright 2016 Mirantis Inc. 2# All Rights Reserved. 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); you may 5# not use this file except in compliance with the License. You may obtain 6# a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13# License for the specific language governing permissions and limitations 14# under the License. 15 16import contextlib 17import json 18 19from osprofiler import _utils as utils 20from osprofiler.drivers.base import get_driver as profiler_get_driver 21from osprofiler import notifier 22from osprofiler import profiler 23from osprofiler import web 24 25from horizon.utils import settings as horizon_settings 26 27 28ROOT_HEADER = 'PARENT_VIEW_TRACE_ID' 29 30 31def init_notifier(connection_str, host="localhost"): 32 _notifier = notifier.create( 33 connection_str, project='horizon', service='horizon', host=host) 34 notifier.set(_notifier) 35 36 37@contextlib.contextmanager 38def traced(request, name, info=None): 39 if info is None: 40 info = {} 41 profiler_instance = profiler.get() 42 if profiler_instance is not None: 43 trace_id = profiler_instance.get_base_id() 44 info['user_id'] = request.user.id 45 with profiler.Trace(name, info=info): 46 yield trace_id 47 else: 48 yield 49 50 51def _get_engine(): 52 connection_str = horizon_settings.get_dict_config( 53 'OPENSTACK_PROFILER', 'receiver_connection_string') 54 return profiler_get_driver(connection_str) 55 56 57def list_traces(): 58 engine = _get_engine() 59 fields = ['base_id', 'timestamp', 'info.request.path', 'info'] 60 traces = engine.list_traces(fields) 61 return [{'id': trace['base_id'], 62 'timestamp': trace['timestamp'], 63 'origin': trace['info']['request']['path']} for trace in traces] 64 65 66def get_trace(trace_id): 67 def rec(_data, level=0): 68 _data['level'] = level 69 _data['is_leaf'] = not _data['children'] 70 _data['visible'] = True 71 _data['childrenVisible'] = True 72 finished = _data['info']['finished'] 73 for child in _data['children']: 74 __, child_finished = rec(child, level + 1) 75 # NOTE(tsufiev): in case of async requests the root request usually 76 # finishes before the dependent requests do so, to we need to 77 # normalize the duration of all requests by the finishing time of 78 # the one which took longest 79 if child_finished > finished: 80 finished = child_finished 81 return _data, finished 82 83 engine = _get_engine() 84 trace = engine.get_report(trace_id) 85 data, max_finished = rec(trace) 86 data['info']['max_finished'] = max_finished 87 return data 88 89 90def update_trace_headers(keys, **kwargs): 91 trace_headers = web.get_trace_id_headers() 92 trace_info = utils.signed_unpack( 93 trace_headers[web.X_TRACE_INFO], trace_headers[web.X_TRACE_HMAC], 94 keys) 95 trace_info.update(kwargs) 96 p = profiler.get() 97 trace_data = utils.signed_pack(trace_info, p.hmac_key) 98 trace_data = [key.decode() if isinstance(key, bytes) 99 else key for key in trace_data] 100 return json.dumps({web.X_TRACE_INFO: trace_data[0], 101 web.X_TRACE_HMAC: trace_data[1]}) 102 103 104if not horizon_settings.get_dict_config('OPENSTACK_PROFILER', 'enabled'): 105 def trace(function): 106 return function 107else: 108 def trace(function): 109 func_name = function.__module__ + '.' + function.__name__ 110 decorator = profiler.trace(func_name) 111 return decorator(function) 112