Skip to content

Commit 3955ae0

Browse files
authored
Merge pull request #266 from appsignal/fix-collector-mode-helpers
Fix collector mode helpers
2 parents 0650df9 + 2b413fb commit 3955ae0

5 files changed

Lines changed: 116 additions & 3 deletions

File tree

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
bump: patch
3+
type: fix
4+
---
5+
6+
Emit the `appsignal.action_name` attribute instead of `appsignal.root_name`
7+
when `set_root_name` is used in collector mode. The collector only reads the
8+
action name from `appsignal.action_name`, so root names set this way were
9+
previously ignored, falling back to the OpenTelemetry span name.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
bump: patch
3+
type: fix
4+
---
5+
6+
Emit the `db.system.name` and `db.query.text` semantic-convention attributes
7+
instead of `appsignal.sql_body` when `set_sql_body` is used in collector
8+
mode. This fixes SQL query sanitization when using the experimental collector
9+
mode.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
bump: patch
3+
type: fix
4+
---
5+
6+
Update the OpenTelemetry span name directly when `set_name` is used in
7+
collector mode, instead of setting the `appsignal.name` attribute. The
8+
collector uses the span name as-is, so names set this way were previously
9+
ignored.

src/appsignal/tracing.py

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,24 @@ def _set_prefixed_attribute(
4444
_set_attribute(f"{prefix}.{suffix}", value, span)
4545

4646

47+
def _update_span_name(name: str, span: Span | None = None) -> None:
48+
span = span or trace.get_current_span()
49+
50+
if span is trace.INVALID_SPAN:
51+
logger.debug("There is no active span, cannot set `name`")
52+
return
53+
54+
span.update_name(name)
55+
56+
57+
def _use_collector() -> bool:
58+
# Imported here to avoid an import cycle with the client module.
59+
from .client import Client
60+
61+
config = Client.config()
62+
return config is not None and config.should_use_collector()
63+
64+
4765
def set_params(params: Any, span: Span | None = None) -> None:
4866
_set_serialised_attribute("appsignal.request.parameters", params, span)
4967

@@ -65,7 +83,13 @@ def set_header(header: str, value: Any, span: Span | None = None) -> None:
6583

6684

6785
def set_name(name: str, span: Span | None = None) -> None:
68-
_set_attribute("appsignal.name", name, span)
86+
# The collector uses the span name directly, and its enrichers cannot
87+
# override it, so update the span name. The agent derives the name from
88+
# semantic conventions, so set an attribute it honors as a hard override.
89+
if _use_collector():
90+
_update_span_name(name, span)
91+
else:
92+
_set_attribute("appsignal.name", name, span)
6993

7094

7195
def set_category(category: str, span: Span | None = None) -> None:
@@ -77,15 +101,27 @@ def set_body(body: str, span: Span | None = None) -> None:
77101

78102

79103
def set_sql_body(body: str, span: Span | None = None) -> None:
80-
_set_attribute("appsignal.sql_body", body, span)
104+
# The collector sanitizes SQL via the `db.query.text` attribute, gated on
105+
# `db.system.name` being set to a recognized SQL system. The agent
106+
# sanitizes via the `appsignal.sql_body` magic attribute.
107+
if _use_collector():
108+
_set_attribute("db.system.name", "other_sql", span)
109+
_set_attribute("db.query.text", body, span)
110+
else:
111+
_set_attribute("appsignal.sql_body", body, span)
81112

82113

83114
def set_namespace(namespace: str, span: Span | None = None) -> None:
84115
_set_attribute("appsignal.namespace", namespace, span)
85116

86117

87118
def set_root_name(root_name: str, span: Span | None = None) -> None:
88-
_set_attribute("appsignal.root_name", root_name, span)
119+
# The collector reads the action name from `appsignal.action_name`, while
120+
# the agent reads it from `appsignal.root_name`.
121+
if _use_collector():
122+
_set_attribute("appsignal.action_name", root_name, span)
123+
else:
124+
_set_attribute("appsignal.root_name", root_name, span)
89125

90126

91127
def set_error(error: Exception, span: Span | None = None) -> None:

tests/test_tracing.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
set_sql_body,
2020
set_tag,
2121
)
22+
from appsignal.client import Client
2223

2324

2425
tracer = trace.get_tracer("appsignal/tests")
@@ -64,6 +65,55 @@ def test_set_attributes(spans):
6465
}
6566

6667

68+
def test_set_root_name_collector_mode(spans):
69+
Client(
70+
active=True,
71+
name="MyApp",
72+
push_api_key="0000-0000-0000-0000",
73+
collector_endpoint="https://custom-endpoint.appsignal.com",
74+
)
75+
76+
with tracer.start_as_current_span("span"):
77+
set_root_name("Root name")
78+
79+
attributes = dict(spans()[0].attributes)
80+
assert attributes["appsignal.action_name"] == "Root name"
81+
assert "appsignal.root_name" not in attributes
82+
83+
84+
def test_set_name_collector_mode(spans):
85+
Client(
86+
active=True,
87+
name="MyApp",
88+
push_api_key="0000-0000-0000-0000",
89+
collector_endpoint="https://custom-endpoint.appsignal.com",
90+
)
91+
92+
with tracer.start_as_current_span("span"):
93+
set_name("New name")
94+
95+
span = spans()[0]
96+
assert span.name == "New name"
97+
assert "appsignal.name" not in dict(span.attributes)
98+
99+
100+
def test_set_sql_body_collector_mode(spans):
101+
Client(
102+
active=True,
103+
name="MyApp",
104+
push_api_key="0000-0000-0000-0000",
105+
collector_endpoint="https://custom-endpoint.appsignal.com",
106+
)
107+
108+
with tracer.start_as_current_span("span"):
109+
set_sql_body("SELECT * FROM users")
110+
111+
attributes = dict(spans()[0].attributes)
112+
assert attributes["db.system.name"] == "other_sql"
113+
assert attributes["db.query.text"] == "SELECT * FROM users"
114+
assert "appsignal.sql_body" not in attributes
115+
116+
67117
def test_set_attributes_on_span(spans):
68118
with tracer.start_as_current_span("parent span") as span:
69119
with tracer.start_as_current_span("child span"):

0 commit comments

Comments
 (0)