Merge "Enable profiling of angular pages (with async requests)"
This commit is contained in:
commit
5166f74f44
@ -24,6 +24,7 @@ import re
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from horizon import conf
|
from horizon import conf
|
||||||
|
from openstack_dashboard.contrib.developer.profiler import api as profiler
|
||||||
|
|
||||||
|
|
||||||
def openstack(request):
|
def openstack(request):
|
||||||
@ -61,8 +62,14 @@ def openstack(request):
|
|||||||
context['WEBROOT'] = getattr(settings, "WEBROOT", "/")
|
context['WEBROOT'] = getattr(settings, "WEBROOT", "/")
|
||||||
|
|
||||||
# Adding profiler support flag
|
# Adding profiler support flag
|
||||||
enabled = getattr(settings, 'OPENSTACK_PROFILER', {}).get('enabled', False)
|
profiler_settings = getattr(settings, 'OPENSTACK_PROFILER', {})
|
||||||
context['profiler_enabled'] = enabled
|
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
|
# Search for external plugins and append to javascript message catalog
|
||||||
# internal plugins are under the openstack_dashboard domain
|
# internal plugins are under the openstack_dashboard domain
|
||||||
|
@ -14,14 +14,18 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import contextlib
|
import contextlib
|
||||||
|
import json
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from osprofiler import _utils as utils
|
||||||
from osprofiler.drivers.base import get_driver as profiler_get_driver
|
from osprofiler.drivers.base import get_driver as profiler_get_driver
|
||||||
from osprofiler import notifier
|
from osprofiler import notifier
|
||||||
from osprofiler import profiler
|
from osprofiler import profiler
|
||||||
|
from osprofiler import web
|
||||||
from six.moves.urllib.parse import urlparse
|
from six.moves.urllib.parse import urlparse
|
||||||
|
|
||||||
|
|
||||||
|
ROOT_HEADER = 'PARENT_VIEW_TRACE_ID'
|
||||||
PROFILER_SETTINGS = getattr(settings, 'OPENSTACK_PROFILER', {})
|
PROFILER_SETTINGS = getattr(settings, 'OPENSTACK_PROFILER', {})
|
||||||
|
|
||||||
|
|
||||||
@ -88,15 +92,36 @@ def get_trace(request, trace_id):
|
|||||||
_data['is_leaf'] = not len(_data['children'])
|
_data['is_leaf'] = not len(_data['children'])
|
||||||
_data['visible'] = True
|
_data['visible'] = True
|
||||||
_data['childrenVisible'] = True
|
_data['childrenVisible'] = True
|
||||||
|
finished = _data['info']['finished']
|
||||||
for child in _data['children']:
|
for child in _data['children']:
|
||||||
rec(child, level + 1)
|
__, child_finished = rec(child, level + 1)
|
||||||
return _data
|
# 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)
|
engine = _get_engine(request)
|
||||||
trace = engine.get_report(trace_id)
|
trace = engine.get_report(trace_id)
|
||||||
# throw away toplevel node which is dummy and doesn't contain any info,
|
# NOTE(tsufiev): throw away toplevel node which is dummy and doesn't
|
||||||
# use its first and only child as the toplevel node
|
# contain any info, use its first and only child as the toplevel node
|
||||||
return rec(trace['children'][0])
|
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):
|
if not PROFILER_SETTINGS.get('enabled', False):
|
||||||
|
@ -34,12 +34,25 @@ PROFILER_ENABLED = PROFILER_CONF.get('enabled', False)
|
|||||||
|
|
||||||
|
|
||||||
class ProfilerClientMiddleware(object):
|
class ProfilerClientMiddleware(object):
|
||||||
|
profiler_headers = [
|
||||||
|
('HTTP_X_TRACE_INFO', 'X-Trace-Info'),
|
||||||
|
('HTTP_X_TRACE_HMAC', 'X-Trace-HMAC')
|
||||||
|
]
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
if not PROFILER_ENABLED:
|
if not PROFILER_ENABLED:
|
||||||
raise exceptions.MiddlewareNotUsed()
|
raise exceptions.MiddlewareNotUsed()
|
||||||
super(ProfilerClientMiddleware, self).__init__()
|
super(ProfilerClientMiddleware, self).__init__()
|
||||||
|
|
||||||
|
def is_async_profiling(self, request):
|
||||||
|
return self.profiler_headers[0][0] in request.META
|
||||||
|
|
||||||
def process_request(self, request):
|
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:
|
if 'profile_page' in request.COOKIES:
|
||||||
hmac_key = PROFILER_CONF.get('keys')[0]
|
hmac_key = PROFILER_CONF.get('keys')[0]
|
||||||
profiler.init(hmac_key)
|
profiler.init(hmac_key)
|
||||||
@ -76,8 +89,8 @@ class ProfilerMiddleware(object):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def process_view(self, request, view_func, view_args, view_kwargs):
|
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
|
return None
|
||||||
|
|
||||||
trace_info = profiler_utils.signed_unpack(
|
trace_info = profiler_utils.signed_unpack(
|
||||||
@ -98,6 +111,8 @@ class ProfilerMiddleware(object):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
with api.traced(request, view_func.__name__, info) as trace_id:
|
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)
|
response = view_func(request, *view_args, **view_kwargs)
|
||||||
url = reverse('horizon:developer:profiler:index')
|
url = reverse('horizon:developer:profiler:index')
|
||||||
message = safestring.mark_safe(
|
message = safestring.mark_safe(
|
||||||
@ -115,8 +130,4 @@ class ProfilerMiddleware(object):
|
|||||||
|
|
||||||
def process_response(self, request, response):
|
def process_response(self, request, response):
|
||||||
self.clear_profiling_cookies(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
|
return response
|
||||||
|
@ -51,7 +51,7 @@
|
|||||||
* are used while rendering trace node.
|
* are used while rendering trace node.
|
||||||
*/
|
*/
|
||||||
sharedProfilerController.$inject = [
|
sharedProfilerController.$inject = [
|
||||||
'$modal',
|
'$uibModal',
|
||||||
'$rootScope',
|
'$rootScope',
|
||||||
'$templateCache',
|
'$templateCache',
|
||||||
'horizon.dashboard.developer.profiler.basePath'
|
'horizon.dashboard.developer.profiler.basePath'
|
||||||
@ -95,13 +95,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getWidth(data, rootData) {
|
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;
|
var duration = (data.info.finished - data.info.started) * 100.0 / full_duration;
|
||||||
return (duration >= 0.5) ? duration : 0.5;
|
return (duration >= 0.5) ? duration : 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getStarted(data, rootData) {
|
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;
|
return data.info.started * 100.0 / full_duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,7 +154,7 @@
|
|||||||
ctrl.profilePage = profilePage;
|
ctrl.profilePage = profilePage;
|
||||||
|
|
||||||
function profilePage() {
|
function profilePage() {
|
||||||
$cookies.put('profile_page', true);
|
$cookies.put('profile_page', true, {path: window.location.pathname});
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,12 +29,31 @@
|
|||||||
|
|
||||||
config.$inject = [
|
config.$inject = [
|
||||||
'$provide',
|
'$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/';
|
var path = $windowProvider.$get().STATIC_URL + 'dashboard/developer/profiler/';
|
||||||
$provide.constant('horizon.dashboard.developer.profiler.basePath', path);
|
$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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
@ -9,10 +9,10 @@
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="progress-text">
|
<div class="progress-text">
|
||||||
<progress>
|
<uib-progress>
|
||||||
<bar value="ctrl.getStarted(data, rootData)" type="transparent"></bar>
|
<uib-bar value="ctrl.getStarted(data, rootData)" type="transparent"></uib-bar>
|
||||||
<bar value="ctrl.getWidth(data, rootData)" type="info"></bar>
|
<uib-bar value="ctrl.getWidth(data, rootData)" type="info"></uib-bar>
|
||||||
</progress>
|
</uib-progress>
|
||||||
<span class="progress-bar-text">
|
<span class="progress-bar-text">
|
||||||
{$ data.info.finished - data.info.started $} ms
|
{$ data.info.finished - data.info.started $} ms
|
||||||
</span>
|
</span>
|
||||||
|
@ -66,6 +66,17 @@
|
|||||||
{% endcache %}
|
{% endcache %}
|
||||||
{% endcompress %}
|
{% endcompress %}
|
||||||
|
|
||||||
|
{% if profiler_enabled %}
|
||||||
|
<script>
|
||||||
|
(function() {
|
||||||
|
angular.module('horizon.dashboard.developer.profiler')
|
||||||
|
.constant('horizon.dashboard.developer.profiler.headers',
|
||||||
|
{% if x_trace_info %}{{ x_trace_info|safe }}{% else %}{}{% endif %}
|
||||||
|
);
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% comment %} Client-side Templates (These should *not* be inside the "compress" tag.) {% endcomment %}
|
{% comment %} Client-side Templates (These should *not* be inside the "compress" tag.) {% endcomment %}
|
||||||
{% include "horizon/client_side/templates.html" %}
|
{% include "horizon/client_side/templates.html" %}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user