Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle null fastapi route.name and route.operation_id #547

Merged
merged 1 commit into from
Oct 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions logfire/_internal/integrations/fastapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,12 +171,12 @@ async def solve_dependencies(self, request: Request | WebSocket, original: Await
span.set_attribute(SpanAttributes.HTTP_METHOD, request.method)
route: APIRoute | APIWebSocketRoute | None = request.scope.get('route')
if route: # pragma: no branch
root_span.set_attribute('fastapi.route.name', route.name)
span.set_attribute('fastapi.route.name', route.name)
span.set_attribute(SpanAttributes.HTTP_ROUTE, route.path)
fastapi_route_attributes: dict[str, Any] = {'fastapi.route.name': route.name}
if isinstance(route, APIRoute): # pragma: no branch
root_span.set_attribute('fastapi.route.operation_id', route.operation_id)
span.set_attribute('fastapi.route.operation_id', route.operation_id)
fastapi_route_attributes['fastapi.route.operation_id'] = route.operation_id
set_user_attributes_on_raw_span(root_span, fastapi_route_attributes)
span.set_attributes(fastapi_route_attributes)

result: Any = await original

Expand Down
2 changes: 1 addition & 1 deletion logfire/_internal/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -1777,7 +1777,7 @@ def set_attribute(self, key: str, value: Any) -> None:
if self._span is not None: # pragma: no branch
self._span.set_attribute(key, otel_value)

def set_attributes(self, attributes: dict[str, otel_types.AttributeValue]) -> None:
def set_attributes(self, attributes: dict[str, Any]) -> None:
"""Sets the given attributes on the span."""
for key, value in attributes.items():
self.set_attribute(key, value)
Expand Down
42 changes: 27 additions & 15 deletions tests/otel_integrations/test_fastapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ def test_path_param(client: TestClient, exporter: TestExporter) -> None:
'http.route': '/with_path_param/{param}',
'logfire.null_args': ('fastapi.route.operation_id',),
'logfire.level_num': 5,
'logfire.json_schema': '{"type":"object","properties":{"http.method":{},"fastapi.route.name":{},"http.route":{},"fastapi.route.operation_id":{}}}',
'logfire.json_schema': '{"type":"object","properties":{"http.method":{},"http.route":{},"fastapi.route.name":{},"fastapi.route.operation_id":{}}}',
},
},
{
Expand Down Expand Up @@ -397,6 +397,8 @@ def test_path_param(client: TestClient, exporter: TestExporter) -> None:
'client.port': 50000,
'http.route': '/with_path_param/{param}',
'fastapi.route.name': 'with_path_param',
'logfire.null_args': ('fastapi.route.operation_id',),
'logfire.json_schema': '{"type":"object","properties":{"fastapi.route.name":{},"fastapi.route.operation_id":{}}}',
'http.status_code': 200,
'http.response.status_code': 200,
},
Expand Down Expand Up @@ -492,7 +494,7 @@ def test_fastapi_instrumentation(client: TestClient, exporter: TestExporter) ->
'http.route': '/',
'logfire.null_args': ('fastapi.route.operation_id',),
'logfire.level_num': 5,
'logfire.json_schema': '{"type":"object","properties":{"http.method":{},"fastapi.route.name":{},"http.route":{},"fastapi.route.operation_id":{}}}',
'logfire.json_schema': '{"type":"object","properties":{"http.method":{},"http.route":{},"fastapi.route.name":{},"fastapi.route.operation_id":{}}}',
},
},
{
Expand Down Expand Up @@ -634,6 +636,8 @@ def test_fastapi_instrumentation(client: TestClient, exporter: TestExporter) ->
'client.port': 50000,
'http.route': '/',
'fastapi.route.name': 'homepage',
'logfire.null_args': ('fastapi.route.operation_id',),
'logfire.json_schema': '{"type":"object","properties":{"fastapi.route.name":{},"fastapi.route.operation_id":{}}}',
'http.status_code': 200,
'http.response.status_code': 200,
},
Expand Down Expand Up @@ -765,7 +769,7 @@ def test_fastapi_arguments(client: TestClient, exporter: TestExporter) -> None:
'fastapi.arguments.values': '{"foo":"foo_val"}',
'fastapi.arguments.errors': '[{"type":"int_parsing","loc":["query","bar"],"msg":"Input should be a valid integer, unable to parse string as an integer","input":"bar_val"}]',
'custom_attr': 'custom_value',
'logfire.json_schema': '{"type":"object","properties":{"custom_attr":{},"fastapi.arguments.values":{"type":"object"},"fastapi.arguments.errors":{"type":"array","items":{"type":"object","properties":{"loc":{"type":"array","x-python-datatype":"tuple"}}}}}}',
'logfire.json_schema': '{"type":"object","properties":{"fastapi.route.name":{},"fastapi.route.operation_id":{},"custom_attr":{},"fastapi.arguments.values":{"type":"object"},"fastapi.arguments.errors":{"type":"array","items":{"type":"object","properties":{"loc":{"type":"array","x-python-datatype":"tuple"}}}}}}',
'http.status_code': 422,
'http.response.status_code': 422,
},
Expand Down Expand Up @@ -896,7 +900,7 @@ def test_get_fastapi_arguments(client: TestClient, exporter: TestExporter) -> No
'fastapi.arguments.values': '{"foo":"foo_val","bar":1}',
'fastapi.arguments.errors': '[]',
'custom_attr': 'custom_value',
'logfire.json_schema': '{"type":"object","properties":{"custom_attr":{},"fastapi.arguments.values":{"type":"object"},"fastapi.arguments.errors":{"type":"array"}}}',
'logfire.json_schema': '{"type":"object","properties":{"fastapi.route.name":{},"fastapi.route.operation_id":{},"custom_attr":{},"fastapi.arguments.values":{"type":"object"},"fastapi.arguments.errors":{"type":"array"}}}',
'http.status_code': 200,
'http.response.status_code': 200,
},
Expand Down Expand Up @@ -1027,7 +1031,7 @@ def test_first_lvl_subapp_fastapi_arguments(client: TestClient, exporter: TestEx
'fastapi.arguments.values': '{"foo":"foo_val","bar":1}',
'fastapi.arguments.errors': '[]',
'custom_attr': 'custom_value',
'logfire.json_schema': '{"type":"object","properties":{"custom_attr":{},"fastapi.arguments.values":{"type":"object"},"fastapi.arguments.errors":{"type":"array"}}}',
'logfire.json_schema': '{"type":"object","properties":{"fastapi.route.name":{},"fastapi.route.operation_id":{},"custom_attr":{},"fastapi.arguments.values":{"type":"object"},"fastapi.arguments.errors":{"type":"array"}}}',
'http.status_code': 200,
'http.response.status_code': 200,
},
Expand Down Expand Up @@ -1158,7 +1162,7 @@ def test_second_lvl_subapp_fastapi_arguments(client: TestClient, exporter: TestE
'fastapi.arguments.values': '{"foo":"foo_val","bar":1}',
'fastapi.arguments.errors': '[]',
'custom_attr': 'custom_value',
'logfire.json_schema': '{"type":"object","properties":{"custom_attr":{},"fastapi.arguments.values":{"type":"object"},"fastapi.arguments.errors":{"type":"array"}}}',
'logfire.json_schema': '{"type":"object","properties":{"fastapi.route.name":{},"fastapi.route.operation_id":{},"custom_attr":{},"fastapi.arguments.values":{"type":"object"},"fastapi.arguments.errors":{"type":"array"}}}',
'http.status_code': 200,
'http.response.status_code': 200,
},
Expand Down Expand Up @@ -1188,7 +1192,7 @@ def test_fastapi_unhandled_exception(client: TestClient, exporter: TestExporter)
'http.route': '/exception',
'logfire.null_args': ('fastapi.route.operation_id',),
'logfire.level_num': 5,
'logfire.json_schema': '{"type":"object","properties":{"http.method":{},"fastapi.route.name":{},"http.route":{},"fastapi.route.operation_id":{}}}',
'logfire.json_schema': '{"type":"object","properties":{"http.method":{},"http.route":{},"fastapi.route.name":{},"fastapi.route.operation_id":{}}}',
},
},
{
Expand Down Expand Up @@ -1252,6 +1256,8 @@ def test_fastapi_unhandled_exception(client: TestClient, exporter: TestExporter)
'client.port': 50000,
'http.route': '/exception',
'fastapi.route.name': 'exception',
'logfire.null_args': ('fastapi.route.operation_id',),
'logfire.json_schema': '{"type":"object","properties":{"fastapi.route.name":{},"fastapi.route.operation_id":{}}}',
'logfire.level_num': 17,
},
'events': [
Expand Down Expand Up @@ -1294,7 +1300,7 @@ def test_fastapi_handled_exception(client: TestClient, exporter: TestExporter) -
'http.route': '/validation_error',
'logfire.null_args': ('fastapi.route.operation_id',),
'logfire.level_num': 5,
'logfire.json_schema': '{"type":"object","properties":{"http.method":{},"fastapi.route.name":{},"http.route":{},"fastapi.route.operation_id":{}}}',
'logfire.json_schema': '{"type":"object","properties":{"http.method":{},"http.route":{},"fastapi.route.name":{},"fastapi.route.operation_id":{}}}',
},
},
{
Expand Down Expand Up @@ -1386,6 +1392,8 @@ def test_fastapi_handled_exception(client: TestClient, exporter: TestExporter) -
'client.port': 50000,
'http.route': '/validation_error',
'fastapi.route.name': 'validation_error',
'logfire.null_args': ('fastapi.route.operation_id',),
'logfire.json_schema': '{"type":"object","properties":{"fastapi.route.name":{},"fastapi.route.operation_id":{}}}',
'http.status_code': 422,
'http.response.status_code': 422,
},
Expand Down Expand Up @@ -1430,7 +1438,7 @@ def test_scrubbing(client: TestClient, exporter: TestExporter) -> None:
'http.route': '/secret/{path_param}',
'fastapi.route.name': 'secret',
'logfire.null_args': ('fastapi.route.operation_id',),
'logfire.json_schema': '{"type":"object","properties":{"http.method":{},"fastapi.route.name":{},"http.route":{},"fastapi.route.operation_id":{},"values":{"type":"object"},"errors":{"type":"array"},"custom_attr":{}}}',
'logfire.json_schema': '{"type":"object","properties":{"http.method":{},"http.route":{},"fastapi.route.name":{},"fastapi.route.operation_id":{},"values":{"type":"object"},"errors":{"type":"array"},"custom_attr":{}}}',
'logfire.scrubbed': IsJson(
[
{'path': ['attributes', 'values', 'path_param'], 'matched_substring': 'auth'},
Expand Down Expand Up @@ -1519,10 +1527,11 @@ def test_scrubbing(client: TestClient, exporter: TestExporter) -> None:
'http.route': '/secret/{path_param}',
'http.request.header.testauthorization': ("[Scrubbed due to 'auth']",),
'fastapi.route.name': 'secret',
'logfire.null_args': ('fastapi.route.operation_id',),
'fastapi.arguments.values': '{"path_param": "[Scrubbed due to \'auth\']", "foo": "foo_val", "password": "[Scrubbed due to \'password\']", "testauthorization": "[Scrubbed due to \'auth\']"}',
'fastapi.arguments.errors': '[]',
'custom_attr': 'custom_value',
'logfire.json_schema': '{"type":"object","properties":{"custom_attr":{},"fastapi.arguments.values":{"type":"object"},"fastapi.arguments.errors":{"type":"array"}}}',
'logfire.json_schema': '{"type":"object","properties":{"fastapi.route.name":{},"fastapi.route.operation_id":{},"custom_attr":{},"fastapi.arguments.values":{"type":"object"},"fastapi.arguments.errors":{"type":"array"}}}',
'http.status_code': 200,
'http.response.status_code': 200,
'logfire.scrubbed': IsJson(
Expand Down Expand Up @@ -1613,7 +1622,7 @@ def test_request_hooks_without_send_receiev_spans(exporter: TestExporter):
'http.route': '/echo_body',
'fastapi.route.name': 'echo_body',
'logfire.null_args': ('fastapi.route.operation_id',),
'logfire.json_schema': '{"type":"object","properties":{"http.method":{},"fastapi.route.name":{},"http.route":{},"fastapi.route.operation_id":{},"values":{"type":"object"},"errors":{"type":"array"}}}',
'logfire.json_schema': '{"type":"object","properties":{"http.method":{},"http.route":{},"fastapi.route.name":{},"fastapi.route.operation_id":{},"values":{"type":"object"},"errors":{"type":"array"}}}',
},
},
{
Expand Down Expand Up @@ -1714,9 +1723,10 @@ def test_request_hooks_without_send_receiev_spans(exporter: TestExporter):
'http.route': '/echo_body',
'attr_key': 'attr_val',
'fastapi.route.name': 'echo_body',
'logfire.null_args': ('fastapi.route.operation_id',),
'fastapi.arguments.values': '{}',
'fastapi.arguments.errors': '[]',
'logfire.json_schema': '{"type":"object","properties":{"attr_key":{},"fastapi.arguments.values":{"type":"object"},"fastapi.arguments.errors":{"type":"array"}}}',
'logfire.json_schema': '{"type":"object","properties":{"attr_key":{},"fastapi.route.name":{},"fastapi.route.operation_id":{},"fastapi.arguments.values":{"type":"object"},"fastapi.arguments.errors":{"type":"array"}}}',
'http.status_code': 200,
'http.response.status_code': 200,
},
Expand Down Expand Up @@ -1761,7 +1771,7 @@ def test_request_hooks_with_send_receive_spans(exporter: TestExporter):
'http.route': '/echo_body',
'fastapi.route.name': 'echo_body',
'logfire.null_args': ('fastapi.route.operation_id',),
'logfire.json_schema': '{"type":"object","properties":{"http.method":{},"fastapi.route.name":{},"http.route":{},"fastapi.route.operation_id":{},"values":{"type":"object"},"errors":{"type":"array"}}}',
'logfire.json_schema': '{"type":"object","properties":{"http.method":{},"http.route":{},"fastapi.route.name":{},"fastapi.route.operation_id":{},"values":{"type":"object"},"errors":{"type":"array"}}}',
},
},
{
Expand Down Expand Up @@ -1903,9 +1913,10 @@ def test_request_hooks_with_send_receive_spans(exporter: TestExporter):
'http.route': '/echo_body',
'attr_key': 'attr_val',
'fastapi.route.name': 'echo_body',
'logfire.null_args': ('fastapi.route.operation_id',),
'fastapi.arguments.values': '{}',
'fastapi.arguments.errors': '[]',
'logfire.json_schema': '{"type":"object","properties":{"attr_key":{},"fastapi.arguments.values":{"type":"object"},"fastapi.arguments.errors":{"type":"array"}}}',
'logfire.json_schema': '{"type":"object","properties":{"attr_key":{},"fastapi.route.name":{},"fastapi.route.operation_id":{},"fastapi.arguments.values":{"type":"object"},"fastapi.arguments.errors":{"type":"array"}}}',
'http.status_code': 200,
'http.response.status_code': 200,
},
Expand Down Expand Up @@ -1935,7 +1946,7 @@ def test_websocket(client: TestClient, exporter: TestExporter) -> None:
'fastapi.route.name': 'websocket_endpoint',
'http.route': '/ws/{name}',
'logfire.level_num': 5,
'logfire.json_schema': '{"type":"object","properties":{"fastapi.route.name":{},"http.route":{}}}',
'logfire.json_schema': '{"type":"object","properties":{"http.route":{},"fastapi.route.name":{}}}',
},
},
{
Expand Down Expand Up @@ -2051,6 +2062,7 @@ def test_websocket(client: TestClient, exporter: TestExporter) -> None:
'client.port': 50000,
'http.route': '/ws/{name}',
'fastapi.route.name': 'websocket_endpoint',
'logfire.json_schema': '{"type":"object","properties":{"fastapi.route.name":{}}}',
'http.status_code': 200,
'http.response.status_code': 200,
},
Expand Down