Skip to content

Commit 88280ba

Browse files
committed
refactor: align telemetry signals with the latest OTel semantic conventions
- move custom attribute keys out of reserved OTel namespaces to flow.* prefixes - define official keys once in shared SemConvAttributes/SemConvMetrics/CacheAttributes (flow-php/telemetry) - HTTP spans: client name is bare {method}, server.port always set; server span gains url.query/user_agent.original, drops url.full - messenger: destination = transport, remove metrics_duration_unit option and enum, duration fixed at seconds - phpunit: official test.* registry keys, new test.suite.run.status, durations ms→s - UCUM units (By, {operation}, {row}, {test}, {row}/s) and low-cardinality span names (filesystem.read/write, cache.set/delete/clear); upgrading.md migration tables
1 parent a74b1a4 commit 88280ba

110 files changed

Lines changed: 2097 additions & 883 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

documentation/components/bridges/phpunit-telemetry-bridge.md

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -258,21 +258,24 @@ When enabled, the extension creates spans for:
258258

259259
Each span includes attributes like:
260260

261-
- `test.suite` - Test suite name
262-
- `test.id` - Test identifier
263-
- `test.name` - Test name
264-
- `test.class` - Test class name
265-
- `test.method` - Test method name
266-
- `test.status` - Test result status (passed, failed, errored, skipped, incomplete)
261+
- `test.suite.name` - Test suite name
262+
- `test.suite.run.status` - Suite outcome (success, failure, skipped)
263+
- `test.case.name` - Test name
264+
- `test.case.result.status` - Test result status (passed, failed, errored, skipped, incomplete)
265+
- `flow.phpunit.test.id` - Test identifier
266+
- `flow.phpunit.test.class` - Test class name
267+
- `flow.phpunit.test.method` - Test method name
268+
- `flow.phpunit.test.memory.peak` / `flow.phpunit.test.memory.delta` - Memory usage in bytes
267269

268270
### Metrics
269271

270272
When enabled, the extension records:
271273

272-
- `flow.phpunit.suite.duration` - Histogram of suite execution time
273-
- `flow.phpunit.suite.test_count` - Counter of tests per suite
274-
- `flow.phpunit.test.duration` - Histogram of individual test execution time
275-
- `flow.phpunit.test.count` - Counter of tests by status
274+
- `flow.phpunit.suite.duration` - Histogram of suite execution time (seconds)
275+
- `flow.phpunit.suite.test_count` - Counter of tests per suite (`{test}`)
276+
- `flow.phpunit.test.duration` - Histogram of individual test execution time (seconds)
277+
- `flow.phpunit.test.count` - Counter of tests by status (`{test}`)
278+
- `flow.phpunit.test.memory.peak` / `flow.phpunit.test.memory.delta` - Memory histograms (`By`)
276279

277280
## Running with Docker Compose
278281

documentation/components/bridges/symfony-telemetry-bundle.md

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -780,7 +780,7 @@ per-channel (or per-attribute) severity thresholds are expressible alongside any
780780
These keys exist only for the filter decision — they are never exported (severity already travels in the native OTLP
781781
`severityNumber`/`severityText` fields) and they shadow any user attribute of the same name. Because they live on the
782782
`signal` source, an `all` node that combines severity with another attribute needs that attribute on `signal` too — the
783-
channel's `log.channel` attribute is on `signal` under the default `channel_attribute_target: both`.
783+
channel's `flow.log.channel` attribute is on `signal` under the default `channel_attribute_target: both`.
784784

