def test_perf_transaction_with_collection(benchmark, django_elasticapm_client): django_elasticapm_client.instrumentation_store.get_all() with mock.patch("elasticapm.traces.TransactionsStore.should_collect") as should_collect: should_collect.return_value = False django_elasticapm_client.events = [] client = _TestClient() with override_settings(**middleware_setting(django.VERSION, ['elasticapm.contrib.django.middleware.TracingMiddleware'])): for i in range(10): resp = client_get(client, reverse("render-user-template")) assert resp.status_code == 200 assert len(django_elasticapm_client.events) == 0 # Force collection on next request should_collect.return_value = True @benchmark def result(): # Code to be measured return client_get(client, reverse("render-user-template")) assert result.status_code is 200 assert len(django_elasticapm_client.events) > 0
def test_template_rendering(should_collect, django_elasticapm_client, client): should_collect.return_value = False with override_settings(**middleware_setting( django.VERSION, ['elasticapm.contrib.django.middleware.TracingMiddleware'])): client.get(reverse('render-heavy-template')) client.get(reverse('render-heavy-template')) client.get(reverse('render-heavy-template')) transactions = django_elasticapm_client.instrumentation_store.get_all() assert len(transactions) == 3 spans = transactions[0]['spans'] assert len(spans) == 2, [t['name'] for t in spans] kinds = ['code', 'template.django'] assert set([t['type'] for t in spans]) == set(kinds) assert spans[0]['type'] == 'code' assert spans[0]['name'] == 'something_expensive' assert spans[0]['parent'] == 0 assert spans[1]['type'] == 'template.django' assert spans[1]['name'] == 'list_users.html' assert spans[1]['parent'] is None
def test_template_rendering(instrument, django_elasticapm_client, client): with override_settings(**middleware_setting( django.VERSION, ["elasticapm.contrib.django.middleware.TracingMiddleware"])): client.get(reverse("render-heavy-template")) client.get(reverse("render-heavy-template")) client.get(reverse("render-heavy-template")) transactions = django_elasticapm_client.events[TRANSACTION] assert len(transactions) == 3 spans = django_elasticapm_client.spans_for_transaction(transactions[0]) assert len(spans) == 2, [t["name"] for t in spans] kinds = ["code", "template"] assert set([t["type"] for t in spans]) == set(kinds) assert spans[0]["type"] == "code" assert spans[0]["name"] == "something_expensive" assert spans[0]["parent_id"] == spans[1]["id"] assert spans[1]["type"] == "template" assert spans[1]["subtype"] == "django" assert spans[1]["action"] == "render" assert spans[1]["name"] == "list_users.html" assert spans[1]["parent_id"] == transactions[0]["id"]
def test_template_rendering_django18_jinja2(instrument, django_elasticapm_client, client): with override_settings( TEMPLATES=TEMPLATES, **middleware_setting( django.VERSION, ["elasticapm.contrib.django.middleware.TracingMiddleware"])): client.get(reverse("render-jinja2-template")) client.get(reverse("render-jinja2-template")) client.get(reverse("render-jinja2-template")) transactions = django_elasticapm_client.events[TRANSACTION] assert len(transactions) == 3 spans = django_elasticapm_client.spans_for_transaction(transactions[0]) assert len(spans) == 1, [t["name"] for t in spans] kinds = ["template"] assert set([t["type"] for t in spans]) == set(kinds) assert spans[0]["type"] == "template" assert spans[0]["subtype"] == "jinja2" assert spans[0]["action"] == "render" assert spans[0]["name"] == "jinja2_template.html" assert spans[0]["parent_id"] == transactions[0]["id"]
def test_transaction_request_response_data(django_elasticapm_client, client): client.cookies = SimpleCookie({'foo': 'bar'}) django_elasticapm_client.instrumentation_store.get_all() with override_settings(**middleware_setting( django.VERSION, ['elasticapm.contrib.django.middleware.TracingMiddleware'] )): client.get(reverse('elasticapm-no-error')) assert len(django_elasticapm_client.instrumentation_store) == 1 transactions = django_elasticapm_client.instrumentation_store.get_all() assert len(transactions) == 1 transaction = transactions[0] assert transaction['result'] == 'HTTP 2xx' assert 'request' in transaction['context'] request = transaction['context']['request'] assert request['method'] == 'GET' assert 'headers' in request headers = request['headers'] assert headers['cookie'] == ' foo=bar' env = request['env'] assert 'SERVER_NAME' in env, env.keys() assert env['SERVER_NAME'] == 'testserver' assert 'SERVER_PORT' in env, env.keys() assert env['SERVER_PORT'] == '80' assert 'response' in transaction['context'] response = transaction['context']['response'] assert response['status_code'] == 200 assert response['headers']['my-header'] == 'foo'
def test_template_rendering(should_collect, instrument, django_elasticapm_client, client): should_collect.return_value = False with override_settings(**middleware_setting( django.VERSION, ["elasticapm.contrib.django.middleware.TracingMiddleware"])): client.get(reverse("render-heavy-template")) client.get(reverse("render-heavy-template")) client.get(reverse("render-heavy-template")) transactions = django_elasticapm_client.transaction_store.get_all() assert len(transactions) == 3 spans = transactions[0]["spans"] assert len(spans) == 2, [t["name"] for t in spans] kinds = ["code", "template.django"] assert set([t["type"] for t in spans]) == set(kinds) assert spans[0]["type"] == "code" assert spans[0]["name"] == "something_expensive" assert spans[0]["parent"] == 0 assert spans[1]["type"] == "template.django" assert spans[1]["name"] == "list_users.html" assert spans[1]["parent"] is None
def test_broken_500_handler_with_middleware(django_elasticapm_client, client): with override_settings(BREAK_THAT_500=True): client.handler = MockMiddleware(MockClientHandler()) with override_settings(**middleware_setting(django.VERSION, [])): with pytest.raises(Exception): client.get(reverse('elasticapm-raise-exc')) assert len(django_elasticapm_client.events) == 2 event = django_elasticapm_client.events.pop(0)['errors'][0] assert 'exception' in event exc = event['exception'] assert exc['type'] == 'Exception' assert exc['message'] == 'Exception: view exception' assert event['culprit'] == 'tests.contrib.django.testapp.views.raise_exc' event = django_elasticapm_client.events.pop(0)['errors'][0] assert 'exception' in event exc = event['exception'] assert exc['type'] == 'ValueError' assert exc['message'] == 'ValueError: handler500' assert event['culprit'] == 'tests.contrib.django.testapp.urls.handler500' assert event['handled'] is False
def test_request_metrics_301_append_slash(django_elasticapm_client, client): django_elasticapm_client.instrumentation_store.get_all() # clear the store # enable middleware wrapping django_elasticapm_client.config.instrument_django_middleware = True from elasticapm.contrib.django.middleware import TracingMiddleware TracingMiddleware._elasticapm_instrumented = False with override_settings( APPEND_SLASH=True, **middleware_setting(django.VERSION, [ 'elasticapm.contrib.django.middleware.TracingMiddleware', 'django.middleware.common.CommonMiddleware', ]) ): client.get(reverse('elasticapm-no-error-slash')[:-1]) transactions = django_elasticapm_client.instrumentation_store.get_all() assert transactions[0]['name'] in ( # django <= 1.8 'GET django.middleware.common.CommonMiddleware.process_request', # django 1.9+ 'GET django.middleware.common.CommonMiddleware.process_response', ) assert transactions[0]['result'] == 'HTTP 3xx'
def test_tracing_middleware_uses_test_client(client, django_elasticapm_client): with override_settings(**middleware_setting(django.VERSION, [ 'elasticapm.contrib.django.middleware.TracingMiddleware' ])): client.get('/') transactions = django_elasticapm_client.instrumentation_store.get_all() assert len(transactions) == 1 assert transactions[0]['context']['request']['url']['pathname'] == '/'
def test_request_metrics_404_resolve_error(django_elasticapm_client, client): django_elasticapm_client.instrumentation_store.get_all() # clear the store with override_settings( **middleware_setting(django.VERSION, ['elasticapm.contrib.django.middleware.TracingMiddleware']) ): client.get('/i-dont-exist/') transactions = django_elasticapm_client.instrumentation_store.get_all() assert transactions[0]['name'] == ''
def test_middleware_not_first(): stdout = compat.StringIO() with override_settings(**middleware_setting(django.VERSION, ( 'foo', 'elasticapm.contrib.django.middleware.TracingMiddleware' ))): call_command('elasticapm', 'check', stdout=stdout) output = stdout.getvalue() assert 'not at the first position' in output
def test_request_metrics_name_override(django_elasticapm_client, client): django_elasticapm_client.instrumentation_store.get_all() # clear the store with override_settings( **middleware_setting(django.VERSION, [ 'elasticapm.contrib.django.middleware.TracingMiddleware', 'tests.contrib.django.testapp.middleware.MetricsNameOverrideMiddleware', ]) ): client.get(reverse('elasticapm-no-error')) transactions = django_elasticapm_client.instrumentation_store.get_all() assert transactions[0]['name'] == 'GET foobar'
def test_test_exception(urlopen_mock): stdout = compat.StringIO() resp = mock.Mock(status=200, getheader=lambda h: 'http://example.com') urlopen_mock.return_value = resp with override_settings(**middleware_setting(django.VERSION, [ 'foo', 'elasticapm.contrib.django.middleware.TracingMiddleware' ])): call_command('elasticapm', 'test', stdout=stdout, stderr=stdout) output = stdout.getvalue() assert 'Success! We tracked the error successfully!' in output
def test_response_error_id_middleware(django_elasticapm_client, client): with override_settings(**middleware_setting(django.VERSION, [ 'elasticapm.contrib.django.middleware.ErrorIdMiddleware', 'elasticapm.contrib.django.middleware.Catch404Middleware'])): resp = client.get('/non-existant-page') assert resp.status_code == 404 headers = dict(resp.items()) assert 'X-ElasticAPM-ErrorId' in headers assert len(django_elasticapm_client.events) == 1 event = django_elasticapm_client.events.pop(0)['errors'][0] assert event['id'] == headers['X-ElasticAPM-ErrorId']
def test_404_middleware_with_debug(django_elasticapm_client, client): django_elasticapm_client.config.debug = False with override_settings( DEBUG=True, **middleware_setting(django.VERSION, [ 'elasticapm.contrib.django.middleware.Catch404Middleware' ]) ): resp = client.get('/non-existant-page') assert resp.status_code == 404 assert len(django_elasticapm_client.events) == 0
def test_django_logging_middleware(django_elasticapm_client, client): handler = LoggingHandler() logger = logging.getLogger('logmiddleware') logger.handlers = [] logger.addHandler(handler) with override_settings(**middleware_setting(django.VERSION, ['elasticapm.contrib.django.middleware.LogMiddleware'])): client.get(reverse('elasticapm-logging')) assert len(django_elasticapm_client.events) == 1 event = django_elasticapm_client.events.pop(0)['errors'][0] assert 'request' in event['context'] assert event['context']['request']['url']['pathname'] == reverse('elasticapm-logging')
def test_view_middleware_exception(django_elasticapm_client, client): with override_settings(**middleware_setting(django.VERSION, ['tests.contrib.django.testapp.middleware.BrokenViewMiddleware'])): with pytest.raises(ImportError): client.get(reverse('elasticapm-raise-exc')) assert len(django_elasticapm_client.events) == 1 event = django_elasticapm_client.events.pop(0)['errors'][0] assert 'exception' in event exc = event['exception'] assert exc['type'] == 'ImportError' assert exc['message'] == 'ImportError: view' assert event['culprit'] == 'tests.contrib.django.testapp.middleware.process_view' assert event['handled'] is False
def test_transaction_metrics(django_elasticapm_client, client): django_elasticapm_client.instrumentation_store.get_all() # clear the store with override_settings(**middleware_setting( django.VERSION, ['elasticapm.contrib.django.middleware.TracingMiddleware'] )): assert len(django_elasticapm_client.instrumentation_store) == 0 client.get(reverse('elasticapm-no-error')) assert len(django_elasticapm_client.instrumentation_store) == 1 transactions = django_elasticapm_client.instrumentation_store.get_all() assert len(transactions) == 1 transaction = transactions[0] assert transaction['duration'] > 0 assert transaction['result'] == 'HTTP 2xx' assert transaction['name'] == 'GET tests.contrib.django.testapp.views.no_error'
def test_404_middleware(django_elasticapm_client, client): with override_settings(**middleware_setting(django.VERSION, ['elasticapm.contrib.django.middleware.Catch404Middleware'])): resp = client.get('/non-existant-page') assert resp.status_code == 404 assert len(django_elasticapm_client.events) == 1 event = django_elasticapm_client.events.pop(0)['errors'][0] assert event['log']['level'] == 'info' assert event['log']['logger_name'] == 'http404' assert 'request' in event['context'] request = event['context']['request'] assert request['url']['full'] == u'http://testserver/non-existant-page' assert request['method'] == 'GET' assert request['body'] == None
def test_response_middlware_exception(django_elasticapm_client, client): if django.VERSION[:2] < (1, 3): return with override_settings(**middleware_setting(django.VERSION, ['tests.contrib.django.testapp.middleware.BrokenResponseMiddleware'])): with pytest.raises(ImportError): client.get(reverse('elasticapm-no-error')) assert len(django_elasticapm_client.events) == 1 event = django_elasticapm_client.events.pop(0)['errors'][0] assert 'exception' in event exc = event['exception'] assert exc['type'] == 'ImportError' assert exc['message'] == 'ImportError: response' assert event['culprit'] == 'tests.contrib.django.testapp.middleware.process_response' assert event['handled'] is False
def test_request_metrics_301_prepend_www(django_elasticapm_client, client): django_elasticapm_client.instrumentation_store.get_all() # clear the store # enable middleware wrapping django_elasticapm_client.config.instrument_django_middleware = True from elasticapm.contrib.django.middleware import TracingMiddleware TracingMiddleware._elasticapm_instrumented = False with override_settings( PREPEND_WWW=True, **middleware_setting(django.VERSION, [ 'elasticapm.contrib.django.middleware.TracingMiddleware', 'django.middleware.common.CommonMiddleware', ]) ): client.get(reverse('elasticapm-no-error')) transactions = django_elasticapm_client.instrumentation_store.get_all() assert transactions[0]['name'] == 'GET django.middleware.common.CommonMiddleware.process_request' assert transactions[0]['result'] == 'HTTP 3xx'
def test_perf_database_render(benchmark, client, django_elasticapm_client): responses = [] django_elasticapm_client.instrumentation_store.get_all() with mock.patch("elasticapm.traces.TransactionsStore.should_collect") as should_collect: should_collect.return_value = False with override_settings(**middleware_setting(django.VERSION, ['elasticapm.contrib.django.middleware.TracingMiddleware'])): benchmark(lambda: responses.append( client_get(client, reverse("render-user-template")) )) for resp in responses: assert resp.status_code == 200 transactions = django_elasticapm_client.instrumentation_store.get_all() assert len(transactions) == len(responses) for transaction in transactions: assert len(transaction['spans']) in (102, 103)
def test_stacktrace_filtered_for_elasticapm(client, django_elasticapm_client): with mock.patch( "elasticapm.traces.TransactionsStore.should_collect") as should_collect: should_collect.return_value = False with override_settings(**middleware_setting(django.VERSION, ['elasticapm.contrib.django.middleware.TracingMiddleware'])): resp = client.get(reverse("render-heavy-template")) assert resp.status_code == 200 transactions = django_elasticapm_client.instrumentation_store.get_all() assert transactions[0]['result'] == 'HTTP 2xx' spans = transactions[0]['spans'] expected_signatures = ['transaction', 'list_users.html', 'something_expensive'] assert spans[1]['name'] == 'list_users.html' # Top frame should be inside django rendering assert spans[1]['stacktrace'][0]['module'].startswith('django.template')
def test_perf_template_render(benchmark, client, django_elasticapm_client): responses = [] with mock.patch("elasticapm.traces.TransactionsStore.should_collect") as should_collect: should_collect.return_value = False with override_settings(**middleware_setting(django.VERSION, ['elasticapm.contrib.django.middleware.TracingMiddleware'])): benchmark(lambda: responses.append( client_get(client, reverse("render-heavy-template")) )) for resp in responses: assert resp.status_code == 200 transactions = django_elasticapm_client.instrumentation_store.get_all() # If the test falls right at the change from one minute to another # this will have two items. assert len(transactions) == len(responses) for transaction in transactions: assert len(transaction['spans']) == 2 assert transaction['result'] == 'HTTP 2xx'
def test_stacktraces_have_templates(client, django_elasticapm_client): # only Django 1.9+ have the necessary information stored on Node/Template # instances when TEMPLATE_DEBUG = False TEMPLATE_DEBUG = django.VERSION < (1, 9) with mock.patch("elasticapm.traces.TransactionsStore.should_collect") as should_collect: should_collect.return_value = False TEMPLATES_copy = deepcopy(settings.TEMPLATES) TEMPLATES_copy[0]['OPTIONS']['debug'] = TEMPLATE_DEBUG with override_settings( TEMPLATE_DEBUG=TEMPLATE_DEBUG, TEMPLATES=TEMPLATES_copy, **middleware_setting(django.VERSION, [ 'elasticapm.contrib.django.middleware.TracingMiddleware' ]) ): resp = client.get(reverse("render-heavy-template")) assert resp.status_code == 200 transactions = django_elasticapm_client.instrumentation_store.get_all() assert len(transactions) == 1 transaction = transactions[0] assert transaction['result'] == 'HTTP 2xx' spans = transaction['spans'] assert len(spans) == 2, [t['name'] for t in spans] expected_names = {'list_users.html', 'something_expensive'} assert {t['name'] for t in spans} == expected_names assert spans[0]['name'] == 'something_expensive' # Find the template for frame in spans[0]['stacktrace']: if frame['lineno'] == 4 and frame['filename'].endswith( 'django/testapp/templates/list_users.html' ): break else: assert False is True, "Template was not found"
def test_request_metrics_contrib_redirect(django_elasticapm_client, client): django_elasticapm_client.instrumentation_store.get_all() # clear the store # enable middleware wrapping django_elasticapm_client.config.instrument_django_middleware = True from elasticapm.contrib.django.middleware import TracingMiddleware TracingMiddleware._elasticapm_instrumented = False s = Site.objects.get(pk=1) Redirect.objects.create(site=s, old_path='/redirect/me/', new_path='/here/') with override_settings( **middleware_setting(django.VERSION, [ 'elasticapm.contrib.django.middleware.TracingMiddleware', 'django.contrib.redirects.middleware.RedirectFallbackMiddleware', ]) ): response = client.get('/redirect/me/') transactions = django_elasticapm_client.instrumentation_store.get_all() assert transactions[0]['name'] == 'GET django.contrib.redirects.middleware.RedirectFallbackMiddleware.process_response' assert transactions[0]['result'] == 'HTTP 3xx'
def test_template_rendering_django18_jinja2(should_collect, instrument, django_elasticapm_client, client): should_collect.return_value = False with override_settings( TEMPLATES=TEMPLATES, **middleware_setting(django.VERSION, ['elasticapm.contrib.django.middleware.TracingMiddleware']) ): client.get(reverse('render-jinja2-template')) client.get(reverse('render-jinja2-template')) client.get(reverse('render-jinja2-template')) transactions = django_elasticapm_client.instrumentation_store.get_all() assert len(transactions) == 3 spans = transactions[0]['spans'] assert len(spans) == 1, [t['name'] for t in spans] kinds = ['template.jinja2'] assert set([t['type'] for t in spans]) == set(kinds) assert spans[0]['type'] == 'template.jinja2' assert spans[0]['name'] == 'jinja2_template.html' assert spans[0]['parent'] is None
def test_template_rendering_django18_jinja2(should_collect, instrument, django_elasticapm_client, client): should_collect.return_value = False with override_settings( TEMPLATES=TEMPLATES, **middleware_setting( django.VERSION, ["elasticapm.contrib.django.middleware.TracingMiddleware"])): client.get(reverse("render-jinja2-template")) client.get(reverse("render-jinja2-template")) client.get(reverse("render-jinja2-template")) transactions = django_elasticapm_client.transaction_store.get_all() assert len(transactions) == 3 spans = transactions[0]["spans"] assert len(spans) == 1, [t["name"] for t in spans] kinds = ["template.jinja2"] assert set([t["type"] for t in spans]) == set(kinds) assert spans[0]["type"] == "template.jinja2" assert spans[0]["name"] == "jinja2_template.html" assert spans[0]["parent"] is None
def pytest_configure(config): try: from django.conf import settings except ImportError: settings = None if settings is not None and not settings.configured: import django settings_dict = dict( SECRET_KEY="42", DATABASES={ "default": { "ENGINE": "django.db.backends.sqlite3", "NAME": "elasticapm_tests.db", "TEST_NAME": "elasticapm_tests.db", "TEST": {"NAME": "elasticapm_tests.db"}, } }, TEST_DATABASE_NAME="elasticapm_tests.db", INSTALLED_APPS=[ "django.contrib.auth", "django.contrib.admin", "django.contrib.sessions", "django.contrib.sites", "django.contrib.redirects", "django.contrib.contenttypes", "elasticapm.contrib.django", "tests.contrib.django.testapp", ], ROOT_URLCONF="tests.contrib.django.testapp.urls", DEBUG=False, SITE_ID=1, BROKER_HOST="localhost", BROKER_PORT=5672, BROKER_USER="******", BROKER_PASSWORD="******", BROKER_VHOST="/", CELERY_ALWAYS_EAGER=True, TEMPLATE_DEBUG=False, TEMPLATE_DIRS=[BASE_TEMPLATE_DIR], ALLOWED_HOSTS=["*"], TEMPLATES=[ { "BACKEND": "django.template.backends.django.DjangoTemplates", "DIRS": [BASE_TEMPLATE_DIR], "OPTIONS": { "context_processors": ["django.contrib.auth.context_processors.auth"], "loaders": ["django.template.loaders.filesystem.Loader"], "debug": False, }, } ], ELASTIC_APM={ "METRICS_INTERVAL": "0ms", "TRANSPORT_CLASS": "tests.fixtures.DummyTransport", }, # avoid autostarting the metrics collector thread ) settings_dict.update( **middleware_setting( django.VERSION, [ "django.contrib.sessions.middleware.SessionMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", "django.contrib.messages.middleware.MessageMiddleware", ], ) ) settings.configure(**settings_dict) if hasattr(django, "setup"): django.setup()
def test_middleware_not_set(): stdout = compat.StringIO() with override_settings(**middleware_setting(django.VERSION, ())): call_command('elasticapm', 'check', stdout=stdout) output = stdout.getvalue() assert 'Tracing middleware not configured!' in output