Commit c63ed04a authored by Alexandre Vincent's avatar Alexandre Vincent
Browse files

Merge branch 'avincent/request_headers_to_appinsights_customdimensions' into 'master'

Logging request headers to customdimensions

See merge request !172
parents 4469f772 9f0ae23e
Pipeline #51763 passed with stages
in 12 minutes and 45 seconds
......@@ -369,6 +369,7 @@ def check_environment(configuration):
AUTHORIZATION_HEADER_NAME = 'Authorization'
APP_KEY_HEADER_NAME = 'appKey'
APP_ID_HEADER_NAME = 'x-app-id'
CORRELATION_ID_HEADER_NAME = 'correlation-id'
REQUEST_ID_HEADER_NAME = 'Request-ID'
PARTITION_ID_HEADER_NAME = 'data-partition-id'
......@@ -87,6 +87,13 @@ class TracingMiddleware(BaseHTTPMiddleware):
tracer.add_attribute_to_current_span(
attribute_key=conf.CORRELATION_ID_HEADER_NAME,
attribute_value=correlation_id)
ctx_partition_id = get_or_create_ctx().partition_id
partition_id = ctx_partition_id if ctx_partition_id is not None \
else request.headers.get(conf.PARTITION_ID_HEADER_NAME)
tracer.add_attribute_to_current_span(
attribute_key=conf.PARTITION_ID_HEADER_NAME,
attribute_value=partition_id)
request_content_type = request.headers.get("Content-type")
tracer.add_attribute_to_current_span(attribute_key="request.header Content-type",
......@@ -96,6 +103,10 @@ class TracingMiddleware(BaseHTTPMiddleware):
tracer.add_attribute_to_current_span(attribute_key="request.header Content-length",
attribute_value=request_content_length)
app_id = request.headers.get(conf.APP_ID_HEADER_NAME)
tracer.add_attribute_to_current_span(attribute_key=conf.APP_ID_HEADER_NAME,
attribute_value=app_id)
@staticmethod
def _after_request(request: Request, response: Response, tracer):
......
# Copyright 2021 Schlumberger
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import pytest
from pytest_httpx import HTTPXMock
from app.clients import make_storage_record_client, make_search_client
from app.utils import Context, get_or_create_ctx
from tests.unit.test_utils import ctx_fixture
@pytest.mark.asyncio
async def test_fwd_correlation_id_to_outgoing_request_to_storage(ctx_fixture: Context, httpx_mock: HTTPXMock):
storage_url = "http://example.com" # well formed url required
expected_correlation_id = 'some-correlation-id'
ctx = ctx_fixture.with_correlation_id(expected_correlation_id).with_auth("foobar")
Context.set_current(ctx)
# safety: make sure no methods on tracer have been called yet
assert ctx.tracer.method_calls == []
async with make_storage_record_client(storage_url) as storage_client:
httpx_mock.add_response(match_headers={'correlation-id': expected_correlation_id})
# force to use endpoint which does not return a response to skip model validation
response = await storage_client.delete_record(id="123", data_partition_id="test")
assert response is not None
# make sure correlation-id is traced when doing a request to storage
ctx.tracer.add_attribute_to_current_span.assert_any_call(
attribute_key='correlation-id',
attribute_value=expected_correlation_id
)
@pytest.mark.asyncio
async def test_fwd_correlation_id_to_outgoing_request_to_search(ctx_fixture: Context, httpx_mock: HTTPXMock):
storage_url = "http://example.com" # well formed url required
expected_correlation_id = 'some-correlation-id'
ctx = ctx_fixture.with_correlation_id(expected_correlation_id).with_auth("foobar")
Context.set_current(ctx)
# safety: make sure no methods on tracer have been called yet
assert ctx.tracer.method_calls == []
async with make_search_client(storage_url) as search_client:
httpx_mock.add_response(match_headers={'correlation-id': expected_correlation_id})
# force to use endpoint which does not return a response to skip model validation
response = await search_client.delete_index(kind="kind", data_partition_id="test")
assert response is not None
# make sure correlation-id is traced when doing a request to search
ctx.tracer.add_attribute_to_current_span.assert_any_call(
attribute_key='correlation-id',
attribute_value=expected_correlation_id
)
# Copyright 2021 Schlumberger
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from opencensus.trace import base_exporter
from fastapi.testclient import TestClient
import pytest
from app.wdms_app import wdms_app, DDMS_V2_PATH
from app.utils import get_or_create_ctx
from tests.unit.test_utils import NopeLogger
# Initialize traces exporter in app with a custom one to allow validating our traces
class ExporterInTest(base_exporter.Exporter):
def __init__(self) -> None:
self.exported = []
def export(self, span_datas):
self.exported += span_datas
def find(self, correlation_id):
for sd in self.exported:
if sd.attributes.get('correlation-id') == correlation_id:
return sd
@pytest.fixture()
def ctx_fixture():
""" Create context with a real tracer in it """
ctx = get_or_create_ctx().set_current_with_value(logger=NopeLogger())
yield ctx
@pytest.fixture
def client(ctx_fixture):
yield TestClient(wdms_app)
wdms_app.dependency_overrides = {}
def build_url(path: str):
return DDMS_V2_PATH + path
def test_about_call_creates_correlation_id_if_absent(client: TestClient):
# Initialize traces exporter in app, like it is in app's startup_event
wdms_app.trace_exporter = ExporterInTest()
# no header -> works fine
response = client.get(build_url("/about"))
assert response.status_code == 200
# one call was exported, with correlation-id
assert len(wdms_app.trace_exporter.exported) == 1 # one call => one export
spandata = wdms_app.trace_exporter.exported[0]
assert 'correlation-id' in spandata.attributes.keys()
assert spandata.attributes['correlation-id'] is not None
def test_about_call_traces_existing_correlation_id(client: TestClient):
# Initialize traces exporter in app, like it is in app's startup_event
wdms_app.trace_exporter = ExporterInTest()
# no header -> works fine
response = client.get(build_url("/about"), headers={'correlation-id': 'some correlation id'})
assert response.status_code == 200
# one call was exported, with correlation-id
assert len(wdms_app.trace_exporter.exported) == 1 # one call => one export
spandata = wdms_app.trace_exporter.exported[0]
assert 'correlation-id' in spandata.attributes.keys()
assert spandata.attributes['correlation-id'] == 'some correlation id'
@pytest.mark.parametrize("header_name",[
'x-app-id',
'data-partition-id'
])
def test_about_call_traces_request_header(header_name, client: TestClient):
# Initialize traces exporter in app, like it is in app's startup_event
wdms_app.trace_exporter = ExporterInTest()
# no header -> works fine
response = client.get(build_url("/about"))
assert response.status_code == 200
# one call was exported, without header
assert len(wdms_app.trace_exporter.exported) == 1 # one call => one export
spandata = wdms_app.trace_exporter.exported[0]
assert header_name in spandata.attributes.keys()
assert spandata.attributes[header_name] is None
# with header -> works as well
client.get(build_url("/about"), headers={header_name: "some value"})
assert response.status_code == 200
# a second call was exported, with header
assert len(wdms_app.trace_exporter.exported) == 2 # one call => one export
spandata = wdms_app.trace_exporter.exported[1]
assert header_name in spandata.attributes.keys()
assert spandata.attributes[header_name] == "some value"
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment