Skip to content

Derive the collector-mode parameter options - #287

Open
unflxw wants to merge 12 commits into
mainfrom
derive-collector-params-options
Open

unflxw wants to merge 12 commits into
mainfrom
derive-collector-params-options

Conversation

@unflxw

@unflxw unflxw commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

Derive the collector-mode parameter options

Collector mode keeps the request payload, the function parameters and
the query parameters in an attribute each, with an option per kind to
filter it and to suppress it. The package ignores filter_parameters
and send_params there, so an application that filtered a password in
agent mode stops filtering it under a collector, and one that turned
parameter reporting off reports parameters again. Derive the six options
from the two whenever the application set one of the two and left the
replacement alone. Derived values go in a configuration source of their
own, ranked below every other source, so an option the application names
wins over one derived for it.

Deprecate the params options in collector mode

Collector mode reads filter_parameters and send_params to derive the
six options that replace them, and those six name which kind of
parameters they mean. Warn at startup that the option is deprecated in
collector mode, and name the value to set for each option that replaces
it, which AppSignal has already derived in order to fall back to it. The
warning that the two are ignored goes with it, along with the one
pointing filter_attributes at filter_parameters, which nothing
derives a value for.

Add a helper for each kind of parameters

Collector mode keeps the request payload, the function parameters and
the query parameters in an attribute each, and the package writes to the
request payload. An application reporting a job's arguments has
filter_request_payload and send_request_payload applied to them,
rather than the two options that name function parameters. Add a helper
per kind, so a caller says which kind it is reporting. Agent mode has
one slot for all three, and every helper writes there.

Say which parameters our instrumentation reports

The Django and Flask instrumentation hands parameters to a helper that
names no kind, so a request's query string is reported as a request
payload and filtered by the options that name the payload. Call the
helper that names the kind instead. Django reports two kinds at once,
and agent mode has one place for them, so it hands both over under the
keys that say which is which. Flask reports one kind, so it hands that
over on its own, without the args key.

Move the warn-once helper to a module of its own

_Once calls a function the first time it is called and never again,
which is how the package emits a deprecation warning without repeating
it. It lives in the heartbeat module, which pulls in check_in, so
anything else that warns once imports the whole of that. Move it and the
warning function beside it into a module of their own.

Deprecate set_params when a collector is used

The set_params helper reports parameters without naming their kind, so
collector mode reports whatever it is given as the request payload, with
the options that name a request payload applied to it. Warn the first
time it is called in collector mode, naming the three helpers that say
which kind they report. Agent mode keeps every kind in one place, so the
warning is limited to collector mode.

Name the source of each config option in diagnose

The diagnose report prints every configuration option with the value it
holds. Reading it means guessing whether a value is a default or came
from the environment, which is the first thing somebody checks when an
option is not what they set. Print the source beside the value, and, for
an option more than one source holds, list each source with what it
holds in the order they are merged. The order comes from the constant
the merge walks.

Report the configured response headers

The HTTP instrumentation reports a header only when the header is named
in the environment variable for its kind. The package sets the request
variable from request_headers and leaves the response one unset, so no
http.response.header.* attribute is produced and response_headers
has nothing to apply to. Set both variables from the option that names
them.

Name a header the way the convention does

The collector keeps a header whose arriving name is in the allowlist the
package sends, compared by exact match. That name follows the
OpenTelemetry semantic convention: the header's own name, lowercased,
with its dashes kept. The request_headers and response_headers
options hold whatever the application wrote, and someone naming the
Content-Type header is as likely to write it that way as
content-type.

Normalize a header's name to the convention's spelling before sending
it, for the two options and for the name set_header is given in
collector mode.

Read an empty list option from the environment

A list option read from the environment is split on commas, and
splitting an empty string gives a list holding one empty string. An
empty variable is how a list is emptied from the environment, so
APPSIGNAL_RESPONSE_HEADERS= names one header that cannot exist. Read
an empty variable as an empty list, so that the diagnose report shows
the value the application set, and an option left at its default reads
as unchanged.

Default the list options to empty lists

Twelve list options have no default and hold None until they are
configured, while the Ruby gem and the documentation for these options
give them an empty list. Default them to an empty list. The agent is
told about them as an empty string rather than an unset variable, and
the collector receives an empty list rather than no attribute, which is
what stops it reporting every response header it receives.

Point the diagnose tests at the derived source

The diagnose integration tests live in a repository shared with the
other language integrations, and assert the full list of sources a
report carries. The package merges a derived source, so the
expectations there carry it too.

@backlog-helper

