Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -468,10 +468,30 @@ defmodule OpentelemetryBandit do
|> Tracer.set_attributes()
end

maybe_update_span_name(conn)
Comment thread
lunks marked this conversation as resolved.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This couldn't be moved to the start event?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. We only have the URL path, not the route name. call_plug! sets it, but the span has already been started:
https://github.com/mtrudel/bandit/blob/1.10.3/lib/bandit/pipeline.ex#L38-L42

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tsloughter I am gonna defer to you, I am not sure if changing midway the span name is a major concern or not

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you folks think there's a better approach to accomplish a similar result, let me know.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry. This is fine.


Tracer.end_span()
Ctx.clear()
end

defp maybe_update_span_name(conn) do
case conn.private do
%{plug_route: {route, _fun}} ->
Comment thread
lunks marked this conversation as resolved.
method = sanitize_method(conn.method)
Tracer.update_name("#{method} #{route}")
Comment thread
lunks marked this conversation as resolved.
Tracer.set_attribute(HTTPAttributes.http_route(), route)

_ ->
:ok
end
end

defp sanitize_method(method) do
if parse_method(method) == HTTPAttributes.http_request_method_values().other,
do: "HTTP",
else: method
end

@doc false
def handle_request_exception(meta, config) do
Tracer.set_status(OpenTelemetry.status(:error, ""))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,63 @@ defmodule OpentelemetryBanditTest do
assert [] = :otel_events.list(events)
end

test "updates span name and sets http.route from conn.private[:plug_route]" do
OpentelemetryBandit.setup()
port = start_server()

Req.get("http://localhost:#{port}/with_route")

assert_receive {:span,
span(
name: "GET /items/:id",
kind: :server,
attributes: span_attrs
)}

assert Map.get(:otel_attributes.map(span_attrs), HTTPAttributes.http_route()) ==
"/items/:id"
end

test "updates span name with POST method and route" do
OpentelemetryBandit.setup()
port = start_server()

Req.post("http://localhost:#{port}/post_with_route", body: "")

assert_receive {:span,
span(
name: "POST /items",
kind: :server,
attributes: span_attrs
)}

attrs = :otel_attributes.map(span_attrs)
assert Map.get(attrs, HTTPAttributes.http_route()) == "/items"
assert Map.get(attrs, HTTPAttributes.http_response_status_code()) == 201
end

test "updates span name with route on 5xx response" do
OpentelemetryBandit.setup()
port = start_server()

Req.get("http://localhost:#{port}/error_with_route", retry: false)

expected_status = OpenTelemetry.status(:error, "")

assert_receive {:span,
span(
name: "GET /items/:id",
kind: :server,
attributes: span_attrs,
status: ^expected_status
)}

attrs = :otel_attributes.map(span_attrs)
assert Map.get(attrs, HTTPAttributes.http_route()) == "/items/:id"
assert Map.get(attrs, HTTPAttributes.http_response_status_code()) == 500
assert Map.get(attrs, ErrorAttributes.error_type()) == "500"
end

test "with halted request" do
OpentelemetryBandit.setup()
port = start_server()
Expand Down Expand Up @@ -522,6 +579,25 @@ defmodule OpentelemetryBanditTest do
conn |> send_resp(200, "OK")
end

def with_route(conn) do
conn
|> put_private(:plug_route, {"/items/:id", fn -> nil end})
|> send_resp(200, "OK")
end

def post_with_route(conn) do
conn
|> put_private(:plug_route, {"/items", fn -> nil end})
|> send_resp(201, "Created")
end

def error_with_route(conn) do
conn
|> put_private(:plug_route, {"/items/:id", fn -> nil end})
|> send_resp(500, "Internal Server Error")
|> halt()
end

def with_body(conn) do
conn
|> put_resp_content_type("application/json")
Expand Down