785785
Keep `error`+ from the `payments` channel but `debug`+ from the `importer` channel (`exclude: false` keeps only
786786
matching records — exactly the case a single global [`severity_filtering`](#pipeline-logger_provider-only) threshold
@@ -797,10 +797,10 @@ flow_telemetry:
797797
matcher:
798798
any:
799799
- all:
800-
- { path: log.channel, mode: equal, value: payments }
800+
- { path: flow.log.channel, mode: equal, value: payments }
801801
- { path: log.severity, mode: greater_than_equal, value: 17 } # error+
802802
- all:
803-
- { path: log.channel, mode: equal, value: importer }
803+
- { path: flow.log.channel, mode: equal, value: importer }
804804
- { path: log.severity, mode: greater_than_equal, value: 5 } # debug+
805805
sink:
806806
type: batching
@@ -1181,8 +1181,8 @@ the request span (same instrumentation scope, kind `INTERNAL`). They are emitted
11811181
exists, so disabling `http_kernel` or excluding the path produces none.
11821182

11831183
- `trace_controller` (default **true**) — the controller **body** execution. The span is named after the
1184-
resolved controller (e.g. `App\Controller\OrderController::import`) and carries `code.namespace`,
1185-
`code.function` and `controller` attributes. It starts after argument resolution and completes at
1184+
resolved controller (e.g. `App\Controller\OrderController::import`) and carries `code.function.name`
1185+
and `flow.symfony.controller` attributes. It starts after argument resolution and completes at
11861186
`kernel.view`/`kernel.response`, so it excludes resolution time and appears in both the OTLP export and the
11871187
Flow Telemetry profiler panel.
11881188
- `trace_controller_resolution` (default **false**) — controller resolution
@@ -1281,7 +1281,6 @@ flow_telemetry:
12811281
context_propagation: true # Propagate context across message boundaries
12821282
trace: true # Emit a per-message span (default true)
12831283
metrics: true # Emit messaging metrics (default true)
1284-
metrics_duration_unit: s # process.duration histogram unit: 's' (OTEL semconv, default) or 'ms'
12851284
```
12861285

12871286
The tracing middleware is injected into **every** message bus automatically — no manual `framework.messenger`
@@ -1328,12 +1327,11 @@ in-flight requests complete in the background.
13281327

13291328
| Metric | Instrument | Unit | Emitted | Attributes |
13301329
|--------------------------------------|------------|---------------|-------------------------------|-----------------------------------------------------------------------------------------------------------------------------------|
1331-
| `messaging.client.consumed.messages` | Counter | `{message}` | once per **received** message | `messaging.system`, `messaging.operation.name=process`, `messaging.destination.name`, `messaging.consumer.group.name` (transport) |
1332-
| `messaging.process.duration` | Histogram | `s` (or `ms`) | per **received** message | same as above, plus `error.type` when the handler fails |
1333-
| `messaging.client.sent.messages` | Counter | `{message}` | once per **sent** message | `messaging.system`, `messaging.operation.name=send`, `messaging.destination.name` |
1330+
| `messaging.client.consumed.messages` | Counter | `{message}` | once per **received** message | `messaging.system`, `messaging.operation.name=process`, `messaging.destination.name` (transport) |
1331+
| `messaging.process.duration` | Histogram | `s` | per **received** message | same as above, plus `error.type` when the handler fails |
1332+
| `messaging.client.sent.messages` | Counter | `{message}` | once per **sent** message | `messaging.system`, `messaging.operation.name=send` |
13341333

1335-
`metrics_duration_unit` controls the `messaging.process.duration` histogram: `s` (default) uses seconds with the
1336-
OTEL-recommended bucket boundaries; `ms` uses milliseconds with Flow's native histogram buckets.
1334+
`messaging.process.duration` is recorded in seconds with the OTEL-recommended bucket boundaries.
13371335

13381336
#### Twig
13391337

@@ -1550,7 +1548,7 @@ flow_telemetry:
15501548

15511549
Channels let you route different parts of your application to different telemetry loggers — the Flow Telemetry
15521550
equivalent of Monolog channels — without installing Monolog. Each channel is a [named logger](#named-instruments);
1553-
messages emitted through it carry a `log.channel` attribute, so you can filter and group them per channel in your
1551+
messages emitted through it carry a `flow.log.channel` attribute, so you can filter and group them per channel in your
15541552
backend. Where that attribute is placed — the instrumentation scope, every emitted record, or both — is controlled by
15551553
[`channel_attribute_target`](#channel-attribute-placement).
15561554

@@ -1592,10 +1590,10 @@ services:
15921590
**Behavior:**
15931591

15941592
- Each distinct channel is synthesized on demand as `flow.telemetry.<channel>.logger` (+ its PSR-3 wrapper
1595-
`flow.telemetry.<channel>.logger.psr3`), carrying a `log.channel: <channel>` attribute placed per
1593+
`flow.telemetry.<channel>.logger.psr3`), carrying a `flow.log.channel: <channel>` attribute placed per
15961594
[`channel_attribute_target`](#channel-attribute-placement) — unless a logger of that name already exists, which is
15971595
then
1598-
reused untouched (so the `log.channel` attribute is only added to loggers the bundle creates).
1596+
reused untouched (so the `flow.log.channel` attribute is only added to loggers the bundle creates).
15991597
- To route a service to the bundle's main logger, use the `default` channel (`#[WithTelemetryChannel('default')]`).
16001598
A `default` logger always exists, so it is reused as-is rather than re-created. Every channel name — including `app`,
16011599
which carries no special meaning here — behaves the same way.
@@ -1605,7 +1603,7 @@ services:
16051603
- On a tagged service, an explicit `@logger` reference is rewritten to the channel logger as well — in both constructor
16061604
arguments and method calls (e.g. `setLogger()`), preserving the reference's invalid-behavior flag.
16071605
- A channel already declared under `loggers` is **not** overwritten, so you can customise its `version`, `schema_url`,
1608-
or `attributes`. Because declaring it opts out of synthesis, set `log.channel` yourself if you want it:
1606+
or `attributes`. Because declaring it opts out of synthesis, set `flow.log.channel` yourself if you want it:
16091607

16101608
```yaml
16111609
flow_telemetry:
@@ -1614,7 +1612,7 @@ flow_telemetry:
16141612
version: '1.0.0'
16151613
attributes:
16161614
scope:
1617-
log.channel: events # not auto-added for a declared logger — set it explicitly
1615+
flow.log.channel: events # not auto-added for a declared logger — set it explicitly
16181616
team: checkout
16191617
```
16201618

@@ -1636,7 +1634,7 @@ flow_telemetry:
16361634
```
16371635

16381636
Each framework channel becomes `flow.telemetry.<channel>.logger` (e.g. `flow.telemetry.router.logger`,
1639-
`flow.telemetry.http_client.logger`), carries the `log.channel` attribute (placed per
1637+
`flow.telemetry.http_client.logger`), carries the `flow.log.channel` attribute (placed per
16401638
[`channel_attribute_target`](#channel-attribute-placement)), and gets the
16411639
`LoggerInterface $<channel>Logger` / `Logger $<channel>Logger` autowiring aliases — the same treatment as an explicitly
16421640
tagged service. A channel you declare under `loggers` still wins, so you can customise any framework channel's scope.
@@ -1646,7 +1644,7 @@ regardless of this flag.
16461644

16471645
#### Channel Attribute Placement
16481646

1649-
The synthesized `log.channel` attribute can be attached to the instrumentation **scope**, to every emitted **signal**
1647+
The synthesized `flow.log.channel` attribute can be attached to the instrumentation **scope**, to every emitted **signal**
16501648
(log record), or to **both**. `channel_attribute_target` controls this for **all** synthesized channel loggers —
16511649
framework-captured and `#[WithTelemetryChannel]` alike:
16521650

@@ -1655,15 +1653,15 @@ flow_telemetry:
16551653
channel_attribute_target: both # both (default) | scope | signal
16561654
```
16571655

1658-
- **`scope`** — `log.channel` sits on the instrumentation scope. Filter by channel with an
1656+
- **`scope`** — `flow.log.channel` sits on the instrumentation scope. Filter by channel with an
16591657
[`attribute_filtering`](#attribute_filtering) processor using `sources: [scope]`. Not present on individual records.
1660-
- **`signal`** — `log.channel` is merged into every emitted record, so it is visible per-record and filterable with the
1658+
- **`signal`** — `flow.log.channel` is merged into every emitted record, so it is visible per-record and filterable with the
16611659
default `sources: [signal]` (the closest match to how Monolog stamps the channel on each record).
16621660
- **`both`** (default) — placed on the scope *and* every record, so it is filterable either way at the cost of storing
16631661
the attribute on each record.
16641662

16651663
This only governs channels the bundle synthesizes; a channel you declare yourself under `loggers` opts out of synthesis,
1666-
so set `log.channel` explicitly on whichever of `attributes.scope` / `attributes.signal` you want.
1664+
so set `flow.log.channel` explicitly on whichever of `attributes.scope` / `attributes.signal` you want.
16671665

16681666
> [!NOTE]
16691667
> `capture_framework_channels` rewrites the `logger` reference on framework services to their channel logger. A

documentation/components/core/telemetry.md

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -201,13 +201,14 @@ $transport = otlp_grpc_transport(endpoint: 'localhost:4317');
201201

202202
Every DataFrame execution creates a root span with the following attributes:
203203

204-
| Attribute | Description |
205-
|------------------------------|-----------------------------------------------|
206-
| `dataframe.id` | Unique identifier for the DataFrame execution |
207-
| `rows.total` | Total number of rows processed |
208-
| `rows.throughput.per_second` | Processing throughput |
209-
| `memory.min.mb` | Minimum memory consumption during execution |
210-
| `memory.max.mb` | Maximum memory consumption during execution |
204+
| Attribute | Description |
205+
|---------------------------------------|-----------------------------------------------|
206+
| `flow.etl.dataframe.id` | Unique identifier for the DataFrame execution |
207+
| `flow.etl.dataframe.name` | Configured DataFrame name |
208+
| `flow.etl.rows.total` | Total number of rows processed |
209+
| `flow.etl.rows.throughput.per_second` | Processing throughput |
210+
| `flow.etl.memory.min` | Minimum memory consumption during execution (MB) |
211+
| `flow.etl.memory.max` | Maximum memory consumption during execution (MB) |
211212

212213
When `trace_loading` is enabled, child spans are created for each loader with:
213214

@@ -225,10 +226,10 @@ When `trace_transformations` is enabled, child spans are created for each transf
225226

226227
When `collect_metrics` is enabled:
227228

228-
| Metric | Type | Description |
229-
|-----------------------------|------------|------------------------------------|
230-
| `rows.processed.total` | Counter | Cumulative count of processed rows |
231-
| `rows.processed.throughput` | Throughput | Rows processed per time unit |
229+
| Metric | Type | Unit | Description |
230+
|----------------------------|------------|------------|------------------------------------|
231+
| `flow.etl.rows.processed` | Counter | `{row}` | Cumulative count of processed rows |
232+
| `flow.etl.rows.throughput` | Throughput | `{row}/s` | Rows processed per second |
232233

233234
### Logs
234235

documentation/components/libs/filesystem.md

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -232,15 +232,18 @@ automatically wrap all mounted filesystems with telemetry.
232232

233233
**Spans:**
234234

235-
- `SourceStream` - spans the lifecycle of a read stream from creation to close
236-
- `DestinationStream` - spans the lifecycle of a write stream from creation to close
235+
- `filesystem.read` - spans the lifecycle of a read stream from creation to close
236+
- `filesystem.write` - spans the lifecycle of a write stream from creation to close
237+
238+
Spans carry `flow.filesystem.path.uri`, `flow.filesystem.protocol`, `flow.filesystem.stream.type` and, on close,
239+
`flow.filesystem.bytes.total_read` / `flow.filesystem.bytes.total_written`.
237240

238241
**Metrics:**
239242

240-
- `filesystem.source.bytes_read` - total bytes read from source streams
241-
- `filesystem.source.operations` - number of read operations
242-
- `filesystem.destination.bytes_written` - total bytes written to destination streams
243-
- `filesystem.destination.operations` - number of write operations
243+
- `flow.filesystem.read.size` (`By`) - total bytes read from source streams
244+
- `flow.filesystem.read.operations` (`{operation}`) - number of read operations
245+
- `flow.filesystem.write.size` (`By`) - total bytes written to destination streams
246+
- `flow.filesystem.write.operations` (`{operation}`) - number of write operations
244247

245248
Metadata operations (list, status, rm, mv) are logged but do not create spans.
246249

documentation/components/libs/postgresql/client-telemetry.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -174,10 +174,10 @@ Spans include attributes following [OpenTelemetry database semantic conventions]
174174

175175
### Transaction-Specific Attributes
176176

177-
| Attribute | Description | Example |
178-
|---------------------------------|-------------------------------|----------------|
179-
| `db.transaction.nesting_level` | Current nesting depth | `1`, `2` |
180-
| `db.transaction.savepoint` | Savepoint name (nested txns) | `savepoint_1` |
177+
| Attribute | Description | Example |
178+
|--------------------------------------|-------------------------------|----------------|
179+
| `flow.db.transaction.nesting_level` | Current nesting depth | `1`, `2` |
180+
| `flow.db.transaction.savepoint` | Savepoint name (nested txns) | `savepoint_1` |
181181

182182
## Security Considerations
183183

0 commit comments

Comments
 (0)