Skip to content

Commit de53e54

Browse files
committed
fix(traces): a before_span_send entry that is not callable turns tracing off
Warning and exporting without the entry was the opposite of what a raising hook does, and the opposite of the fail-closed rationale in the client. The resolver now raises, so the client reports the error and leaves tracing off rather than exporting spans the entry was meant to redact.
1 parent fb9bfdc commit de53e54

3 files changed

Lines changed: 16 additions & 26 deletions

File tree

‎posthog/test/tracing/test_config.py‎

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -256,15 +256,14 @@ def hook(span):
256256
assert resolved.before_span_send == (hook,)
257257
assert not caplog.records
258258

259-
def test_ignores_and_warns_about_entries_that_are_not_callable(self, caplog):
260-
caplog.set_level("WARNING", logger="posthog")
261-
259+
def test_rejects_an_entry_that_is_not_callable_rather_than_exporting_unhooked(
260+
self,
261+
):
262262
def hook(span):
263263
return span
264264

265-
resolved = resolve_traces_config({"before_span_send": ["scrub", hook]})
266-
assert resolved.before_span_send == (hook,)
267-
assert any("1 of 2" in r.getMessage() for r in caplog.records)
265+
with pytest.raises(ValueError, match="not callable"):
266+
resolve_traces_config({"before_span_send": ["scrub", hook]})
268267

269268
def test_a_hook_whose_truthiness_raises_is_still_resolved(self):
270269
class Hook:

‎posthog/test/tracing/test_pipeline.py‎

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -761,13 +761,6 @@ def third(span):
761761
assert calls == ["first", "second"]
762762
assert queued(pipeline) == []
763763

764-
def test_ignores_non_callable_entries_and_exports_unhooked(self, caplog):
765-
caplog.set_level("WARNING", logger="posthog")
766-
pipeline, _, _ = make(before_span_send=["not a hook", None])
767-
pipeline.start_span("a").end()
768-
assert len(queued(pipeline)) == 1
769-
assert any("not callable" in r.getMessage() for r in caplog.records)
770-
771764
def test_a_hook_can_remove_the_stacktrace(self):
772765
def strip_stacks(span):
773766
for event in span["events"]:

‎posthog/tracing/_config.py‎

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -123,26 +123,24 @@ def _usable_resource_attributes(value: Any) -> Dict[str, Any]:
123123

124124

125125
def _resolve_before_span_send(value: Any) -> Tuple[Callable, ...]:
126-
"""Keep only the callable hooks, in order.
126+
"""The hooks, in order. Raises for an entry that is not callable.
127127
128-
Anything else is dropped rather than called: a hook that raises drops every
129-
span, which would leave tracing silently off.
128+
The hook is the scrubbing point, so a broken entry turns tracing off
129+
rather than exporting spans the entry was meant to redact. The client
130+
reports the error and leaves tracing off for the life of the client.
130131
"""
131132
if value is None:
132133
return ()
133134
supplied = list(value) if isinstance(value, (list, tuple)) else [value]
134135
# `[enabled and scrub]` yields None or False: no hook, rather than a broken one.
135136
supplied = [hook for hook in supplied if hook is not None and hook is not False]
136-
hooks = tuple(hook for hook in supplied if callable(hook))
137-
if len(hooks) != len(supplied):
138-
log.warning(
139-
"Ignoring %s of %s traces before_span_send entries that are not callable. "
140-
"Spans export without them, so whatever they were redacting is not "
141-
"redacted.",
142-
len(supplied) - len(hooks),
143-
len(supplied),
144-
)
145-
return hooks
137+
for hook in supplied:
138+
if not callable(hook):
139+
raise ValueError(
140+
"traces before_span_send entry {!r} is not callable; tracing is off "
141+
"rather than exporting spans it was meant to redact".format(hook)
142+
)
143+
return tuple(supplied)
146144

147145

148146
def resolve_traces_config(

0 commit comments

Comments
 (0)