Copy link
Copy Markdown

Hi @unflxw,

We've found some issues with your Pull Request.

  • This Pull Request is missing labels. Please add labels to help identify types of Pull Requests. - (More info)

New issue guide | Backlog management | Rules | Feedback

@unflxw
unflxw force-pushed the derive-collector-params-options branch 10 times, most recently from c7d97f7 to 9169860 Compare September 16, 2026 18:12
@unflxw
unflxw marked this pull request as ready for review September 16, 2026 18:31
Collector mode keeps the request payload, the function parameters and
the query parameters in an attribute each, with an option per kind to
filter it and to suppress it. The package ignores `filter_parameters`
and `send_params` there, so an application that filtered a password in
agent mode stops filtering it under a collector, and one that turned
parameter reporting off reports parameters again. Derive the six options
from the two whenever the application set one of the two and left the
replacement alone. Derived values go in a configuration source of their
own, ranked below every other source, so an option the application names
wins over one derived for it.
Collector mode reads `filter_parameters` and `send_params` to derive the
six options that replace them, and those six name which kind of
parameters they mean. Warn at startup that the option is deprecated in
collector mode, and name the value to set for each option that replaces
it, which AppSignal has already derived in order to fall back to it. The
warning that the two are ignored goes with it, along with the one
pointing `filter_attributes` at `filter_parameters`, which nothing
derives a value for.
Collector mode keeps the request payload, the function parameters and
the query parameters in an attribute each, and the package writes to the
request payload. An application reporting a job's arguments has
`filter_request_payload` and `send_request_payload` applied to them,
rather than the two options that name function parameters. Add a helper
per kind, so a caller says which kind it is reporting. Agent mode has
one slot for all three, and every helper writes there.
The Django and Flask instrumentation hands parameters to a helper that
names no kind, so a request's query string is reported as a request
payload and filtered by the options that name the payload. Call the
helper that names the kind instead. Django reports two kinds at once,
and agent mode has one place for them, so it hands both over under the
keys that say which is which. Flask reports one kind, so it hands that
over on its own, without the `args` key.
`_Once` calls a function the first time it is called and never again,
which is how the package emits a deprecation warning without repeating
it. It lives in the heartbeat module, which pulls in `check_in`, so
anything else that warns once imports the whole of that. Move it and the
warning function beside it into a module of their own.
The `set_params` helper reports parameters without naming their kind, so
collector mode reports whatever it is given as the request payload, with
the options that name a request payload applied to it. Warn the first
time it is called in collector mode, naming the three helpers that say
which kind they report. Agent mode keeps every kind in one place, so the
warning is limited to collector mode.
The diagnose report prints every configuration option with the value it
holds. Reading it means guessing whether a value is a default or came
from the environment, which is the first thing somebody checks when an
option is not what they set. Print the source beside the value, and, for
an option more than one source holds, list each source with what it
holds in the order they are merged. The order comes from the constant
the merge walks.
The HTTP instrumentation reports a header only when the header is named
in the environment variable for its kind. The package sets the request
variable from `request_headers` and leaves the response one unset, so no
`http.response.header.*` attribute is produced and `response_headers`
has nothing to apply to. Set both variables from the option that names
them.
The collector keeps a header whose arriving name is in the allowlist the
package sends, compared by exact match. That name follows the
OpenTelemetry semantic convention: the header's own name, lowercased,
with its dashes kept. The `request_headers` and `response_headers`
options hold whatever the application wrote, and someone naming the
`Content-Type` header is as likely to write it that way as
`content-type`.

Normalize a header's name to the convention's spelling before sending
it, for the two options and for the name `set_header` is given in
collector mode.
A list option read from the environment is split on commas, and
splitting an empty string gives a list holding one empty string. An
empty variable is how a list is emptied from the environment, so
`APPSIGNAL_RESPONSE_HEADERS=` names one header that cannot exist. Read
an empty variable as an empty list, so that the diagnose report shows
the value the application set, and an option left at its default reads
as unchanged.
Twelve list options have no default and hold `None` until they are
configured, while the Ruby gem and the documentation for these options
give them an empty list. Default them to an empty list. The agent is
told about them as an empty string rather than an unset variable, and
the collector receives an empty list rather than no attribute, which is
what stops it reporting every response header it receives.
The diagnose integration tests live in a repository shared with the
other language integrations, and assert the full list of sources a
report carries. The package merges a `derived` source, so the
expectations there carry it too.
@unflxw
unflxw force-pushed the derive-collector-params-options branch from 9169860 to 6f89bc0 Compare September 17, 2026 14:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant