diff --git a/openstack_dashboard/context_processors.py b/openstack_dashboard/context_processors.py index b5d4a7a766..db3e482752 100644 --- a/openstack_dashboard/context_processors.py +++ b/openstack_dashboard/context_processors.py @@ -24,6 +24,7 @@ import re from django.conf import settings from horizon import conf +from openstack_dashboard.contrib.developer.profiler import api as profiler def openstack(request): @@ -61,8 +62,14 @@ def openstack(request): context['WEBROOT'] = getattr(settings, "WEBROOT", "/") # Adding profiler support flag - enabled = getattr(settings, 'OPENSTACK_PROFILER', {}).get('enabled', False) - context['profiler_enabled'] = enabled + profiler_settings = getattr(settings, 'OPENSTACK_PROFILER', {}) + profiler_enabled = profiler_settings.get('enabled', False) + context['profiler_enabled'] = profiler_enabled + if profiler_enabled and 'profile_page' in request.COOKIES: + index_view_id = request.META.get(profiler.ROOT_HEADER, '') + hmac_keys = profiler_settings.get('keys', []) + context['x_trace_info'] = profiler.update_trace_headers( + hmac_keys, parent_id=index_view_id) # Search for external plugins and append to javascript message catalog # internal plugins are under the openstack_dashboard domain diff --git a/openstack_dashboard/contrib/developer/profiler/api.py b/openstack_dashboard/contrib/developer/profiler/api.py index a2d58df481..a36e5bfdaa 100644 --- a/openstack_dashboard/contrib/developer/profiler/api.py +++ b/openstack_dashboard/contrib/developer/profiler/api.py @@ -14,14 +14,18 @@ # under the License. import contextlib +import json from django.conf import settings +from osprofiler import _utils as utils from osprofiler.drivers.base import get_driver as profiler_get_driver from osprofiler import notifier from osprofiler import profiler +from osprofiler import web from six.moves.urllib.parse import urlparse +ROOT_HEADER = 'PARENT_VIEW_TRACE_ID' PROFILER_SETTINGS = getattr(settings, 'OPENSTACK_PROFILER', {}) @@ -88,15 +92,36 @@ def get_trace(request, trace_id): _data['is_leaf'] = not len(_data['children']) _data['visible'] = True _data['childrenVisible'] = True + finished = _data['info']['finished'] for child in _data['children']: - rec(child, level + 1) - return _data + __, child_finished = rec(child, level + 1) + # NOTE(tsufiev): in case of async requests the root request usually + # finishes before the dependent requests do so, to we need to + # normalize the duration of all requests by the finishing time of + # the one which took longest + if child_finished > finished: + finished = child_finished + return _data, finished engine = _get_engine(request) trace = engine.get_report(trace_id) - # throw away toplevel node which is dummy and doesn't contain any info, - # use its first and only child as the toplevel node - return rec(trace['children'][0]) + # NOTE(tsufiev): throw away toplevel node which is dummy and doesn't + # contain any info, use its first and only child as the toplevel node + data, max_finished = rec(trace['children'][0]) + data['info']['max_finished'] = max_finished + return data + + +def update_trace_headers(keys, **kwargs): + trace_headers = web.get_trace_id_headers() + trace_info = utils.signed_unpack( + trace_headers[web.X_TRACE_INFO], trace_headers[web.X_TRACE_HMAC], + keys) + trace_info.update(kwargs) + p = profiler.get() + trace_data = utils.signed_pack(trace_info, p.hmac_key) + return json.dumps({web.X_TRACE_INFO: trace_data[0], + web.X_TRACE_HMAC: trace_data[1]}) if not PROFILER_SETTINGS.get('enabled', False): diff --git a/openstack_dashboard/contrib/developer/profiler/middleware.py b/openstack_dashboard/contrib/developer/profiler/middleware.py index cb7c2e1f16..7fec15c992 100644 --- a/openstack_dashboard/contrib/developer/profiler/middleware.py +++ b/openstack_dashboard/contrib/developer/profiler/middleware.py @@ -34,12 +34,25 @@ PROFILER_ENABLED = PROFILER_CONF.get('enabled', False) class ProfilerClientMiddleware(object): + profiler_headers = [ + ('HTTP_X_TRACE_INFO', 'X-Trace-Info'), + ('HTTP_X_TRACE_HMAC', 'X-Trace-HMAC') + ] + def __init__(self): if not PROFILER_ENABLED: raise exceptions.MiddlewareNotUsed() super(ProfilerClientMiddleware, self).__init__() + def is_async_profiling(self, request): + return self.profiler_headers[0][0] in request.META + def process_request(self, request): + if self.is_async_profiling(request): + for src_header, dst_header in self.profiler_headers: + request.META[dst_header] = request.META.get(src_header) + return None + if 'profile_page' in request.COOKIES: hmac_key = PROFILER_CONF.get('keys')[0] profiler.init(hmac_key) @@ -76,8 +89,8 @@ class ProfilerMiddleware(object): return True def process_view(self, request, view_func, view_args, view_kwargs): - # do not profile ajax requests for now - if not self.is_enabled(request) or request.is_ajax(): + + if not self.is_enabled(request): return None trace_info = profiler_utils.signed_unpack( @@ -98,6 +111,8 @@ class ProfilerMiddleware(object): } } with api.traced(request, view_func.__name__, info) as trace_id: + request.META[api.ROOT_HEADER] = profiler.get().get_id() + response = view_func(request, *view_args, **view_kwargs) url = reverse('horizon:developer:profiler:index') message = safestring.mark_safe( @@ -115,8 +130,4 @@ class ProfilerMiddleware(object): def process_response(self, request, response): self.clear_profiling_cookies(request, response) - # do not profile ajax requests for now - if not self.is_enabled(request) or request.is_ajax(): - return response - return response diff --git a/openstack_dashboard/contrib/developer/static/dashboard/developer/profiler/profiler.controller.js b/openstack_dashboard/contrib/developer/static/dashboard/developer/profiler/profiler.controller.js index 096f422bce..9874e165df 100644 --- a/openstack_dashboard/contrib/developer/static/dashboard/developer/profiler/profiler.controller.js +++ b/openstack_dashboard/contrib/developer/static/dashboard/developer/profiler/profiler.controller.js @@ -51,7 +51,7 @@ * are used while rendering trace node. */ sharedProfilerController.$inject = [ - '$modal', + '$uibModal', '$rootScope', '$templateCache', 'horizon.dashboard.developer.profiler.basePath' @@ -95,13 +95,13 @@ } function getWidth(data, rootData) { - var full_duration = rootData.info.finished; + var full_duration = rootData.info.max_finished; var duration = (data.info.finished - data.info.started) * 100.0 / full_duration; return (duration >= 0.5) ? duration : 0.5; } function getStarted(data, rootData) { - var full_duration = rootData.info.finished; + var full_duration = rootData.info.max_finished; return data.info.started * 100.0 / full_duration; } @@ -154,8 +154,8 @@ ctrl.profilePage = profilePage; function profilePage() { - $cookies.put('profile_page', true); + $cookies.put('profile_page', true, {path: window.location.pathname}); window.location.reload(); } } -})(); \ No newline at end of file +})(); diff --git a/openstack_dashboard/contrib/developer/static/dashboard/developer/profiler/profiler.module.js b/openstack_dashboard/contrib/developer/static/dashboard/developer/profiler/profiler.module.js index c3baad77ef..5f2e612b13 100644 --- a/openstack_dashboard/contrib/developer/static/dashboard/developer/profiler/profiler.module.js +++ b/openstack_dashboard/contrib/developer/static/dashboard/developer/profiler/profiler.module.js @@ -29,12 +29,31 @@ config.$inject = [ '$provide', - '$windowProvider' + '$windowProvider', + '$httpProvider', + 'horizon.dashboard.developer.profiler.headers' ]; - function config($provide, $windowProvider) { + function config($provide, $windowProvider, $httpProvider, headers) { var path = $windowProvider.$get().STATIC_URL + 'dashboard/developer/profiler/'; $provide.constant('horizon.dashboard.developer.profiler.basePath', path); + if (Object.keys(headers).length) { + $httpProvider.interceptors.push(function() { + return { + 'request': function(config) { + if (angular.isUndefined(config.headers)) { + config.headers = {}; + } + if (headers) { + angular.forEach(headers, function(value, key) { + config.headers[key] = value; + }); + } + return config; + } + }; + }); + } } })(); diff --git a/openstack_dashboard/contrib/developer/static/dashboard/developer/profiler/profiler.tree-node.html b/openstack_dashboard/contrib/developer/static/dashboard/developer/profiler/profiler.tree-node.html index a5edc20f6f..62388c7643 100644 --- a/openstack_dashboard/contrib/developer/static/dashboard/developer/profiler/profiler.tree-node.html +++ b/openstack_dashboard/contrib/developer/static/dashboard/developer/profiler/profiler.tree-node.html @@ -9,10 +9,10 @@
- - - - + + + + {$ data.info.finished - data.info.started $} ms diff --git a/openstack_dashboard/templates/horizon/_scripts.html b/openstack_dashboard/templates/horizon/_scripts.html index 56dc0981e2..25d46030d8 100644 --- a/openstack_dashboard/templates/horizon/_scripts.html +++ b/openstack_dashboard/templates/horizon/_scripts.html @@ -66,6 +66,17 @@ {% endcache %} {% endcompress %} +{% if profiler_enabled %} + +{% endif %} + {% comment %} Client-side Templates (These should *not* be inside the "compress" tag.) {% endcomment %} {% include "horizon/client_side/templates.html" %}