diff --git a/.nix/pkgs/flow-php/package.nix b/.nix/pkgs/flow-php/package.nix
index 246b78f28..a6455c9bf 100644
--- a/.nix/pkgs/flow-php/package.nix
+++ b/.nix/pkgs/flow-php/package.nix
@@ -11,7 +11,8 @@
with-blackfire ? false,
with-pg-query-ext ? false,
with-arrow-ext ? false,
- with-grpc ? false
+ with-grpc ? false,
+ with-protobuf ? true
}:
let
@@ -27,7 +28,6 @@ let
(php-lz4.override { inherit php; })
(php-snappy.override { inherit php; })
(php-zstd.override { inherit php; })
- protobuf
xmlreader
xmlwriter
zlib
@@ -38,6 +38,7 @@ let
++ (if with-pg-query-ext then [(php-pg-query-ext.override { inherit php; })] else [])
++ (if with-arrow-ext then [(php-arrow-ext.override { inherit php; })] else [])
++ (if with-grpc then [grpc] else [])
+ ++ (if with-protobuf then [protobuf] else [])
);
in
flowPHP.buildEnv {
diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php
index 38548a9a3..0003c74c8 100644
--- a/.php-cs-fixer.php
+++ b/.php-cs-fixer.php
@@ -33,6 +33,8 @@
])
->exclude([
'Flow/Parquet/ThriftModel',
+ 'Opentelemetry',
+ 'GPBMetadata',
'Flow/CLI/Tests/Integration',
'Flow/ETL/Tests/Unit/Loader',
'Flow/ETL/Tests/Unit/Exception',
diff --git a/compose.yml.dist b/compose.yml.dist
index 9362388cc..f39a0878a 100644
--- a/compose.yml.dist
+++ b/compose.yml.dist
@@ -107,7 +107,7 @@ services:
- "8888:8888"
volumes:
- "./docker/otel-collector-config.yaml:/etc/otel-collector-config.yaml:ro"
- - "./var/otel:/var/otel"
+ - "./.scratchpad/migraitons/var/otel:/var/otel"
depends_on:
- aspire
- postgres
diff --git a/composer.json b/composer.json
index 6b122bd5c..fed648cbd 100644
--- a/composer.json
+++ b/composer.json
@@ -58,7 +58,6 @@
"grpc/grpc": "^1.74",
"nikic/php-parser": "^5.3",
"nyholm/psr7": "^1.8",
- "open-telemetry/gen-otlp-protobuf": "^1.8",
"php-http/curl-client": "^2.2",
"php-http/mock-client": "^1.5",
"ramsey/uuid": "^4.5",
@@ -187,6 +186,12 @@
],
"Flow\\ETL\\Adapter\\Doctrine\\": [
"src/adapter/etl-adapter-doctrine/src/Flow/ETL/Adapter/Doctrine"
+ ],
+ "Opentelemetry\\Proto\\": [
+ "src/bridge/telemetry/otlp/src/Opentelemetry/Proto"
+ ],
+ "GPBMetadata\\Opentelemetry\\": [
+ "src/bridge/telemetry/otlp/src/GPBMetadata/Opentelemetry"
]
},
"files": [
@@ -428,6 +433,9 @@
"protoc --php_out=src/lib/postgresql/src --proto_path=src/lib/postgresql/resources/proto pg_query.proto",
"@cs:php:fix"
],
+ "build:telemetry:otlp:protobuf": [
+ "src/bridge/telemetry/otlp/bin/generate-proto.sh"
+ ],
"pre-autoload-dump": [
"Google\\Task\\Composer::cleanup"
],
diff --git a/composer.lock b/composer.lock
index 957fd2b2e..3cf738bda 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "7bf843de65f7d9eea6d21112ca678bef",
+ "content-hash": "126a806538bbe2fdc45647bbe8fa5f56",
"packages": [
{
"name": "async-aws/core",
@@ -859,23 +859,23 @@
},
{
"name": "google/protobuf",
- "version": "v4.33.6",
+ "version": "v5.34.1",
"source": {
"type": "git",
"url": "https://github.com/protocolbuffers/protobuf-php.git",
- "reference": "84b008c23915ed94536737eae46f41ba3bccfe67"
+ "reference": "da52fbc6bb574bfa6693ee2c86f9096f7b7f003b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/84b008c23915ed94536737eae46f41ba3bccfe67",
- "reference": "84b008c23915ed94536737eae46f41ba3bccfe67",
+ "url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/da52fbc6bb574bfa6693ee2c86f9096f7b7f003b",
+ "reference": "da52fbc6bb574bfa6693ee2c86f9096f7b7f003b",
"shasum": ""
},
"require": {
- "php": ">=8.1.0"
+ "php": ">=8.2.0"
},
"require-dev": {
- "phpunit/phpunit": ">=10.5.62 <11.0.0"
+ "phpunit/phpunit": ">=11.5.0 <12.0.0"
},
"suggest": {
"ext-bcmath": "Need to support JSON deserialization"
@@ -897,9 +897,9 @@
"proto"
],
"support": {
- "source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.33.6"
+ "source": "https://github.com/protocolbuffers/protobuf-php/tree/v5.34.1"
},
- "time": "2026-03-18T17:32:05+00:00"
+ "time": "2026-03-19T20:51:56+00:00"
},
{
"name": "guzzlehttp/guzzle",
@@ -5345,69 +5345,6 @@
],
"time": "2024-09-09T07:06:30+00:00"
},
- {
- "name": "open-telemetry/gen-otlp-protobuf",
- "version": "1.9.0",
- "source": {
- "type": "git",
- "url": "https://github.com/opentelemetry-php/gen-otlp-protobuf.git",
- "reference": "a229cf161d42001d64c8f21e8f678581fe1c66b9"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/opentelemetry-php/gen-otlp-protobuf/zipball/a229cf161d42001d64c8f21e8f678581fe1c66b9",
- "reference": "a229cf161d42001d64c8f21e8f678581fe1c66b9",
- "shasum": ""
- },
- "require": {
- "google/protobuf": "^3.22 || ^4.0",
- "php": "^8.0"
- },
- "suggest": {
- "ext-protobuf": "For better performance, when dealing with the protobuf format"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-main": "1.x-dev"
- }
- },
- "autoload": {
- "psr-4": {
- "Opentelemetry\\Proto\\": "Opentelemetry/Proto/",
- "GPBMetadata\\Opentelemetry\\": "GPBMetadata/Opentelemetry/"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "Apache-2.0"
- ],
- "authors": [
- {
- "name": "opentelemetry-php contributors",
- "homepage": "https://github.com/open-telemetry/opentelemetry-php/graphs/contributors"
- }
- ],
- "description": "PHP protobuf files for communication with OpenTelemetry OTLP collectors/servers.",
- "keywords": [
- "Metrics",
- "apm",
- "gRPC",
- "logging",
- "opentelemetry",
- "otel",
- "otlp",
- "protobuf",
- "tracing"
- ],
- "support": {
- "chat": "https://app.slack.com/client/T08PSQ7BQ/C01NFPCV44V",
- "docs": "https://opentelemetry.io/docs/languages/php",
- "issues": "https://github.com/open-telemetry/opentelemetry-php/issues",
- "source": "https://github.com/open-telemetry/opentelemetry-php"
- },
- "time": "2025-10-19T06:44:33+00:00"
- },
{
"name": "php-http/client-common",
"version": "2.7.3",
diff --git a/docker/otel-collector-config.yaml b/docker/otel-collector-config.yaml
index 10f1436df..31a3f8c91 100644
--- a/docker/otel-collector-config.yaml
+++ b/docker/otel-collector-config.yaml
@@ -6,6 +6,13 @@ receivers:
http:
endpoint: 0.0.0.0:4318
+ # OTLP JSON file receiver — picks up JSONL written by flow-php StreamTransport.
+ # Docs: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/otlpjsonfilereceiver
+ otlpjsonfile:
+ include:
+ - /var/otel/*.jsonl
+ start_at: end
+
# PostgreSQL metrics receiver
# Docs: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/postgresqlreceiver
postgresql:
@@ -45,6 +52,16 @@ exporters:
endpoint: aspire:18889
tls:
insecure: true
+ sending_queue:
+ enabled: true
+ storage: file_storage/queue
+ num_consumers: 4
+ queue_size: 10000
+ retry_on_failure:
+ enabled: true
+ initial_interval: 5s
+ max_interval: 30s
+ max_elapsed_time: 0
processors:
# Add service.name to PostgreSQL metrics
@@ -80,9 +97,15 @@ processors:
extensions:
health_check:
endpoint: 0.0.0.0:13133
+ file_storage/queue:
+ directory: /var/lib/otelcol/queue
+ timeout: 10s
+ compaction:
+ directory: /var/lib/otelcol/queue
+ on_start: true
service:
- extensions: [health_check]
+ extensions: [health_check, file_storage/queue]
telemetry:
traces:
sampler:
@@ -138,3 +161,6 @@ service:
logs:
receivers: [otlp]
exporters: [debug, otlp_grpc/aspire]
+ logs/file:
+ receivers: [otlpjsonfile]
+ exporters: [debug, otlp_grpc/aspire]
diff --git a/documentation/components/bridges/monolog-telemetry-bridge.md b/documentation/components/bridges/monolog-telemetry-bridge.md
index 2925e34ee..fe63bf2d2 100644
--- a/documentation/components/bridges/monolog-telemetry-bridge.md
+++ b/documentation/components/bridges/monolog-telemetry-bridge.md
@@ -286,7 +286,7 @@ use function Flow\Telemetry\DSL\{
use function Flow\Bridge\Telemetry\OTLP\DSL\{
otlp_curl_transport,
otlp_json_serializer,
- otlp_log_exporter,
+ otlp_exporter,
};
use function Flow\Bridge\Monolog\Telemetry\DSL\telemetry_handler;
@@ -305,7 +305,7 @@ $transport = otlp_curl_transport(
$telemetry = telemetry(
$resource,
loggerProvider: logger_provider(
- batching_log_processor(otlp_log_exporter($transport))
+ batching_log_processor(otlp_exporter($transport))
),
);
diff --git a/documentation/components/bridges/phpunit-telemetry-bridge.md b/documentation/components/bridges/phpunit-telemetry-bridge.md
index 8eb6b780b..6025d9939 100644
--- a/documentation/components/bridges/phpunit-telemetry-bridge.md
+++ b/documentation/components/bridges/phpunit-telemetry-bridge.md
@@ -41,39 +41,120 @@ Add the extension to your `phpunit.xml.dist`:
| Parameter | Environment variable | Default | Description |
|------------------------|------------------------------------------|-------------------------|----------------------------------------------------------------|
| `service_name` | `FLOW_PHPUNIT_OTEL_SERVICE_NAME` | `phpunit` | Service name reported in telemetry data |
-| `transport` | `FLOW_PHPUNIT_OTEL_TRANSPORT` | `curl` | Transport type: `curl` or `grpc` |
-| `endpoint` | `FLOW_PHPUNIT_OTEL_ENDPOINT` | `http://localhost:4318` | OTLP endpoint URL (for `grpc` use host:port, e.g. `otel:4317`) |
+| `transport` | `FLOW_PHPUNIT_OTEL_TRANSPORT` | `curl` | Transport type: `curl`, `grpc`, or `stream` |
+| `endpoint` | `FLOW_PHPUNIT_OTEL_ENDPOINT` | `http://localhost:4318` | OTLP endpoint URL (curl/grpc) or destination path (stream) |
| `headers` | `FLOW_PHPUNIT_OTEL_HEADERS` | — | Additional headers (see [Authentication](#authentication)) |
| `emit_traces` | `FLOW_PHPUNIT_OTEL_EMIT_TRACES` | `true` | Enable/disable trace emission |
| `emit_metrics` | `FLOW_PHPUNIT_OTEL_EMIT_METRICS` | `true` | Enable/disable metric emission |
| `emit_test_spans` | `FLOW_PHPUNIT_OTEL_EMIT_TEST_SPANS` | `true` | Create individual spans for each test |
| `emit_test_case_spans` | `FLOW_PHPUNIT_OTEL_EMIT_TEST_CASE_SPANS` | `true` | Create spans for test case classes |
+| `batch_size` | `FLOW_PHPUNIT_OTEL_BATCH_SIZE` | `512` | Items per batch for span/metric/log batching processors |
+| `shutdown_timeout_ms` | `FLOW_PHPUNIT_OTEL_SHUTDOWN_TIMEOUT_MS` | `5000` | Wall-clock budget in ms for draining pending requests at shutdown (curl/grpc) |
+| `error_handler` | `FLOW_PHPUNIT_OTEL_ERROR_HANDLER` | `error_log` | How telemetry errors are surfaced (see [Error Handlers](#error-handlers)) |
### Curl transport (`transport=curl`)
-| Parameter | Environment variable | Default | Description |
-|-------------------------|-------------------------------------------|---------|---------------------------------------------|
-| `curl_timeout` | `FLOW_PHPUNIT_OTEL_CURL_TIMEOUT` | `30` | Request timeout in seconds |
-| `curl_connect_timeout` | `FLOW_PHPUNIT_OTEL_CURL_CONNECT_TIMEOUT` | `10` | Connection timeout in seconds |
-| `curl_compression` | `FLOW_PHPUNIT_OTEL_CURL_COMPRESSION` | `false` | Enable automatic response decompression |
-| `curl_follow_redirects` | `FLOW_PHPUNIT_OTEL_CURL_FOLLOW_REDIRECTS` | `true` | Follow HTTP redirects |
-| `curl_max_redirects` | `FLOW_PHPUNIT_OTEL_CURL_MAX_REDIRECTS` | `3` | Maximum number of redirects to follow |
-| `curl_proxy` | `FLOW_PHPUNIT_OTEL_CURL_PROXY` | — | Proxy server URL (e.g. `http://proxy:8080`) |
-| `curl_ssl_verify_peer` | `FLOW_PHPUNIT_OTEL_CURL_SSL_VERIFY_PEER` | `true` | Verify SSL peer certificate |
-| `curl_ssl_verify_host` | `FLOW_PHPUNIT_OTEL_CURL_SSL_VERIFY_HOST` | `true` | Verify SSL host name |
-| `curl_ssl_cert_path` | `FLOW_PHPUNIT_OTEL_CURL_SSL_CERT_PATH` | — | Path to client SSL certificate |
-| `curl_ssl_key_path` | `FLOW_PHPUNIT_OTEL_CURL_SSL_KEY_PATH` | — | Path to client SSL private key |
-| `curl_ca_info_path` | `FLOW_PHPUNIT_OTEL_CURL_CA_INFO_PATH` | — | Path to CA certificate bundle |
-| `curl_serializer` | `FLOW_PHPUNIT_OTEL_CURL_SERIALIZER` | `json` | Payload serializer: `json` or `protobuf` |
+Timeouts are in **milliseconds** (defaults assume a local collector on loopback / sidecar). `timeout_ms` is the
+per-request deadline; `shutdown_timeout_ms` (shared, see above) is a wall-clock budget enforced at shutdown only —
+it lets you keep `timeout_ms` tight for steady-state without freezing graceful exit.
+
+| Parameter | Environment variable | Default | Description |
+|----------------------------|----------------------------------------------|---------|--------------------------------------------------------------|
+| `curl_timeout_ms` | `FLOW_PHPUNIT_OTEL_CURL_TIMEOUT_MS` | `250` | Total request deadline in milliseconds |
+| `curl_connect_timeout_ms` | `FLOW_PHPUNIT_OTEL_CURL_CONNECT_TIMEOUT_MS` | `250` | TCP/TLS connect deadline in milliseconds |
+| `curl_compression` | `FLOW_PHPUNIT_OTEL_CURL_COMPRESSION` | `false` | Enable automatic response decompression |
+| `curl_follow_redirects` | `FLOW_PHPUNIT_OTEL_CURL_FOLLOW_REDIRECTS` | `true` | Follow HTTP redirects |
+| `curl_max_redirects` | `FLOW_PHPUNIT_OTEL_CURL_MAX_REDIRECTS` | `3` | Maximum number of redirects to follow |
+| `curl_proxy` | `FLOW_PHPUNIT_OTEL_CURL_PROXY` | — | Proxy server URL (e.g. `http://proxy:8080`) |
+| `curl_ssl_verify_peer` | `FLOW_PHPUNIT_OTEL_CURL_SSL_VERIFY_PEER` | `true` | Verify SSL peer certificate |
+| `curl_ssl_verify_host` | `FLOW_PHPUNIT_OTEL_CURL_SSL_VERIFY_HOST` | `true` | Verify SSL host name |
+| `curl_ssl_cert_path` | `FLOW_PHPUNIT_OTEL_CURL_SSL_CERT_PATH` | — | Path to client SSL certificate |
+| `curl_ssl_key_path` | `FLOW_PHPUNIT_OTEL_CURL_SSL_KEY_PATH` | — | Path to client SSL private key |
+| `curl_ca_info_path` | `FLOW_PHPUNIT_OTEL_CURL_CA_INFO_PATH` | — | Path to CA certificate bundle |
+| `curl_serializer` | `FLOW_PHPUNIT_OTEL_CURL_SERIALIZER` | `json` | Payload serializer: `json` or `protobuf` |
### gRPC transport (`transport=grpc`)
-Requires the `grpc` PHP extension and the `google/protobuf` + `open-telemetry/gen-otlp-protobuf` packages. Payload is
-always protobuf (per OTLP/gRPC spec).
+Requires the `grpc` PHP extension and the `google/protobuf` package. Payload is always protobuf (per OTLP/gRPC spec).
+gRPC has no separate connect timeout — `grpc_timeout_ms` is the per-call deadline that bounds DNS, connect, send and
+receive together.
-| Parameter | Environment variable | Default | Description |
-|-----------------|-----------------------------------|---------|----------------------------------|
-| `grpc_insecure` | `FLOW_PHPUNIT_OTEL_GRPC_INSECURE` | `true` | Use insecure channel credentials |
+| Parameter | Environment variable | Default | Description |
+|-------------------|-------------------------------------|---------|--------------------------------------------------------|
+| `grpc_timeout_ms` | `FLOW_PHPUNIT_OTEL_GRPC_TIMEOUT_MS` | `250` | Per-call deadline in milliseconds |
+| `grpc_insecure` | `FLOW_PHPUNIT_OTEL_GRPC_INSECURE` | `true` | Use insecure channel credentials |
+
+### Stream transport (`transport=stream`)
+
+Writes JSONL telemetry to a file or `php://` stream wrapper instead of an OTLP collector
+([OTLP File Exporter spec](https://opentelemetry.io/docs/specs/otel/protocol/file-exporter/)). The `endpoint`
+parameter holds the destination path. Useful for local debugging or pipelines that scrape JSONL from disk / stdout.
+
+| Parameter | Environment variable | Default | Description |
+|----------------------------|----------------------------------------------|---------|--------------------------------------------------------------|
+| `stream_file_permissions` | `FLOW_PHPUNIT_OTEL_STREAM_FILE_PERMISSIONS` | `0644` | Permissions for newly created files (ignored for `php://`) |
+| `stream_create_directories`| `FLOW_PHPUNIT_OTEL_STREAM_CREATE_DIRECTORIES`| `true` | Create parent directories of the destination if missing |
+
+## Error Handlers
+
+Telemetry-internal errors (exporter failures, transport timeouts) are routed to a configurable error handler instead of
+being thrown into the test runner. Pick one with the `error_handler` parameter; all options follow the same
+`error_handler_*` parameter family.
+
+| Type | Description |
+|--------------|----------------------------------------------------------------|
+| `error_log` | Default. Writes via PHP's `error_log()` (stderr in CLI). |
+| `noop` | Silently discards every Throwable. |
+| `stream` | Appends to a file path or `php://` stream wrapper. |
+| `syslog` | Local syslog via `openlog` / `syslog` / `closelog`. |
+| `udp_syslog` | RFC 5424 syslog frames over UDP to a remote collector. |
+
+Mixing parameters across handler types throws `InvalidArgumentException` at boot.
+
+### `error_log` (default)
+
+| Parameter | Environment variable | Default | Description |
+|---------------------------------|---------------------------------------------------|--------------------|--------------------------------------------------------------|
+| `error_handler_message_type` | `FLOW_PHPUNIT_OTEL_ERROR_HANDLER_MESSAGE_TYPE` | `operating_system` | `operating_system` (0), `email` (1), `file` (3), `sapi` (4) |
+| `error_handler_expand_newlines` | `FLOW_PHPUNIT_OTEL_ERROR_HANDLER_EXPAND_NEWLINES` | `false` | Emit one `error_log()` call per line of the formatted message |
+| `error_handler_message_prefix` | `FLOW_PHPUNIT_OTEL_ERROR_HANDLER_MESSAGE_PREFIX` | `[flow-telemetry]` | Prefix prepended to every message |
+
+### `noop`
+
+No parameters. Use when you want telemetry-internal failures to stay silent (e.g. CI runs where the collector is
+intentionally absent).
+
+### `stream`
+
+Appends formatted Throwables (one per line) to a destination. The handle is opened lazily on first error and reused.
+
+| Parameter | Environment variable | Default | Description |
+|------------------------------------|------------------------------------------------------|--------------------|--------------------------------------------------------------|
+| `error_handler_destination` | `FLOW_PHPUNIT_OTEL_ERROR_HANDLER_DESTINATION` | — | File path or `php://stdout`/`php://stderr`/etc. (required) |
+| `error_handler_file_permissions` | `FLOW_PHPUNIT_OTEL_ERROR_HANDLER_FILE_PERMISSIONS` | `0644` | Permissions for newly created files (ignored for `php://`) |
+| `error_handler_create_directories` | `FLOW_PHPUNIT_OTEL_ERROR_HANDLER_CREATE_DIRECTORIES` | `true` | Create parent directories of the destination if missing |
+| `error_handler_message_prefix` | `FLOW_PHPUNIT_OTEL_ERROR_HANDLER_MESSAGE_PREFIX` | `[flow-telemetry]` | Prefix prepended to every line |
+
+### `syslog`
+
+| Parameter | Environment variable | Default | Description |
+|----------------------------|----------------------------------------------|------------------|--------------------------------------------------------------|
+| `error_handler_ident` | `FLOW_PHPUNIT_OTEL_ERROR_HANDLER_IDENT` | `flow-telemetry` | Syslog identity tag |
+| `error_handler_facility` | `FLOW_PHPUNIT_OTEL_ERROR_HANDLER_FACILITY` | `user` | RFC 5424 facility (e.g. `user`, `local0`..`local7`, `mail`) |
+| `error_handler_log_opts` | `FLOW_PHPUNIT_OTEL_ERROR_HANDLER_LOG_OPTS` | `1` (`LOG_PID`) | Bitmask of `LOG_*` flags passed to `openlog()` |
+| `error_handler_severity` | `FLOW_PHPUNIT_OTEL_ERROR_HANDLER_SEVERITY` | `error` | RFC 5424 severity (e.g. `error`, `warning`, `notice`, `info`) |
+
+### `udp_syslog`
+
+Sends RFC 5424 syslog frames over UDP. Useful when test runs ship to a centralized log aggregator.
+
+| Parameter | Environment variable | Default | Description |
+|--------------------------|--------------------------------------------|------------------|--------------------------------------------------------------|
+| `error_handler_host` | `FLOW_PHPUNIT_OTEL_ERROR_HANDLER_HOST` | — | Remote syslog host (required) |
+| `error_handler_port` | `FLOW_PHPUNIT_OTEL_ERROR_HANDLER_PORT` | `514` | Remote syslog port |
+| `error_handler_ident` | `FLOW_PHPUNIT_OTEL_ERROR_HANDLER_IDENT` | `flow-telemetry` | Syslog identity tag |
+| `error_handler_facility` | `FLOW_PHPUNIT_OTEL_ERROR_HANDLER_FACILITY` | `user` | RFC 5424 facility |
+| `error_handler_severity` | `FLOW_PHPUNIT_OTEL_ERROR_HANDLER_SEVERITY` | `error` | RFC 5424 severity |
## Authentication
@@ -101,6 +182,21 @@ Invalid header format (missing `=`, empty name) will throw `InvalidArgumentExcep
+
+
+
+```
+
+## Stream Transport
+
+```xml
+
+
+
+
+
+
+
```
@@ -139,8 +235,8 @@ export FLOW_PHPUNIT_OTEL_HEADERS="Authorization=Bearer%20${ALLOY_TOKEN}"
The `otel_collector_url` parameter and its `FLOW_PHPUNIT_OTEL_COLLECTOR_URL` environment variable are deprecated and
trigger `E_USER_DEPRECATED`. They remain functional as an alias for `endpoint` with `transport=curl`.
-Mixing the deprecated parameter with any of the new-shape parameters (`transport`, `endpoint`, `headers`, `curl_*`,
-`grpc_*`) throws `InvalidArgumentException` — migrate fully when you switch.
+Mixing the deprecated parameter with any of the new-shape parameters throws `InvalidArgumentException` — migrate
+fully when you switch.
## Features
diff --git a/documentation/components/bridges/symfony-telemetry-bundle.md b/documentation/components/bridges/symfony-telemetry-bundle.md
index 6d918fad7..2a5b22511 100644
--- a/documentation/components/bridges/symfony-telemetry-bundle.md
+++ b/documentation/components/bridges/symfony-telemetry-bundle.md
@@ -30,6 +30,10 @@ This bundle integrates Flow PHP's Telemetry library with Symfony applications. I
## Configuration Reference
+> Any `error_handler:` field that appears under a provider, processor, or `otlp` exporter references a name from the
+> top-level `error_handlers:` map (see [Error Handlers](#error-handlers)). When omitted it defaults to `default`, which
+> is auto-created as `{ type: error_log }` if the user does not declare one.
+
### Resource Configuration
The `resource` node configures OpenTelemetry Resource attributes that identify your service.
@@ -130,6 +134,188 @@ flow_telemetry:
| `baggage` | W3C Baggage only |
| `service` | Custom propagator service |
+### Error Handlers
+
+Per the OpenTelemetry spec, the SDK MUST NOT throw to user code at runtime. Errors raised by exporters/processors are
+caught and forwarded to a configured error handler. The bundle exposes a **named map** under `error_handlers:` that is
+referenced from provider, processor, and OTLP exporter blocks via `error_handler:` fields.
+
+```yaml
+flow_telemetry:
+ error_handlers:
+ default:
+ type: error_log # default if omitted
+
+ to_file:
+ type: stream
+ destination: '%kernel.logs_dir%/flow-telemetry-errors.log'
+
+ to_syslog:
+ type: syslog
+ facility: local0
+ severity: warning
+
+ fanout:
+ type: composite
+ handlers: [default, to_file, to_syslog]
+
+ silent:
+ type: noop
+
+ custom:
+ type: service
+ service_id: app.my_error_handler
+```
+
+If `error_handlers:` is omitted (or `default` is missing inside it), the bundle injects
+`error_handlers.default = { type: error_log }` automatically so every `error_handler:` reference always resolves.
+
+Service IDs registered by the bundle (predictable for `decorates:`):
+
+- `flow.telemetry.error_handler.` — e.g. `flow.telemetry.error_handler.default`
+
+#### error_log (default)
+
+Writes formatted Throwables via PHP's `error_log()` — stderr in CLI by default, or the `error_log` ini setting otherwise.
+Matches the OTEL spec recommendation to log to standard error output.
+
+| Option | Type | Default | Description |
+|-------------------|---------|-------------------|-------------------------------------------------------------------|
+| `message_type` | enum | `operating_system` | `operating_system` (0), `email` (1), `file` (3), `sapi` (4) |
+| `expand_newlines` | boolean | `false` | Emit one `error_log()` call per line of the formatted message |
+| `message_prefix` | string | `[flow-telemetry]` | Prefix prepended to every message |
+
+#### stream
+
+Appends formatted Throwables (one per line) to a file path or `php://` stream wrapper. The handle is opened lazily on
+the first call and reused.
+
+| Option | Type | Default | Description |
+|----------------------|---------|--------------------|--------------------------------------------------------------|
+| `destination` | string | - | File path or `php://stdout`/`php://stderr`/etc. (required) |
+| `file_permissions` | integer | `0644` | Permissions for newly created files (ignored for `php://`) |
+| `create_directories` | boolean | `true` | Create parent directories of the destination if missing |
+| `message_prefix` | string | `[flow-telemetry]` | Prefix prepended to every line |
+
+#### syslog
+
+Writes via `openlog/syslog/closelog`.
+
+| Option | Type | Default | Description |
+|------------|---------|------------------|--------------------------------------------------------|
+| `ident` | string | `flow-telemetry` | Syslog identity tag |
+| `facility` | enum | `user` | RFC 5424 facility (see table below) |
+| `log_opts` | integer | `LOG_PID` | Bitmask of `LOG_*` flags passed to `openlog()` |
+| `severity` | enum | `error` | RFC 5424 severity (see table below) |
+
+#### udp_syslog
+
+Sends RFC 5424 syslog frames over UDP.
+
+| Option | Type | Default | Description |
+|------------|---------|------------------|----------------------------------------------|
+| `host` | string | - | Remote syslog host (required) |
+| `port` | integer | `514` | Remote syslog port |
+| `ident` | string | `flow-telemetry` | Syslog identity tag |
+| `facility` | enum | `user` | RFC 5424 facility |
+| `severity` | enum | `error` | RFC 5424 severity |
+
+#### composite
+
+Fans an error out to multiple named handlers. Each child invocation is wrapped so a misbehaving handler cannot prevent
+siblings from running.
+
+| Option | Type | Default | Description |
+|------------|-----------------|---------|------------------------------------------------------------|
+| `handlers` | list of strings | - | Names of other entries in `error_handlers:` (required) |
+
+```yaml
+fanout:
+ type: composite
+ handlers: [default, to_file]
+```
+
+#### noop
+
+Discards every Throwable. Intended for tests or explicit silence.
+
+```yaml
+silent: { type: noop }
+```
+
+#### service
+
+Aliases an existing service that implements `Flow\Telemetry\ErrorHandler\ErrorHandler`.
+
+| Option | Type | Default | Description |
+|--------------|--------|---------|----------------------------------------------------------|
+| `service_id` | string | - | Service id of the user-provided handler (required) |
+
+```yaml
+custom:
+ type: service
+ service_id: app.my_error_handler
+```
+
+#### Facility values
+
+| Value | Constant |
+|-----------|---------------|
+| `kernel` | `LOG_KERN` |
+| `user` | `LOG_USER` |
+| `mail` | `LOG_MAIL` |
+| `daemon` | `LOG_DAEMON` |
+| `auth` | `LOG_AUTH` |
+| `syslog` | `LOG_SYSLOG` |
+| `lpr` | `LOG_LPR` |
+| `news` | `LOG_NEWS` |
+| `uucp` | `LOG_UUCP` |
+| `cron` | `LOG_CRON` |
+| `local0`..`local7` | `LOG_LOCAL0`..`LOG_LOCAL7` |
+
+#### Severity values
+
+| Value | Constant |
+|-------------|---------------|
+| `emergency` | `LOG_EMERG` |
+| `alert` | `LOG_ALERT` |
+| `critical` | `LOG_CRIT` |
+| `error` | `LOG_ERR` |
+| `warning` | `LOG_WARNING` |
+| `notice` | `LOG_NOTICE` |
+| `info` | `LOG_INFO` |
+| `debug` | `LOG_DEBUG` |
+
+### Exporters (named definitions)
+
+The bundle exposes a **top-level named map** of exporters. The implementation is selected by the **sub-block name**
+under each entry — there is no separate `type:` key. Each exporter must declare exactly one of the supported
+sub-blocks: `otlp`, `service`, `console`, `memory`, `void`. Per-signal processors reference exporters by name.
+
+```yaml
+flow_telemetry:
+ exporters:
+ otlp: # exporter name
+ otlp: # sub-block selects implementation
+ transport:
+ type: curl
+ endpoint: 'http://otel-collector:4318'
+ encoding: protobuf
+```
+
+| Sub-block | Options | Description |
+|-----------|----------------------------------------|------------------------------------------|
+| `otlp` | `transport: { ... }` | Sends batches over OTLP curl/grpc/service |
+| `service` | `id: ` | Aliases an existing user-provided service |
+| `console` | none (use `~` / `null` / `{}`) | Pretty-prints to console |
+| `memory` | none | Stores batches in memory (testing) |
+| `void` | none | Discards everything (no-op) |
+
+Service IDs registered by the bundle (predictable for `decorates:`):
+
+- `flow.telemetry.exporter.` — e.g. `flow.telemetry.exporter.otlp`
+- `flow.telemetry.exporter..transport` — only when the exporter uses the `otlp` sub-block
+
### TracerProvider
Configures the tracer provider for distributed tracing.
@@ -137,16 +323,16 @@ Configures the tracer provider for distributed tracing.
```yaml
flow_telemetry:
tracer_provider:
+ error_handler: default # name from error_handlers; defaults to "default"
sampler:
- type: always_on # always_on|always_off|trace_id_ratio|parent_based|service
- ratio: 1.0 # Sampling ratio (0.0-1.0, only for trace_id_ratio)
- service_id: null # Custom sampler service (only for type: service)
+ type: always_on # always_on|always_off|trace_id_ratio|parent_based|service
+ ratio: 1.0 # Sampling ratio (0.0-1.0, only for trace_id_ratio)
+ service_id: null # Custom sampler service (only for type: service)
processor:
- type: void # composite|memory|batching|passthrough|void|service
+ type: batching # composite|memory|batching|passthrough|void|service
batch_size: 512
- service_id: null
- exporter:
- type: void # memory|console|void|otlp|service
+ exporter: otlp # name of a top-level exporter
+ error_handler: default
```
**Sampler types:**
@@ -161,31 +347,29 @@ flow_telemetry:
### MeterProvider
-Configures the meter provider for metrics collection.
-
```yaml
flow_telemetry:
meter_provider:
+ error_handler: default
temporality: cumulative # cumulative|delta
processor:
- type: void # composite|memory|batching|passthrough|void|service
+ type: batching
batch_size: 512
- exporter:
- type: void
+ exporter: otlp
+ error_handler: default
```
### LoggerProvider
-Configures the logger provider for log export.
-
```yaml
flow_telemetry:
logger_provider:
+ error_handler: default
processor:
- type: void # composite|memory|batching|passthrough|void|severity_filtering|service
+ type: batching # composite|memory|batching|passthrough|void|severity_filtering|service
batch_size: 512
- exporter:
- type: void
+ exporter: otlp
+ error_handler: default
```
**Severity filtering** (logs only):
@@ -195,19 +379,16 @@ flow_telemetry:
logger_provider:
processor:
type: severity_filtering
- minimum_severity: info # trace|debug|info|warn|error|fatal
+ minimum_severity: warn # trace|debug|info|warn|error|fatal
inner_processor:
type: batching
- exporter:
- type: otlp
- otlp:
- transport:
- endpoint: 'http://otel-collector:4318/v1/logs'
+ exporter: otlp
+ batch_size: 200
```
### Processor Configuration
-Processor types available for tracer_provider, meter_provider, and logger_provider.
+Processor types available for `tracer_provider`, `meter_provider`, and `logger_provider`.
#### void (default)
@@ -220,72 +401,56 @@ processor:
#### passthrough
-Immediately exports each item.
+Immediately exports each item via the referenced exporter.
```yaml
processor:
type: passthrough
- exporter:
- type: console
+ exporter: console
```
#### memory
-Stores in memory (for testing). No additional options.
+Stores in memory (for testing). Still requires a backing exporter for `flush()` to call.
```yaml
processor:
type: memory
+ exporter: memory
```
#### batching
Batches items before export.
-| Option | Type | Default | Description |
-|--------------|---------|---------|---------------------------|
-| `batch_size` | integer | `512` | Number of items per batch |
+| Option | Type | Default | Description |
+|--------------|---------|---------|----------------------------------------------|
+| `batch_size` | integer | `512` | Number of items per batch |
+| `exporter` | string | - | Name of a top-level exporter (required) |
```yaml
processor:
type: batching
batch_size: 512
- exporter:
- type: otlp
- otlp:
- transport:
- endpoint: 'http://otel-collector:4318/v1/traces'
+ exporter: otlp
```
#### composite
-Combines multiple processors.
-
-| Option | Type | Description |
-|--------------|-------|---------------------------|
-| `processors` | array | List of processor configs |
+Combines multiple processors. Each child references its own exporter by name.
```yaml
processor:
type: composite
processors:
- - type: batching
- exporter:
- type: otlp
- otlp:
- transport:
- endpoint: 'http://otel-collector:4318/v1/traces'
- - type: memory
+ - { type: batching, exporter: otlp, batch_size: 512 }
+ - { type: memory, exporter: memory }
```
#### service
Custom processor service.
-| Option | Type | Description |
-|--------------|--------|-------------------------------|
-| `service_id` | string | Symfony service ID (required) |
-
```yaml
processor:
type: service
@@ -294,218 +459,317 @@ processor:
#### severity_filtering (logger_provider only)
-Filters logs by minimum severity level.
-
-| Option | Type | Default | Description |
-|-------------------|--------|---------|----------------------------------------|
-| `minimum_severity`| string | `info` | trace\|debug\|info\|warn\|error\|fatal |
-| `inner_processor` | object | - | Nested processor configuration |
+Filters logs by minimum severity level. The wrapped `inner_processor` is built with the same set of types as a top-level
+processor (except `composite`/`severity_filtering`).
```yaml
processor:
type: severity_filtering
- minimum_severity: info
+ minimum_severity: warn
inner_processor:
type: batching
- exporter:
- type: otlp
- otlp:
- transport:
- endpoint: 'http://otel-collector:4318/v1/logs'
+ exporter: otlp
+ batch_size: 200
```
-### Exporter Configuration
+### Exporter Definitions
-Exporter types available for processor configurations.
+Exporters are declared once at the top level under `exporters:` and referenced from processor blocks by name. Each
+exporter declares exactly one sub-block; the sub-block name selects the implementation.
-#### void (default)
+#### void
-Discards all data. No additional options.
+Discards all data.
```yaml
-exporter:
- type: void
+exporters:
+ drop: { void: ~ }
```
#### memory
-Stores in memory (for testing). No additional options.
+In-memory store for testing. Service exposes `allLogs()`, `allMetrics()`, `allSpans()` accessors via
+`Flow\Telemetry\Provider\Memory\MemoryExporter`.
```yaml
-exporter:
- type: memory
+exporters:
+ capture: { memory: ~ }
```
#### console
-Outputs to console. No additional options.
+Pretty-prints logs, metrics, and spans to the console. Useful for development.
```yaml
-exporter:
- type: console
+exporters:
+ debug: { console: ~ }
```
#### otlp
-Exports to OTLP-compatible backends (Jaeger, Tempo, etc.).
-
-| Option | Type | Description |
-|--------------|--------|------------------------------------|
-| `transport` | object | Transport configuration (required) |
-| `serializer` | object | Serializer configuration |
+Sends batches over an embedded transport. The single OTLP exporter handles all three signals.
```yaml
-exporter:
- type: otlp
+exporters:
otlp:
- transport:
- type: curl
- endpoint: 'http://otel-collector:4318/v1/traces'
- serializer:
- type: json
+ otlp:
+ error_handler: default # name from error_handlers; defaults to "default"
+ transport:
+ type: curl
+ endpoint: 'http://otel-collector:4318'
+ encoding: protobuf
```
#### service
-Custom exporter service.
-
-| Option | Type | Description |
-|--------------|--------|-------------------------------|
-| `service_id` | string | Symfony service ID (required) |
+Aliases an existing user-defined service implementing `Flow\Telemetry\Exporter\Exporter`. This is the escape hatch for
+APM-specific exporters (Datadog, New Relic, custom) that don't go through OTLP — define your exporter as a Symfony
+service in your own `services.yaml` and reference it by id.
```yaml
-exporter:
- type: service
- service_id: 'app.custom_exporter'
+exporters:
+ datadog:
+ service:
+ id: 'app.datadog_telemetry_exporter'
```
### OTLP Transport Configuration
-Transport types for OTLP exporters.
-
-#### curl (default, recommended)
-
-| Option | Type | Default | Description |
-|--------------------|---------|---------|-----------------------------|
-| `endpoint` | string | - | OTLP endpoint URL (required)|
-| `timeout` | integer | `30` | Request timeout in seconds |
-| `connect_timeout` | integer | `10` | Connection timeout |
-| `compression` | boolean | `false` | Enable compression |
-| `follow_redirects` | boolean | `true` | Follow HTTP redirects |
-| `max_redirects` | integer | `3` | Maximum redirects to follow |
-| `proxy` | string | `null` | Proxy URL |
-| `ssl_verify_peer` | boolean | `true` | Verify SSL peer |
-| `ssl_verify_host` | boolean | `true` | Verify SSL host |
-| `ssl_cert_path` | string | `null` | SSL certificate path |
-| `ssl_key_path` | string | `null` | SSL key path |
-| `ca_info_path` | string | `null` | CA info path |
-| `headers` | object | `{}` | Additional HTTP headers |
-
-```yaml
-otlp:
- transport:
- type: curl
- endpoint: 'http://otel-collector:4318/v1/traces'
- timeout: 30
- connect_timeout: 10
- compression: false
- follow_redirects: true
- max_redirects: 3
- proxy: null
- ssl_verify_peer: true
- ssl_verify_host: true
- ssl_cert_path: null
- ssl_key_path: null
- ca_info_path: null
- headers:
- Authorization: 'Bearer token'
-```
-
-#### http
-
-PSR-18 HTTP transport.
-
-| Option | Type | Default | Description |
-|------------------------------|---------|---------|------------------------------|
-| `endpoint` | string | - | OTLP endpoint URL (required) |
-| `timeout` | integer | `30` | Request timeout in seconds |
-| `http_client_service_id` | string | `null` | PSR-18 client service |
-| `request_factory_service_id` | string | `null` | PSR-17 request factory |
-| `stream_factory_service_id` | string | `null` | PSR-17 stream factory |
-
-```yaml
-otlp:
- transport:
- type: http
- endpoint: 'http://otel-collector:4318/v1/traces'
- timeout: 30
- http_client_service_id: null
- request_factory_service_id: null
- stream_factory_service_id: null
+Inside `exporters..otlp.transport`. Required for the `otlp` sub-block.
+
+#### Timeouts
+
+Both curl and gRPC default to **aggressive, local-collector-friendly per-request timeouts** with a separate, looser
+budget for graceful drain at shutdown:
+
+| Setting | Default | Applies to | Bounds |
+|-----------------------|--------:|------------|-------------------------------------------------------------|
+| `timeout_ms` | 250 | curl, grpc | Per-request deadline (curl: total request; grpc: per-call) |
+| `connect_timeout_ms` | 250 | curl only | TCP/TLS connect; gRPC has no separate bound |
+| `shutdown_timeout_ms` | 5000 | curl, grpc | Wall-clock budget for draining pending requests at shutdown |
+
+The defaults assume the recommended deployment: an OpenTelemetry Collector running close to the application (loopback,
+UDS, or sidecar). `shutdown_timeout_ms` is independent of `timeout_ms` — keep the per-request value tight to surface
+collector slowness during steady-state, while still giving graceful exit a longer window to drain. For a remote
+collector across regions, raise both values. See the
+[OTLP bridge Timeouts section](/documentation/components/bridges/telemetry-otlp-bridge.md#timeouts) for the rationale.
+
+#### Failover Transport
+
+Both `curl` and `grpc` transports accept an optional nested `failover:` block. When primary delivery fails, the prior
+batch is forwarded to the failover transport and a `FailoverTransportException` is raised so the operator is informed
+even when failover absorbs the data. Common pattern: **gRPC primary → stream (JSONL on disk) failover** so a downed
+collector still leaves recoverable data the operator can replay later.
+
+```yaml
+exporters:
+ otlp:
+ otlp:
+ transport:
+ type: grpc
+ endpoint: 'otel-collector:4317'
+ timeout_ms: 250
+ failover:
+ type: stream
+ endpoint: '%kernel.logs_dir%/otel-failed.jsonl'
+```
+
+**Constraints**
+
+- The `failover:` block accepts the same fields as the parent transport, except it cannot itself declare a nested
+ `failover:` (single-level depth).
+- Allowed only on `curl` and `grpc` primaries. `failover` under a `stream` or `service` primary is rejected at
+ config-validation time.
+- The bundle registers `flow.telemetry.exporter..failover.transport` for the failover service id.
+
+For the underlying behavior — when a forwarded batch is treated as absorbed vs. lost, the shape of
+`FailoverTransportException`, and the cascade-shutdown contract — see the
+[OTLP bridge Failover Transport section](/documentation/components/bridges/telemetry-otlp-bridge.md#failover-transport).
+
+#### curl (default)
+
+| Option | Type | Default | Description |
+|-----------------------|---------|---------|----------------------------------------------------------------------------|
+| `endpoint` | string | - | OTLP base URL (required) |
+| `timeout_ms` | integer | `250` | Total per-request deadline in **milliseconds** |
+| `connect_timeout_ms` | integer | `250` | TCP/TLS connect deadline in **milliseconds** |
+| `shutdown_timeout_ms` | integer | `5000` | Wall-clock budget in **milliseconds** for draining pending requests at shutdown |
+| `compression` | boolean | `false` | Enable compression |
+| `follow_redirects` | boolean | `true` | Follow HTTP redirects |
+| `max_redirects` | integer | `3` | Maximum redirects to follow |
+| `proxy` | string | `null` | Proxy URL |
+| `ssl_verify_peer` | boolean | `true` | Verify SSL peer |
+| `ssl_verify_host` | boolean | `true` | Verify SSL host |
+| `ssl_cert_path` | string | `null` | SSL certificate path |
+| `ssl_key_path` | string | `null` | SSL key path |
+| `ca_info_path` | string | `null` | CA info path |
+| `headers` | object | `{}` | Additional HTTP headers |
+| `encoding` | enum | `json` | OTLP/HTTP wire encoding: `json` or `protobuf` |
+| `failover` | object | `null` | Optional [failover transport](#failover-transport) |
+
+See [Timeouts](#timeouts) for guidance on the millisecond defaults.
+
+```yaml
+exporters:
+ otlp:
+ otlp:
+ transport:
+ type: curl
+ endpoint: 'http://otel-collector:4318'
+ timeout_ms: 250
+ connect_timeout_ms: 250
+ compression: true
+ headers:
+ Authorization: 'Bearer token'
+ encoding: protobuf
```
#### grpc
-gRPC transport
+gRPC transport. `encoding` is rejected by validation (OTLP/gRPC mandates Protobuf, built internally), and
+`connect_timeout_ms` is rejected because gRPC has no separate connect bound — `timeout_ms` is the per-call deadline
+covering DNS, connect, send and receive together.
-| Option | Type | Default | Description |
-|------------|---------|---------|------------------------------|
-| `endpoint` | string | - | OTLP endpoint URL (required) |
-| `insecure` | boolean | `false` | Allow insecure connections |
+| Option | Type | Default | Description |
+|-----------------------|---------|---------|----------------------------------------------------------------------------|
+| `endpoint` | string | - | gRPC endpoint (required) |
+| `timeout_ms` | integer | `250` | Per-call deadline in **milliseconds** |
+| `shutdown_timeout_ms` | integer | `5000` | Wall-clock budget in **milliseconds** for draining pending calls at shutdown |
+| `insecure` | boolean | `true` | Allow insecure connections |
+| `headers` | object | `{}` | gRPC metadata |
+| `failover` | object | `null` | Optional [failover transport](#failover-transport) |
```yaml
-otlp:
- transport:
- type: grpc
- endpoint: 'http://otel-collector:4317'
- insecure: true
+exporters:
+ otlp_grpc:
+ otlp:
+ transport:
+ type: grpc
+ endpoint: 'otel-collector:4317'
+ timeout_ms: 250
+ insecure: false
```
-#### service
+#### stream
-Custom transport service.
+OTLP File Exporter ([spec](https://opentelemetry.io/docs/specs/otel/protocol/file-exporter/)). Writes one JSON Line
+per batch to the configured destination — either an absolute file path or a `php://` stream wrapper — with
+`LOCK_EX` around each `fwrite`. Only JSON encoding is supported per the spec; `encoding` and HTTP-specific options
+(`timeout`, `ssl_*`, `headers`, etc.) are rejected at config time.
-| Option | Type | Description |
-|--------------|--------|-------------------------------|
-| `service_id` | string | Symfony service ID (required) |
+| Option | Type | Default | Description |
+|-----------------------|---------|----------|------------------------------------------------------------------------------|
+| `endpoint` | string | - | File path or `php://` stream wrapper URI (required) |
+| `file_permissions` | integer | `0644` | File mode applied when creating new files; ignored for `php://` destinations |
+| `create_directories` | boolean | `true` | Create the destination's parent directories if missing; ignored for `php://` destinations |
```yaml
-otlp:
- transport:
- type: service
- service_id: 'app.custom_transport'
+exporters:
+ otlp_logs_file:
+ otlp:
+ transport:
+ type: stream
+ endpoint: '%kernel.project_dir%/var/otel/logs.jsonl'
+ file_permissions: 0640
+ create_directories: true
+
+ otlp_logs_stdout:
+ otlp:
+ transport:
+ type: stream
+ endpoint: 'php://stdout'
```
-### OTLP Serializer Configuration
+To write all three signal types to one file, point three exporters at the same destination — the OpenTelemetry
+Collector's `otlpjsonfile` receiver handles mixed `resourceLogs` / `resourceMetrics` / `resourceSpans` lines.
-Serializer types for OTLP exporters.
+#### service
-#### json (default)
+Aliases an existing transport service ID inside the OTLP exporter.
```yaml
-serializer:
- type: json
+exporters:
+ otlp:
+ otlp:
+ transport:
+ type: service
+ service_id: 'app.custom_transport'
```
-#### protobuf
+### Multiple OTLP backends
+
+Each signal can target its own collector by declaring multiple named exporters and referencing them per provider.
```yaml
-serializer:
- type: protobuf
+flow_telemetry:
+ exporters:
+ otlp_traces:
+ otlp:
+ transport:
+ type: grpc
+ endpoint: 'http://traces:4317'
+ insecure: false
+ otlp_metrics:
+ otlp:
+ transport:
+ type: curl
+ endpoint: 'http://metrics:4318'
+ encoding: protobuf
+ otlp_logs:
+ otlp:
+ transport:
+ type: curl
+ endpoint: 'http://logs:4318'
+ encoding: json
+
+ tracer_provider:
+ processor: { type: batching, exporter: otlp_traces, batch_size: 1024 }
+ meter_provider:
+ processor: { type: batching, exporter: otlp_metrics, batch_size: 256 }
+ logger_provider:
+ processor: { type: batching, exporter: otlp_logs, batch_size: 100 }
```
-#### service
+### Migrating from older config
-Custom serializer service.
+The schema replaced the `type: ` discriminator with a sub-block whose key matches the implementation. There is no
+BC shim.
-| Option | Type | Description |
-|--------------|--------|-------------------------------|
-| `service_id` | string | Symfony service ID (required) |
+**Before (legacy schema)**
```yaml
-serializer:
- type: service
- service_id: 'app.custom_serializer'
+flow_telemetry:
+ exporters:
+ otlp:
+ type: otlp
+ transport:
+ type: curl
+ endpoint: 'http://otel-collector:4318'
+ encoding: protobuf
+ custom:
+ type: service
+ service_id: 'app.x'
+ debug: { type: console }
+```
+
+**After**
+
+```yaml
+flow_telemetry:
+ exporters:
+ otlp:
+ otlp:
+ transport:
+ type: curl
+ endpoint: 'http://otel-collector:4318'
+ encoding: protobuf
+ custom:
+ service:
+ id: 'app.x'
+ debug: { console: ~ }
+
+ tracer_provider:
+ processor: { type: batching, exporter: otlp }
```
### Instrumentation
@@ -834,6 +1098,33 @@ flow_telemetry:
propagator:
type: w3c
+ exporters:
+ otlp_traces:
+ otlp:
+ transport:
+ type: curl
+ endpoint: '%env(OTEL_EXPORTER_OTLP_TRACES_ENDPOINT)%'
+ timeout_ms: 250
+ connect_timeout_ms: 250
+ headers:
+ Authorization: 'Bearer %env(OTEL_AUTH_TOKEN)%'
+ encoding: protobuf
+ failover:
+ type: stream
+ endpoint: '%kernel.logs_dir%/otel-traces-failed.jsonl'
+ otlp_metrics:
+ otlp:
+ transport:
+ type: curl
+ endpoint: '%env(OTEL_EXPORTER_OTLP_METRICS_ENDPOINT)%'
+ encoding: protobuf
+ otlp_logs:
+ otlp:
+ transport:
+ type: curl
+ endpoint: '%env(OTEL_EXPORTER_OTLP_LOGS_ENDPOINT)%'
+ encoding: protobuf
+
tracer_provider:
sampler:
type: trace_id_ratio
@@ -841,25 +1132,13 @@ flow_telemetry:
processor:
type: batching
batch_size: 512
- exporter:
- type: otlp
- otlp:
- transport:
- type: curl
- endpoint: '%env(OTEL_EXPORTER_OTLP_TRACES_ENDPOINT)%'
- timeout: 30
- headers:
- Authorization: 'Bearer %env(OTEL_AUTH_TOKEN)%'
+ exporter: otlp_traces
meter_provider:
temporality: cumulative
processor:
type: batching
- exporter:
- type: otlp
- otlp:
- transport:
- endpoint: '%env(OTEL_EXPORTER_OTLP_METRICS_ENDPOINT)%'
+ exporter: otlp_metrics
logger_provider:
processor:
@@ -867,11 +1146,7 @@ flow_telemetry:
minimum_severity: info
inner_processor:
type: batching
- exporter:
- type: otlp
- otlp:
- transport:
- endpoint: '%env(OTEL_EXPORTER_OTLP_LOGS_ENDPOINT)%'
+ exporter: otlp_logs
loggers:
app:
diff --git a/documentation/components/bridges/telemetry-otlp-bridge.md b/documentation/components/bridges/telemetry-otlp-bridge.md
index a4c8da3a8..0294bdede 100644
--- a/documentation/components/bridges/telemetry-otlp-bridge.md
+++ b/documentation/components/bridges/telemetry-otlp-bridge.md
@@ -18,36 +18,15 @@ capabilities.
For detailed installation instructions, see the [installation page](/documentation/installation/packages/telemetry-otlp-bridge.md).
-### Transports
-
-| Transport | Required Extension | Supported Serializers |
-|-----------|--------------------|-----------------------|
-| **Curl** | `ext-curl` | JSON, Protobuf |
-| **HTTP** | - | JSON, Protobuf |
-| **gRPC** | `ext-grpc` | Protobuf |
-
-### Serializers
-
-| Serializer | Required Packages | Supported Transports |
-|--------------|-------------------------------------------------------|----------------------|
-| **JSON** | - | Curl, HTTP |
-| **Protobuf** | `google/protobuf`, `open-telemetry/gen-otlp-protobuf` | Curl, HTTP, gRPC |
-
-To install Protobuf dependencies:
-
-```
-composer require google/protobuf open-telemetry/gen-otlp-protobuf
-```
-
## Transports
The bridge provides three transport options for sending telemetry data to OTLP endpoints.
-| Transport | Protocol | Use Case | Requirements |
-|-----------|--------------|----------------------------------|---------------|
-| **Curl** | HTTP (async) | Production, low latency | ext-curl |
-| **HTTP** | HTTP (sync) | Standard PSR-18 integration | PSR-18 client |
-| **gRPC** | gRPC | High-performance binary protocol | ext-grpc |
+| Transport | Protocol | Use Case | Requirements |
+|------------|---------------|--------------------------------------------------------------------------|---------------|
+| **Curl** | HTTP (async) | Production, low latency | ext-curl |
+| **gRPC** | gRPC | High-performance binary protocol | ext-grpc |
+| **Stream** | JSONL | Sidecar collectors, log shippers, FaaS / Kubernetes stdout/err scraping | None |
### Curl Transport (Recommended)
@@ -68,115 +47,236 @@ $transport = otlp_curl_transport(
serializer: otlp_json_serializer(),
);
-// With custom options
+// With custom options (timeouts in milliseconds — see "Timeouts" below)
$transport = otlp_curl_transport(
endpoint: 'https://otlp.example.com:4318',
serializer: otlp_json_serializer(),
options: otlp_curl_options()
- ->withTimeout(60)
- ->withConnectTimeout(15)
+ ->withTimeout(2000)
+ ->withConnectTimeout(500)
->withHeader('Authorization', 'Bearer your-token')
->withCompression(),
);
```
-### HTTP Transport (PSR-18)
+### gRPC Transport
-The HTTP transport uses any PSR-18 compatible HTTP client for synchronous requests. This is useful when you want to
-integrate with an existing HTTP client in your application.
+The gRPC transport uses the native gRPC protocol for high-performance binary communication. It requires the `ext-grpc`
+PHP extension. OTLP/gRPC mandates Protobuf, so the transport instantiates the protobuf request factory internally —
+no serializer parameter.
```php
'Bearer your-token',
],
+ insecure: false, // Use TLS
+ timeoutMs: 2000, // call deadline: connect + send + receive
);
```
-### gRPC Transport
+> **Note**: gRPC has no separate connect timeout — `timeoutMs` is the per-call deadline that bounds DNS, connect, send, and receive together. See [Timeouts](#timeouts).
-The gRPC transport uses the native gRPC protocol for high-performance binary communication. It requires the `ext-grpc`
-PHP extension.
+### Stream Transport
+
+The Stream transport implements the [OTLP File Exporter spec](https://opentelemetry.io/docs/specs/otel/protocol/file-exporter/).
+It writes JSONL to either an absolute file path or a `php://` stream wrapper. The handle is opened once in
+the constructor and reused across `send()` calls; each call appends one JSON Line under `LOCK_EX` so concurrent
+writers interleave at line boundaries.
+
+Only JSON encoding is supported per the spec — the transport hard-codes the JSON serializer; there is no
+serializer parameter.
```php
'Bearer your-token',
- ],
- insecure: false, // Use TLS
+// Stream wrapper destinations for FaaS / Kubernetes log scraping
+$transport = otlp_stream_transport('php://stdout');
+$transport = otlp_stream_transport('php://stderr');
+
+// Tweak file mode and disable directory creation
+$transport = otlp_stream_transport(
+ destination: '/var/log/otel/logs.jsonl',
+ filePermissions: 0o640,
+ createDirectories: false,
);
```
-## Serializers
+`filePermissions` and `createDirectories` apply only when the destination is a file path; they are ignored for
+`php://` URIs. For production deployments.
+
+One transport instance writes to one destination. To split logs, metrics, and traces across multiple files,
+build three transports and wire them to three exporters. To keep all signals in one file (the OpenTelemetry
+Collector handles mixed JSONL just fine), reuse the same destination across exporters.
-The bridge provides two serialization formats for OTLP data.
+## Wire Encoding
-| Serializer | Format | Size | Readability | Dependencies |
-|--------------|--------|---------|----------------|-------------------------------------------------------|
-| **JSON** | Text | Larger | Human-readable | None |
-| **Protobuf** | Binary | Smaller | Not readable | `google/protobuf`, `open-telemetry/gen-otlp-protobuf` |
+The OTLP spec defines fixed encodings per transport. The bridge enforces them.
-### JSON Serializer
+| Transport | JSON | Protobuf | Notes |
+|-----------|:----:|:--------:|----------------------------------------------------------------------|
+| Curl | ✅ | ✅ | OTLP/HTTP supports both; pick a serializer when constructing the transport |
+| gRPC | ❌ | ✅ | OTLP/gRPC mandates Protobuf; the transport builds it internally |
+| Stream | ✅ | ❌ | OTLP File Exporter spec only supports JSON |
-The default serializer that produces human-readable JSON. Best for development and debugging.
+For curl, pass `otlp_json_serializer()` (default) or `otlp_protobuf_serializer()`:
```php
withTimeout(5000)
+ ->withConnectTimeout(1000),
+);
+
+$remoteGrpc = otlp_grpc_transport(
+ endpoint: 'otlp.example.com:4317',
+ insecure: false,
+ timeoutMs: 5000,
+);
```
+> **Why milliseconds**: telemetry exports to a local collector complete in microseconds; the second-granularity API in
+> earlier versions could not express tight, realistic deadlines. A negative value to `withTimeout()` /
+> `withConnectTimeout()` raises `\InvalidArgumentException`.
+
+## Failover Transport
+
+Both `CurlTransport` and `GrpcTransport` accept an optional `Transport $failover` argument. When set, batches that
+failed on the primary are forwarded to the failover transport so telemetry is preserved even when the primary backend
+is unreachable. A typical pairing is **gRPC primary → Stream (JSONL on disk) failover** so a downed collector still
+leaves recoverable data the operator can replay later.
+
+### How it works
+
+The transport defers error handling to the *next* `send()` or `shutdown()`. When the next call drains the prior
+in-flight request:
+
+1. **Primary OK** — the batch is dropped from the pending list. Nothing else happens.
+2. **Primary failed, failover accepted** — the prior batch is forwarded to `failover->send()`; data preserved. The
+ transport still raises a `FailoverTransportException` so the operator is informed primary is degraded.
+3. **Primary failed, failover also failed** — the prior batch is lost. The exception carries both throwables in the
+ `failures` list.
+
+In every case the **current** request is dispatched to the primary *before* the exception is raised, so the new batch
+is never lost due to a prior failure.
+
+On `shutdown()`, the primary drains pending requests, applies the same forwarding logic, then calls
+`failover->shutdown()` (cascade). The failover lifecycle is owned by the primary — you do not need to shut it down
+separately.
+
+### Behavior matrix
+
+| Primary | Failover send | Outcome | Exception |
+|----------|---------------|---------------------------------------------------------|---------------------------------------|
+| OK | — | Data delivered | none |
+| Failed | OK | Data preserved via failover | `FailoverTransportException` (1 entry, `failover: null`) |
+| Failed | Failed | Data lost; both errors surfaced | `FailoverTransportException` (1 entry, both errors) |
+
+`FailoverTransportException` extends `TransportException`, so existing `catch (TransportException $e)` blocks in
+exporters keep working. The structured `$exception->failures` list is available when you want per-batch detail:
+
+```php
+foreach ($exception->failures as $failure) {
+ $failure['primary']; // \Throwable — the primary error
+ $failure['failover']; // \Throwable|null — null means failover absorbed the batch
+}
+```
+
+### Examples
+
+```php
+withTimeout(30)
+ ->withTimeout(2000)
->withCompression();
$transport = otlp_curl_transport(
@@ -221,11 +319,13 @@ $transport = otlp_curl_transport(
options: $options,
);
+$exporter = otlp_exporter($transport);
+
$telemetry = telemetry(
$resource,
- tracer_provider(batching_span_processor(otlp_span_exporter($transport))),
- meter_provider(batching_metric_processor(otlp_metric_exporter($transport))),
- logger_provider(batching_log_processor(otlp_log_exporter($transport))),
+ tracer_provider(batching_span_processor($exporter)),
+ meter_provider(batching_metric_processor($exporter)),
+ logger_provider(batching_log_processor($exporter)),
);
// Register shutdown handler for graceful termination
@@ -254,7 +354,7 @@ use function Flow\Bridge\Telemetry\OTLP\DSL\{
otlp_curl_transport,
otlp_curl_options,
otlp_protobuf_serializer,
- otlp_span_exporter,
+ otlp_exporter,
};
$transport = otlp_curl_transport(
@@ -266,7 +366,7 @@ $transport = otlp_curl_transport(
$telemetry = telemetry(
resource(['service.name' => 'my-app']),
- tracer_provider(batching_span_processor(otlp_span_exporter($transport))),
+ tracer_provider(batching_span_processor(otlp_exporter($transport))),
);
```
@@ -285,7 +385,7 @@ use function Flow\Bridge\Telemetry\OTLP\DSL\{
otlp_curl_transport,
otlp_curl_options,
otlp_protobuf_serializer,
- otlp_span_exporter,
+ otlp_exporter,
};
$transport = otlp_curl_transport(
@@ -297,7 +397,7 @@ $transport = otlp_curl_transport(
$telemetry = telemetry(
resource(['service.name' => 'my-app']),
- tracer_provider(batching_span_processor(otlp_span_exporter($transport))),
+ tracer_provider(batching_span_processor(otlp_exporter($transport))),
);
```
@@ -394,18 +494,21 @@ With this setup, your PHP application only needs to know about `http://otel-coll
The `CurlTransportOptions` class provides a fluent interface for configuring the curl transport.
-| Method | Description | Default |
-|-----------------------------------------------------------|-------------------------|------------|
-| `withTimeout(int $seconds)` | Request timeout | 30 |
-| `withConnectTimeout(int $seconds)` | Connection timeout | 10 |
-| `withHeader(string $name, string $value)` | Add a single header | - |
-| `withHeaders(array $headers)` | Set all headers | [] |
-| `withCompression(bool $enabled)` | Enable gzip compression | false |
-| `withSslVerification(bool $verifyPeer, bool $verifyHost)` | SSL verification | true, true |
-| `withSslCertificate(string $certPath, ?string $keyPath)` | Client certificate | - |
-| `withCaInfo(string $caInfoPath)` | CA certificate bundle | - |
-| `withProxy(string $proxy)` | Proxy server | - |
-| `withFollowRedirects(bool $follow, int $maxRedirects)` | Redirect behavior | true, 3 |
+| Method | Description | Default |
+|-----------------------------------------------------------|----------------------------------------|------------|
+| `withTimeout(int $milliseconds)` | Per-request total timeout (ms) | 1000 |
+| `withConnectTimeout(int $milliseconds)` | TCP/TLS connect timeout (ms) | 250 |
+| `withShutdownTimeout(int $milliseconds)` | Wall-clock drain budget at shutdown | 5000 |
+| `withHeader(string $name, string $value)` | Add a single header | - |
+| `withHeaders(array $headers)` | Set all headers | [] |
+| `withCompression(bool $enabled)` | Enable gzip compression | false |
+| `withSslVerification(bool $verifyPeer, bool $verifyHost)` | SSL verification | true, true |
+| `withSslCertificate(string $certPath, ?string $keyPath)` | Client certificate | - |
+| `withCaInfo(string $caInfoPath)` | CA certificate bundle | - |
+| `withProxy(string $proxy)` | Proxy server | - |
+| `withFollowRedirects(bool $follow, int $maxRedirects)` | Redirect behavior | true, 3 |
+
+See [Timeouts](#timeouts) for guidance on the default values.
**Example:**
@@ -415,8 +518,8 @@ The `CurlTransportOptions` class provides a fluent interface for configuring the
use function Flow\Bridge\Telemetry\OTLP\DSL\otlp_curl_options;
$options = otlp_curl_options()
- ->withTimeout(60)
- ->withConnectTimeout(15)
+ ->withTimeout(2000)
+ ->withConnectTimeout(500)
->withHeader('Authorization', 'Bearer token')
->withHeader('X-Custom-Header', 'value')
->withCompression()
diff --git a/documentation/components/core/telemetry.md b/documentation/components/core/telemetry.md
index 80730a853..80fb4649f 100644
--- a/documentation/components/core/telemetry.md
+++ b/documentation/components/core/telemetry.md
@@ -26,7 +26,7 @@ use Flow\Clock\SystemClock;
use function Flow\ETL\DSL\{config_builder, df, from_array, telemetry_options, to_output};
use function Flow\Telemetry\DSL\{
batching_log_processor, batching_metric_processor, batching_span_processor,
- console_log_exporter, console_metric_exporter, console_span_exporter,
+ console_exporter, console_exporter, console_exporter,
logger_provider, memory_context_storage, meter_provider, resource, telemetry, tracer_provider
};
@@ -35,9 +35,9 @@ $contextStorage = memory_context_storage();
$telemetry = telemetry(
resource(['service.name' => 'my-etl-pipeline']),
- tracer_provider(batching_span_processor(console_span_exporter()), $clock, $contextStorage),
- meter_provider(batching_metric_processor(console_metric_exporter()), $clock),
- logger_provider(batching_log_processor(console_log_exporter()), $clock, $contextStorage),
+ tracer_provider(batching_span_processor(console_exporter()), $clock, $contextStorage),
+ meter_provider(batching_metric_processor(console_exporter()), $clock),
+ logger_provider(batching_log_processor(console_exporter()), $clock, $contextStorage),
);
$telemetry->registerShutdownFunction();
@@ -113,17 +113,17 @@ Console exporters output telemetry data directly to stdout with ASCII table form
```php
use function Flow\Telemetry\DSL\{
- console_span_exporter, console_metric_exporter, console_log_exporter
+ console_exporter, console_exporter, console_exporter
};
// Spans are displayed as tables with trace IDs, durations, and attributes
-$spanExporter = console_span_exporter(colors: true);
+$spanExporter = console_exporter(colors: true);
// Metrics show counters and throughput values
-$metricExporter = console_metric_exporter(colors: true);
+$metricExporter = console_exporter(colors: true);
// Logs are formatted with severity-based coloring
-$logExporter = console_log_exporter(colors: true, maxBodyLength: 100);
+$logExporter = console_exporter(colors: true, maxBodyLength: 100);
```
### OTLP Export (Production)
@@ -139,7 +139,7 @@ use function Flow\Telemetry\DSL\{
};
use function Flow\Bridge\Telemetry\OTLP\DSL\{
otlp_curl_transport, otlp_json_serializer,
- otlp_log_exporter, otlp_metric_exporter, otlp_span_exporter
+ otlp_exporter, otlp_exporter, otlp_exporter
};
$clock = new SystemClock(new DateTimeZone('UTC'));
@@ -154,32 +154,15 @@ $telemetry = telemetry(
'service.version' => '1.0.0',
'service.namespace' => 'my-company',
]),
- tracer_provider(batching_span_processor(otlp_span_exporter($transport)), $clock, $contextStorage),
- meter_provider(batching_metric_processor(otlp_metric_exporter($transport)), $clock),
- logger_provider(batching_log_processor(otlp_log_exporter($transport)), $clock, $contextStorage),
+ tracer_provider(batching_span_processor(otlp_exporter($transport)), $clock, $contextStorage),
+ meter_provider(batching_metric_processor(otlp_exporter($transport)), $clock),
+ logger_provider(batching_log_processor(otlp_exporter($transport)), $clock, $contextStorage),
);
$telemetry->registerShutdownFunction();
```
### Transport Options
-#### HTTP Transport (PSR-18)
-
-Use with any PSR-18 compatible HTTP client:
-
-```php
-use function Flow\Bridge\Telemetry\OTLP\DSL\{otlp_http_transport, otlp_json_serializer};
-
-$transport = otlp_http_transport(
- client: $psr18Client,
- requestFactory: $psr17Factory,
- streamFactory: $psr17Factory,
- endpoint: 'http://localhost:4318',
- serializer: otlp_json_serializer(),
- headers: ['Authorization' => 'Bearer token'],
-);
-```
-
#### Curl Transport (Async)
Recommended for better performance - uses curl_multi for non-blocking I/O:
@@ -200,15 +183,13 @@ $transport = otlp_curl_transport(
#### gRPC Transport
-For gRPC endpoints (requires ext-grpc):
+For gRPC endpoints (requires ext-grpc). OTLP/gRPC mandates Protobuf, so the transport builds the request factory
+internally — no serializer parameter.
```php
-use function Flow\Bridge\Telemetry\OTLP\DSL\{otlp_grpc_transport, otlp_protobuf_serializer};
+use function Flow\Bridge\Telemetry\OTLP\DSL\otlp_grpc_transport;
-$transport = otlp_grpc_transport(
- endpoint: 'localhost:4317',
- serializer: otlp_protobuf_serializer(),
-);
+$transport = otlp_grpc_transport(endpoint: 'localhost:4317');
```
## Collected Data
@@ -323,24 +304,23 @@ pass_through_log_processor($exporter)
| Function | Description |
|----------|-------------|
-| `console_span_exporter()` | Export spans to console |
-| `console_metric_exporter()` | Export metrics to console |
-| `console_log_exporter()` | Export logs to console |
+| `console_exporter()` | Export spans to console |
+| `console_exporter()` | Export metrics to console |
+| `console_exporter()` | Export logs to console |
### Exporters (OTLP)
| Function | Description |
|----------|-------------|
-| `otlp_span_exporter()` | Export spans via OTLP |
-| `otlp_metric_exporter()` | Export metrics via OTLP |
-| `otlp_log_exporter()` | Export logs via OTLP |
+| `otlp_exporter()` | Export spans via OTLP |
+| `otlp_exporter()` | Export metrics via OTLP |
+| `otlp_exporter()` | Export logs via OTLP |
### Transport (OTLP)
| Function | Description |
|----------|-------------|
| `otlp_curl_transport()` | Async curl transport |
-| `otlp_http_transport()` | PSR-18 HTTP transport |
| `otlp_grpc_transport()` | gRPC transport |
| `otlp_json_serializer()` | JSON serialization |
| `otlp_protobuf_serializer()` | Protobuf serialization |
@@ -349,9 +329,9 @@ pass_through_log_processor($exporter)
| Function | Description |
|----------|-------------|
-| `memory_span_exporter()` | Store spans in memory |
-| `memory_metric_exporter()` | Store metrics in memory |
-| `memory_log_exporter()` | Store logs in memory |
+| `memory_exporter()` | Store spans in memory |
+| `memory_exporter()` | Store metrics in memory |
+| `memory_exporter()` | Store logs in memory |
| `void_span_processor()` | No-op span processor |
| `void_metric_processor()` | No-op metric processor |
| `void_log_processor()` | No-op log processor |
diff --git a/documentation/components/libs/telemetry.md b/documentation/components/libs/telemetry.md
index 29ab08215..dd97e817f 100644
--- a/documentation/components/libs/telemetry.md
+++ b/documentation/components/libs/telemetry.md
@@ -97,9 +97,9 @@ use function Flow\Telemetry\DSL\{
pass_through_span_processor,
pass_through_metric_processor,
pass_through_log_processor,
- console_span_exporter,
- console_metric_exporter,
- console_log_exporter,
+ console_exporter,
+ console_exporter,
+ console_exporter,
};
$resource = resource([
@@ -109,9 +109,9 @@ $resource = resource([
$telemetry = telemetry(
$resource,
- tracer_provider(pass_through_span_processor(console_span_exporter())),
- meter_provider(pass_through_metric_processor(console_metric_exporter())),
- logger_provider(pass_through_log_processor(console_log_exporter())),
+ tracer_provider(pass_through_span_processor(console_exporter())),
+ meter_provider(pass_through_metric_processor(console_exporter())),
+ logger_provider(pass_through_log_processor(console_exporter())),
);
// All telemetry will be printed to console
@@ -483,10 +483,10 @@ This reduces network overhead and improves performance in production environment
*/
+public function transports() : array;
+```
+
+| Implementation | Package | Description |
+|-------------------|----------------------------------|---------------------------------------------------|
+| `VoidExporter` | `flow-php/telemetry` | Discards all data (disabled telemetry) |
+| `MemoryExporter` | `flow-php/telemetry` | Stores logs/metrics/spans in memory (testing) |
+| `ConsoleExporter` | `flow-php/telemetry` | Renders logs/metrics/spans to console (debugging) |
+| `OTLPExporter` | `flow-php/telemetry-otlp-bridge` | Sends batches over the configured Transport |
-### Serializer
+---
-**Interface:** `Flow\Telemetry\Serializer\Serializer`
+### Batch value objects
-Converts telemetry data structures (spans, metrics, logs) into wire formats for transmission over transports.
+Three small immutable value objects in `Flow\Telemetry\Batch` carry the payload across Transport / Exporter boundaries:
-| Implementation | Package | Description |
-|----------------------|----------------------------------|------------------------------------------|
-| `JsonSerializer` | `flow-php/telemetry-otlp-bridge` | OTLP JSON format |
-| `ProtobufSerializer` | `flow-php/telemetry-otlp-bridge` | OTLP Protobuf format (requires ext-grpc) |
+- `LogsBatch(array $entries)`
+- `MetricsBatch(array $metrics)`
+- `TracesBatch(array $spans)`
---
-## Tracer Contracts
-
-### SpanProcessor
+### ContextStorage
-**Interface:** `Flow\Telemetry\Tracer\SpanProcessor`
+**Interface:** `Flow\Telemetry\Context\ContextStorage`
-Receives span lifecycle events (`onStart`, `onEnd`) and determines how spans are buffered and exported. The processor sits between the Tracer and SpanExporter.
+Stores and retrieves telemetry context (active span, baggage) within a request lifecycle. Enables automatic context
+propagation to child spans.
-| Implementation | Package | Description |
-|----------------------------|----------------------|----------------------------------------------------|
-| `PassThroughSpanProcessor` | `flow-php/telemetry` | Exports each span immediately when it ends |
-| `BatchingSpanProcessor` | `flow-php/telemetry` | Buffers spans and exports in configurable batches |
-| `CompositeSpanProcessor` | `flow-php/telemetry` | Delegates to multiple processors |
-| `MemorySpanProcessor` | `flow-php/telemetry` | Stores spans in memory for testing |
-| `VoidSpanProcessor` | `flow-php/telemetry` | No-op processor that discards all spans |
+| Implementation | Package | Description |
+|------------------------|----------------------|--------------------------------------------|
+| `MemoryContextStorage` | `flow-php/telemetry` | In-memory storage for single-threaded apps |
---
-### SpanExporter
+## Tracer Contracts
+
+### SpanProcessor
-**Interface:** `Flow\Telemetry\Tracer\SpanExporter`
+**Interface:** `Flow\Telemetry\Tracer\SpanProcessor`
-Exports completed spans to external observability backends. Receives batches of spans from processors.
+Receives span lifecycle events (`onStart`, `onEnd`) and determines how spans are buffered and exported. The processor
+sits between the Tracer and the unified `Exporter`. The `exporter()` accessor returns the unified `Exporter`.
-| Implementation | Package | Description |
-|-----------------------|----------------------------------|---------------------------------------|
-| `ConsoleSpanExporter` | `flow-php/telemetry` | Prints spans to console (development) |
-| `MemorySpanExporter` | `flow-php/telemetry` | Stores spans in memory (testing) |
-| `VoidSpanExporter` | `flow-php/telemetry` | Discards all spans (disabled tracing) |
-| `OTLPSpanExporter` | `flow-php/telemetry-otlp-bridge` | Exports to OTLP-compatible backends |
+| Implementation | Package | Description |
+|----------------------------|----------------------|---------------------------------------------------|
+| `PassThroughSpanProcessor` | `flow-php/telemetry` | Exports each span immediately when it ends |
+| `BatchingSpanProcessor` | `flow-php/telemetry` | Buffers spans and exports in configurable batches |
+| `CompositeSpanProcessor` | `flow-php/telemetry` | Delegates to multiple processors |
+| `MemorySpanProcessor` | `flow-php/telemetry` | Stores spans in memory for testing |
+| `VoidSpanProcessor` | `flow-php/telemetry` | No-op processor that discards all spans |
---
@@ -85,14 +99,15 @@ Exports completed spans to external observability backends. Receives batches of
**Interface:** `Flow\Telemetry\Tracer\Sampler\Sampler`
-Makes sampling decisions for traces. Determines whether a span should be recorded and exported based on configurable rules.
+Makes sampling decisions for traces. Determines whether a span should be recorded and exported based on configurable
+rules.
-| Implementation | Package | Description |
-|----------------------------|----------------------|------------------------------------------------------|
-| `AlwaysOnSampler` | `flow-php/telemetry` | Records all traces |
-| `AlwaysOffSampler` | `flow-php/telemetry` | Records no traces |
-| `TraceIdRatioBasedSampler` | `flow-php/telemetry` | Records a configurable percentage of traces |
-| `ParentBasedSampler` | `flow-php/telemetry` | Inherits sampling decision from parent span context |
+| Implementation | Package | Description |
+|----------------------------|----------------------|-----------------------------------------------------|
+| `AlwaysOnSampler` | `flow-php/telemetry` | Records all traces |
+| `AlwaysOffSampler` | `flow-php/telemetry` | Records no traces |
+| `TraceIdRatioBasedSampler` | `flow-php/telemetry` | Records a configurable percentage of traces |
+| `ParentBasedSampler` | `flow-php/telemetry` | Inherits sampling decision from parent span context |
---
@@ -102,9 +117,9 @@ Makes sampling decisions for traces. Determines whether a span should be recorde
Represents timestamped events that occurred during a span's lifetime (e.g., exceptions, state changes).
-| Implementation | Package | Description |
-|-----------------|----------------------|------------------------------------|
-| `GenericEvent` | `flow-php/telemetry` | General-purpose event with attributes |
+| Implementation | Package | Description |
+|----------------|----------------------|---------------------------------------|
+| `GenericEvent` | `flow-php/telemetry` | General-purpose event with attributes |
---
@@ -114,30 +129,16 @@ Represents timestamped events that occurred during a span's lifetime (e.g., exce
**Interface:** `Flow\Telemetry\Meter\MetricProcessor`
-Processes metric measurements from instruments. Determines how metrics are aggregated and when they are exported.
-
-| Implementation | Package | Description |
-|------------------------------|----------------------|------------------------------------------------------|
-| `PassThroughMetricProcessor` | `flow-php/telemetry` | Exports each metric immediately when recorded |
-| `BatchingMetricProcessor` | `flow-php/telemetry` | Buffers metrics and exports in configurable batches |
-| `CompositeMetricProcessor` | `flow-php/telemetry` | Delegates to multiple processors |
-| `MemoryMetricProcessor` | `flow-php/telemetry` | Stores metrics in memory for testing |
-| `VoidMetricProcessor` | `flow-php/telemetry` | No-op processor that discards all metrics |
-
----
-
-### MetricExporter
-
-**Interface:** `Flow\Telemetry\Meter\MetricExporter`
-
-Exports collected metrics to external observability backends. Receives batches of metrics from processors.
+Processes metric measurements from instruments. Determines how metrics are aggregated and when they are exported. The
+`exporter()` accessor returns the unified `Exporter`.
-| Implementation | Package | Description |
-|-------------------------|----------------------------------|-----------------------------------------|
-| `ConsoleMetricExporter` | `flow-php/telemetry` | Prints metrics to console (development) |
-| `MemoryMetricExporter` | `flow-php/telemetry` | Stores metrics in memory (testing) |
-| `VoidMetricExporter` | `flow-php/telemetry` | Discards all metrics (disabled metrics) |
-| `OTLPMetricExporter` | `flow-php/telemetry-otlp-bridge` | Exports to OTLP-compatible backends |
+| Implementation | Package | Description |
+|------------------------------|----------------------|-----------------------------------------------------|
+| `PassThroughMetricProcessor` | `flow-php/telemetry` | Exports each metric immediately when recorded |
+| `BatchingMetricProcessor` | `flow-php/telemetry` | Buffers metrics and exports in configurable batches |
+| `CompositeMetricProcessor` | `flow-php/telemetry` | Delegates to multiple processors |
+| `MemoryMetricProcessor` | `flow-php/telemetry` | Stores metrics in memory for testing |
+| `VoidMetricProcessor` | `flow-php/telemetry` | No-op processor that discards all metrics |
---
@@ -147,12 +148,12 @@ Exports collected metrics to external observability backends. Receives batches o
Base interface for metric instruments. Instruments are the API through which measurements are recorded.
-| Implementation | Package | Description |
-|-----------------|----------------------|-----------------------------------------------------|
-| `Counter` | `flow-php/telemetry` | Monotonically increasing value (e.g., requests) |
-| `UpDownCounter` | `flow-php/telemetry` | Value that can increase or decrease (e.g., queue) |
-| `Gauge` | `flow-php/telemetry` | Point-in-time measurement (e.g., CPU usage) |
-| `Histogram` | `flow-php/telemetry` | Distribution of values (e.g., latency) |
+| Implementation | Package | Description |
+|-----------------|----------------------|---------------------------------------------------|
+| `Counter` | `flow-php/telemetry` | Monotonically increasing value (e.g., requests) |
+| `UpDownCounter` | `flow-php/telemetry` | Value that can increase or decrease (e.g., queue) |
+| `Gauge` | `flow-php/telemetry` | Point-in-time measurement (e.g., CPU usage) |
+| `Histogram` | `flow-php/telemetry` | Distribution of values (e.g., latency) |
---
@@ -162,30 +163,17 @@ Base interface for metric instruments. Instruments are the API through which mea
**Interface:** `Flow\Telemetry\Logger\LogProcessor`
-Processes log records from loggers. Determines how logs are buffered and when they are exported.
-
-| Implementation | Package | Description |
-|---------------------------|----------------------|----------------------------------------------------|
-| `PassThroughLogProcessor` | `flow-php/telemetry` | Exports each log immediately when recorded |
-| `BatchingLogProcessor` | `flow-php/telemetry` | Buffers logs and exports in configurable batches |
-| `CompositeLogProcessor` | `flow-php/telemetry` | Delegates to multiple processors |
-| `MemoryLogProcessor` | `flow-php/telemetry` | Stores logs in memory for testing |
-| `VoidLogProcessor` | `flow-php/telemetry` | No-op processor that discards all logs |
-
----
-
-### LogExporter
-
-**Interface:** `Flow\Telemetry\Logger\LogExporter`
+Processes log records from loggers. Determines how logs are buffered and when they are exported. The `exporter()`
+accessor returns the unified `Exporter`.
-Exports log records to external observability backends. Receives batches of logs from processors.
-
-| Implementation | Package | Description |
-|----------------------|----------------------------------|--------------------------------------|
-| `ConsoleLogExporter` | `flow-php/telemetry` | Prints logs to console (development) |
-| `MemoryLogExporter` | `flow-php/telemetry` | Stores logs in memory (testing) |
-| `VoidLogExporter` | `flow-php/telemetry` | Discards all logs (disabled logging) |
-| `OTLPLogExporter` | `flow-php/telemetry-otlp-bridge` | Exports to OTLP-compatible backends |
+| Implementation | Package | Description |
+|---------------------------------|----------------------|---------------------------------------------------|
+| `PassThroughLogProcessor` | `flow-php/telemetry` | Exports each log immediately when recorded |
+| `BatchingLogProcessor` | `flow-php/telemetry` | Buffers logs and exports in configurable batches |
+| `CompositeLogProcessor` | `flow-php/telemetry` | Delegates to multiple processors |
+| `SeverityFilteringLogProcessor` | `flow-php/telemetry` | Filters log entries by minimum severity threshold |
+| `MemoryLogProcessor` | `flow-php/telemetry` | Stores logs in memory for testing |
+| `VoidLogProcessor` | `flow-php/telemetry` | No-op processor that discards all logs |
---
@@ -210,9 +198,9 @@ Extracts and injects trace context across process boundaries using text-based ca
Propagates application-specific key-value pairs (baggage) across process boundaries alongside trace context.
-| Implementation | Package | Description |
-|----------------|----------------------|----------------------------------|
-| `W3CBaggage` | `flow-php/telemetry` | W3C Baggage standard |
+| Implementation | Package | Description |
+|----------------|----------------------|----------------------|
+| `W3CBaggage` | `flow-php/telemetry` | W3C Baggage standard |
---
@@ -230,31 +218,35 @@ Abstraction over the transport mechanism for context propagation. Provides get/s
## Implementing Custom Contracts
-All contracts are designed for extension. To implement a custom exporter, processor, or transport:
-
-1. Implement the relevant interface
-2. Pass your implementation to the appropriate provider or processor
+All contracts are designed for extension. To implement a custom exporter:
```php
$spans
- */
- public function export(array $spans): bool
+ public function export(LogsBatch|MetricsBatch|TracesBatch $batch) : bool
{
- foreach ($spans as $span) {
- // Send to your custom backend
- }
+ match (true) {
+ $batch instanceof TracesBatch => $this->writeSpans($batch->spans),
+ $batch instanceof MetricsBatch => $this->writeMetrics($batch->metrics),
+ $batch instanceof LogsBatch => $this->writeLogs($batch->entries),
+ };
+
return true;
}
+
+ public function transports() : array
+ {
+ return [new VoidTransport()];
+ }
+
+ // ... per-signal helpers
}
-// Use with any processor
-$processor = pass_through_span_processor(new MyCustomSpanExporter());
+$processor = pass_through_span_processor(new MyCustomExporter());
```
diff --git a/documentation/contributing/nix.md b/documentation/contributing/nix.md
index 1207d390b..5eb15410a 100644
--- a/documentation/contributing/nix.md
+++ b/documentation/contributing/nix.md
@@ -140,6 +140,16 @@ nix-shell --arg with-arrow-ext false --arg with-rust true
See [Rust - Arrow Extension Development](/documentation/contributing/rust.md) for details.
+### Protobuf / gRPC Code Generation (protoc)
+
+Adds `protoc` and `grpc_php_plugin` to the shell. Required when regenerating OTLP protobuf and gRPC PHP classes via `composer build:telemetry:otlp:protobuf`.
+
+```shell
+nix-shell --arg with-protoc true
+```
+
+The `ext-protobuf` and `ext-grpc` PHP extensions are part of the default shell (`with-protobuf` / `with-grpc`, both default `true`); pass `false` to opt out.
+
### Combining Multiple Options
You can combine multiple arguments:
diff --git a/documentation/installation/packages/phpunit-telemetry-bridge.md b/documentation/installation/packages/phpunit-telemetry-bridge.md
index e1b87a7f6..9ba32f99f 100644
--- a/documentation/installation/packages/phpunit-telemetry-bridge.md
+++ b/documentation/installation/packages/phpunit-telemetry-bridge.md
@@ -29,8 +29,7 @@ The default `curl` transport works with the core dependencies only. To use the `
- PHP extension [ext-grpc](https://github.com/grpc/grpc/tree/master/src/php)
- [grpc/grpc](https://packagist.org/packages/grpc/grpc)
- [google/protobuf](https://packagist.org/packages/google/protobuf)
-- [open-telemetry/gen-otlp-protobuf](https://packagist.org/packages/open-telemetry/gen-otlp-protobuf)
```bash
-composer require --dev grpc/grpc google/protobuf open-telemetry/gen-otlp-protobuf
+composer require --dev grpc/grpc google/protobuf
```
diff --git a/documentation/installation/packages/telemetry-otlp-bridge.md b/documentation/installation/packages/telemetry-otlp-bridge.md
index 78233d2ca..222216b68 100644
--- a/documentation/installation/packages/telemetry-otlp-bridge.md
+++ b/documentation/installation/packages/telemetry-otlp-bridge.md
@@ -18,14 +18,9 @@ seo_description: >
composer require flow-php/telemetry-otlp-bridge:~--FLOW_PHP_VERSION--
```
-## Core Dependencies
-
-- [psr/http-client](https://packagist.org/packages/psr/http-client)
-- [psr/http-factory](https://packagist.org/packages/psr/http-factory)
-
## Suggested Dependencies
```bash
-# For gRPC transport support (requires ext-grpc PHP extension)
-composer require google/protobuf open-telemetry/gen-otlp-protobuf
+# For Protobuf serializer (used by Curl+Protobuf or gRPC transports)
+composer require google/protobuf
```
diff --git a/phpstan.neon b/phpstan.neon
index a788a85e7..a948ac95b 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -108,6 +108,8 @@ parameters:
- src/lib/parquet/src/Flow/Parquet/BinaryReader/*
- src/lib/parquet/src/Flow/Parquet/Dremel/ColumnData/DefinitionConverter.php
- src/lib/postgresql/src/Flow/PostgreSql/Protobuf/*
+ - src/bridge/telemetry/otlp/src/Opentelemetry/*
+ - src/bridge/telemetry/otlp/src/GPBMetadata/*
- src/bridge/symfony/filesystem-bundle/src/Flow/Bridge/Symfony/FilesystemBundle/Resources/config/*
- src/bridge/symfony/postgresql-bundle/src/Flow/Bridge/Symfony/PostgreSqlBundle/Resources/config/*
- src/bridge/symfony/postgresql-bundle/tests/Flow/Bridge/Symfony/PostgreSqlBundle/Tests/Integration/Command/*
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index f827baf8f..a911a836b 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -312,6 +312,8 @@
src/lib/parquet/src/Flow/Parquet/Thrift
+ src/bridge/telemetry/otlp/src/GPBMetadata
+ src/bridge/telemetry/otlp/src/Opentelemetry
src/lib/postgresql/src/Flow/PostgreSql/Protobuf
src/core/etl/src/Flow/ETL/DSL/functions.php
src/lib/postgresql/src/Flow/PostgreSql/DSL/functions.php
diff --git a/rector.src.php b/rector.src.php
index ba28e7f50..ec17acb33 100644
--- a/rector.src.php
+++ b/rector.src.php
@@ -26,6 +26,8 @@
ArrowFunctionDelegatingCallToFirstClassCallableRector::class,
StringClassNameToClassConstantRector::class,
__DIR__ . '/src/lib/parquet/src/Flow/Parquet/ThriftModel/*',
+ __DIR__ . '/src/bridge/telemetry/otlp/src/Opentelemetry/*',
+ __DIR__ . '/src/bridge/telemetry/otlp/src/GPBMetadata/*',
// rector 2.4.2 hangs parsing this file; revisit after upstream fix
__DIR__ . '/src/core/etl/src/Flow/ETL/Formatter/ASCII/ASCIIValue.php',
// Symfony DI requires array format for setFactory(), first-class callable syntax is not supported
diff --git a/shell.nix b/shell.nix
index 288c857b9..59dc45768 100644
--- a/shell.nix
+++ b/shell.nix
@@ -9,7 +9,9 @@
with-rust ? false,
with-terraform ? false,
with-wasm ? false,
- with-grpc ? false
+ with-grpc ? true,
+ with-protobuf ? true,
+ with-protoc ? false
}:
assert (!(with-rust && with-arrow-ext)) || builtins.throw "Cannot use --arg with-rust true and --arg with-arrow-ext true together. Use: --arg with-arrow-ext false --arg with-rust true";
@@ -64,7 +66,7 @@ let
php = pkgs.callPackage ./.nix/pkgs/flow-php/package.nix {
php = base-php;
inherit php-snappy php-lz4 php-brotli php-zstd php-pg-query-ext php-arrow-ext
- with-pcov with-xdebug with-blackfire with-pg-query-ext with-arrow-ext with-grpc;
+ with-pcov with-xdebug with-blackfire with-pg-query-ext with-arrow-ext with-grpc with-protobuf;
};
in
pkgs.mkShell {
@@ -118,6 +120,12 @@ pkgs.mkShell {
pkgs.pkg-config
php.unwrapped.dev
]
+ ++ pkgs.lib.optionals with-protoc [
+ # protoc + grpc_php_plugin for regenerating protobuf/gRPC PHP classes
+ pkgs.protobuf
+ pkgs.grpc
+ pkgs.git
+ ]
;
shellHook = ''
diff --git a/src/bridge/monolog/telemetry/src/Flow/Bridge/Monolog/Telemetry/DSL/functions.php b/src/bridge/monolog/telemetry/src/Flow/Bridge/Monolog/Telemetry/DSL/functions.php
index 2b2000f00..42e70a452 100644
--- a/src/bridge/monolog/telemetry/src/Flow/Bridge/Monolog/Telemetry/DSL/functions.php
+++ b/src/bridge/monolog/telemetry/src/Flow/Bridge/Monolog/Telemetry/DSL/functions.php
@@ -6,6 +6,7 @@
use Flow\Bridge\Monolog\Telemetry\{LogRecordConverter, SeverityMapper, TelemetryHandler, ValueNormalizer};
use Flow\ETL\Attribute\{DocumentationDSL, Module, Type as DSLType};
+use Flow\Telemetry\ErrorHandler\{ErrorHandler, ErrorLogHandler};
use Flow\Telemetry\Logger\{Logger, Severity};
use Monolog\Level;
@@ -146,6 +147,7 @@ function telemetry_handler(
LogRecordConverter $converter = new LogRecordConverter(),
Level $level = Level::Debug,
bool $bubble = true,
+ ErrorHandler $errorHandler = new ErrorLogHandler(),
) : TelemetryHandler {
- return new TelemetryHandler($logger, $converter, $level, $bubble);
+ return new TelemetryHandler($logger, $converter, $level, $bubble, $errorHandler);
}
diff --git a/src/bridge/monolog/telemetry/src/Flow/Bridge/Monolog/Telemetry/TelemetryHandler.php b/src/bridge/monolog/telemetry/src/Flow/Bridge/Monolog/Telemetry/TelemetryHandler.php
index 929958e73..826bcf0b4 100644
--- a/src/bridge/monolog/telemetry/src/Flow/Bridge/Monolog/Telemetry/TelemetryHandler.php
+++ b/src/bridge/monolog/telemetry/src/Flow/Bridge/Monolog/Telemetry/TelemetryHandler.php
@@ -4,6 +4,7 @@
namespace Flow\Bridge\Monolog\Telemetry;
+use Flow\Telemetry\ErrorHandler\{ErrorHandler, ErrorLogHandler};
use Flow\Telemetry\Logger\Logger;
use Monolog\Handler\AbstractProcessingHandler;
use Monolog\{Level, LogRecord};
@@ -43,12 +44,17 @@ public function __construct(
private readonly LogRecordConverter $converter = new LogRecordConverter(),
Level $level = Level::Debug,
bool $bubble = true,
+ private readonly ErrorHandler $errorHandler = new ErrorLogHandler(),
) {
parent::__construct($level, $bubble);
}
protected function write(LogRecord $record) : void
{
- $this->logger->emit($this->converter->convert($record));
+ try {
+ $this->logger->emit($this->converter->convert($record));
+ } catch (\Throwable $e) {
+ $this->errorHandler->handle($e);
+ }
}
}
diff --git a/src/bridge/monolog/telemetry/tests/Flow/Bridge/Monolog/Telemetry/Tests/Integration/TelemetryTestContext.php b/src/bridge/monolog/telemetry/tests/Flow/Bridge/Monolog/Telemetry/Tests/Integration/TelemetryTestContext.php
index de7345f71..7f6cae11f 100644
--- a/src/bridge/monolog/telemetry/tests/Flow/Bridge/Monolog/Telemetry/Tests/Integration/TelemetryTestContext.php
+++ b/src/bridge/monolog/telemetry/tests/Flow/Bridge/Monolog/Telemetry/Tests/Integration/TelemetryTestContext.php
@@ -8,7 +8,7 @@
use Flow\Telemetry\Logger\{Logger, LoggerProvider};
use Flow\Telemetry\Provider\Clock\SystemClock;
use Flow\Telemetry\Provider\Memory\MemoryLogProcessor;
-use Flow\Telemetry\Provider\Void\{VoidLogExporter, VoidSpanProcessor};
+use Flow\Telemetry\Provider\Void\{VoidExporter, VoidSpanProcessor};
use Flow\Telemetry\Resource;
use Flow\Telemetry\Tracer\{Tracer, TracerProvider};
@@ -34,7 +34,7 @@ public static function create(
$contextStorage = new MemoryContextStorage();
$clock = new SystemClock();
$resource ??= Resource::create(['service.name' => 'test-service']);
- $processor = new MemoryLogProcessor(new VoidLogExporter());
+ $processor = new MemoryLogProcessor(new VoidExporter());
$loggerProvider = new LoggerProvider(
$processor,
@@ -65,7 +65,7 @@ public static function createWithTracing(
$contextStorage = new MemoryContextStorage();
$clock = new SystemClock();
$resource ??= Resource::create(['service.name' => 'test-service']);
- $processor = new MemoryLogProcessor(new VoidLogExporter());
+ $processor = new MemoryLogProcessor(new VoidExporter());
$loggerProvider = new LoggerProvider(
$processor,
diff --git a/src/bridge/monolog/telemetry/tests/Flow/Bridge/Monolog/Telemetry/Tests/Unit/TelemetryHandlerErrorHandlingTest.php b/src/bridge/monolog/telemetry/tests/Flow/Bridge/Monolog/Telemetry/Tests/Unit/TelemetryHandlerErrorHandlingTest.php
new file mode 100644
index 000000000..3b7865c45
--- /dev/null
+++ b/src/bridge/monolog/telemetry/tests/Flow/Bridge/Monolog/Telemetry/Tests/Unit/TelemetryHandlerErrorHandlingTest.php
@@ -0,0 +1,74 @@
+expectNotToPerformAssertions();
+
+ $throwingClock = new class implements ClockInterface {
+ public function now() : \DateTimeImmutable
+ {
+ throw new \RuntimeException('clock blew up');
+ }
+ };
+
+ $loggerProvider = new LoggerProvider(
+ new MemoryLogProcessor(new VoidExporter()),
+ $throwingClock,
+ new MemoryContextStorage(),
+ );
+
+ $logger = $loggerProvider->logger(Resource::empty(), 'test-scope');
+ $spy = new ErrorHandlerSpy();
+ $handler = new TelemetryHandler($logger, level: Level::Debug, errorHandler: $spy);
+
+ $monolog = new MonologLogger('test-channel');
+ $monolog->pushHandler($handler);
+
+ $monolog->info('hello');
+ }
+
+ public function test_routes_emit_failures_to_error_handler() : void
+ {
+ $throwingClock = new class implements ClockInterface {
+ public function now() : \DateTimeImmutable
+ {
+ throw new \RuntimeException('clock blew up');
+ }
+ };
+
+ $loggerProvider = new LoggerProvider(
+ new MemoryLogProcessor(new VoidExporter()),
+ $throwingClock,
+ new MemoryContextStorage(),
+ );
+
+ $logger = $loggerProvider->logger(Resource::empty(), 'test-scope');
+ $spy = new ErrorHandlerSpy();
+ $handler = new TelemetryHandler($logger, errorHandler: $spy);
+
+ $monolog = new MonologLogger('test-channel');
+ $monolog->pushHandler($handler);
+
+ $monolog->info('hello');
+
+ self::assertSame(1, $spy->count());
+ self::assertSame('clock blew up', $spy->last()?->getMessage());
+ }
+}
diff --git a/src/bridge/monolog/telemetry/tests/Flow/Bridge/Monolog/Telemetry/Tests/Unit/TelemetryHandlerTest.php b/src/bridge/monolog/telemetry/tests/Flow/Bridge/Monolog/Telemetry/Tests/Unit/TelemetryHandlerTest.php
index c7c5f5011..d5c714dde 100644
--- a/src/bridge/monolog/telemetry/tests/Flow/Bridge/Monolog/Telemetry/Tests/Unit/TelemetryHandlerTest.php
+++ b/src/bridge/monolog/telemetry/tests/Flow/Bridge/Monolog/Telemetry/Tests/Unit/TelemetryHandlerTest.php
@@ -9,7 +9,7 @@
use Flow\Telemetry\Logger\{LoggerProvider, Severity};
use Flow\Telemetry\Provider\Clock\SystemClock;
use Flow\Telemetry\Provider\Memory\MemoryLogProcessor;
-use Flow\Telemetry\Provider\Void\VoidLogExporter;
+use Flow\Telemetry\Provider\Void\VoidExporter;
use Flow\Telemetry\Resource;
use Monolog\{Level, Logger as MonologLogger};
use PHPUnit\Framework\Attributes\{CoversClass, DataProvider};
@@ -39,7 +39,7 @@ public static function levelToSeverityProvider() : \Generator
protected function setUp() : void
{
- $this->processor = new MemoryLogProcessor(new VoidLogExporter());
+ $this->processor = new MemoryLogProcessor(new VoidExporter());
$loggerProvider = new LoggerProvider(
$this->processor,
@@ -57,7 +57,7 @@ protected function setUp() : void
public function test_handler_accepts_custom_converter() : void
{
- $processor = new MemoryLogProcessor(new VoidLogExporter());
+ $processor = new MemoryLogProcessor(new VoidExporter());
$loggerProvider = new LoggerProvider(
$processor,
@@ -245,7 +245,7 @@ public function test_handler_normalizes_objects_without_to_string_to_class_name(
public function test_handler_respects_minimum_level() : void
{
- $processor = new MemoryLogProcessor(new VoidLogExporter());
+ $processor = new MemoryLogProcessor(new VoidExporter());
$loggerProvider = new LoggerProvider(
$processor,
diff --git a/src/bridge/phpunit/telemetry/src/Flow/Bridge/PHPUnit/Telemetry/Configuration.php b/src/bridge/phpunit/telemetry/src/Flow/Bridge/PHPUnit/Telemetry/Configuration.php
index d5d37c266..c7a02f10e 100644
--- a/src/bridge/phpunit/telemetry/src/Flow/Bridge/PHPUnit/Telemetry/Configuration.php
+++ b/src/bridge/phpunit/telemetry/src/Flow/Bridge/PHPUnit/Telemetry/Configuration.php
@@ -4,15 +4,44 @@
namespace Flow\Bridge\PHPUnit\Telemetry;
+use Flow\Telemetry\ErrorHandler\{ErrorLogMessageType, SyslogFacility, SyslogSeverity};
use PHPUnit\Runner\Extension\ParameterCollection;
final readonly class Configuration
{
+ public const int DEFAULT_BATCH_SIZE = 512;
+
+ public const int DEFAULT_CONNECT_TIMEOUT_MS = 250;
+
public const string DEFAULT_ENDPOINT = 'http://localhost:4318';
+ public const int DEFAULT_FILE_PERMISSIONS = 0644;
+
+ public const string DEFAULT_MESSAGE_PREFIX = '[flow-telemetry]';
+
+ public const int DEFAULT_SHUTDOWN_TIMEOUT_MS = 5000;
+
+ public const int DEFAULT_TIMEOUT_MS = 250;
+
+ public const string ERROR_HANDLER_LOG = 'error_log';
+
+ public const string ERROR_HANDLER_NOOP = 'noop';
+
+ public const string ERROR_HANDLER_STREAM = 'stream';
+
+ public const string ERROR_HANDLER_SYSLOG = 'syslog';
+
+ public const string ERROR_HANDLER_UDP_SYSLOG = 'udp_syslog';
+
+ public const string TRANSPORT_CURL = 'curl';
+
+ public const string TRANSPORT_GRPC = 'grpc';
+
+ public const string TRANSPORT_STREAM = 'stream';
+
private const array CURL_SPECIFIC_PARAMS = [
- 'curl_timeout',
- 'curl_connect_timeout',
+ 'curl_timeout_ms',
+ 'curl_connect_timeout_ms',
'curl_compression',
'curl_follow_redirects',
'curl_max_redirects',
@@ -27,23 +56,60 @@
private const string ENV_PREFIX = 'FLOW_PHPUNIT_OTEL_';
+ private const array ERROR_LOG_HANDLER_PARAMS = [
+ 'error_handler_message_type',
+ 'error_handler_expand_newlines',
+ 'error_handler_message_prefix',
+ ];
+
private const array GRPC_SPECIFIC_PARAMS = [
'grpc_insecure',
+ 'grpc_timeout_ms',
];
private const array SHARED_PARAMS = [
'transport',
'endpoint',
'headers',
+ 'shutdown_timeout_ms',
+ ];
+
+ private const array STREAM_HANDLER_PARAMS = [
+ 'error_handler_destination',
+ 'error_handler_file_permissions',
+ 'error_handler_create_directories',
+ 'error_handler_message_prefix',
+ ];
+
+ private const array STREAM_TRANSPORT_PARAMS = [
+ 'stream_file_permissions',
+ 'stream_create_directories',
+ ];
+
+ private const array SYSLOG_HANDLER_PARAMS = [
+ 'error_handler_ident',
+ 'error_handler_facility',
+ 'error_handler_log_opts',
+ 'error_handler_severity',
+ ];
+
+ private const array UDP_SYSLOG_HANDLER_PARAMS = [
+ 'error_handler_host',
+ 'error_handler_port',
+ 'error_handler_ident',
+ 'error_handler_facility',
+ 'error_handler_severity',
];
public function __construct(
public string $serviceName,
- public CurlTransportConfig|GrpcTransportConfig $transport,
+ public CurlTransportConfig|GrpcTransportConfig|StreamTransportConfig $transport,
public bool $emitTraces,
public bool $emitMetrics,
public bool $emitTestSpans,
public bool $emitTestCaseSpans,
+ public int $batchSize,
+ public ErrorLogHandlerConfig|NullErrorHandlerConfig|StreamErrorHandlerConfig|SyslogErrorHandlerConfig|UdpSyslogErrorHandlerConfig $errorHandler,
) {
}
@@ -54,7 +120,7 @@ public static function fromParameters(ParameterCollection $parameters) : self
if ($legacyUrl !== null) {
self::rejectParams(
$parameters,
- \array_merge(self::SHARED_PARAMS, self::CURL_SPECIFIC_PARAMS, self::GRPC_SPECIFIC_PARAMS),
+ \array_merge(self::SHARED_PARAMS, self::CURL_SPECIFIC_PARAMS, self::GRPC_SPECIFIC_PARAMS, self::STREAM_TRANSPORT_PARAMS),
'Deprecated parameter "otel_collector_url" cannot be mixed with new parameter "%s", migrate fully to the new parameter shape.',
);
@\trigger_error(
@@ -63,57 +129,82 @@ public static function fromParameters(ParameterCollection $parameters) : self
);
}
- $transportType = self::resolve($parameters, 'transport') ?? 'curl';
+ return new self(
+ serviceName: self::resolve($parameters, 'service_name') ?? 'phpunit',
+ transport: self::resolveTransport($parameters, $legacyUrl),
+ emitTraces: self::resolveBool($parameters, 'emit_traces', true),
+ emitMetrics: self::resolveBool($parameters, 'emit_metrics', true),
+ emitTestSpans: self::resolveBool($parameters, 'emit_test_spans', true),
+ emitTestCaseSpans: self::resolveBool($parameters, 'emit_test_case_spans', true),
+ batchSize: self::resolveInt($parameters, 'batch_size', self::DEFAULT_BATCH_SIZE),
+ errorHandler: self::resolveErrorHandler($parameters),
+ );
+ }
- if (!\in_array($transportType, ['curl', 'grpc'], true)) {
- throw new \InvalidArgumentException(\sprintf(
- 'Invalid transport "%s", expected "curl" or "grpc".',
- $transportType,
- ));
+ private static function buildErrorLogHandlerConfig(ParameterCollection $parameters) : ErrorLogHandlerConfig
+ {
+ self::rejectForeignHandlerParams($parameters, self::ERROR_HANDLER_LOG, self::ERROR_LOG_HANDLER_PARAMS);
+
+ return new ErrorLogHandlerConfig(
+ messageType: self::resolveErrorLogMessageType($parameters),
+ expandNewlines: self::resolveBool($parameters, 'error_handler_expand_newlines', false),
+ messagePrefix: self::resolve($parameters, 'error_handler_message_prefix') ?? self::DEFAULT_MESSAGE_PREFIX,
+ );
+ }
+
+ private static function buildNullErrorHandlerConfig(ParameterCollection $parameters) : NullErrorHandlerConfig
+ {
+ self::rejectForeignHandlerParams($parameters, self::ERROR_HANDLER_NOOP, []);
+
+ return new NullErrorHandlerConfig();
+ }
+
+ private static function buildStreamErrorHandlerConfig(ParameterCollection $parameters) : StreamErrorHandlerConfig
+ {
+ self::rejectForeignHandlerParams($parameters, self::ERROR_HANDLER_STREAM, self::STREAM_HANDLER_PARAMS);
+
+ $destination = self::resolve($parameters, 'error_handler_destination');
+
+ if ($destination === null || $destination === '') {
+ throw new \InvalidArgumentException('Parameter "error_handler_destination" is required for error_handler "stream".');
}
- $endpoint = $legacyUrl
- ?? self::resolve($parameters, 'endpoint')
- ?? self::DEFAULT_ENDPOINT;
+ return new StreamErrorHandlerConfig(
+ destination: $destination,
+ filePermissions: self::resolveFilePermissions($parameters, 'error_handler_file_permissions'),
+ createDirectories: self::resolveBool($parameters, 'error_handler_create_directories', true),
+ messagePrefix: self::resolve($parameters, 'error_handler_message_prefix') ?? self::DEFAULT_MESSAGE_PREFIX,
+ );
+ }
- $headers = self::parseHeaders(self::resolve($parameters, 'headers') ?? '');
+ private static function buildSyslogErrorHandlerConfig(ParameterCollection $parameters) : SyslogErrorHandlerConfig
+ {
+ self::rejectForeignHandlerParams($parameters, self::ERROR_HANDLER_SYSLOG, self::SYSLOG_HANDLER_PARAMS);
- if ($transportType === 'curl') {
- self::rejectParams($parameters, self::GRPC_SPECIFIC_PARAMS, 'Parameter "%s" cannot be used with transport "curl".');
+ return new SyslogErrorHandlerConfig(
+ ident: self::resolve($parameters, 'error_handler_ident') ?? 'flow-telemetry',
+ facility: self::resolveSyslogFacility($parameters),
+ logOpts: self::resolveInt($parameters, 'error_handler_log_opts', \LOG_PID),
+ severity: self::resolveSyslogSeverity($parameters),
+ );
+ }
- $transport = new CurlTransportConfig(
- endpoint: $endpoint,
- headers: $headers,
- timeout: self::resolveInt($parameters, 'curl_timeout', 30),
- connectTimeout: self::resolveInt($parameters, 'curl_connect_timeout', 10),
- compression: self::resolveBool($parameters, 'curl_compression', false),
- followRedirects: self::resolveBool($parameters, 'curl_follow_redirects', true),
- maxRedirects: self::resolveInt($parameters, 'curl_max_redirects', 3),
- proxy: self::resolve($parameters, 'curl_proxy'),
- sslVerifyPeer: self::resolveBool($parameters, 'curl_ssl_verify_peer', true),
- sslVerifyHost: self::resolveBool($parameters, 'curl_ssl_verify_host', true),
- sslCertPath: self::resolve($parameters, 'curl_ssl_cert_path'),
- sslKeyPath: self::resolve($parameters, 'curl_ssl_key_path'),
- caInfoPath: self::resolve($parameters, 'curl_ca_info_path'),
- serializer: self::resolveSerializer($parameters),
- );
- } else {
- self::rejectParams($parameters, self::CURL_SPECIFIC_PARAMS, 'Parameter "%s" cannot be used with transport "grpc".');
+ private static function buildUdpSyslogErrorHandlerConfig(ParameterCollection $parameters) : UdpSyslogErrorHandlerConfig
+ {
+ self::rejectForeignHandlerParams($parameters, self::ERROR_HANDLER_UDP_SYSLOG, self::UDP_SYSLOG_HANDLER_PARAMS);
- $transport = new GrpcTransportConfig(
- endpoint: $endpoint,
- headers: $headers,
- insecure: self::resolveBool($parameters, 'grpc_insecure', true),
- );
+ $host = self::resolve($parameters, 'error_handler_host');
+
+ if ($host === null || $host === '') {
+ throw new \InvalidArgumentException('Parameter "error_handler_host" is required for error_handler "udp_syslog".');
}
- return new self(
- serviceName: self::resolve($parameters, 'service_name') ?? 'phpunit',
- transport: $transport,
- emitTraces: self::resolveBool($parameters, 'emit_traces', true),
- emitMetrics: self::resolveBool($parameters, 'emit_metrics', true),
- emitTestSpans: self::resolveBool($parameters, 'emit_test_spans', true),
- emitTestCaseSpans: self::resolveBool($parameters, 'emit_test_case_spans', true),
+ return new UdpSyslogErrorHandlerConfig(
+ host: $host,
+ port: self::resolveInt($parameters, 'error_handler_port', 514),
+ ident: self::resolve($parameters, 'error_handler_ident') ?? 'flow-telemetry',
+ facility: self::resolveSyslogFacility($parameters),
+ severity: self::resolveSyslogSeverity($parameters),
);
}
@@ -166,6 +257,27 @@ private static function readEnv(string $fullName) : ?string
return $value;
}
+ /**
+ * @param list $allowedParams
+ */
+ private static function rejectForeignHandlerParams(ParameterCollection $parameters, string $type, array $allowedParams) : void
+ {
+ $foreign = \array_values(\array_diff(
+ \array_values(\array_unique(\array_merge(
+ self::ERROR_LOG_HANDLER_PARAMS,
+ self::STREAM_HANDLER_PARAMS,
+ self::SYSLOG_HANDLER_PARAMS,
+ self::UDP_SYSLOG_HANDLER_PARAMS,
+ ))),
+ $allowedParams,
+ ));
+ self::rejectParams(
+ $parameters,
+ $foreign,
+ \sprintf('Parameter "%%s" cannot be used with error_handler "%s".', $type),
+ );
+ }
+
/**
* @param list $forbidden
*/
@@ -218,6 +330,75 @@ private static function resolveBool(ParameterCollection $parameters, string $nam
));
}
+ private static function resolveErrorHandler(
+ ParameterCollection $parameters,
+ ) : ErrorLogHandlerConfig|NullErrorHandlerConfig|StreamErrorHandlerConfig|SyslogErrorHandlerConfig|UdpSyslogErrorHandlerConfig {
+ $type = self::resolve($parameters, 'error_handler') ?? self::ERROR_HANDLER_LOG;
+
+ return match ($type) {
+ self::ERROR_HANDLER_LOG => self::buildErrorLogHandlerConfig($parameters),
+ self::ERROR_HANDLER_NOOP => self::buildNullErrorHandlerConfig($parameters),
+ self::ERROR_HANDLER_STREAM => self::buildStreamErrorHandlerConfig($parameters),
+ self::ERROR_HANDLER_SYSLOG => self::buildSyslogErrorHandlerConfig($parameters),
+ self::ERROR_HANDLER_UDP_SYSLOG => self::buildUdpSyslogErrorHandlerConfig($parameters),
+ default => throw new \InvalidArgumentException(\sprintf(
+ 'Invalid error_handler "%s", expected one of: %s.',
+ $type,
+ \implode(', ', [
+ self::ERROR_HANDLER_LOG,
+ self::ERROR_HANDLER_NOOP,
+ self::ERROR_HANDLER_STREAM,
+ self::ERROR_HANDLER_SYSLOG,
+ self::ERROR_HANDLER_UDP_SYSLOG,
+ ]),
+ )),
+ };
+ }
+
+ private static function resolveErrorLogMessageType(ParameterCollection $parameters) : ErrorLogMessageType
+ {
+ $value = self::resolve($parameters, 'error_handler_message_type');
+
+ if ($value === null) {
+ return ErrorLogMessageType::OperatingSystem;
+ }
+
+ return match ($value) {
+ 'operating_system' => ErrorLogMessageType::OperatingSystem,
+ 'email' => ErrorLogMessageType::Email,
+ 'file' => ErrorLogMessageType::File,
+ 'sapi' => ErrorLogMessageType::Sapi,
+ default => throw new \InvalidArgumentException(\sprintf(
+ 'Invalid error_handler_message_type "%s", expected "operating_system", "email", "file" or "sapi".',
+ $value,
+ )),
+ };
+ }
+
+ private static function resolveFilePermissions(ParameterCollection $parameters, string $name) : int
+ {
+ $raw = self::resolve($parameters, $name);
+
+ if ($raw === null) {
+ return self::DEFAULT_FILE_PERMISSIONS;
+ }
+
+ // Allow octal literals like "0640" by interpreting strings starting with "0" as octal.
+ $value = \str_starts_with($raw, '0') && \ctype_digit($raw)
+ ? \intval($raw, 8)
+ : self::resolveInt($parameters, $name, self::DEFAULT_FILE_PERMISSIONS);
+
+ if ($value < 0 || $value > 0o777) {
+ throw new \InvalidArgumentException(\sprintf(
+ 'Invalid file_permissions value "%s" for parameter "%s", expected octal between 0 and 0777.',
+ $raw,
+ $name,
+ ));
+ }
+
+ return $value;
+ }
+
private static function resolveInt(ParameterCollection $parameters, string $name, int $default) : int
{
$value = self::resolve($parameters, $name);
@@ -271,4 +452,131 @@ private static function resolveSerializer(ParameterCollection $parameters) : Ser
return $serializer;
}
+
+ private static function resolveSyslogFacility(ParameterCollection $parameters) : SyslogFacility
+ {
+ $value = self::resolve($parameters, 'error_handler_facility');
+
+ if ($value === null) {
+ return SyslogFacility::User;
+ }
+
+ return match ($value) {
+ 'auth' => SyslogFacility::Auth,
+ 'cron' => SyslogFacility::Cron,
+ 'daemon' => SyslogFacility::Daemon,
+ 'kernel' => SyslogFacility::Kernel,
+ 'local0' => SyslogFacility::Local0,
+ 'local1' => SyslogFacility::Local1,
+ 'local2' => SyslogFacility::Local2,
+ 'local3' => SyslogFacility::Local3,
+ 'local4' => SyslogFacility::Local4,
+ 'local5' => SyslogFacility::Local5,
+ 'local6' => SyslogFacility::Local6,
+ 'local7' => SyslogFacility::Local7,
+ 'lpr' => SyslogFacility::Lpr,
+ 'mail' => SyslogFacility::Mail,
+ 'news' => SyslogFacility::News,
+ 'syslog' => SyslogFacility::Syslog,
+ 'user' => SyslogFacility::User,
+ 'uucp' => SyslogFacility::Uucp,
+ default => throw new \InvalidArgumentException(\sprintf('Invalid error_handler_facility "%s".', $value)),
+ };
+ }
+
+ private static function resolveSyslogSeverity(ParameterCollection $parameters) : SyslogSeverity
+ {
+ $value = self::resolve($parameters, 'error_handler_severity');
+
+ if ($value === null) {
+ return SyslogSeverity::Error;
+ }
+
+ return match ($value) {
+ 'alert' => SyslogSeverity::Alert,
+ 'critical' => SyslogSeverity::Critical,
+ 'debug' => SyslogSeverity::Debug,
+ 'emergency' => SyslogSeverity::Emergency,
+ 'error' => SyslogSeverity::Error,
+ 'info' => SyslogSeverity::Info,
+ 'notice' => SyslogSeverity::Notice,
+ 'warning' => SyslogSeverity::Warning,
+ default => throw new \InvalidArgumentException(\sprintf('Invalid error_handler_severity "%s".', $value)),
+ };
+ }
+
+ private static function resolveTransport(
+ ParameterCollection $parameters,
+ ?string $legacyUrl,
+ ) : CurlTransportConfig|GrpcTransportConfig|StreamTransportConfig {
+ $transportType = self::resolve($parameters, 'transport') ?? self::TRANSPORT_CURL;
+
+ if (!\in_array($transportType, [self::TRANSPORT_CURL, self::TRANSPORT_GRPC, self::TRANSPORT_STREAM], true)) {
+ throw new \InvalidArgumentException(\sprintf(
+ 'Invalid transport "%s", expected "%s", "%s" or "%s".',
+ $transportType,
+ self::TRANSPORT_CURL,
+ self::TRANSPORT_GRPC,
+ self::TRANSPORT_STREAM,
+ ));
+ }
+
+ $endpoint = $legacyUrl
+ ?? self::resolve($parameters, 'endpoint')
+ ?? self::DEFAULT_ENDPOINT;
+
+ $headers = self::parseHeaders(self::resolve($parameters, 'headers') ?? '');
+
+ if ($transportType === self::TRANSPORT_CURL) {
+ self::rejectParams($parameters, self::GRPC_SPECIFIC_PARAMS, 'Parameter "%s" cannot be used with transport "curl".');
+ self::rejectParams($parameters, self::STREAM_TRANSPORT_PARAMS, 'Parameter "%s" cannot be used with transport "curl".');
+
+ return new CurlTransportConfig(
+ endpoint: $endpoint,
+ headers: $headers,
+ timeoutMs: self::resolveInt($parameters, 'curl_timeout_ms', self::DEFAULT_TIMEOUT_MS),
+ connectTimeoutMs: self::resolveInt($parameters, 'curl_connect_timeout_ms', self::DEFAULT_CONNECT_TIMEOUT_MS),
+ shutdownTimeoutMs: self::resolveInt($parameters, 'shutdown_timeout_ms', self::DEFAULT_SHUTDOWN_TIMEOUT_MS),
+ compression: self::resolveBool($parameters, 'curl_compression', false),
+ followRedirects: self::resolveBool($parameters, 'curl_follow_redirects', true),
+ maxRedirects: self::resolveInt($parameters, 'curl_max_redirects', 3),
+ proxy: self::resolve($parameters, 'curl_proxy'),
+ sslVerifyPeer: self::resolveBool($parameters, 'curl_ssl_verify_peer', true),
+ sslVerifyHost: self::resolveBool($parameters, 'curl_ssl_verify_host', true),
+ sslCertPath: self::resolve($parameters, 'curl_ssl_cert_path'),
+ sslKeyPath: self::resolve($parameters, 'curl_ssl_key_path'),
+ caInfoPath: self::resolve($parameters, 'curl_ca_info_path'),
+ serializer: self::resolveSerializer($parameters),
+ );
+ }
+
+ if ($transportType === self::TRANSPORT_GRPC) {
+ self::rejectParams($parameters, self::CURL_SPECIFIC_PARAMS, 'Parameter "%s" cannot be used with transport "grpc".');
+ self::rejectParams($parameters, self::STREAM_TRANSPORT_PARAMS, 'Parameter "%s" cannot be used with transport "grpc".');
+
+ return new GrpcTransportConfig(
+ endpoint: $endpoint,
+ headers: $headers,
+ insecure: self::resolveBool($parameters, 'grpc_insecure', true),
+ timeoutMs: self::resolveInt($parameters, 'grpc_timeout_ms', self::DEFAULT_TIMEOUT_MS),
+ shutdownTimeoutMs: self::resolveInt($parameters, 'shutdown_timeout_ms', self::DEFAULT_SHUTDOWN_TIMEOUT_MS),
+ );
+ }
+
+ self::rejectParams($parameters, self::CURL_SPECIFIC_PARAMS, 'Parameter "%s" cannot be used with transport "stream".');
+ self::rejectParams($parameters, self::GRPC_SPECIFIC_PARAMS, 'Parameter "%s" cannot be used with transport "stream".');
+ self::rejectParams($parameters, ['headers', 'shutdown_timeout_ms'], 'Parameter "%s" cannot be used with transport "stream".');
+
+ $rawEndpoint = self::resolve($parameters, 'endpoint');
+
+ if ($rawEndpoint === null || $rawEndpoint === '') {
+ throw new \InvalidArgumentException('Parameter "endpoint" is required for transport "stream" (file path or php:// stream wrapper URI).');
+ }
+
+ return new StreamTransportConfig(
+ destination: $rawEndpoint,
+ filePermissions: self::resolveFilePermissions($parameters, 'stream_file_permissions'),
+ createDirectories: self::resolveBool($parameters, 'stream_create_directories', true),
+ );
+ }
}
diff --git a/src/bridge/phpunit/telemetry/src/Flow/Bridge/PHPUnit/Telemetry/CurlTransportConfig.php b/src/bridge/phpunit/telemetry/src/Flow/Bridge/PHPUnit/Telemetry/CurlTransportConfig.php
index b328f95ea..44fa5b07f 100644
--- a/src/bridge/phpunit/telemetry/src/Flow/Bridge/PHPUnit/Telemetry/CurlTransportConfig.php
+++ b/src/bridge/phpunit/telemetry/src/Flow/Bridge/PHPUnit/Telemetry/CurlTransportConfig.php
@@ -12,8 +12,9 @@
public function __construct(
public string $endpoint,
public array $headers,
- public int $timeout,
- public int $connectTimeout,
+ public int $timeoutMs,
+ public int $connectTimeoutMs,
+ public int $shutdownTimeoutMs,
public bool $compression,
public bool $followRedirects,
public int $maxRedirects,
diff --git a/src/bridge/phpunit/telemetry/src/Flow/Bridge/PHPUnit/Telemetry/ErrorLogHandlerConfig.php b/src/bridge/phpunit/telemetry/src/Flow/Bridge/PHPUnit/Telemetry/ErrorLogHandlerConfig.php
new file mode 100644
index 000000000..adee03b37
--- /dev/null
+++ b/src/bridge/phpunit/telemetry/src/Flow/Bridge/PHPUnit/Telemetry/ErrorLogHandlerConfig.php
@@ -0,0 +1,17 @@
+transport);
+ $otlpExporter = otlp_exporter($transport, self::buildErrorHandler($config->errorHandler));
+ $voidExporter = void_exporter();
$spanProcessor = $config->emitTraces
- ? pass_through_span_processor(otlp_span_exporter($transport))
- : pass_through_span_processor(void_span_exporter());
+ ? batching_span_processor($otlpExporter, $config->batchSize)
+ : batching_span_processor($voidExporter, $config->batchSize);
$metricProcessor = $config->emitMetrics
- ? pass_through_metric_processor(otlp_metric_exporter($transport))
- : pass_through_metric_processor(void_metric_exporter());
+ ? batching_metric_processor($otlpExporter, $config->batchSize)
+ : batching_metric_processor($voidExporter, $config->batchSize);
- $logProcessor = pass_through_log_processor(void_log_exporter());
+ $logProcessor = batching_log_processor($voidExporter, $config->batchSize);
return telemetry(
$telemetryResource,
@@ -49,8 +52,9 @@ public static function create(Configuration $config) : Telemetry
private static function buildCurlTransport(CurlTransportConfig $config) : Transport
{
$options = otlp_curl_options()
- ->withTimeout($config->timeout)
- ->withConnectTimeout($config->connectTimeout)
+ ->withTimeout($config->timeoutMs)
+ ->withConnectTimeout($config->connectTimeoutMs)
+ ->withShutdownTimeout($config->shutdownTimeoutMs)
->withCompression($config->compression)
->withFollowRedirects($config->followRedirects, $config->maxRedirects)
->withSslVerification($config->sslVerifyPeer, $config->sslVerifyHost);
@@ -79,21 +83,64 @@ private static function buildCurlTransport(CurlTransportConfig $config) : Transp
return otlp_curl_transport($config->endpoint, $serializer, $options);
}
+ private static function buildErrorHandler(
+ ErrorLogHandlerConfig|NullErrorHandlerConfig|StreamErrorHandlerConfig|SyslogErrorHandlerConfig|UdpSyslogErrorHandlerConfig $config,
+ ) : ErrorHandler {
+ return match (true) {
+ $config instanceof ErrorLogHandlerConfig => new ErrorLogHandler(
+ messageType: $config->messageType,
+ expandNewlines: $config->expandNewlines,
+ messagePrefix: $config->messagePrefix,
+ ),
+ $config instanceof NullErrorHandlerConfig => new NullErrorHandler(),
+ $config instanceof StreamErrorHandlerConfig => new StreamHandler(
+ destination: $config->destination,
+ filePermissions: $config->filePermissions,
+ createDirectories: $config->createDirectories,
+ messagePrefix: $config->messagePrefix,
+ ),
+ $config instanceof SyslogErrorHandlerConfig => new SyslogHandler(
+ ident: $config->ident,
+ facility: $config->facility,
+ logOpts: $config->logOpts,
+ severity: $config->severity,
+ ),
+ $config instanceof UdpSyslogErrorHandlerConfig => new UdpSyslogHandler(
+ host: $config->host,
+ port: $config->port,
+ ident: $config->ident,
+ facility: $config->facility,
+ severity: $config->severity,
+ ),
+ };
+ }
+
private static function buildGrpcTransport(GrpcTransportConfig $config) : Transport
{
return otlp_grpc_transport(
endpoint: $config->endpoint,
- serializer: otlp_protobuf_serializer(),
headers: $config->headers,
insecure: $config->insecure,
+ timeoutMs: $config->timeoutMs,
+ shutdownTimeoutMs: $config->shutdownTimeoutMs,
+ );
+ }
+
+ private static function buildStreamTransport(StreamTransportConfig $config) : Transport
+ {
+ return otlp_stream_transport(
+ destination: $config->destination,
+ filePermissions: $config->filePermissions,
+ createDirectories: $config->createDirectories,
);
}
- private static function buildTransport(CurlTransportConfig|GrpcTransportConfig $config) : Transport
+ private static function buildTransport(CurlTransportConfig|GrpcTransportConfig|StreamTransportConfig $config) : Transport
{
return match (true) {
$config instanceof CurlTransportConfig => self::buildCurlTransport($config),
$config instanceof GrpcTransportConfig => self::buildGrpcTransport($config),
+ $config instanceof StreamTransportConfig => self::buildStreamTransport($config),
};
}
}
diff --git a/src/bridge/phpunit/telemetry/src/Flow/Bridge/PHPUnit/Telemetry/UdpSyslogErrorHandlerConfig.php b/src/bridge/phpunit/telemetry/src/Flow/Bridge/PHPUnit/Telemetry/UdpSyslogErrorHandlerConfig.php
new file mode 100644
index 000000000..a8cd1fe84
--- /dev/null
+++ b/src/bridge/phpunit/telemetry/src/Flow/Bridge/PHPUnit/Telemetry/UdpSyslogErrorHandlerConfig.php
@@ -0,0 +1,19 @@
+transport->serializer);
}
+ public function test_curl_shutdown_timeout_ms_parsed() : void
+ {
+ $config = Configuration::fromParameters(ParameterCollection::fromArray([
+ 'transport' => 'curl',
+ 'shutdown_timeout_ms' => '7500',
+ ]));
+
+ self::assertInstanceOf(CurlTransportConfig::class, $config->transport);
+ self::assertSame(7500, $config->transport->shutdownTimeoutMs);
+ }
+
public function test_curl_ssl_options_parsed() : void
{
$config = Configuration::fromParameters(ParameterCollection::fromArray([
@@ -176,13 +188,24 @@ public function test_curl_ssl_options_parsed() : void
public function test_curl_timeout_parsed() : void
{
$config = Configuration::fromParameters(ParameterCollection::fromArray([
- 'curl_timeout' => '120',
- 'curl_connect_timeout' => '45',
+ 'curl_timeout_ms' => '2500',
+ 'curl_connect_timeout_ms' => '500',
]));
self::assertInstanceOf(CurlTransportConfig::class, $config->transport);
- self::assertSame(120, $config->transport->timeout);
- self::assertSame(45, $config->transport->connectTimeout);
+ self::assertSame(2500, $config->transport->timeoutMs);
+ self::assertSame(500, $config->transport->connectTimeoutMs);
+ }
+
+ public function test_curl_transport_rejects_stream_params() : void
+ {
+ $this->expectException(\InvalidArgumentException::class);
+ $this->expectExceptionMessage('Parameter "stream_file_permissions" cannot be used with transport "curl"');
+
+ Configuration::fromParameters(ParameterCollection::fromArray([
+ 'transport' => 'curl',
+ 'stream_file_permissions' => '0640',
+ ]));
}
public function test_curl_with_grpc_specific_param_throws() : void
@@ -196,6 +219,16 @@ public function test_curl_with_grpc_specific_param_throws() : void
]));
}
+ public function test_curl_with_grpc_timeout_throws() : void
+ {
+ $this->expectException(\InvalidArgumentException::class);
+ $this->expectExceptionMessage('Parameter "grpc_timeout_ms" cannot be used with transport "curl"');
+
+ Configuration::fromParameters(ParameterCollection::fromArray([
+ 'grpc_timeout_ms' => '500',
+ ]));
+ }
+
public function test_default_configuration_uses_curl_transport_with_localhost_endpoint() : void
{
$config = Configuration::fromParameters(ParameterCollection::fromArray([]));
@@ -209,8 +242,8 @@ public function test_default_configuration_uses_curl_transport_with_localhost_en
self::assertInstanceOf(CurlTransportConfig::class, $config->transport);
self::assertSame('http://localhost:4318', $config->transport->endpoint);
self::assertSame([], $config->transport->headers);
- self::assertSame(30, $config->transport->timeout);
- self::assertSame(10, $config->transport->connectTimeout);
+ self::assertSame(Configuration::DEFAULT_TIMEOUT_MS, $config->transport->timeoutMs);
+ self::assertSame(Configuration::DEFAULT_CONNECT_TIMEOUT_MS, $config->transport->connectTimeoutMs);
self::assertFalse($config->transport->compression);
self::assertTrue($config->transport->followRedirects);
self::assertSame(3, $config->transport->maxRedirects);
@@ -223,6 +256,16 @@ public function test_default_configuration_uses_curl_transport_with_localhost_en
self::assertSame(SerializerType::JSON, $config->transport->serializer);
}
+ public function test_default_error_handler_is_error_log() : void
+ {
+ $config = Configuration::fromParameters(ParameterCollection::fromArray([]));
+
+ self::assertInstanceOf(ErrorLogHandlerConfig::class, $config->errorHandler);
+ self::assertSame(ErrorLogMessageType::OperatingSystem, $config->errorHandler->messageType);
+ self::assertFalse($config->errorHandler->expandNewlines);
+ self::assertSame(Configuration::DEFAULT_MESSAGE_PREFIX, $config->errorHandler->messagePrefix);
+ }
+
public function test_emit_flags_can_be_disabled() : void
{
$config = Configuration::fromParameters(ParameterCollection::fromArray([
@@ -420,6 +463,40 @@ public function test_env_var_transport_selects_grpc_when_xml_says_curl() : void
}
}
+ public function test_error_handler_can_be_set_to_noop() : void
+ {
+ $config = Configuration::fromParameters(ParameterCollection::fromArray([
+ 'error_handler' => 'noop',
+ ]));
+
+ self::assertInstanceOf(NullErrorHandlerConfig::class, $config->errorHandler);
+ }
+
+ public function test_error_log_handler_invalid_message_type_throws() : void
+ {
+ $this->expectException(\InvalidArgumentException::class);
+ $this->expectExceptionMessage('Invalid error_handler_message_type "smoke"');
+
+ Configuration::fromParameters(ParameterCollection::fromArray([
+ 'error_handler_message_type' => 'smoke',
+ ]));
+ }
+
+ public function test_error_log_handler_options_parsed() : void
+ {
+ $config = Configuration::fromParameters(ParameterCollection::fromArray([
+ 'error_handler' => 'error_log',
+ 'error_handler_message_type' => 'sapi',
+ 'error_handler_expand_newlines' => 'true',
+ 'error_handler_message_prefix' => '[custom]',
+ ]));
+
+ self::assertInstanceOf(ErrorLogHandlerConfig::class, $config->errorHandler);
+ self::assertSame(ErrorLogMessageType::Sapi, $config->errorHandler->messageType);
+ self::assertTrue($config->errorHandler->expandNewlines);
+ self::assertSame('[custom]', $config->errorHandler->messagePrefix);
+ }
+
public function test_grpc_headers_parsed() : void
{
$config = Configuration::fromParameters(ParameterCollection::fromArray([
@@ -444,6 +521,30 @@ public function test_grpc_insecure_false_parsed() : void
self::assertFalse($config->transport->insecure);
}
+ public function test_grpc_shutdown_timeout_ms_parsed() : void
+ {
+ $config = Configuration::fromParameters(ParameterCollection::fromArray([
+ 'transport' => 'grpc',
+ 'endpoint' => 'otel:4317',
+ 'shutdown_timeout_ms' => '7500',
+ ]));
+
+ self::assertInstanceOf(GrpcTransportConfig::class, $config->transport);
+ self::assertSame(7500, $config->transport->shutdownTimeoutMs);
+ }
+
+ public function test_grpc_timeout_ms_parsed() : void
+ {
+ $config = Configuration::fromParameters(ParameterCollection::fromArray([
+ 'transport' => 'grpc',
+ 'endpoint' => 'otel:4317',
+ 'grpc_timeout_ms' => '2500',
+ ]));
+
+ self::assertInstanceOf(GrpcTransportConfig::class, $config->transport);
+ self::assertSame(2500, $config->transport->timeoutMs);
+ }
+
public function test_grpc_transport_selected() : void
{
$config = Configuration::fromParameters(ParameterCollection::fromArray([
@@ -455,6 +556,7 @@ public function test_grpc_transport_selected() : void
self::assertSame('otel:4317', $config->transport->endpoint);
self::assertSame([], $config->transport->headers);
self::assertTrue($config->transport->insecure);
+ self::assertSame(Configuration::DEFAULT_TIMEOUT_MS, $config->transport->timeoutMs);
}
public function test_grpc_with_curl_specific_param_throws() : void
@@ -472,12 +574,12 @@ public function test_grpc_with_curl_specific_param_throws() : void
public function test_grpc_with_curl_timeout_throws() : void
{
$this->expectException(\InvalidArgumentException::class);
- $this->expectExceptionMessage('Parameter "curl_timeout" cannot be used with transport "grpc"');
+ $this->expectExceptionMessage('Parameter "curl_timeout_ms" cannot be used with transport "grpc"');
Configuration::fromParameters(ParameterCollection::fromArray([
'transport' => 'grpc',
'endpoint' => 'otel:4317',
- 'curl_timeout' => '30',
+ 'curl_timeout_ms' => '1000',
]));
}
@@ -491,20 +593,30 @@ public function test_invalid_boolean_value_throws() : void
]));
}
+ public function test_invalid_error_handler_throws() : void
+ {
+ $this->expectException(\InvalidArgumentException::class);
+ $this->expectExceptionMessage('Invalid error_handler "smoke", expected one of: error_log, noop, stream, syslog, udp_syslog');
+
+ Configuration::fromParameters(ParameterCollection::fromArray([
+ 'error_handler' => 'smoke',
+ ]));
+ }
+
public function test_invalid_integer_value_throws() : void
{
$this->expectException(\InvalidArgumentException::class);
- $this->expectExceptionMessage('Invalid integer value "abc" for parameter "curl_timeout"');
+ $this->expectExceptionMessage('Invalid integer value "abc" for parameter "curl_timeout_ms"');
Configuration::fromParameters(ParameterCollection::fromArray([
- 'curl_timeout' => 'abc',
+ 'curl_timeout_ms' => 'abc',
]));
}
public function test_invalid_transport_value_throws() : void
{
$this->expectException(\InvalidArgumentException::class);
- $this->expectExceptionMessage('Invalid transport "rest", expected "curl" or "grpc"');
+ $this->expectExceptionMessage('Invalid transport "rest", expected "curl", "grpc" or "stream"');
Configuration::fromParameters(ParameterCollection::fromArray([
'transport' => 'rest',
@@ -553,6 +665,17 @@ public function test_legacy_otel_collector_url_maps_to_curl_endpoint() : void
self::assertStringContainsString('otel_collector_url', $captured['message']);
}
+ public function test_noop_error_handler_rejects_specific_params() : void
+ {
+ $this->expectException(\InvalidArgumentException::class);
+ $this->expectExceptionMessage('Parameter "error_handler_message_prefix" cannot be used with error_handler "noop".');
+
+ Configuration::fromParameters(ParameterCollection::fromArray([
+ 'error_handler' => 'noop',
+ 'error_handler_message_prefix' => '[unused]',
+ ]));
+ }
+
public function test_server_superglobal_wins_over_getenv() : void
{
$_SERVER['FLOW_PHPUNIT_OTEL_ENDPOINT'] = 'https://from-server:4318';
@@ -577,4 +700,162 @@ public function test_service_name_parsed() : void
self::assertSame('my-suite', $config->serviceName);
}
+
+ public function test_stream_error_handler_parsed() : void
+ {
+ $config = Configuration::fromParameters(ParameterCollection::fromArray([
+ 'error_handler' => 'stream',
+ 'error_handler_destination' => '/tmp/flow-telemetry.log',
+ 'error_handler_file_permissions' => '0640',
+ 'error_handler_create_directories' => 'false',
+ 'error_handler_message_prefix' => '[telemetry]',
+ ]));
+
+ self::assertInstanceOf(StreamErrorHandlerConfig::class, $config->errorHandler);
+ self::assertSame('/tmp/flow-telemetry.log', $config->errorHandler->destination);
+ self::assertSame(0o640, $config->errorHandler->filePermissions);
+ self::assertFalse($config->errorHandler->createDirectories);
+ self::assertSame('[telemetry]', $config->errorHandler->messagePrefix);
+ }
+
+ public function test_stream_error_handler_rejects_syslog_params() : void
+ {
+ $this->expectException(\InvalidArgumentException::class);
+ $this->expectExceptionMessage('Parameter "error_handler_facility" cannot be used with error_handler "stream".');
+
+ Configuration::fromParameters(ParameterCollection::fromArray([
+ 'error_handler' => 'stream',
+ 'error_handler_destination' => '/tmp/x.log',
+ 'error_handler_facility' => 'local0',
+ ]));
+ }
+
+ public function test_stream_error_handler_requires_destination() : void
+ {
+ $this->expectException(\InvalidArgumentException::class);
+ $this->expectExceptionMessage('Parameter "error_handler_destination" is required for error_handler "stream"');
+
+ Configuration::fromParameters(ParameterCollection::fromArray([
+ 'error_handler' => 'stream',
+ ]));
+ }
+
+ public function test_stream_transport_defaults() : void
+ {
+ $config = Configuration::fromParameters(ParameterCollection::fromArray([
+ 'transport' => 'stream',
+ 'endpoint' => 'php://stderr',
+ ]));
+
+ self::assertInstanceOf(StreamTransportConfig::class, $config->transport);
+ self::assertSame('php://stderr', $config->transport->destination);
+ self::assertSame(Configuration::DEFAULT_FILE_PERMISSIONS, $config->transport->filePermissions);
+ self::assertTrue($config->transport->createDirectories);
+ }
+
+ public function test_stream_transport_parsed() : void
+ {
+ $config = Configuration::fromParameters(ParameterCollection::fromArray([
+ 'transport' => 'stream',
+ 'endpoint' => '/var/log/otel.jsonl',
+ 'stream_file_permissions' => '0640',
+ 'stream_create_directories' => 'false',
+ ]));
+
+ self::assertInstanceOf(StreamTransportConfig::class, $config->transport);
+ self::assertSame('/var/log/otel.jsonl', $config->transport->destination);
+ self::assertSame(0o640, $config->transport->filePermissions);
+ self::assertFalse($config->transport->createDirectories);
+ }
+
+ public function test_stream_transport_rejects_curl_params() : void
+ {
+ $this->expectException(\InvalidArgumentException::class);
+ $this->expectExceptionMessage('Parameter "curl_compression" cannot be used with transport "stream"');
+
+ Configuration::fromParameters(ParameterCollection::fromArray([
+ 'transport' => 'stream',
+ 'endpoint' => 'php://stderr',
+ 'curl_compression' => 'true',
+ ]));
+ }
+
+ public function test_stream_transport_rejects_shutdown_timeout_ms() : void
+ {
+ $this->expectException(\InvalidArgumentException::class);
+ $this->expectExceptionMessage('Parameter "shutdown_timeout_ms" cannot be used with transport "stream"');
+
+ Configuration::fromParameters(ParameterCollection::fromArray([
+ 'transport' => 'stream',
+ 'endpoint' => 'php://stderr',
+ 'shutdown_timeout_ms' => '5000',
+ ]));
+ }
+
+ public function test_stream_transport_requires_endpoint() : void
+ {
+ $this->expectException(\InvalidArgumentException::class);
+ $this->expectExceptionMessage('Parameter "endpoint" is required for transport "stream"');
+
+ Configuration::fromParameters(ParameterCollection::fromArray([
+ 'transport' => 'stream',
+ ]));
+ }
+
+ public function test_syslog_error_handler_invalid_facility_throws() : void
+ {
+ $this->expectException(\InvalidArgumentException::class);
+ $this->expectExceptionMessage('Invalid error_handler_facility "invalid"');
+
+ Configuration::fromParameters(ParameterCollection::fromArray([
+ 'error_handler' => 'syslog',
+ 'error_handler_facility' => 'invalid',
+ ]));
+ }
+
+ public function test_syslog_error_handler_parsed() : void
+ {
+ $config = Configuration::fromParameters(ParameterCollection::fromArray([
+ 'error_handler' => 'syslog',
+ 'error_handler_ident' => 'flow-test',
+ 'error_handler_facility' => 'local3',
+ 'error_handler_log_opts' => '5',
+ 'error_handler_severity' => 'warning',
+ ]));
+
+ self::assertInstanceOf(SyslogErrorHandlerConfig::class, $config->errorHandler);
+ self::assertSame('flow-test', $config->errorHandler->ident);
+ self::assertSame(SyslogFacility::Local3, $config->errorHandler->facility);
+ self::assertSame(5, $config->errorHandler->logOpts);
+ self::assertSame(SyslogSeverity::Warning, $config->errorHandler->severity);
+ }
+
+ public function test_udp_syslog_error_handler_parsed() : void
+ {
+ $config = Configuration::fromParameters(ParameterCollection::fromArray([
+ 'error_handler' => 'udp_syslog',
+ 'error_handler_host' => '192.0.2.1',
+ 'error_handler_port' => '5140',
+ 'error_handler_ident' => 'flow-remote',
+ 'error_handler_facility' => 'mail',
+ 'error_handler_severity' => 'info',
+ ]));
+
+ self::assertInstanceOf(UdpSyslogErrorHandlerConfig::class, $config->errorHandler);
+ self::assertSame('192.0.2.1', $config->errorHandler->host);
+ self::assertSame(5140, $config->errorHandler->port);
+ self::assertSame('flow-remote', $config->errorHandler->ident);
+ self::assertSame(SyslogFacility::Mail, $config->errorHandler->facility);
+ self::assertSame(SyslogSeverity::Info, $config->errorHandler->severity);
+ }
+
+ public function test_udp_syslog_error_handler_requires_host() : void
+ {
+ $this->expectException(\InvalidArgumentException::class);
+ $this->expectExceptionMessage('Parameter "error_handler_host" is required for error_handler "udp_syslog"');
+
+ Configuration::fromParameters(ParameterCollection::fromArray([
+ 'error_handler' => 'udp_syslog',
+ ]));
+ }
}
diff --git a/src/bridge/phpunit/telemetry/tests/Flow/Bridge/PHPUnit/Telemetry/Tests/Unit/Subscriber/TestFinishedSubscriberTest.php b/src/bridge/phpunit/telemetry/tests/Flow/Bridge/PHPUnit/Telemetry/Tests/Unit/Subscriber/TestFinishedSubscriberTest.php
index 4ebf023b9..b7472035e 100644
--- a/src/bridge/phpunit/telemetry/tests/Flow/Bridge/PHPUnit/Telemetry/Tests/Unit/Subscriber/TestFinishedSubscriberTest.php
+++ b/src/bridge/phpunit/telemetry/tests/Flow/Bridge/PHPUnit/Telemetry/Tests/Unit/Subscriber/TestFinishedSubscriberTest.php
@@ -4,7 +4,7 @@
namespace Flow\Bridge\PHPUnit\Telemetry\Tests\Unit\Subscriber;
-use function Flow\Telemetry\DSL\{memory_span_processor, void_span_exporter};
+use function Flow\Telemetry\DSL\{memory_span_processor, void_exporter};
use Flow\Bridge\PHPUnit\Telemetry\{SpanStack, TestStatusRegistry};
use Flow\Bridge\PHPUnit\Telemetry\Subscriber\TestFinishedSubscriber;
@@ -16,7 +16,7 @@ final class TestFinishedSubscriberTest extends TestCase
{
public function test_clears_status_registry_when_emit_test_spans_is_disabled() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$telemetry = TelemetryMother::withSpanProcessor($spanProcessor);
$spanStack = new SpanStack();
$config = ConfigurationMother::withDisabledTestSpans();
@@ -33,7 +33,7 @@ public function test_clears_status_registry_when_emit_test_spans_is_disabled() :
public function test_does_not_pop_span_when_emit_test_spans_is_disabled() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$telemetry = TelemetryMother::withSpanProcessor($spanProcessor);
$spanStack = new SpanStack();
$config = ConfigurationMother::withDisabledTestSpans();
@@ -54,7 +54,7 @@ public function test_does_not_pop_span_when_emit_test_spans_is_disabled() : void
public function test_pops_span_when_emit_test_spans_is_enabled() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$telemetry = TelemetryMother::withSpanProcessor($spanProcessor);
$spanStack = new SpanStack();
$config = ConfigurationMother::default();
diff --git a/src/bridge/phpunit/telemetry/tests/Flow/Bridge/PHPUnit/Telemetry/Tests/Unit/Subscriber/TestPreparationStartedSubscriberTest.php b/src/bridge/phpunit/telemetry/tests/Flow/Bridge/PHPUnit/Telemetry/Tests/Unit/Subscriber/TestPreparationStartedSubscriberTest.php
index 2f877b34a..fa9167138 100644
--- a/src/bridge/phpunit/telemetry/tests/Flow/Bridge/PHPUnit/Telemetry/Tests/Unit/Subscriber/TestPreparationStartedSubscriberTest.php
+++ b/src/bridge/phpunit/telemetry/tests/Flow/Bridge/PHPUnit/Telemetry/Tests/Unit/Subscriber/TestPreparationStartedSubscriberTest.php
@@ -4,7 +4,7 @@
namespace Flow\Bridge\PHPUnit\Telemetry\Tests\Unit\Subscriber;
-use function Flow\Telemetry\DSL\{memory_span_processor, void_span_exporter};
+use function Flow\Telemetry\DSL\{memory_span_processor, void_exporter};
use Flow\Bridge\PHPUnit\Telemetry\SpanStack;
use Flow\Bridge\PHPUnit\Telemetry\Subscriber\TestPreparationStartedSubscriber;
@@ -15,7 +15,7 @@ final class TestPreparationStartedSubscriberTest extends TestCase
{
public function test_creates_span_when_emit_test_spans_is_enabled() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$telemetry = TelemetryMother::withSpanProcessor($spanProcessor);
$spanStack = new SpanStack();
$config = ConfigurationMother::default();
@@ -29,7 +29,7 @@ public function test_creates_span_when_emit_test_spans_is_enabled() : void
public function test_does_not_create_span_when_emit_test_spans_is_disabled() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$telemetry = TelemetryMother::withSpanProcessor($spanProcessor);
$spanStack = new SpanStack();
$config = ConfigurationMother::withDisabledTestSpans();
diff --git a/src/bridge/phpunit/telemetry/tests/Flow/Bridge/PHPUnit/Telemetry/Tests/Unit/TelemetryFactoryTest.php b/src/bridge/phpunit/telemetry/tests/Flow/Bridge/PHPUnit/Telemetry/Tests/Unit/TelemetryFactoryTest.php
new file mode 100644
index 000000000..8a9f3ef2e
--- /dev/null
+++ b/src/bridge/phpunit/telemetry/tests/Flow/Bridge/PHPUnit/Telemetry/Tests/Unit/TelemetryFactoryTest.php
@@ -0,0 +1,220 @@
+shutdown();
+ }
+
+ public function test_create_with_disabled_metrics_routes_metrics_to_void() : void
+ {
+ $telemetry = TelemetryFactory::create(ConfigurationMother::withDisabledMetrics());
+
+ self::assertInstanceOf(Telemetry::class, $telemetry);
+ $telemetry->shutdown();
+ }
+
+ public function test_create_with_disabled_traces_routes_spans_to_void() : void
+ {
+ $telemetry = TelemetryFactory::create(ConfigurationMother::withDisabledTraces());
+
+ self::assertInstanceOf(Telemetry::class, $telemetry);
+ $telemetry->shutdown();
+ }
+
+ public function test_create_with_error_log_handler() : void
+ {
+ $config = $this->configWithErrorHandler(new ErrorLogHandlerConfig(
+ messageType: ErrorLogMessageType::Sapi,
+ expandNewlines: true,
+ messagePrefix: '[test]',
+ ));
+
+ $telemetry = TelemetryFactory::create($config);
+
+ self::assertInstanceOf(Telemetry::class, $telemetry);
+ $telemetry->shutdown();
+ }
+
+ public function test_create_with_full_curl_options_covers_optional_branches() : void
+ {
+ $config = new Configuration(
+ serviceName: 'phpunit',
+ transport: new CurlTransportConfig(
+ endpoint: 'http://localhost:4318',
+ headers: ['Authorization' => 'Bearer token', 'X-Custom' => 'value'],
+ timeoutMs: 250,
+ connectTimeoutMs: 250,
+ shutdownTimeoutMs: 5000,
+ compression: true,
+ followRedirects: true,
+ maxRedirects: 5,
+ proxy: 'http://proxy:8080',
+ sslVerifyPeer: false,
+ sslVerifyHost: false,
+ sslCertPath: '/tmp/cert.pem',
+ sslKeyPath: '/tmp/key.pem',
+ caInfoPath: '/tmp/ca.pem',
+ serializer: SerializerType::PROTOBUF,
+ ),
+ emitTraces: true,
+ emitMetrics: true,
+ emitTestSpans: true,
+ emitTestCaseSpans: true,
+ batchSize: 256,
+ errorHandler: ConfigurationMother::defaultErrorHandler(),
+ );
+
+ $telemetry = TelemetryFactory::create($config);
+
+ self::assertInstanceOf(Telemetry::class, $telemetry);
+ $telemetry->shutdown();
+ }
+
+ public function test_create_with_grpc_transport() : void
+ {
+ if (!\extension_loaded('grpc')) {
+ self::markTestSkipped('grpc extension is required');
+ }
+
+ $config = new Configuration(
+ serviceName: 'phpunit',
+ transport: new GrpcTransportConfig(
+ endpoint: 'localhost:4317',
+ headers: ['api-key' => 'x'],
+ insecure: true,
+ timeoutMs: 250,
+ shutdownTimeoutMs: 5000,
+ ),
+ emitTraces: true,
+ emitMetrics: true,
+ emitTestSpans: true,
+ emitTestCaseSpans: true,
+ batchSize: Configuration::DEFAULT_BATCH_SIZE,
+ errorHandler: ConfigurationMother::defaultErrorHandler(),
+ );
+
+ $telemetry = TelemetryFactory::create($config);
+
+ self::assertInstanceOf(Telemetry::class, $telemetry);
+ $telemetry->shutdown();
+ }
+
+ public function test_create_with_null_error_handler() : void
+ {
+ $config = $this->configWithErrorHandler(new NullErrorHandlerConfig());
+
+ $telemetry = TelemetryFactory::create($config);
+
+ self::assertInstanceOf(Telemetry::class, $telemetry);
+ $telemetry->shutdown();
+ }
+
+ public function test_create_with_stream_error_handler() : void
+ {
+ $config = $this->configWithErrorHandler(new StreamErrorHandlerConfig(
+ destination: 'php://memory',
+ filePermissions: 0644,
+ createDirectories: true,
+ messagePrefix: '[test]',
+ ));
+
+ $telemetry = TelemetryFactory::create($config);
+
+ self::assertInstanceOf(Telemetry::class, $telemetry);
+ $telemetry->shutdown();
+ }
+
+ public function test_create_with_stream_transport() : void
+ {
+ $config = new Configuration(
+ serviceName: 'phpunit',
+ transport: new StreamTransportConfig(
+ destination: 'php://memory',
+ filePermissions: 0644,
+ createDirectories: true,
+ ),
+ emitTraces: true,
+ emitMetrics: true,
+ emitTestSpans: true,
+ emitTestCaseSpans: true,
+ batchSize: Configuration::DEFAULT_BATCH_SIZE,
+ errorHandler: ConfigurationMother::defaultErrorHandler(),
+ );
+
+ $telemetry = TelemetryFactory::create($config);
+
+ self::assertInstanceOf(Telemetry::class, $telemetry);
+ $telemetry->shutdown();
+ }
+
+ public function test_create_with_syslog_error_handler() : void
+ {
+ $config = $this->configWithErrorHandler(new SyslogErrorHandlerConfig(
+ ident: 'flow-test',
+ facility: SyslogFacility::Local0,
+ logOpts: \LOG_PID,
+ severity: SyslogSeverity::Warning,
+ ));
+
+ $telemetry = TelemetryFactory::create($config);
+
+ self::assertInstanceOf(Telemetry::class, $telemetry);
+ $telemetry->shutdown();
+ }
+
+ public function test_create_with_udp_syslog_error_handler() : void
+ {
+ $config = $this->configWithErrorHandler(new UdpSyslogErrorHandlerConfig(
+ host: '127.0.0.1',
+ port: 514,
+ ident: 'flow-test',
+ facility: SyslogFacility::User,
+ severity: SyslogSeverity::Info,
+ ));
+
+ $telemetry = TelemetryFactory::create($config);
+
+ self::assertInstanceOf(Telemetry::class, $telemetry);
+ $telemetry->shutdown();
+ }
+
+ private function configWithErrorHandler(
+ ErrorLogHandlerConfig|NullErrorHandlerConfig|StreamErrorHandlerConfig|SyslogErrorHandlerConfig|UdpSyslogErrorHandlerConfig $errorHandler,
+ ) : Configuration {
+ return new Configuration(
+ serviceName: 'phpunit',
+ transport: ConfigurationMother::defaultTransport(),
+ emitTraces: true,
+ emitMetrics: true,
+ emitTestSpans: true,
+ emitTestCaseSpans: true,
+ batchSize: Configuration::DEFAULT_BATCH_SIZE,
+ errorHandler: $errorHandler,
+ );
+ }
+}
diff --git a/src/bridge/psr18/telemetry/tests/Flow/Bridge/Psr18/Telemetry/Tests/Integration/PSR18TraceableClientIntegrationTest.php b/src/bridge/psr18/telemetry/tests/Flow/Bridge/Psr18/Telemetry/Tests/Integration/PSR18TraceableClientIntegrationTest.php
index 8360bbdbe..87dac624f 100644
--- a/src/bridge/psr18/telemetry/tests/Flow/Bridge/Psr18/Telemetry/Tests/Integration/PSR18TraceableClientIntegrationTest.php
+++ b/src/bridge/psr18/telemetry/tests/Flow/Bridge/Psr18/Telemetry/Tests/Integration/PSR18TraceableClientIntegrationTest.php
@@ -10,7 +10,7 @@
use Flow\Telemetry\Meter\MeterProvider;
use Flow\Telemetry\Provider\Clock\SystemClock;
use Flow\Telemetry\Provider\Memory\{MemoryLogProcessor, MemoryMetricProcessor, MemorySpanProcessor};
-use Flow\Telemetry\Provider\Void\{VoidLogExporter, VoidMetricExporter, VoidSpanExporter};
+use Flow\Telemetry\Provider\Void\VoidExporter;
use Flow\Telemetry\{Resource, Telemetry};
use Flow\Telemetry\Tracer\{SpanKind, TracerProvider};
use Nyholm\Psr7\Request;
@@ -22,7 +22,7 @@ final class PSR18TraceableClientIntegrationTest extends TestCase
{
public function test_real_http_request_creates_span() : void
{
- $spanProcessor = new MemorySpanProcessor(new VoidSpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new VoidExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$httpClient = new Psr18Client(new MockHttpClient(new MockResponse('ok', ['http_code' => 200])));
@@ -63,8 +63,8 @@ private function createTelemetry(MemorySpanProcessor $spanProcessor) : Telemetry
'service.version' => '1.0.0',
]),
new TracerProvider($spanProcessor, $clock, $contextStorage),
- new MeterProvider(new MemoryMetricProcessor(new VoidMetricExporter()), $clock),
- new LoggerProvider(new MemoryLogProcessor(new VoidLogExporter()), $clock, $contextStorage),
+ new MeterProvider(new MemoryMetricProcessor(new VoidExporter()), $clock),
+ new LoggerProvider(new MemoryLogProcessor(new VoidExporter()), $clock, $contextStorage),
);
}
}
diff --git a/src/bridge/psr18/telemetry/tests/Flow/Bridge/Psr18/Telemetry/Tests/Unit/PSR18TraceableClientTest.php b/src/bridge/psr18/telemetry/tests/Flow/Bridge/Psr18/Telemetry/Tests/Unit/PSR18TraceableClientTest.php
index 62108a6f8..e1edc3faf 100644
--- a/src/bridge/psr18/telemetry/tests/Flow/Bridge/Psr18/Telemetry/Tests/Unit/PSR18TraceableClientTest.php
+++ b/src/bridge/psr18/telemetry/tests/Flow/Bridge/Psr18/Telemetry/Tests/Unit/PSR18TraceableClientTest.php
@@ -10,7 +10,7 @@
use Flow\Telemetry\Meter\MeterProvider;
use Flow\Telemetry\Provider\Clock\SystemClock;
use Flow\Telemetry\Provider\Memory\{MemoryLogProcessor, MemoryMetricProcessor, MemorySpanProcessor};
-use Flow\Telemetry\Provider\Void\{VoidLogExporter, VoidMetricExporter, VoidSpanExporter};
+use Flow\Telemetry\Provider\Void\VoidExporter;
use Flow\Telemetry\{Resource, Telemetry};
use Flow\Telemetry\Tracer\{SpanKind, TracerProvider};
use Nyholm\Psr7\{Request, Response};
@@ -21,7 +21,7 @@ final class PSR18TraceableClientTest extends TestCase
{
public function test_exception_is_recorded_and_rethrown() : void
{
- $spanProcessor = new MemorySpanProcessor(new VoidSpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new VoidExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$exception = new \RuntimeException('Connection failed');
@@ -58,7 +58,7 @@ public function test_exception_is_recorded_and_rethrown() : void
public function test_request_with_4xx_status_creates_error_span() : void
{
- $spanProcessor = new MemorySpanProcessor(new VoidSpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new VoidExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$mockClient = $this->createMock(ClientInterface::class);
@@ -81,7 +81,7 @@ public function test_request_with_4xx_status_creates_error_span() : void
public function test_request_with_5xx_status_creates_error_span() : void
{
- $spanProcessor = new MemorySpanProcessor(new VoidSpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new VoidExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$mockClient = $this->createMock(ClientInterface::class);
@@ -104,7 +104,7 @@ public function test_request_with_5xx_status_creates_error_span() : void
public function test_span_has_correct_attributes() : void
{
- $spanProcessor = new MemorySpanProcessor(new VoidSpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new VoidExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$mockClient = $this->createMock(ClientInterface::class);
@@ -130,7 +130,7 @@ public function test_span_has_correct_attributes() : void
public function test_span_kind_is_client() : void
{
- $spanProcessor = new MemorySpanProcessor(new VoidSpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new VoidExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$mockClient = $this->createMock(ClientInterface::class);
@@ -148,7 +148,7 @@ public function test_span_kind_is_client() : void
public function test_successful_request_creates_span_with_ok_status() : void
{
- $spanProcessor = new MemorySpanProcessor(new VoidSpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new VoidExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$mockClient = $this->createMock(ClientInterface::class);
@@ -181,8 +181,8 @@ private function createTelemetry(MemorySpanProcessor $spanProcessor) : Telemetry
'service.version' => '1.0.0',
]),
new TracerProvider($spanProcessor, $clock, $contextStorage),
- new MeterProvider(new MemoryMetricProcessor(new VoidMetricExporter()), $clock),
- new LoggerProvider(new MemoryLogProcessor(new VoidLogExporter()), $clock, $contextStorage),
+ new MeterProvider(new MemoryMetricProcessor(new VoidExporter()), $clock),
+ new LoggerProvider(new MemoryLogProcessor(new VoidExporter()), $clock, $contextStorage),
);
}
}
diff --git a/src/bridge/psr3/telemetry/src/Flow/Bridge/Psr3/Telemetry/DSL/functions.php b/src/bridge/psr3/telemetry/src/Flow/Bridge/Psr3/Telemetry/DSL/functions.php
index eb932ed2f..e66a7aea5 100644
--- a/src/bridge/psr3/telemetry/src/Flow/Bridge/Psr3/Telemetry/DSL/functions.php
+++ b/src/bridge/psr3/telemetry/src/Flow/Bridge/Psr3/Telemetry/DSL/functions.php
@@ -6,6 +6,7 @@
use Flow\Bridge\Psr3\Telemetry\{LogRecordConverter, SeverityMapper, TelemetryLogger, ValueNormalizer};
use Flow\ETL\Attribute\{DocumentationDSL, Module, Type as DSLType};
+use Flow\Telemetry\ErrorHandler\{ErrorHandler, ErrorLogHandler};
use Flow\Telemetry\Logger\{Logger, Severity};
/**
@@ -26,8 +27,9 @@
function psr3_telemetry_logger(
Logger $logger,
LogRecordConverter $converter = new LogRecordConverter(),
+ ErrorHandler $errorHandler = new ErrorLogHandler(),
) : TelemetryLogger {
- return new TelemetryLogger($logger, $converter);
+ return new TelemetryLogger($logger, $converter, $errorHandler);
}
/**
diff --git a/src/bridge/psr3/telemetry/src/Flow/Bridge/Psr3/Telemetry/TelemetryLogger.php b/src/bridge/psr3/telemetry/src/Flow/Bridge/Psr3/Telemetry/TelemetryLogger.php
index 7cd1d97cb..b2b6702e7 100644
--- a/src/bridge/psr3/telemetry/src/Flow/Bridge/Psr3/Telemetry/TelemetryLogger.php
+++ b/src/bridge/psr3/telemetry/src/Flow/Bridge/Psr3/Telemetry/TelemetryLogger.php
@@ -5,14 +5,16 @@
namespace Flow\Bridge\Psr3\Telemetry;
use Flow\Bridge\Psr3\Telemetry\Exception\InvalidArgumentException;
+use Flow\Telemetry\ErrorHandler\{ErrorHandler, ErrorLogHandler};
use Flow\Telemetry\Logger\Logger;
-use Psr\Log\AbstractLogger;
+use Psr\Log\{AbstractLogger, InvalidArgumentException as PsrInvalidArgumentException};
final class TelemetryLogger extends AbstractLogger
{
public function __construct(
private readonly Logger $logger,
private readonly LogRecordConverter $converter = new LogRecordConverter(),
+ private readonly ErrorHandler $errorHandler = new ErrorLogHandler(),
) {
}
@@ -25,6 +27,12 @@ public function log($level, string|\Stringable $message, array $context = []) :
throw new InvalidArgumentException('PSR-3 log level must be a string or Stringable.');
}
- $this->logger->emit($this->converter->convert($level, $message, $context));
+ try {
+ $this->logger->emit($this->converter->convert($level, $message, $context));
+ } catch (PsrInvalidArgumentException $e) {
+ throw $e;
+ } catch (\Throwable $e) {
+ $this->errorHandler->handle($e);
+ }
}
}
diff --git a/src/bridge/psr3/telemetry/tests/Flow/Bridge/Psr3/Telemetry/Tests/Integration/TelemetryTestContext.php b/src/bridge/psr3/telemetry/tests/Flow/Bridge/Psr3/Telemetry/Tests/Integration/TelemetryTestContext.php
index da9a9f52d..4013c2cdd 100644
--- a/src/bridge/psr3/telemetry/tests/Flow/Bridge/Psr3/Telemetry/Tests/Integration/TelemetryTestContext.php
+++ b/src/bridge/psr3/telemetry/tests/Flow/Bridge/Psr3/Telemetry/Tests/Integration/TelemetryTestContext.php
@@ -8,7 +8,7 @@
use Flow\Telemetry\Logger\{Logger, LoggerProvider};
use Flow\Telemetry\Provider\Clock\SystemClock;
use Flow\Telemetry\Provider\Memory\MemoryLogProcessor;
-use Flow\Telemetry\Provider\Void\VoidLogExporter;
+use Flow\Telemetry\Provider\Void\VoidExporter;
use Flow\Telemetry\Resource;
final readonly class TelemetryTestContext
@@ -24,7 +24,7 @@ public static function create(
string $scope = 'psr3-test-app',
string $version = 'unknown',
) : self {
- $processor = new MemoryLogProcessor(new VoidLogExporter());
+ $processor = new MemoryLogProcessor(new VoidExporter());
$logger = (new LoggerProvider(
$processor,
diff --git a/src/bridge/psr3/telemetry/tests/Flow/Bridge/Psr3/Telemetry/Tests/Unit/TelemetryLoggerErrorHandlingTest.php b/src/bridge/psr3/telemetry/tests/Flow/Bridge/Psr3/Telemetry/Tests/Unit/TelemetryLoggerErrorHandlingTest.php
new file mode 100644
index 000000000..d4073b1ef
--- /dev/null
+++ b/src/bridge/psr3/telemetry/tests/Flow/Bridge/Psr3/Telemetry/Tests/Unit/TelemetryLoggerErrorHandlingTest.php
@@ -0,0 +1,100 @@
+logger(Resource::empty(), 'test-scope');
+ $spy = new ErrorHandlerSpy();
+ $psr3 = new TelemetryLogger($logger, errorHandler: $spy);
+
+ $psr3->log(LogLevel::INFO, 'hello');
+
+ self::assertSame(0, $spy->count());
+ }
+
+ public function test_invalid_level_is_not_routed_to_error_handler() : void
+ {
+ $loggerProvider = new LoggerProvider(
+ new MemoryLogProcessor(new VoidExporter()),
+ new SystemClock(),
+ new MemoryContextStorage(),
+ );
+
+ $logger = $loggerProvider->logger(Resource::empty(), 'test-scope');
+ $spy = new ErrorHandlerSpy();
+ $psr3 = new TelemetryLogger($logger, errorHandler: $spy);
+
+ try {
+ $psr3->log(new \stdClass(), 'message');
+ } catch (InvalidArgumentException) {
+ }
+
+ self::assertSame(0, $spy->count());
+ }
+
+ public function test_routes_emit_failures_to_error_handler() : void
+ {
+ $throwingClock = new class implements ClockInterface {
+ public function now() : \DateTimeImmutable
+ {
+ throw new \RuntimeException('clock blew up');
+ }
+ };
+
+ $loggerProvider = new LoggerProvider(
+ new MemoryLogProcessor(new VoidExporter()),
+ $throwingClock,
+ new MemoryContextStorage(),
+ );
+
+ $logger = $loggerProvider->logger(Resource::empty(), 'test-scope');
+ $spy = new ErrorHandlerSpy();
+ $psr3 = new TelemetryLogger($logger, errorHandler: $spy);
+
+ $psr3->info('hello');
+
+ self::assertSame(1, $spy->count());
+ self::assertSame('clock blew up', $spy->last()?->getMessage());
+ }
+
+ public function test_still_throws_on_invalid_level_per_psr3() : void
+ {
+ $loggerProvider = new LoggerProvider(
+ new MemoryLogProcessor(new VoidExporter()),
+ new SystemClock(),
+ new MemoryContextStorage(),
+ );
+
+ $logger = $loggerProvider->logger(Resource::empty(), 'test-scope');
+ $spy = new ErrorHandlerSpy();
+ $psr3 = new TelemetryLogger($logger, errorHandler: $spy);
+
+ $this->expectException(InvalidArgumentException::class);
+
+ $psr3->log(new \stdClass(), 'message');
+ }
+}
diff --git a/src/bridge/psr3/telemetry/tests/Flow/Bridge/Psr3/Telemetry/Tests/Unit/TelemetryLoggerTest.php b/src/bridge/psr3/telemetry/tests/Flow/Bridge/Psr3/Telemetry/Tests/Unit/TelemetryLoggerTest.php
index 73d9574c5..cb3ccac20 100644
--- a/src/bridge/psr3/telemetry/tests/Flow/Bridge/Psr3/Telemetry/Tests/Unit/TelemetryLoggerTest.php
+++ b/src/bridge/psr3/telemetry/tests/Flow/Bridge/Psr3/Telemetry/Tests/Unit/TelemetryLoggerTest.php
@@ -10,7 +10,7 @@
use Flow\Telemetry\Logger\{LoggerProvider, Severity};
use Flow\Telemetry\Provider\Clock\SystemClock;
use Flow\Telemetry\Provider\Memory\MemoryLogProcessor;
-use Flow\Telemetry\Provider\Void\VoidLogExporter;
+use Flow\Telemetry\Provider\Void\VoidExporter;
use Flow\Telemetry\Resource;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
@@ -39,7 +39,7 @@ public static function level_to_severity_provider() : \Generator
protected function setUp() : void
{
- $this->processor = new MemoryLogProcessor(new VoidLogExporter());
+ $this->processor = new MemoryLogProcessor(new VoidExporter());
$logger = (new LoggerProvider(
$this->processor,
@@ -66,7 +66,7 @@ public function test_critical_emits_fatal_severity() : void
public function test_custom_converter_is_used() : void
{
- $processor = new MemoryLogProcessor(new VoidLogExporter());
+ $processor = new MemoryLogProcessor(new VoidExporter());
$logger = (new LoggerProvider(
$processor,
new SystemClock(),
diff --git a/src/bridge/symfony/telemetry-bundle/src/Flow/Bridge/Symfony/TelemetryBundle/DependencyInjection/Compiler/OTLPAvailabilityPass.php b/src/bridge/symfony/telemetry-bundle/src/Flow/Bridge/Symfony/TelemetryBundle/DependencyInjection/Compiler/OTLPAvailabilityPass.php
index b2b3fd744..e91eccae4 100644
--- a/src/bridge/symfony/telemetry-bundle/src/Flow/Bridge/Symfony/TelemetryBundle/DependencyInjection/Compiler/OTLPAvailabilityPass.php
+++ b/src/bridge/symfony/telemetry-bundle/src/Flow/Bridge/Symfony/TelemetryBundle/DependencyInjection/Compiler/OTLPAvailabilityPass.php
@@ -10,7 +10,7 @@
final class OTLPAvailabilityPass implements CompilerPassInterface
{
- private const string OTLP_BRIDGE_CLASS = 'Flow\\Bridge\\Telemetry\\OTLP\\Exporter\\OTLPSpanExporter';
+ private const string OTLP_BRIDGE_CLASS = 'Flow\\Bridge\\Telemetry\\OTLP\\Exporter\\OTLPExporter';
public function process(ContainerBuilder $container) : void
{
diff --git a/src/bridge/symfony/telemetry-bundle/src/Flow/Bridge/Symfony/TelemetryBundle/DependencyInjection/Configuration.php b/src/bridge/symfony/telemetry-bundle/src/Flow/Bridge/Symfony/TelemetryBundle/DependencyInjection/Configuration.php
index 3d62bc60a..2ec798cb9 100644
--- a/src/bridge/symfony/telemetry-bundle/src/Flow/Bridge/Symfony/TelemetryBundle/DependencyInjection/Configuration.php
+++ b/src/bridge/symfony/telemetry-bundle/src/Flow/Bridge/Symfony/TelemetryBundle/DependencyInjection/Configuration.php
@@ -144,10 +144,16 @@ public function getConfigTreeBuilder() : TreeBuilder
->end()
->end()
->end()
+ ->append($this->errorHandlersNode())
+ ->append($this->exportersNode())
->arrayNode('tracer_provider')
->info('TracerProvider configuration. Defaults to void if omitted.')
->addDefaultsIfNotSet()
->children()
+ ->scalarNode('error_handler')
+ ->info('Name of an error_handler entry forwarded to the TracerProvider')
+ ->defaultValue('default')
+ ->end()
->arrayNode('sampler')
->info('Trace sampler configuration')
->addDefaultsIfNotSet()
@@ -175,6 +181,10 @@ public function getConfigTreeBuilder() : TreeBuilder
->info('MeterProvider configuration. Defaults to void if omitted.')
->addDefaultsIfNotSet()
->children()
+ ->scalarNode('error_handler')
+ ->info('Name of an error_handler entry forwarded to the MeterProvider')
+ ->defaultValue('default')
+ ->end()
->enumNode('temporality')
->info('Aggregation temporality')
->values(['cumulative', 'delta'])
@@ -187,6 +197,10 @@ public function getConfigTreeBuilder() : TreeBuilder
->info('LoggerProvider configuration. Defaults to void if omitted.')
->addDefaultsIfNotSet()
->children()
+ ->scalarNode('error_handler')
+ ->info('Name of an error_handler entry forwarded to the LoggerProvider')
+ ->defaultValue('default')
+ ->end()
->append($this->processorNode('log'))
->end()
->end()
@@ -384,140 +398,323 @@ public function getConfigTreeBuilder() : TreeBuilder
return $treeBuilder;
}
- private function exporterNode(string $signalType) : ArrayNodeDefinition
+ private function applyTransportSchema(ArrayNodeDefinition $node, bool $allowFailover) : void
+ {
+ $node
+ ->beforeNormalization()
+ ->always(static function (mixed $v) use ($allowFailover) : mixed {
+ if (!\is_array($v)) {
+ return $v;
+ }
+
+ if (($v['type'] ?? null) === 'grpc' && \array_key_exists('connect_timeout_ms', $v)) {
+ throw new InvalidConfigurationException(
+ 'The "connect_timeout_ms" parameter is not supported when transport.type is "grpc"; gRPC uses the per-call deadline (timeout_ms) for connection establishment too.',
+ );
+ }
+
+ if (($v['type'] ?? null) === 'stream') {
+ $forbidden = [
+ 'timeout_ms',
+ 'connect_timeout_ms',
+ 'shutdown_timeout_ms',
+ 'compression',
+ 'follow_redirects',
+ 'max_redirects',
+ 'proxy',
+ 'ssl_verify_peer',
+ 'ssl_verify_host',
+ 'ssl_cert_path',
+ 'ssl_key_path',
+ 'ca_info_path',
+ 'headers',
+ 'insecure',
+ ];
+
+ foreach ($forbidden as $key) {
+ if (\array_key_exists($key, $v)) {
+ throw new InvalidConfigurationException(\sprintf(
+ 'The "%s" parameter is not supported when transport.type is "stream".',
+ $key,
+ ));
+ }
+ }
+ }
+
+ $encodingRejection = match ($v['type'] ?? null) {
+ 'stream' => 'only JSON encoding is allowed by the OTLP File Exporter spec',
+ 'grpc' => 'OTLP/gRPC mandates Protobuf encoding',
+ default => null,
+ };
+
+ if ($encodingRejection !== null && \array_key_exists('encoding', $v)) {
+ throw new InvalidConfigurationException(\sprintf(
+ 'The "encoding" parameter is not supported when transport.type is "%s"; %s.',
+ $v['type'],
+ $encodingRejection,
+ ));
+ }
+
+ if ($allowFailover && \array_key_exists('failover', $v) && \is_array($v['failover']) && $v['failover'] !== []) {
+ $primaryType = $v['type'] ?? 'curl';
+
+ if (!\in_array($primaryType, ['curl', 'grpc'], true)) {
+ throw new InvalidConfigurationException(\sprintf(
+ 'The "failover" block is only supported for transport.type "curl" or "grpc"; got "%s".',
+ $primaryType,
+ ));
+ }
+ }
+
+ return $v;
+ })
+ ->end()
+ ->validate()
+ ->ifTrue(static function (array $v) : bool {
+ if (($v['type'] ?? null) !== 'stream') {
+ return false;
+ }
+
+ $endpoint = $v['endpoint'] ?? null;
+
+ return !\is_string($endpoint) || $endpoint === '';
+ })
+ ->thenInvalid('The "endpoint" parameter is required and must be a non-empty string when transport.type is "stream" (used as the destination file path or php:// stream wrapper URI).')
+ ->end();
+
+ $children = $node->children();
+
+ $children
+ ->enumNode('type')
+ ->info("Transport type: 'curl', 'grpc', 'stream', 'service'")
+ ->values(['curl', 'grpc', 'stream', 'service'])
+ ->defaultValue('curl')
+ ->end()
+ ->scalarNode('endpoint')
+ ->info('OTLP endpoint URL for curl/grpc, or destination file path / php:// stream wrapper URI for stream (required unless type: service)')
+ ->defaultNull()
+ ->end()
+ ->integerNode('file_permissions')
+ ->info('Permissions applied when creating new files (stream only; ignored for php:// destinations)')
+ ->defaultValue(0644)
+ ->min(0)
+ ->max(0777)
+ ->end()
+ ->booleanNode('create_directories')
+ ->info('Create parent directories of the destination path if they do not exist (stream only; ignored for php:// destinations)')
+ ->defaultTrue()
+ ->end()
+ ->integerNode('timeout_ms')
+ ->info('Per-request deadline in milliseconds (curl: total request; grpc: call deadline). Default 250ms.')
+ ->defaultValue(250)
+ ->min(1)
+ ->end()
+ ->arrayNode('headers')
+ ->info('Additional HTTP headers')
+ ->normalizeKeys(false)
+ ->useAttributeAsKey('name')
+ ->prototype('scalar')->end()
+ ->end()
+ ->integerNode('connect_timeout_ms')
+ ->info('Connection-establishment deadline in milliseconds (curl only). Default 250ms.')
+ ->defaultValue(250)
+ ->min(1)
+ ->end()
+ ->integerNode('shutdown_timeout_ms')
+ ->info('Wall-clock budget in milliseconds for draining pending requests at shutdown (curl/grpc). Default 5000ms.')
+ ->defaultValue(5000)
+ ->min(1)
+ ->end()
+ ->booleanNode('compression')
+ ->info('Enable automatic response decompression (curl only)')
+ ->defaultFalse()
+ ->end()
+ ->booleanNode('follow_redirects')
+ ->info('Follow HTTP redirects (curl only)')
+ ->defaultTrue()
+ ->end()
+ ->integerNode('max_redirects')
+ ->info('Maximum number of redirects to follow (curl only)')
+ ->defaultValue(3)
+ ->min(0)
+ ->end()
+ ->scalarNode('proxy')
+ ->info('Proxy server URL (curl only)')
+ ->defaultNull()
+ ->end()
+ ->booleanNode('ssl_verify_peer')
+ ->info('Verify SSL peer certificate (curl only)')
+ ->defaultTrue()
+ ->end()
+ ->booleanNode('ssl_verify_host')
+ ->info('Verify SSL host name (curl only)')
+ ->defaultTrue()
+ ->end()
+ ->scalarNode('ssl_cert_path')
+ ->info('Path to SSL client certificate (curl only)')
+ ->defaultNull()
+ ->end()
+ ->scalarNode('ssl_key_path')
+ ->info('Path to SSL client private key (curl only)')
+ ->defaultNull()
+ ->end()
+ ->scalarNode('ca_info_path')
+ ->info('Path to CA certificate bundle (curl only)')
+ ->defaultNull()
+ ->end()
+ ->booleanNode('insecure')
+ ->info('Allow insecure connections (grpc only)')
+ ->defaultTrue()
+ ->end()
+ ->scalarNode('service_id')
+ ->info('Custom transport service ID (only for type: service)')
+ ->defaultNull()
+ ->end()
+ ->enumNode('encoding')
+ ->info('OTLP wire encoding (curl only); JSON or Protobuf as defined by the OTLP/HTTP spec')
+ ->values(['json', 'protobuf'])
+ ->defaultValue('json')
+ ->end();
+
+ if ($allowFailover) {
+ $children->append($this->transportNode('failover', allowFailover: false));
+ }
+
+ $children->end();
+ }
+
+ private function errorHandlersNode() : ArrayNodeDefinition
{
- $builder = new TreeBuilder('exporter');
+ $builder = new TreeBuilder('error_handlers');
/** @var ArrayNodeDefinition $node */
$node = $builder->getRootNode();
+ $supportedTypes = ['error_log', 'stream', 'syslog', 'udp_syslog', 'composite', 'noop', 'service'];
+ $facilities = ['auth', 'cron', 'daemon', 'kernel', 'local0', 'local1', 'local2', 'local3', 'local4', 'local5', 'local6', 'local7', 'lpr', 'mail', 'news', 'syslog', 'user', 'uucp'];
+ $severities = ['alert', 'critical', 'debug', 'emergency', 'error', 'info', 'notice', 'warning'];
+ $messageTypes = ['operating_system', 'email', 'file', 'sapi'];
+
$node
- ->info(\ucfirst($signalType) . ' exporter configuration')
- ->addDefaultsIfNotSet()
- ->children()
- ->enumNode('type')
- ->values(['memory', 'console', 'void', 'otlp', 'service'])
- ->defaultValue('void')
+ ->info('Named error handler definitions referenced by providers, processors, and OTLP exporters via "error_handler:" fields. If "default" is omitted it is auto-created with type: error_log.')
+ ->useAttributeAsKey('name')
+ ->arrayPrototype()
+ ->children()
+ ->enumNode('type')
+ ->values($supportedTypes)
+ ->defaultValue('error_log')
+ ->end()
+ ->enumNode('message_type')
+ ->info('error_log message type (only for type: error_log)')
+ ->values($messageTypes)
+ ->defaultValue('operating_system')
+ ->end()
+ ->booleanNode('expand_newlines')
+ ->info('Emit one error_log() call per line (only for type: error_log)')
+ ->defaultFalse()
+ ->end()
+ ->scalarNode('message_prefix')
+ ->info('Prefix prepended to each formatted Throwable (error_log + stream)')
+ ->defaultValue('[flow-telemetry]')
+ ->end()
+ ->scalarNode('destination')
+ ->info('File path or php:// stream URI (required for type: stream)')
+ ->defaultNull()
+ ->end()
+ ->integerNode('file_permissions')
+ ->info('Permissions applied when creating new files (only for type: stream)')
+ ->defaultValue(0644)
+ ->min(0)
+ ->max(0777)
+ ->end()
+ ->booleanNode('create_directories')
+ ->info('Create parent directories of the destination if they do not exist (only for type: stream)')
+ ->defaultTrue()
+ ->end()
+ ->scalarNode('ident')
+ ->info('Syslog identity tag (syslog + udp_syslog)')
+ ->defaultValue('flow-telemetry')
+ ->end()
+ ->enumNode('facility')
+ ->info('Syslog facility (syslog + udp_syslog)')
+ ->values($facilities)
+ ->defaultValue('user')
+ ->end()
+ ->integerNode('log_opts')
+ ->info('Bitmask of LOG_* options passed to openlog() (only for type: syslog)')
+ ->defaultValue(\LOG_PID)
+ ->end()
+ ->enumNode('severity')
+ ->info('Syslog severity (syslog + udp_syslog)')
+ ->values($severities)
+ ->defaultValue('error')
+ ->end()
+ ->scalarNode('host')
+ ->info('Remote syslog host (required for type: udp_syslog)')
+ ->defaultNull()
+ ->end()
+ ->integerNode('port')
+ ->info('Remote syslog port (only for type: udp_syslog)')
+ ->defaultValue(514)
+ ->min(1)
+ ->max(65535)
+ ->end()
+ ->arrayNode('handlers')
+ ->info('Named error_handler entries fanned-out to (only for type: composite)')
+ ->scalarPrototype()->end()
+ ->end()
+ ->scalarNode('service_id')
+ ->info('Custom error handler service ID (only for type: service)')
+ ->defaultNull()
+ ->end()
->end()
- ->scalarNode('service_id')
- ->info('Custom exporter service ID (only for type: service)')
- ->defaultNull()
+ ->end();
+
+ return $node;
+ }
+
+ private function exportersNode() : ArrayNodeDefinition
+ {
+ $builder = new TreeBuilder('exporters');
+ /** @var ArrayNodeDefinition $node */
+ $node = $builder->getRootNode();
+
+ $supportedTypes = ['otlp', 'service', 'console', 'memory', 'void'];
+
+ $node
+ ->info('Named exporter definitions referenced from per-signal processor blocks. The sub-block under each name selects the exporter implementation; "otlp" carries an embedded transport, "service" aliases an external service id.')
+ ->useAttributeAsKey('name')
+ ->arrayPrototype()
+ ->validate()
+ ->ifTrue(static function (array $v) use ($supportedTypes) : bool {
+ $set = 0;
+
+ foreach ($supportedTypes as $type) {
+ if (\array_key_exists($type, $v) && $v[$type] !== null) {
+ $set++;
+ }
+ }
+
+ return $set !== 1;
+ })
+ ->thenInvalid('Exporter must declare exactly one of: otlp, service, console, memory, void.')
->end()
- ->arrayNode('otlp')
- ->info('OTLP exporter configuration (only for type: otlp)')
- ->children()
- ->arrayNode('transport')
- ->info('OTLP transport configuration')
- ->addDefaultsIfNotSet()
- ->beforeNormalization()
- ->always(static function (mixed $v) : mixed {
- if (\is_array($v) && ($v['type'] ?? null) === 'grpc' && \array_key_exists('timeout', $v)) {
- throw new InvalidConfigurationException(
- 'The "timeout" parameter is not supported when transport.type is "grpc".',
- );
- }
-
- return $v;
- })
- ->end()
- ->children()
- ->enumNode('type')
- ->values(['curl', 'http', 'grpc', 'service'])
- ->defaultValue('curl')
- ->end()
- ->scalarNode('endpoint')
- ->info('OTLP endpoint URL (required)')
- ->isRequired()
- ->cannotBeEmpty()
- ->end()
- ->integerNode('timeout')
- ->info('Request timeout in seconds (not supported for grpc transport)')
- ->defaultValue(30)
- ->min(1)
- ->end()
- ->arrayNode('headers')
- ->info('Additional HTTP headers')
- ->normalizeKeys(false)
- ->useAttributeAsKey('name')
- ->prototype('scalar')->end()
- ->end()
- ->integerNode('connect_timeout')
- ->info('Connection timeout in seconds (only for curl)')
- ->defaultValue(10)
- ->min(1)
- ->end()
- ->booleanNode('compression')
- ->info('Enable automatic response decompression (only for curl)')
- ->defaultFalse()
- ->end()
- ->booleanNode('follow_redirects')
- ->info('Follow HTTP redirects (only for curl)')
- ->defaultTrue()
- ->end()
- ->integerNode('max_redirects')
- ->info('Maximum number of redirects to follow (only for curl)')
- ->defaultValue(3)
- ->min(0)
- ->end()
- ->scalarNode('proxy')
- ->info('Proxy server URL (only for curl, e.g., "http://proxy:8080")')
- ->defaultNull()
- ->end()
- ->booleanNode('ssl_verify_peer')
- ->info('Verify SSL peer certificate (only for curl)')
- ->defaultTrue()
- ->end()
- ->booleanNode('ssl_verify_host')
- ->info('Verify SSL host name (only for curl)')
- ->defaultTrue()
- ->end()
- ->scalarNode('ssl_cert_path')
- ->info('Path to SSL client certificate (only for curl)')
- ->defaultNull()
- ->end()
- ->scalarNode('ssl_key_path')
- ->info('Path to SSL client private key (only for curl)')
- ->defaultNull()
- ->end()
- ->scalarNode('ca_info_path')
- ->info('Path to CA certificate bundle (only for curl)')
- ->defaultNull()
- ->end()
- ->scalarNode('http_client_service_id')
- ->info('PSR-18 HTTP client service ID (only for http transport)')
- ->defaultNull()
- ->end()
- ->scalarNode('request_factory_service_id')
- ->info('PSR-17 request factory service ID (only for http transport)')
- ->defaultNull()
- ->end()
- ->scalarNode('stream_factory_service_id')
- ->info('PSR-17 stream factory service ID (only for http transport)')
- ->defaultNull()
- ->end()
- ->booleanNode('insecure')
- ->info('Allow insecure connections (only for grpc)')
- ->defaultTrue()
- ->end()
- ->scalarNode('service_id')
- ->info('Custom transport service ID (only for type: service)')
- ->defaultNull()
- ->end()
- ->arrayNode('serializer')
- ->info('Serializer configuration')
- ->addDefaultsIfNotSet()
- ->children()
- ->enumNode('type')
- ->values(['json', 'protobuf', 'service'])
- ->defaultValue('json')
- ->end()
- ->scalarNode('service_id')
- ->info('Custom serializer service ID (only for type: service)')
- ->defaultNull()
- ->end()
- ->end()
- ->end()
- ->end()
- ->end()
+ ->children()
+ ->append($this->otlpExporterNode())
+ ->append($this->serviceExporterNode())
+ ->arrayNode('console')
+ ->info('Console exporter (no options)')
+ ->treatNullLike([])
+ ->canBeUnset()
+ ->end()
+ ->arrayNode('memory')
+ ->info('Memory exporter (no options)')
+ ->treatNullLike([])
+ ->canBeUnset()
+ ->end()
+ ->arrayNode('void')
+ ->info('Void/no-op exporter (no options)')
+ ->treatNullLike([])
+ ->canBeUnset()
->end()
->end()
->end();
@@ -545,11 +742,42 @@ private function innerProcessorNode(string $signalType) : ArrayNodeDefinition
->defaultValue(512)
->min(1)
->end()
+ ->scalarNode('exporter')
+ ->info('Name of a top-level exporter referenced by this processor')
+ ->defaultNull()
+ ->end()
->scalarNode('service_id')
->info('Custom processor service ID (only for type: service)')
->defaultNull()
->end()
- ->append($this->exporterNode($signalType))
+ ->scalarNode('error_handler')
+ ->info('Name of an error_handler entry forwarded to the inner processor')
+ ->defaultValue('default')
+ ->end()
+ ->end();
+
+ return $node;
+ }
+
+ private function otlpExporterNode() : ArrayNodeDefinition
+ {
+ $builder = new TreeBuilder('otlp');
+ /** @var ArrayNodeDefinition $node */
+ $node = $builder->getRootNode();
+
+ $node
+ ->info('OTLP exporter — embeds its transport configuration inline')
+ ->canBeUnset()
+ ->validate()
+ ->ifTrue(static fn (array $v) : bool => !\is_array($v['transport'] ?? null) || \count($v['transport']) === 0)
+ ->thenInvalid('OTLP exporter requires a "transport" configuration block.')
+ ->end()
+ ->children()
+ ->scalarNode('error_handler')
+ ->info('Name of an error_handler entry forwarded to the OTLP exporter')
+ ->defaultValue('default')
+ ->end()
+ ->append($this->transportNode())
->end();
return $node;
@@ -582,6 +810,10 @@ private function processorNode(string $signalType) : ArrayNodeDefinition
->defaultValue(512)
->min(1)
->end()
+ ->scalarNode('exporter')
+ ->info('Name of a top-level exporter referenced by this processor')
+ ->defaultNull()
+ ->end()
->scalarNode('service_id')
->info('Custom processor service ID (only for type: service)')
->defaultNull()
@@ -591,6 +823,10 @@ private function processorNode(string $signalType) : ArrayNodeDefinition
->values(['trace', 'debug', 'info', 'warn', 'error', 'fatal'])
->defaultValue('info')
->end()
+ ->scalarNode('error_handler')
+ ->info('Name of an error_handler entry forwarded to this processor')
+ ->defaultValue('default')
+ ->end()
->arrayNode('processors')
->info('Array of processor configurations (only for type: composite)')
->arrayPrototype()
@@ -603,6 +839,9 @@ private function processorNode(string $signalType) : ArrayNodeDefinition
->defaultValue(512)
->min(1)
->end()
+ ->scalarNode('exporter')
+ ->defaultNull()
+ ->end()
->scalarNode('service_id')
->defaultNull()
->end()
@@ -610,15 +849,51 @@ private function processorNode(string $signalType) : ArrayNodeDefinition
->values(['trace', 'debug', 'info', 'warn', 'error', 'fatal'])
->defaultValue('info')
->end()
- ->append($this->exporterNode($signalType))
+ ->scalarNode('error_handler')
+ ->info('Name of an error_handler entry forwarded to this child processor')
+ ->defaultValue('default')
+ ->end()
->append($this->innerProcessorNode($signalType))
->end()
->end()
->end()
- ->append($this->exporterNode($signalType))
->append($this->innerProcessorNode($signalType))
->end();
return $node;
}
+
+ private function serviceExporterNode() : ArrayNodeDefinition
+ {
+ $builder = new TreeBuilder('service');
+ /** @var ArrayNodeDefinition $node */
+ $node = $builder->getRootNode();
+
+ $node
+ ->info('Aliases an existing Symfony service implementing Flow\\Telemetry\\Exporter\\Exporter')
+ ->canBeUnset()
+ ->children()
+ ->scalarNode('id')
+ ->info('Service id of the user-provided exporter (required)')
+ ->isRequired()
+ ->cannotBeEmpty()
+ ->end()
+ ->end();
+
+ return $node;
+ }
+
+ private function transportNode(string $name = 'transport', bool $allowFailover = true) : ArrayNodeDefinition
+ {
+ $builder = new TreeBuilder($name);
+ /** @var ArrayNodeDefinition $node */
+ $node = $builder->getRootNode();
+
+ $node->info($allowFailover
+ ? 'Transport configuration (required when exporter type is "otlp")'
+ : 'Optional failover transport receiving prior batches when the primary transport fails (curl/grpc primaries only).');
+ $this->applyTransportSchema($node, allowFailover: $allowFailover);
+
+ return $node;
+ }
}
diff --git a/src/bridge/symfony/telemetry-bundle/src/Flow/Bridge/Symfony/TelemetryBundle/DependencyInjection/FlowTelemetryExtension.php b/src/bridge/symfony/telemetry-bundle/src/Flow/Bridge/Symfony/TelemetryBundle/DependencyInjection/FlowTelemetryExtension.php
index ab76035e6..9ea607934 100644
--- a/src/bridge/symfony/telemetry-bundle/src/Flow/Bridge/Symfony/TelemetryBundle/DependencyInjection/FlowTelemetryExtension.php
+++ b/src/bridge/symfony/telemetry-bundle/src/Flow/Bridge/Symfony/TelemetryBundle/DependencyInjection/FlowTelemetryExtension.php
@@ -7,11 +7,12 @@
use Flow\Bridge\Psr3\Telemetry\{LogRecordConverter, TelemetryLogger};
use Flow\Bridge\Symfony\TelemetryBundle\Exception\RuntimeException;
use Flow\Bridge\Symfony\TelemetryBundle\Resource\Detector\SymfonyDeploymentDetector;
-use Flow\Bridge\Telemetry\OTLP\Exporter\{OTLPLogExporter, OTLPMetricExporter, OTLPSpanExporter};
+use Flow\Bridge\Telemetry\OTLP\Exporter\OTLPExporter;
use Flow\Bridge\Telemetry\OTLP\Serializer\{JsonSerializer, ProtobufSerializer};
-use Flow\Bridge\Telemetry\OTLP\Transport\{CurlTransport, CurlTransportOptions, GrpcTransport, HttpTransport};
+use Flow\Bridge\Telemetry\OTLP\Transport\{CurlTransport, CurlTransportOptions, GrpcTransport, StreamTransport};
use Flow\Telemetry\{Attributes, Logger\Logger, Meter\Meter, Tracer\Tracer};
use Flow\Telemetry\Context\MemoryContextStorage;
+use Flow\Telemetry\ErrorHandler\{CompositeErrorHandler, ErrorLogHandler, ErrorLogMessageType, NullErrorHandler, StreamHandler, SyslogFacility, SyslogHandler, SyslogSeverity, UdpSyslogHandler};
use Flow\Telemetry\Logger\{LoggerProvider, Severity};
use Flow\Telemetry\Logger\Processor\{BatchingLogProcessor,
CompositeLogProcessor,
@@ -21,19 +22,9 @@
use Flow\Telemetry\Meter\Processor\{BatchingMetricProcessor, CompositeMetricProcessor, PassThroughMetricProcessor};
use Flow\Telemetry\Propagation\{CompositePropagator, W3CBaggage, W3CTraceContext};
use Flow\Telemetry\Provider\Clock\SystemClock;
-use Flow\Telemetry\Provider\Console\{ConsoleLogExporter, ConsoleMetricExporter, ConsoleSpanExporter};
-use Flow\Telemetry\Provider\Memory\{MemoryLogExporter,
- MemoryLogProcessor,
- MemoryMetricExporter,
- MemoryMetricProcessor,
- MemorySpanExporter,
- MemorySpanProcessor};
-use Flow\Telemetry\Provider\Void\{VoidLogExporter,
- VoidLogProcessor,
- VoidMetricExporter,
- VoidMetricProcessor,
- VoidSpanExporter,
- VoidSpanProcessor};
+use Flow\Telemetry\Provider\Console\ConsoleExporter;
+use Flow\Telemetry\Provider\Memory\{MemoryExporter, MemoryLogProcessor, MemoryMetricProcessor, MemorySpanProcessor};
+use Flow\Telemetry\Provider\Void\{VoidExporter, VoidLogProcessor, VoidMetricProcessor, VoidSpanProcessor};
use Flow\Telemetry\Resource\Detector\{CachingDetector,
ChainDetector,
ComposerDetector,
@@ -67,7 +58,7 @@ public function load(array $configs, ContainerBuilder $container) : void
{
$loader = new PhpFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
$configuration = new Configuration();
- /** @var array{resource: array{detectors?: array{enabled?: bool, static?: array{cache?: array{enabled?: bool, path?: null|string}, os?: array{enabled?: bool}, host?: array{enabled?: bool}, service?: array{enabled?: bool}, deployment?: array{enabled?: bool}, environment?: array{enabled?: bool}}, dynamic?: array{process?: array{enabled?: bool}}}, custom?: array}, clock_service_id?: null|string, framework_logger?: null|string, context_storage?: array{type?: string, service_id?: null|string}, propagator?: array{type?: string, service_id?: null|string}, tracer_provider?: array, meter_provider?: array, logger_provider?: array, instrumentation?: array{http_kernel?: array{enabled?: bool, exclude_paths?: array, context_propagation?: bool}, console?: array{enabled?: bool, exclude_commands?: array}, messenger?: array{enabled?: bool, context_propagation?: bool}, twig?: array{enabled?: bool, trace_templates?: bool, trace_blocks?: bool, trace_macros?: bool, exclude_templates?: array}, http_client?: array{enabled?: bool, exclude_clients?: array}, psr18_client?: array{enabled?: bool, exclude_clients?: array}, dbal?: array{enabled?: bool, log_sql?: bool, max_sql_length?: int, exclude_connections?: array}, cache?: array{enabled?: bool, exclude_pools?: array}}, tracers?: array}>, meters?: array}>, loggers?: array}>} $config */
+ /** @var array{resource: array{detectors?: array{enabled?: bool, static?: array{cache?: array{enabled?: bool, path?: null|string}, os?: array{enabled?: bool}, host?: array{enabled?: bool}, service?: array{enabled?: bool}, deployment?: array{enabled?: bool}, environment?: array{enabled?: bool}}, dynamic?: array{process?: array{enabled?: bool}}}, custom?: array}, clock_service_id?: null|string, framework_logger?: null|string, context_storage?: array{type?: string, service_id?: null|string}, propagator?: array{type?: string, service_id?: null|string}, exporters?: array>, tracer_provider?: array, meter_provider?: array, logger_provider?: array, instrumentation?: array{http_kernel?: array{enabled?: bool, exclude_paths?: array, context_propagation?: bool}, console?: array{enabled?: bool, exclude_commands?: array}, messenger?: array{enabled?: bool, context_propagation?: bool}, twig?: array{enabled?: bool, trace_templates?: bool, trace_blocks?: bool, trace_macros?: bool, exclude_templates?: array}, http_client?: array{enabled?: bool, exclude_clients?: array}, psr18_client?: array{enabled?: bool, exclude_clients?: array}, dbal?: array{enabled?: bool, log_sql?: bool, max_sql_length?: int, exclude_connections?: array}, cache?: array{enabled?: bool, exclude_pools?: array}}, tracers?: array}>, meters?: array}>, loggers?: array}>} $config */
$config = $this->processConfiguration($configuration, $configs);
$container->setParameter('flow.telemetry.framework_logger', $config['framework_logger'] ?? null);
@@ -77,8 +68,10 @@ public function load(array $configs, ContainerBuilder $container) : void
$loggers = ($config['loggers'] ?? []) + ['default' => []];
$this->registerGlobalServices($config, $container);
+ $this->registerErrorHandlers($config['error_handlers'] ?? [], $container);
$this->registerPropagator($config['propagator'] ?? [], $container);
$this->registerResource($config['resource'], $container);
+ $this->registerNamedExporters($config['exporters'] ?? [], $container);
$this->registerTelemetry($config, $container);
$this->registerInstrumentation($config['instrumentation'] ?? [], $container, $loader);
$this->registerTracers($tracers, $container);
@@ -87,125 +80,242 @@ public function load(array $configs, ContainerBuilder $container) : void
}
/**
- * @param array $config
+ * @param array $transportConfig
*/
- private function buildInnerLogProcessor(array $config, string $serviceIdPrefix, ContainerBuilder $container) : string
+ private function buildEmbeddedOtlpTransport(string $exporterName, array $transportConfig, ContainerBuilder $container, bool $allowFailover = true) : string
{
- $processorServiceId = $serviceIdPrefix . '.processor';
- $type = $config['type'] ?? 'void';
+ $transportServiceId = 'flow.telemetry.exporter.' . $exporterName . '.transport';
+ $type = $transportConfig['type'] ?? 'curl';
+
+ if ($type === 'service') {
+ $customServiceId = $transportConfig['service_id'] ?? null;
+
+ if ($customServiceId === null) {
+ throw new RuntimeException(\sprintf('service_id is required when exporter "%s" transport type is "service"', $exporterName));
+ }
+ $container->setAlias($transportServiceId, $customServiceId);
+
+ return $transportServiceId;
+ }
+
+ $endpoint = $transportConfig['endpoint'] ?? null;
+
+ if (!\is_string($endpoint) || $endpoint === '') {
+ throw new RuntimeException(\sprintf('exporter "%s" transport requires an endpoint', $exporterName));
+ }
+
+ if ($type === 'stream') {
+ $definition = new Definition(StreamTransport::class);
+ $definition->setArgument(0, $endpoint);
+ $definition->setArgument(1, $transportConfig['file_permissions'] ?? 0644);
+ $definition->setArgument(2, $transportConfig['create_directories'] ?? true);
+ $container->setDefinition($transportServiceId, $definition);
+
+ return $transportServiceId;
+ }
switch ($type) {
- case 'service':
- $customServiceId = $config['service_id'] ?? null;
+ case 'curl':
+ $optionsServiceId = $transportServiceId . '.options';
+ $optionsDefinition = new Definition(CurlTransportOptions::class);
+ $optionsDefinition->addMethodCall('withTimeout', [$transportConfig['timeout_ms'] ?? CurlTransportOptions::DEFAULT_TIMEOUT_MS]);
+ $optionsDefinition->addMethodCall('withConnectTimeout', [$transportConfig['connect_timeout_ms'] ?? CurlTransportOptions::DEFAULT_CONNECT_TIMEOUT_MS]);
+ $optionsDefinition->addMethodCall('withShutdownTimeout', [$transportConfig['shutdown_timeout_ms'] ?? CurlTransportOptions::DEFAULT_SHUTDOWN_TIMEOUT_MS]);
- if ($customServiceId === null) {
- throw new RuntimeException('service_id is required when processor type is "service"');
+ $headers = $transportConfig['headers'] ?? [];
+
+ foreach ($headers as $headerName => $headerValue) {
+ $optionsDefinition->addMethodCall('withHeader', [(string) $headerName, (string) $headerValue]);
}
- $container->setAlias($processorServiceId, $customServiceId);
- break;
+ if ($transportConfig['compression'] ?? false) {
+ $optionsDefinition->addMethodCall('withCompression', [true]);
+ }
- case 'void':
- $container->setDefinition($processorServiceId, new Definition(VoidLogProcessor::class));
+ $optionsDefinition->addMethodCall('withFollowRedirects', [
+ $transportConfig['follow_redirects'] ?? true,
+ $transportConfig['max_redirects'] ?? 3,
+ ]);
- break;
+ if (($transportConfig['proxy'] ?? null) !== null) {
+ $optionsDefinition->addMethodCall('withProxy', [$transportConfig['proxy']]);
+ }
- case 'memory':
- $exporterServiceId = $this->buildLogExporter(
- $config['exporter'] ?? [],
- $processorServiceId,
- $container
- );
- $definition = new Definition(MemoryLogProcessor::class);
- $definition->setArgument(0, new Reference($exporterServiceId));
- $container->setDefinition($processorServiceId, $definition);
+ $optionsDefinition->addMethodCall('withSslVerification', [
+ $transportConfig['ssl_verify_peer'] ?? true,
+ $transportConfig['ssl_verify_host'] ?? true,
+ ]);
- break;
+ if (($transportConfig['ssl_cert_path'] ?? null) !== null) {
+ $optionsDefinition->addMethodCall('withSslCertificate', [
+ $transportConfig['ssl_cert_path'],
+ $transportConfig['ssl_key_path'] ?? null,
+ ]);
+ }
- case 'batching':
- $exporterServiceId = $this->buildLogExporter(
- $config['exporter'] ?? [],
- $processorServiceId,
- $container
- );
- $definition = new Definition(BatchingLogProcessor::class);
- $definition->setArgument(0, new Reference($exporterServiceId));
- $definition->setArgument(1, $config['batch_size'] ?? 512);
- $container->setDefinition($processorServiceId, $definition);
+ if (($transportConfig['ca_info_path'] ?? null) !== null) {
+ $optionsDefinition->addMethodCall('withCaInfo', [$transportConfig['ca_info_path']]);
+ }
+
+ $container->setDefinition($optionsServiceId, $optionsDefinition);
+
+ $serializerClass = match ($transportConfig['encoding'] ?? 'json') {
+ 'protobuf' => ProtobufSerializer::class,
+ default => JsonSerializer::class,
+ };
+
+ $definition = new Definition(CurlTransport::class);
+ $definition->setArgument(0, $endpoint);
+ $definition->setArgument(1, new Definition($serializerClass));
+ $definition->setArgument(2, new Reference($optionsServiceId));
+
+ $failoverReference = $this->buildFailoverTransport($exporterName, $transportConfig, $container, $allowFailover);
+
+ if ($failoverReference !== null) {
+ $definition->setArgument(3, $failoverReference);
+ }
+
+ $container->setDefinition($transportServiceId, $definition);
break;
- case 'passthrough':
- $exporterServiceId = $this->buildLogExporter(
- $config['exporter'] ?? [],
- $processorServiceId,
- $container
- );
- $definition = new Definition(PassThroughLogProcessor::class);
- $definition->setArgument(0, new Reference($exporterServiceId));
- $container->setDefinition($processorServiceId, $definition);
+ case 'grpc':
+ $definition = new Definition(GrpcTransport::class);
+ $definition->setArgument(0, $endpoint);
+ $definition->setArgument(1, $transportConfig['headers'] ?? []);
+ $definition->setArgument(2, $transportConfig['insecure'] ?? true);
+ $definition->setArgument(3, $transportConfig['timeout_ms'] ?? GrpcTransport::DEFAULT_TIMEOUT_MS);
+ $definition->setArgument(4, $transportConfig['shutdown_timeout_ms'] ?? GrpcTransport::DEFAULT_SHUTDOWN_TIMEOUT_MS);
+
+ $failoverReference = $this->buildFailoverTransport($exporterName, $transportConfig, $container, $allowFailover);
+
+ if ($failoverReference !== null) {
+ $definition->setArgument(5, $failoverReference);
+ }
+
+ $container->setDefinition($transportServiceId, $definition);
break;
default:
- throw new RuntimeException(\sprintf('Unknown inner log processor type: %s', (string) $type));
+ throw new RuntimeException(\sprintf('Unknown transport type "%s" for exporter "%s"', (string) $type, $exporterName));
}
- return $processorServiceId;
+ return $transportServiceId;
}
/**
- * @param array $config
+ * @param array $handlerConfig
*/
- private function buildLogExporter(array $config, string $serviceIdPrefix, ContainerBuilder $container) : string
+ private function buildErrorHandlerDefinition(string $name, array $handlerConfig, ContainerBuilder $container) : void
{
- $exporterServiceId = $serviceIdPrefix . '.exporter';
- $type = $config['type'] ?? 'void';
+ $serviceId = 'flow.telemetry.error_handler.' . $name;
+ $type = $handlerConfig['type'] ?? 'error_log';
switch ($type) {
- case 'service':
- $customServiceId = $config['service_id'] ?? null;
+ case 'error_log':
+ $definition = new Definition(ErrorLogHandler::class);
+ $definition->setArgument(0, $this->mapErrorLogMessageType($handlerConfig['message_type'] ?? 'operating_system'));
+ $definition->setArgument(1, $handlerConfig['expand_newlines'] ?? false);
+ $definition->setArgument(2, $handlerConfig['message_prefix'] ?? '[flow-telemetry]');
+ $container->setDefinition($serviceId, $definition);
- if ($customServiceId === null) {
- throw new RuntimeException('service_id is required when exporter type is "service"');
+ break;
+
+ case 'stream':
+ $destination = $handlerConfig['destination'] ?? null;
+
+ if (!\is_string($destination) || $destination === '') {
+ throw new RuntimeException(\sprintf('error_handler "%s" of type "stream" requires a non-empty "destination"', $name));
}
- $container->setAlias($exporterServiceId, $customServiceId);
+ $definition = new Definition(StreamHandler::class);
+ $definition->setArgument(0, $destination);
+ $definition->setArgument(1, $handlerConfig['file_permissions'] ?? 0644);
+ $definition->setArgument(2, $handlerConfig['create_directories'] ?? true);
+ $definition->setArgument(3, $handlerConfig['message_prefix'] ?? '[flow-telemetry]');
+ $container->setDefinition($serviceId, $definition);
break;
- case 'void':
- $container->setDefinition($exporterServiceId, new Definition(VoidLogExporter::class));
+ case 'syslog':
+ $definition = new Definition(SyslogHandler::class);
+ $definition->setArgument(0, $handlerConfig['ident'] ?? 'flow-telemetry');
+ $definition->setArgument(1, $this->mapSyslogFacility($handlerConfig['facility'] ?? 'user'));
+ $definition->setArgument(2, $handlerConfig['log_opts'] ?? \LOG_PID);
+ $definition->setArgument(3, $this->mapSyslogSeverity($handlerConfig['severity'] ?? 'error'));
+ $container->setDefinition($serviceId, $definition);
break;
- case 'memory':
- $container->setDefinition($exporterServiceId, new Definition(MemoryLogExporter::class));
+ case 'udp_syslog':
+ $host = $handlerConfig['host'] ?? null;
+
+ if (!\is_string($host) || $host === '') {
+ throw new RuntimeException(\sprintf('error_handler "%s" of type "udp_syslog" requires a non-empty "host"', $name));
+ }
+ $definition = new Definition(UdpSyslogHandler::class);
+ $definition->setArgument(0, $host);
+ $definition->setArgument(1, $handlerConfig['port'] ?? 514);
+ $definition->setArgument(2, $handlerConfig['ident'] ?? 'flow-telemetry');
+ $definition->setArgument(3, $this->mapSyslogFacility($handlerConfig['facility'] ?? 'user'));
+ $definition->setArgument(4, $this->mapSyslogSeverity($handlerConfig['severity'] ?? 'error'));
+ $container->setDefinition($serviceId, $definition);
break;
- case 'console':
- $container->setDefinition($exporterServiceId, new Definition(ConsoleLogExporter::class));
+ case 'composite':
+ $children = $handlerConfig['handlers'] ?? [];
+
+ if (!\is_array($children) || \count($children) === 0) {
+ throw new RuntimeException(\sprintf('error_handler "%s" of type "composite" requires a non-empty "handlers" list', $name));
+ }
+ $childRefs = [];
+
+ foreach ($children as $childName) {
+ $childRefs[] = $this->resolveErrorHandlerReference($childName, $container);
+ }
+ $container->setDefinition($serviceId, new Definition(CompositeErrorHandler::class, $childRefs));
break;
- case 'otlp':
- $container->setParameter('flow.telemetry.otlp_configured', true);
- $transportServiceId = $this->buildOTLPTransport(
- $config['otlp']['transport'] ?? [],
- $exporterServiceId,
- $container
- );
- $definition = new Definition(OTLPLogExporter::class);
- $definition->setArgument(0, new Reference($transportServiceId));
- $container->setDefinition($exporterServiceId, $definition);
+ case 'noop':
+ $container->setDefinition($serviceId, new Definition(NullErrorHandler::class));
+
+ break;
+
+ case 'service':
+ $customServiceId = $handlerConfig['service_id'] ?? null;
+
+ if (!\is_string($customServiceId) || $customServiceId === '') {
+ throw new RuntimeException(\sprintf('error_handler "%s" of type "service" requires a non-empty "service_id"', $name));
+ }
+ $container->setAlias($serviceId, $customServiceId);
break;
default:
- throw new RuntimeException(\sprintf('Unknown log exporter type: %s', (string) $type));
+ throw new RuntimeException(\sprintf('Unknown error_handler type "%s" for handler "%s"', (string) $type, $name));
+ }
+ }
+
+ /**
+ * @param array $transportConfig
+ */
+ private function buildFailoverTransport(string $exporterName, array $transportConfig, ContainerBuilder $container, bool $allowFailover) : ?Reference
+ {
+ if (!$allowFailover) {
+ return null;
+ }
+
+ $failoverConfig = $transportConfig['failover'] ?? null;
+
+ if (!\is_array($failoverConfig) || $failoverConfig === []) {
+ return null;
}
- return $exporterServiceId;
+ $failoverServiceId = $this->buildEmbeddedOtlpTransport($exporterName . '.failover', $failoverConfig, $container, allowFailover: false);
+
+ return new Reference($failoverServiceId);
}
/**
@@ -216,11 +326,13 @@ private function buildLoggerProvider(array $config, ContainerBuilder $container)
$providerServiceId = 'flow.telemetry.logger_provider';
$processorServiceId = $this->buildLogProcessor($config['processor'] ?? [], $providerServiceId, $container);
+ $errorHandlerRef = $this->resolveErrorHandlerReference($config['error_handler'] ?? 'default', $container);
$definition = new Definition(LoggerProvider::class);
$definition->setArgument(0, new Reference($processorServiceId));
$definition->setArgument(1, new Reference('flow.telemetry.clock'));
$definition->setArgument(2, new Reference('flow.telemetry.context_storage'));
+ $definition->setArgument('$errorHandler', $errorHandlerRef);
$container->setDefinition($providerServiceId, $definition);
return $providerServiceId;
@@ -233,6 +345,7 @@ private function buildLogProcessor(array $config, string $serviceIdPrefix, Conta
{
$processorServiceId = $serviceIdPrefix . '.processor';
$type = $config['type'] ?? 'void';
+ $errorHandlerRef = $this->resolveErrorHandlerReference($config['error_handler'] ?? 'default', $container);
switch ($type) {
case 'service':
@@ -251,38 +364,29 @@ private function buildLogProcessor(array $config, string $serviceIdPrefix, Conta
break;
case 'memory':
- $exporterServiceId = $this->buildLogExporter(
- $config['exporter'] ?? [],
- $processorServiceId,
- $container
- );
+ $exporterRef = $this->resolveExporterReference('log', $config['exporter'] ?? null, $container);
$definition = new Definition(MemoryLogProcessor::class);
- $definition->setArgument(0, new Reference($exporterServiceId));
+ $definition->setArgument(0, $exporterRef);
+ $definition->setArgument(1, $errorHandlerRef);
$container->setDefinition($processorServiceId, $definition);
break;
case 'batching':
- $exporterServiceId = $this->buildLogExporter(
- $config['exporter'] ?? [],
- $processorServiceId,
- $container
- );
+ $exporterRef = $this->resolveExporterReference('log', $config['exporter'] ?? null, $container);
$definition = new Definition(BatchingLogProcessor::class);
- $definition->setArgument(0, new Reference($exporterServiceId));
+ $definition->setArgument(0, $exporterRef);
$definition->setArgument(1, $config['batch_size'] ?? 512);
+ $definition->setArgument(2, $errorHandlerRef);
$container->setDefinition($processorServiceId, $definition);
break;
case 'passthrough':
- $exporterServiceId = $this->buildLogExporter(
- $config['exporter'] ?? [],
- $processorServiceId,
- $container
- );
+ $exporterRef = $this->resolveExporterReference('log', $config['exporter'] ?? null, $container);
$definition = new Definition(PassThroughLogProcessor::class);
- $definition->setArgument(0, new Reference($exporterServiceId));
+ $definition->setArgument(0, $exporterRef);
+ $definition->setArgument(1, $errorHandlerRef);
$container->setDefinition($processorServiceId, $definition);
break;
@@ -302,13 +406,14 @@ private function buildLogProcessor(array $config, string $serviceIdPrefix, Conta
}
$definition = new Definition(CompositeLogProcessor::class);
$definition->setArgument(0, $processorRefs);
+ $definition->setArgument(1, $errorHandlerRef);
$container->setDefinition($processorServiceId, $definition);
break;
case 'severity_filtering':
$innerProcessorConfig = $config['inner_processor'] ?? [];
- $innerProcessorServiceId = $this->buildInnerLogProcessor(
+ $innerProcessorServiceId = $this->buildLogProcessor(
$innerProcessorConfig,
$processorServiceId . '.inner',
$container
@@ -336,6 +441,7 @@ private function buildMeterProvider(array $config, ContainerBuilder $container)
$providerServiceId = 'flow.telemetry.meter_provider';
$processorServiceId = $this->buildMetricProcessor($config['processor'] ?? [], $providerServiceId, $container);
+ $errorHandlerRef = $this->resolveErrorHandlerReference($config['error_handler'] ?? 'default', $container);
$temporality = ($config['temporality'] ?? 'cumulative') === 'delta'
? AggregationTemporality::DELTA
@@ -345,65 +451,12 @@ private function buildMeterProvider(array $config, ContainerBuilder $container)
$definition->setArgument(0, new Reference($processorServiceId));
$definition->setArgument(1, new Reference('flow.telemetry.clock'));
$definition->setArgument(2, $temporality);
+ $definition->setArgument('$errorHandler', $errorHandlerRef);
$container->setDefinition($providerServiceId, $definition);
return $providerServiceId;
}
- /**
- * @param array $config
- */
- private function buildMetricExporter(array $config, string $serviceIdPrefix, ContainerBuilder $container) : string
- {
- $exporterServiceId = $serviceIdPrefix . '.exporter';
- $type = $config['type'] ?? 'void';
-
- switch ($type) {
- case 'service':
- $customServiceId = $config['service_id'] ?? null;
-
- if ($customServiceId === null) {
- throw new RuntimeException('service_id is required when exporter type is "service"');
- }
- $container->setAlias($exporterServiceId, $customServiceId);
-
- break;
-
- case 'void':
- $container->setDefinition($exporterServiceId, new Definition(VoidMetricExporter::class));
-
- break;
-
- case 'memory':
- $container->setDefinition($exporterServiceId, new Definition(MemoryMetricExporter::class));
-
- break;
-
- case 'console':
- $container->setDefinition($exporterServiceId, new Definition(ConsoleMetricExporter::class));
-
- break;
-
- case 'otlp':
- $container->setParameter('flow.telemetry.otlp_configured', true);
- $transportServiceId = $this->buildOTLPTransport(
- $config['otlp']['transport'] ?? [],
- $exporterServiceId,
- $container
- );
- $definition = new Definition(OTLPMetricExporter::class);
- $definition->setArgument(0, new Reference($transportServiceId));
- $container->setDefinition($exporterServiceId, $definition);
-
- break;
-
- default:
- throw new RuntimeException(\sprintf('Unknown metric exporter type: %s', (string) $type));
- }
-
- return $exporterServiceId;
- }
-
/**
* @param array $config
*/
@@ -411,6 +464,7 @@ private function buildMetricProcessor(array $config, string $serviceIdPrefix, Co
{
$processorServiceId = $serviceIdPrefix . '.processor';
$type = $config['type'] ?? 'void';
+ $errorHandlerRef = $this->resolveErrorHandlerReference($config['error_handler'] ?? 'default', $container);
switch ($type) {
case 'service':
@@ -429,38 +483,29 @@ private function buildMetricProcessor(array $config, string $serviceIdPrefix, Co
break;
case 'memory':
- $exporterServiceId = $this->buildMetricExporter(
- $config['exporter'] ?? [],
- $processorServiceId,
- $container
- );
+ $exporterRef = $this->resolveExporterReference('metric', $config['exporter'] ?? null, $container);
$definition = new Definition(MemoryMetricProcessor::class);
- $definition->setArgument(0, new Reference($exporterServiceId));
+ $definition->setArgument(0, $exporterRef);
+ $definition->setArgument(1, $errorHandlerRef);
$container->setDefinition($processorServiceId, $definition);
break;
case 'batching':
- $exporterServiceId = $this->buildMetricExporter(
- $config['exporter'] ?? [],
- $processorServiceId,
- $container
- );
+ $exporterRef = $this->resolveExporterReference('metric', $config['exporter'] ?? null, $container);
$definition = new Definition(BatchingMetricProcessor::class);
- $definition->setArgument(0, new Reference($exporterServiceId));
+ $definition->setArgument(0, $exporterRef);
$definition->setArgument(1, $config['batch_size'] ?? 512);
+ $definition->setArgument(2, $errorHandlerRef);
$container->setDefinition($processorServiceId, $definition);
break;
case 'passthrough':
- $exporterServiceId = $this->buildMetricExporter(
- $config['exporter'] ?? [],
- $processorServiceId,
- $container
- );
+ $exporterRef = $this->resolveExporterReference('metric', $config['exporter'] ?? null, $container);
$definition = new Definition(PassThroughMetricProcessor::class);
- $definition->setArgument(0, new Reference($exporterServiceId));
+ $definition->setArgument(0, $exporterRef);
+ $definition->setArgument(1, $errorHandlerRef);
$container->setDefinition($processorServiceId, $definition);
break;
@@ -480,6 +525,7 @@ private function buildMetricProcessor(array $config, string $serviceIdPrefix, Co
}
$definition = new Definition(CompositeMetricProcessor::class);
$definition->setArgument(0, $processorRefs);
+ $definition->setArgument(1, $errorHandlerRef);
$container->setDefinition($processorServiceId, $definition);
break;
@@ -491,165 +537,6 @@ private function buildMetricProcessor(array $config, string $serviceIdPrefix, Co
return $processorServiceId;
}
- /**
- * @param array{type?: string, service_id?: string} $config
- */
- private function buildOTLPSerializer(array $config, string $serviceIdPrefix, ContainerBuilder $container) : string
- {
- $serializerServiceId = $serviceIdPrefix . '.serializer';
- $type = $config['type'] ?? 'json';
-
- switch ($type) {
- case 'service':
- $customServiceId = $config['service_id'] ?? null;
-
- if ($customServiceId === null) {
- throw new RuntimeException('service_id is required when serializer type is "service"');
- }
- $container->setAlias($serializerServiceId, $customServiceId);
-
- break;
-
- case 'json':
- $definition = new Definition(JsonSerializer::class);
- $container->setDefinition($serializerServiceId, $definition);
-
- break;
-
- case 'protobuf':
- $definition = new Definition(ProtobufSerializer::class);
- $container->setDefinition($serializerServiceId, $definition);
-
- break;
-
- default:
- throw new RuntimeException(\sprintf('Unknown OTLP serializer type: %s', (string) $type));
- }
-
- return $serializerServiceId;
- }
-
- /**
- * @param array $config
- */
- private function buildOTLPTransport(array $config, string $serviceIdPrefix, ContainerBuilder $container) : string
- {
- $transportServiceId = $serviceIdPrefix . '.transport';
- $type = $config['type'];
-
- if ($type === 'service') {
- $customServiceId = $config['service_id'] ?? null;
-
- if ($customServiceId === null) {
- throw new RuntimeException('service_id is required when transport type is "service"');
- }
- $container->setAlias($transportServiceId, $customServiceId);
-
- return $transportServiceId;
- }
-
- $endpoint = $config['endpoint'];
- $timeout = $config['timeout'];
- $headers = $config['headers'];
-
- $serializerServiceId = $this->buildOTLPSerializer($config['serializer'], $transportServiceId, $container);
-
- switch ($type) {
- case 'curl':
- $optionsServiceId = $transportServiceId . '.options';
- $optionsDefinition = new Definition(CurlTransportOptions::class);
- $optionsDefinition->addMethodCall('withTimeout', [$timeout]);
- $optionsDefinition->addMethodCall('withConnectTimeout', [$config['connect_timeout']]);
-
- foreach ($headers as $headerName => $headerValue) {
- $optionsDefinition->addMethodCall('withHeader', [(string) $headerName, (string) $headerValue]);
- }
-
- if ($config['compression']) {
- $optionsDefinition->addMethodCall('withCompression', [true]);
- }
-
- $optionsDefinition->addMethodCall('withFollowRedirects', [
- $config['follow_redirects'],
- $config['max_redirects'],
- ]);
-
- if ($config['proxy'] !== null) {
- $optionsDefinition->addMethodCall('withProxy', [$config['proxy']]);
- }
-
- $optionsDefinition->addMethodCall('withSslVerification', [
- $config['ssl_verify_peer'],
- $config['ssl_verify_host'],
- ]);
-
- if ($config['ssl_cert_path'] !== null) {
- $optionsDefinition->addMethodCall('withSslCertificate', [
- $config['ssl_cert_path'],
- $config['ssl_key_path'],
- ]);
- }
-
- if ($config['ca_info_path'] !== null) {
- $optionsDefinition->addMethodCall('withCaInfo', [$config['ca_info_path']]);
- }
-
- $container->setDefinition($optionsServiceId, $optionsDefinition);
-
- $definition = new Definition(CurlTransport::class);
- $definition->setArgument(0, $endpoint);
- $definition->setArgument(1, new Reference($serializerServiceId));
- $definition->setArgument(2, new Reference($optionsServiceId));
- $container->setDefinition($transportServiceId, $definition);
-
- break;
-
- case 'http':
- $httpClientServiceId = $config['http_client_service_id'] ?? null;
- $requestFactoryServiceId = $config['request_factory_service_id'] ?? null;
- $streamFactoryServiceId = $config['stream_factory_service_id'] ?? null;
-
- if ($httpClientServiceId === null) {
- throw new RuntimeException('http_client_service_id is required when transport type is "http"');
- }
-
- if ($requestFactoryServiceId === null) {
- throw new RuntimeException('request_factory_service_id is required when transport type is "http"');
- }
-
- if ($streamFactoryServiceId === null) {
- throw new RuntimeException('stream_factory_service_id is required when transport type is "http"');
- }
-
- $definition = new Definition(HttpTransport::class);
- $definition->setArgument('$httpClient', new Reference($httpClientServiceId));
- $definition->setArgument('$requestFactory', new Reference($requestFactoryServiceId));
- $definition->setArgument('$streamFactory', new Reference($streamFactoryServiceId));
- $definition->setArgument('$endpoint', $endpoint);
- $definition->setArgument('$serializer', new Reference($serializerServiceId));
- $definition->setArgument('$headers', $headers);
- $container->setDefinition($transportServiceId, $definition);
-
- break;
-
- case 'grpc':
- $insecure = $config['insecure'];
- $definition = new Definition(GrpcTransport::class);
- $definition->setArgument(0, $endpoint);
- $definition->setArgument(1, new Reference($serializerServiceId));
- $definition->setArgument(2, $headers);
- $definition->setArgument(3, $insecure);
- $container->setDefinition($transportServiceId, $definition);
-
- break;
-
- default:
- throw new RuntimeException(\sprintf('Unknown OTLP transport type: %s', (string) $type));
- }
-
- return $transportServiceId;
- }
-
/**
* @param array $config
*/
@@ -704,60 +591,6 @@ private function buildSampler(array $config, ContainerBuilder $container) : stri
return $samplerServiceId;
}
- /**
- * @param array $config
- */
- private function buildSpanExporter(array $config, string $serviceIdPrefix, ContainerBuilder $container) : string
- {
- $exporterServiceId = $serviceIdPrefix . '.exporter';
- $type = $config['type'] ?? 'void';
-
- switch ($type) {
- case 'service':
- $customServiceId = $config['service_id'] ?? null;
-
- if ($customServiceId === null) {
- throw new RuntimeException('service_id is required when exporter type is "service"');
- }
- $container->setAlias($exporterServiceId, $customServiceId);
-
- break;
-
- case 'void':
- $container->setDefinition($exporterServiceId, new Definition(VoidSpanExporter::class));
-
- break;
-
- case 'memory':
- $container->setDefinition($exporterServiceId, new Definition(MemorySpanExporter::class));
-
- break;
-
- case 'console':
- $container->setDefinition($exporterServiceId, new Definition(ConsoleSpanExporter::class));
-
- break;
-
- case 'otlp':
- $container->setParameter('flow.telemetry.otlp_configured', true);
- $transportServiceId = $this->buildOTLPTransport(
- $config['otlp']['transport'] ?? [],
- $exporterServiceId,
- $container
- );
- $definition = new Definition(OTLPSpanExporter::class);
- $definition->setArgument(0, new Reference($transportServiceId));
- $container->setDefinition($exporterServiceId, $definition);
-
- break;
-
- default:
- throw new RuntimeException(\sprintf('Unknown span exporter type: %s', (string) $type));
- }
-
- return $exporterServiceId;
- }
-
/**
* @param array $config
*/
@@ -765,6 +598,7 @@ private function buildSpanProcessor(array $config, string $serviceIdPrefix, Cont
{
$processorServiceId = $serviceIdPrefix . '.processor';
$type = $config['type'] ?? 'void';
+ $errorHandlerRef = $this->resolveErrorHandlerReference($config['error_handler'] ?? 'default', $container);
switch ($type) {
case 'service':
@@ -783,38 +617,29 @@ private function buildSpanProcessor(array $config, string $serviceIdPrefix, Cont
break;
case 'memory':
- $exporterServiceId = $this->buildSpanExporter(
- $config['exporter'] ?? [],
- $processorServiceId,
- $container
- );
+ $exporterRef = $this->resolveExporterReference('span', $config['exporter'] ?? null, $container);
$definition = new Definition(MemorySpanProcessor::class);
- $definition->setArgument(0, new Reference($exporterServiceId));
+ $definition->setArgument(0, $exporterRef);
+ $definition->setArgument(1, $errorHandlerRef);
$container->setDefinition($processorServiceId, $definition);
break;
case 'batching':
- $exporterServiceId = $this->buildSpanExporter(
- $config['exporter'] ?? [],
- $processorServiceId,
- $container
- );
+ $exporterRef = $this->resolveExporterReference('span', $config['exporter'] ?? null, $container);
$definition = new Definition(BatchingSpanProcessor::class);
- $definition->setArgument(0, new Reference($exporterServiceId));
+ $definition->setArgument(0, $exporterRef);
$definition->setArgument(1, $config['batch_size'] ?? 512);
+ $definition->setArgument(2, $errorHandlerRef);
$container->setDefinition($processorServiceId, $definition);
break;
case 'passthrough':
- $exporterServiceId = $this->buildSpanExporter(
- $config['exporter'] ?? [],
- $processorServiceId,
- $container
- );
+ $exporterRef = $this->resolveExporterReference('span', $config['exporter'] ?? null, $container);
$definition = new Definition(PassThroughSpanProcessor::class);
- $definition->setArgument(0, new Reference($exporterServiceId));
+ $definition->setArgument(0, $exporterRef);
+ $definition->setArgument(1, $errorHandlerRef);
$container->setDefinition($processorServiceId, $definition);
break;
@@ -834,6 +659,7 @@ private function buildSpanProcessor(array $config, string $serviceIdPrefix, Cont
}
$definition = new Definition(CompositeSpanProcessor::class);
$definition->setArgument(0, $processorRefs);
+ $definition->setArgument(1, $errorHandlerRef);
$container->setDefinition($processorServiceId, $definition);
break;
@@ -854,17 +680,30 @@ private function buildTracerProvider(array $config, ContainerBuilder $container)
$processorServiceId = $this->buildSpanProcessor($config['processor'] ?? [], $providerServiceId, $container);
$samplerServiceId = $this->buildSampler($config['sampler'] ?? [], $container);
+ $errorHandlerRef = $this->resolveErrorHandlerReference($config['error_handler'] ?? 'default', $container);
$definition = new Definition(TracerProvider::class);
$definition->setArgument(0, new Reference($processorServiceId));
$definition->setArgument(1, new Reference('flow.telemetry.clock'));
$definition->setArgument(2, new Reference('flow.telemetry.context_storage'));
$definition->setArgument(3, new Reference($samplerServiceId));
+ $definition->setArgument('$errorHandler', $errorHandlerRef);
$container->setDefinition($providerServiceId, $definition);
return $providerServiceId;
}
+ private function mapErrorLogMessageType(string $value) : ErrorLogMessageType
+ {
+ return match ($value) {
+ 'operating_system' => ErrorLogMessageType::OperatingSystem,
+ 'email' => ErrorLogMessageType::Email,
+ 'file' => ErrorLogMessageType::File,
+ 'sapi' => ErrorLogMessageType::Sapi,
+ default => throw new RuntimeException(\sprintf('Unknown error_log message_type: %s', $value)),
+ };
+ }
+
private function mapSeverity(string $severity) : Severity
{
return match ($severity) {
@@ -878,6 +717,46 @@ private function mapSeverity(string $severity) : Severity
};
}
+ private function mapSyslogFacility(string $value) : SyslogFacility
+ {
+ return match ($value) {
+ 'auth' => SyslogFacility::Auth,
+ 'cron' => SyslogFacility::Cron,
+ 'daemon' => SyslogFacility::Daemon,
+ 'kernel' => SyslogFacility::Kernel,
+ 'local0' => SyslogFacility::Local0,
+ 'local1' => SyslogFacility::Local1,
+ 'local2' => SyslogFacility::Local2,
+ 'local3' => SyslogFacility::Local3,
+ 'local4' => SyslogFacility::Local4,
+ 'local5' => SyslogFacility::Local5,
+ 'local6' => SyslogFacility::Local6,
+ 'local7' => SyslogFacility::Local7,
+ 'lpr' => SyslogFacility::Lpr,
+ 'mail' => SyslogFacility::Mail,
+ 'news' => SyslogFacility::News,
+ 'syslog' => SyslogFacility::Syslog,
+ 'user' => SyslogFacility::User,
+ 'uucp' => SyslogFacility::Uucp,
+ default => throw new RuntimeException(\sprintf('Unknown syslog facility: %s', $value)),
+ };
+ }
+
+ private function mapSyslogSeverity(string $value) : SyslogSeverity
+ {
+ return match ($value) {
+ 'alert' => SyslogSeverity::Alert,
+ 'critical' => SyslogSeverity::Critical,
+ 'debug' => SyslogSeverity::Debug,
+ 'emergency' => SyslogSeverity::Emergency,
+ 'error' => SyslogSeverity::Error,
+ 'info' => SyslogSeverity::Info,
+ 'notice' => SyslogSeverity::Notice,
+ 'warning' => SyslogSeverity::Warning,
+ default => throw new RuntimeException(\sprintf('Unknown syslog severity: %s', $value)),
+ };
+ }
+
/**
* @param array $config
*/
@@ -886,6 +765,34 @@ private function readConfigEnabled(string $path, ContainerBuilder $container, ar
return $this->configsEnabled[$path] ??= parent::isConfigEnabled($container, $config);
}
+ /**
+ * @param array> $config
+ */
+ private function registerErrorHandlers(array $config, ContainerBuilder $container) : void
+ {
+ if (!\array_key_exists('default', $config)) {
+ $config = ['default' => ['type' => 'error_log']] + $config;
+ }
+
+ $compositeNames = [];
+
+ foreach ($config as $name => $handlerConfig) {
+ $type = $handlerConfig['type'] ?? 'error_log';
+
+ if ($type === 'composite') {
+ $compositeNames[] = $name;
+
+ continue;
+ }
+
+ $this->buildErrorHandlerDefinition((string) $name, $handlerConfig, $container);
+ }
+
+ foreach ($compositeNames as $name) {
+ $this->buildErrorHandlerDefinition((string) $name, $config[$name], $container);
+ }
+ }
+
/**
* @param array $config
*/
@@ -1037,6 +944,64 @@ private function registerMeters(array $config, ContainerBuilder $container) : vo
}
}
+ /**
+ * @param array> $config
+ */
+ private function registerNamedExporters(array $config, ContainerBuilder $container) : void
+ {
+ foreach ($config as $name => $exporterConfig) {
+ $serviceId = 'flow.telemetry.exporter.' . $name;
+
+ if (\array_key_exists('void', $exporterConfig)) {
+ $container->setDefinition($serviceId, new Definition(VoidExporter::class));
+
+ continue;
+ }
+
+ if (\array_key_exists('memory', $exporterConfig)) {
+ $container->setDefinition($serviceId, new Definition(MemoryExporter::class));
+
+ continue;
+ }
+
+ if (\array_key_exists('console', $exporterConfig)) {
+ $container->setDefinition($serviceId, new Definition(ConsoleExporter::class));
+
+ continue;
+ }
+
+ if (\array_key_exists('service', $exporterConfig)) {
+ $customServiceId = $exporterConfig['service']['id'] ?? null;
+
+ if (!\is_string($customServiceId) || $customServiceId === '') {
+ throw new RuntimeException(\sprintf('exporter "%s" of type "service" requires "service.id"', $name));
+ }
+ $container->setAlias($serviceId, $customServiceId);
+
+ continue;
+ }
+
+ if (\array_key_exists('otlp', $exporterConfig)) {
+ $container->setParameter('flow.telemetry.otlp_configured', true);
+ $transportConfig = $exporterConfig['otlp']['transport'] ?? null;
+
+ if (!\is_array($transportConfig) || \count($transportConfig) === 0) {
+ throw new RuntimeException(\sprintf('exporter "%s" of type "otlp" requires an inline "transport" configuration', $name));
+ }
+ $transportServiceId = $this->buildEmbeddedOtlpTransport($name, $transportConfig, $container);
+ $errorHandlerRef = $this->resolveErrorHandlerReference($exporterConfig['otlp']['error_handler'] ?? 'default', $container);
+ $definition = new Definition(OTLPExporter::class);
+ $definition->setArgument(0, new Reference($transportServiceId));
+ $definition->setArgument(1, $errorHandlerRef);
+ $container->setDefinition($serviceId, $definition);
+
+ continue;
+ }
+
+ throw new RuntimeException(\sprintf('exporter "%s" must declare exactly one of: otlp, service, console, memory, void', $name));
+ }
+ }
+
/**
* @param array{http_kernel?: array{enabled?: bool, exclude_routes?: array, exclude_paths?: array, context_propagation?: bool}, console?: array{enabled?: bool, exclude_commands?: array}, messenger?: array{enabled?: bool, context_propagation?: bool}, twig?: array{enabled?: bool, trace_templates?: bool, trace_blocks?: bool, trace_macros?: bool, exclude_templates?: array}, http_client?: array{enabled?: bool, exclude_clients?: array}, psr18_client?: array{enabled?: bool, exclude_clients?: array}, dbal?: array{enabled?: bool, log_sql?: bool, max_sql_length?: int, exclude_connections?: array}, cache?: array{enabled?: bool, exclude_pools?: array}} $config
*/
@@ -1300,4 +1265,34 @@ private function registerTracers(array $config, ContainerBuilder $container) : v
$container->setDefinition('flow.telemetry.' . $name . '.tracer', $definition);
}
}
+
+ private function resolveErrorHandlerReference(mixed $name, ContainerBuilder $container) : Reference
+ {
+ if (!\is_string($name) || $name === '') {
+ $name = 'default';
+ }
+
+ $serviceId = 'flow.telemetry.error_handler.' . $name;
+
+ if (!$container->hasDefinition($serviceId) && !$container->hasAlias($serviceId)) {
+ throw new RuntimeException(\sprintf('Unknown error_handler "%s"; declare it under flow_telemetry.error_handlers', $name));
+ }
+
+ return new Reference($serviceId);
+ }
+
+ private function resolveExporterReference(string $signalLabel, mixed $exporterName, ContainerBuilder $container) : Reference
+ {
+ if (!\is_string($exporterName) || $exporterName === '') {
+ throw new RuntimeException(\sprintf('Missing "exporter" reference for %s processor; expected a name from top-level "exporters"', $signalLabel));
+ }
+
+ $serviceId = 'flow.telemetry.exporter.' . $exporterName;
+
+ if (!$container->hasDefinition($serviceId) && !$container->hasAlias($serviceId)) {
+ throw new RuntimeException(\sprintf('%s processor references unknown exporter "%s"', \ucfirst($signalLabel), $exporterName));
+ }
+
+ return new Reference($serviceId);
+ }
}
diff --git a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/FlowTelemetryExtensionTest.php b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/FlowTelemetryExtensionTest.php
index 773c8b9e0..78eee4d8c 100644
--- a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/FlowTelemetryExtensionTest.php
+++ b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/FlowTelemetryExtensionTest.php
@@ -4,28 +4,24 @@
namespace Flow\Bridge\Symfony\TelemetryBundle\Tests\Integration;
-use Flow\Bridge\Psr3\Telemetry\TelemetryLogger;
use Flow\Bridge\Symfony\TelemetryBundle\DependencyInjection\Compiler\{FrameworkLoggerPass, OTLPAvailabilityPass};
use Flow\Bridge\Symfony\TelemetryBundle\DependencyInjection\FlowTelemetryExtension;
use Flow\Bridge\Symfony\TelemetryBundle\Exception\RuntimeException;
-use Flow\Bridge\Symfony\TelemetryBundle\Tests\Fixtures\Logger\StubLogger;
use Flow\Bridge\Symfony\TelemetryBundle\Tests\Fixtures\TestKernel;
-use Flow\Telemetry\Context\MemoryContextStorage;
-use Flow\Telemetry\{Logger\Logger, Meter\Meter, Resource, Telemetry, Tracer\Tracer};
-use Flow\Telemetry\Logger\LoggerProvider;
-use Flow\Telemetry\Logger\Processor\{BatchingLogProcessor, CompositeLogProcessor, PassThroughLogProcessor};
-use Flow\Telemetry\Meter\MeterProvider;
-use Flow\Telemetry\Meter\Processor\{BatchingMetricProcessor, CompositeMetricProcessor, PassThroughMetricProcessor};
+use Flow\Bridge\Telemetry\OTLP\Exporter\OTLPExporter;
+use Flow\Bridge\Telemetry\OTLP\Transport\{CurlTransport, GrpcTransport, StreamTransport};
+use Flow\Telemetry\ErrorHandler\{CompositeErrorHandler, ErrorLogHandler, NullErrorHandler, StreamHandler, SyslogHandler, UdpSyslogHandler};
+use Flow\Telemetry\Logger\Processor\{BatchingLogProcessor, SeverityFilteringLogProcessor};
+use Flow\Telemetry\Meter\Processor\BatchingMetricProcessor;
use Flow\Telemetry\Provider\Clock\SystemClock;
-use Flow\Telemetry\Provider\Console\{ConsoleLogExporter, ConsoleMetricExporter, ConsoleSpanExporter};
-use Flow\Telemetry\Provider\Memory\{MemoryLogExporter, MemoryLogProcessor, MemoryMetricExporter, MemoryMetricProcessor, MemorySpanExporter, MemorySpanProcessor};
-use Flow\Telemetry\Provider\Void\{VoidLogExporter, VoidLogProcessor, VoidMetricExporter, VoidMetricProcessor, VoidSpanExporter, VoidSpanProcessor};
+use Flow\Telemetry\Provider\Console\ConsoleExporter;
+use Flow\Telemetry\Provider\Memory\MemorySpanProcessor;
+use Flow\Telemetry\Provider\Void\{VoidExporter, VoidLogProcessor, VoidMetricProcessor, VoidSpanProcessor};
use Flow\Telemetry\Resource\Detector\CachingDetector;
-use Flow\Telemetry\Tracer\Processor\{BatchingSpanProcessor, CompositeSpanProcessor, PassThroughSpanProcessor};
-use Flow\Telemetry\Tracer\Sampler\{AlwaysOffSampler, AlwaysOnSampler, ParentBasedSampler, TraceIdRatioBasedSampler};
-use Flow\Telemetry\Tracer\TracerProvider;
-use PHPUnit\Framework\Attributes\CoversClass;
-use Symfony\Component\DependencyInjection\{ContainerBuilder, Definition};
+use Flow\Telemetry\{Resource, Telemetry};
+use Flow\Telemetry\Tracer\Processor\{BatchingSpanProcessor, CompositeSpanProcessor};
+use PHPUnit\Framework\Attributes\{CoversClass, TestWith};
+use Symfony\Component\DependencyInjection\{ContainerBuilder, Definition, Reference};
use Symfony\Component\HttpKernel\Log\Logger as SymfonyDefaultLogger;
#[CoversClass(FlowTelemetryExtension::class)]
@@ -91,576 +87,38 @@ public function test_caching_detector_writes_to_configured_path() : void
}
}
- public function test_composite_log_processor() : void
- {
- $this->bootKernel([
- 'config' => static function (TestKernel $kernel) : void {
- $kernel->addTestExtensionConfig('flow_telemetry', [
- 'resource' => [],
- 'logger_provider' => [
- 'processor' => [
- 'type' => 'composite',
- 'processors' => [
- ['type' => 'memory', 'exporter' => ['type' => 'memory']],
- ['type' => 'passthrough', 'exporter' => ['type' => 'console']],
- ],
- ],
- ],
- ]);
- },
- ]);
-
- self::assertInstanceOf(CompositeLogProcessor::class, $this->getContainer()->get('flow.telemetry.logger_provider.processor'));
- }
-
- public function test_composite_metric_processor() : void
- {
- $this->bootKernel([
- 'config' => static function (TestKernel $kernel) : void {
- $kernel->addTestExtensionConfig('flow_telemetry', [
- 'resource' => [],
- 'meter_provider' => [
- 'processor' => [
- 'type' => 'composite',
- 'processors' => [
- ['type' => 'memory', 'exporter' => ['type' => 'memory']],
- ['type' => 'passthrough', 'exporter' => ['type' => 'console']],
- ],
- ],
- ],
- ]);
- },
- ]);
-
- self::assertInstanceOf(CompositeMetricProcessor::class, $this->getContainer()->get('flow.telemetry.meter_provider.processor'));
- }
-
- public function test_composite_span_processor() : void
- {
- $this->bootKernel([
- 'config' => static function (TestKernel $kernel) : void {
- $kernel->addTestExtensionConfig('flow_telemetry', [
- 'resource' => [],
- 'tracer_provider' => [
- 'processor' => [
- 'type' => 'composite',
- 'processors' => [
- ['type' => 'memory', 'exporter' => ['type' => 'memory']],
- ['type' => 'passthrough', 'exporter' => ['type' => 'console']],
- ],
- ],
- ],
- ]);
- },
- ]);
-
- $container = $this->getContainer();
-
- self::assertInstanceOf(CompositeSpanProcessor::class, $container->get('flow.telemetry.tracer_provider.processor'));
- self::assertInstanceOf(MemorySpanProcessor::class, $container->get('flow.telemetry.tracer_provider.processor.0.processor'));
- self::assertInstanceOf(PassThroughSpanProcessor::class, $container->get('flow.telemetry.tracer_provider.processor.1.processor'));
- }
-
- public function test_custom_service_reference_for_exporter() : void
- {
- $container = new ContainerBuilder();
- $container->setParameter('kernel.project_dir', __DIR__);
- $container->setParameter('kernel.cache_dir', \sys_get_temp_dir());
- $container->setParameter('kernel.environment', 'test');
-
- $container->register('my.custom.span_exporter', MemorySpanExporter::class)->setPublic(true);
-
- $extension = new FlowTelemetryExtension();
- $extension->load([
- [
- 'resource' => [],
- 'tracer_provider' => [
- 'processor' => [
- 'type' => 'passthrough',
- 'exporter' => [
- 'type' => 'service',
- 'service_id' => 'my.custom.span_exporter',
- ],
- ],
- ],
- ],
- ], $container);
-
- $this->symfonyContext()->makeFlowServicesPublic($container);
- $container->compile();
-
- self::assertSame(
- $container->get('my.custom.span_exporter'),
- $container->get('flow.telemetry.tracer_provider.processor.exporter')
- );
- }
-
- public function test_custom_service_reference_for_processor() : void
- {
- $container = new ContainerBuilder();
- $container->setParameter('kernel.project_dir', __DIR__);
- $container->setParameter('kernel.cache_dir', \sys_get_temp_dir());
- $container->setParameter('kernel.environment', 'test');
-
- $container->register('my.custom.span_processor', VoidSpanProcessor::class)->setPublic(true);
-
- $extension = new FlowTelemetryExtension();
- $extension->load([
- [
- 'resource' => [],
- 'tracer_provider' => [
- 'processor' => [
- 'type' => 'service',
- 'service_id' => 'my.custom.span_processor',
- ],
- ],
- ],
- ], $container);
-
- $this->symfonyContext()->makeFlowServicesPublic($container);
- $container->compile();
-
- self::assertSame(
- $container->get('my.custom.span_processor'),
- $container->get('flow.telemetry.tracer_provider.processor')
- );
- }
-
- public function test_custom_service_reference_for_sampler() : void
- {
- $container = new ContainerBuilder();
- $container->setParameter('kernel.project_dir', __DIR__);
- $container->setParameter('kernel.cache_dir', \sys_get_temp_dir());
- $container->setParameter('kernel.environment', 'test');
-
- $container->register('my.custom.sampler', AlwaysOffSampler::class)->setPublic(true);
-
- $extension = new FlowTelemetryExtension();
- $extension->load([
- [
- 'resource' => [],
- 'tracer_provider' => [
- 'sampler' => [
- 'type' => 'service',
- 'service_id' => 'my.custom.sampler',
- ],
- ],
- ],
- ], $container);
-
- $this->symfonyContext()->makeFlowServicesPublic($container);
- $container->compile();
-
- self::assertSame(
- $container->get('my.custom.sampler'),
- $container->get('flow.telemetry.tracer_provider.sampler')
- );
- }
-
- public function test_default_logger_meter_tracer_are_always_registered_when_user_defined_none() : void
- {
- $this->bootKernel([
- 'config' => static function (TestKernel $kernel) : void {
- $kernel->addTestExtensionConfig('flow_telemetry', [
- 'resource' => [],
- ]);
- },
- ]);
-
- $container = $this->getContainer();
-
- self::assertInstanceOf(Logger::class, $container->get('flow.telemetry.default.logger'));
- self::assertInstanceOf(Meter::class, $container->get('flow.telemetry.default.meter'));
- self::assertInstanceOf(Tracer::class, $container->get('flow.telemetry.default.tracer'));
- self::assertInstanceOf(TelemetryLogger::class, $container->get('flow.telemetry.default.logger.psr3'));
- }
-
- public function test_default_named_instances_are_registered_alongside_user_defined_ones() : void
- {
- $this->bootKernel([
- 'config' => static function (TestKernel $kernel) : void {
- $kernel->addTestExtensionConfig('flow_telemetry', [
- 'resource' => [],
- 'loggers' => ['app' => []],
- 'meters' => ['app' => []],
- 'tracers' => ['app' => []],
- ]);
- },
- ]);
-
- $container = $this->getContainer();
-
- self::assertInstanceOf(Logger::class, $container->get('flow.telemetry.app.logger'));
- self::assertInstanceOf(Logger::class, $container->get('flow.telemetry.default.logger'));
- self::assertInstanceOf(Meter::class, $container->get('flow.telemetry.app.meter'));
- self::assertInstanceOf(Meter::class, $container->get('flow.telemetry.default.meter'));
- self::assertInstanceOf(Tracer::class, $container->get('flow.telemetry.app.tracer'));
- self::assertInstanceOf(Tracer::class, $container->get('flow.telemetry.default.tracer'));
- self::assertInstanceOf(TelemetryLogger::class, $container->get('flow.telemetry.app.logger.psr3'));
- self::assertInstanceOf(TelemetryLogger::class, $container->get('flow.telemetry.default.logger.psr3'));
- }
-
- public function test_flow_telemetry_is_aliased_to_telemetry_class() : void
- {
- $this->bootKernel([
- 'config' => static function (TestKernel $kernel) : void {
- $kernel->addTestExtensionConfig('flow_telemetry', [
- 'resource' => [],
- ]);
- },
- ]);
-
- $container = $this->getContainer();
-
- self::assertTrue($container->has(Telemetry::class));
- self::assertSame($container->get('flow.telemetry'), $container->get(Telemetry::class));
- }
-
- public function test_framework_logger_aliases_symfony_logger_service_to_psr3_wrapper() : void
- {
- $this->bootKernel([
- 'config' => static function (TestKernel $kernel) : void {
- $kernel->addTestExtensionConfig('flow_telemetry', [
- 'resource' => [],
- 'loggers' => ['app' => []],
- 'framework_logger' => 'app',
- ]);
- },
- ]);
-
- $container = $this->getContainer();
-
- self::assertTrue($container->has('logger'));
- self::assertInstanceOf(TelemetryLogger::class, $container->get('logger'));
- }
-
- public function test_framework_logger_throws_when_referenced_logger_is_not_configured() : void
- {
- self::expectException(RuntimeException::class);
- self::expectExceptionMessage('flow.telemetry.missing.logger.psr3');
-
- $this->bootKernel([
- 'config' => static function (TestKernel $kernel) : void {
- $kernel->addTestExtensionConfig('flow_telemetry', [
- 'resource' => [],
- 'framework_logger' => 'missing',
- ]);
- },
- ]);
- }
-
- public function test_full_configuration_scenario() : void
- {
- $this->bootKernel([
- 'config' => static function (TestKernel $kernel) : void {
- $kernel->addTestExtensionConfig('flow_telemetry', [
- 'resource' => [
- 'custom' => [
- 'service.name' => 'my-application',
- 'service.version' => '3.0.0',
- 'deployment.environment.name' => 'staging',
- ],
- ],
- 'tracer_provider' => [
- 'sampler' => [
- 'type' => 'trace_id_ratio',
- 'ratio' => 0.75,
- ],
- 'processor' => [
- 'type' => 'batching',
- 'batch_size' => 1024,
- 'exporter' => ['type' => 'console'],
- ],
- ],
- 'meter_provider' => [
- 'temporality' => 'delta',
- 'processor' => [
- 'type' => 'passthrough',
- 'exporter' => ['type' => 'memory'],
- ],
- ],
- 'logger_provider' => [
- 'processor' => [
- 'type' => 'memory',
- 'exporter' => ['type' => 'console'],
- ],
- ],
- ]);
- },
- ]);
-
- $container = $this->getContainer();
-
- /** @var resource $resource */
- $resource = $container->get('flow.telemetry.resource');
- self::assertSame('my-application', $resource->get('service.name'));
- self::assertSame('3.0.0', $resource->get('service.version'));
- self::assertSame('staging', $resource->get('deployment.environment.name'));
-
- self::assertInstanceOf(Telemetry::class, $container->get('flow.telemetry'));
-
- self::assertInstanceOf(TracerProvider::class, $container->get('flow.telemetry.tracer_provider'));
- self::assertInstanceOf(TraceIdRatioBasedSampler::class, $container->get('flow.telemetry.tracer_provider.sampler'));
- self::assertInstanceOf(BatchingSpanProcessor::class, $container->get('flow.telemetry.tracer_provider.processor'));
- self::assertInstanceOf(ConsoleSpanExporter::class, $container->get('flow.telemetry.tracer_provider.processor.exporter'));
-
- self::assertInstanceOf(MeterProvider::class, $container->get('flow.telemetry.meter_provider'));
- self::assertInstanceOf(PassThroughMetricProcessor::class, $container->get('flow.telemetry.meter_provider.processor'));
- self::assertInstanceOf(MemoryMetricExporter::class, $container->get('flow.telemetry.meter_provider.processor.exporter'));
-
- self::assertInstanceOf(LoggerProvider::class, $container->get('flow.telemetry.logger_provider'));
- self::assertInstanceOf(MemoryLogProcessor::class, $container->get('flow.telemetry.logger_provider.processor'));
- self::assertInstanceOf(ConsoleLogExporter::class, $container->get('flow.telemetry.logger_provider.processor.exporter'));
- }
-
- public function test_log_exporter_console_type() : void
- {
- $this->bootKernel([
- 'config' => static function (TestKernel $kernel) : void {
- $kernel->addTestExtensionConfig('flow_telemetry', [
- 'resource' => [],
- 'logger_provider' => ['processor' => ['type' => 'passthrough', 'exporter' => ['type' => 'console']]],
- ]);
- },
- ]);
-
- self::assertInstanceOf(ConsoleLogExporter::class, $this->getContainer()->get('flow.telemetry.logger_provider.processor.exporter'));
- }
-
- public function test_log_exporter_memory_type() : void
- {
- $this->bootKernel([
- 'config' => static function (TestKernel $kernel) : void {
- $kernel->addTestExtensionConfig('flow_telemetry', [
- 'resource' => [],
- 'logger_provider' => ['processor' => ['type' => 'passthrough', 'exporter' => ['type' => 'memory']]],
- ]);
- },
- ]);
-
- self::assertInstanceOf(MemoryLogExporter::class, $this->getContainer()->get('flow.telemetry.logger_provider.processor.exporter'));
- }
-
- public function test_log_exporter_void_type() : void
- {
- $this->bootKernel([
- 'config' => static function (TestKernel $kernel) : void {
- $kernel->addTestExtensionConfig('flow_telemetry', [
- 'resource' => [],
- 'logger_provider' => ['processor' => ['type' => 'passthrough', 'exporter' => ['type' => 'void']]],
- ]);
- },
- ]);
-
- self::assertInstanceOf(VoidLogExporter::class, $this->getContainer()->get('flow.telemetry.logger_provider.processor.exporter'));
- }
-
- public function test_log_processor_batching_type() : void
- {
- $this->bootKernel([
- 'config' => static function (TestKernel $kernel) : void {
- $kernel->addTestExtensionConfig('flow_telemetry', [
- 'resource' => [],
- 'logger_provider' => ['processor' => ['type' => 'batching', 'batch_size' => 256, 'exporter' => ['type' => 'void']]],
- ]);
- },
- ]);
-
- self::assertInstanceOf(BatchingLogProcessor::class, $this->getContainer()->get('flow.telemetry.logger_provider.processor'));
- }
-
- public function test_log_processor_memory_type() : void
- {
- $this->bootKernel([
- 'config' => static function (TestKernel $kernel) : void {
- $kernel->addTestExtensionConfig('flow_telemetry', [
- 'resource' => [],
- 'logger_provider' => ['processor' => ['type' => 'memory', 'exporter' => ['type' => 'void']]],
- ]);
- },
- ]);
-
- self::assertInstanceOf(MemoryLogProcessor::class, $this->getContainer()->get('flow.telemetry.logger_provider.processor'));
- }
-
- public function test_log_processor_passthrough_type() : void
- {
- $this->bootKernel([
- 'config' => static function (TestKernel $kernel) : void {
- $kernel->addTestExtensionConfig('flow_telemetry', [
- 'resource' => [],
- 'logger_provider' => ['processor' => ['type' => 'passthrough', 'exporter' => ['type' => 'void']]],
- ]);
- },
- ]);
-
- self::assertInstanceOf(PassThroughLogProcessor::class, $this->getContainer()->get('flow.telemetry.logger_provider.processor'));
- }
-
- public function test_log_processor_void_type() : void
- {
- $this->bootKernel([
- 'config' => static function (TestKernel $kernel) : void {
- $kernel->addTestExtensionConfig('flow_telemetry', [
- 'resource' => [],
- 'logger_provider' => ['processor' => ['type' => 'void']],
- ]);
- },
- ]);
-
- self::assertInstanceOf(VoidLogProcessor::class, $this->getContainer()->get('flow.telemetry.logger_provider.processor'));
- }
-
- public function test_metric_exporter_console_type() : void
- {
- $this->bootKernel([
- 'config' => static function (TestKernel $kernel) : void {
- $kernel->addTestExtensionConfig('flow_telemetry', [
- 'resource' => [],
- 'meter_provider' => ['processor' => ['type' => 'passthrough', 'exporter' => ['type' => 'console']]],
- ]);
- },
- ]);
-
- self::assertInstanceOf(ConsoleMetricExporter::class, $this->getContainer()->get('flow.telemetry.meter_provider.processor.exporter'));
- }
-
- public function test_metric_exporter_memory_type() : void
- {
- $this->bootKernel([
- 'config' => static function (TestKernel $kernel) : void {
- $kernel->addTestExtensionConfig('flow_telemetry', [
- 'resource' => [],
- 'meter_provider' => ['processor' => ['type' => 'passthrough', 'exporter' => ['type' => 'memory']]],
- ]);
- },
- ]);
-
- self::assertInstanceOf(MemoryMetricExporter::class, $this->getContainer()->get('flow.telemetry.meter_provider.processor.exporter'));
- }
-
- public function test_metric_exporter_void_type() : void
- {
- $this->bootKernel([
- 'config' => static function (TestKernel $kernel) : void {
- $kernel->addTestExtensionConfig('flow_telemetry', [
- 'resource' => [],
- 'meter_provider' => ['processor' => ['type' => 'passthrough', 'exporter' => ['type' => 'void']]],
- ]);
- },
- ]);
-
- self::assertInstanceOf(VoidMetricExporter::class, $this->getContainer()->get('flow.telemetry.meter_provider.processor.exporter'));
- }
-
- public function test_metric_processor_batching_type() : void
- {
- $this->bootKernel([
- 'config' => static function (TestKernel $kernel) : void {
- $kernel->addTestExtensionConfig('flow_telemetry', [
- 'resource' => [],
- 'meter_provider' => ['processor' => ['type' => 'batching', 'batch_size' => 200, 'exporter' => ['type' => 'void']]],
- ]);
- },
- ]);
-
- self::assertInstanceOf(BatchingMetricProcessor::class, $this->getContainer()->get('flow.telemetry.meter_provider.processor'));
- }
-
- public function test_metric_processor_memory_type() : void
- {
- $this->bootKernel([
- 'config' => static function (TestKernel $kernel) : void {
- $kernel->addTestExtensionConfig('flow_telemetry', [
- 'resource' => [],
- 'meter_provider' => ['processor' => ['type' => 'memory', 'exporter' => ['type' => 'void']]],
- ]);
- },
- ]);
-
- self::assertInstanceOf(MemoryMetricProcessor::class, $this->getContainer()->get('flow.telemetry.meter_provider.processor'));
- }
-
- public function test_metric_processor_passthrough_type() : void
- {
- $this->bootKernel([
- 'config' => static function (TestKernel $kernel) : void {
- $kernel->addTestExtensionConfig('flow_telemetry', [
- 'resource' => [],
- 'meter_provider' => ['processor' => ['type' => 'passthrough', 'exporter' => ['type' => 'void']]],
- ]);
- },
- ]);
-
- self::assertInstanceOf(PassThroughMetricProcessor::class, $this->getContainer()->get('flow.telemetry.meter_provider.processor'));
- }
-
- public function test_metric_processor_void_type() : void
- {
- $this->bootKernel([
- 'config' => static function (TestKernel $kernel) : void {
- $kernel->addTestExtensionConfig('flow_telemetry', [
- 'resource' => [],
- 'meter_provider' => ['processor' => ['type' => 'void']],
- ]);
- },
- ]);
-
- self::assertInstanceOf(VoidMetricProcessor::class, $this->getContainer()->get('flow.telemetry.meter_provider.processor'));
- }
-
- public function test_minimal_configuration_creates_telemetry_with_void_processors() : void
+ public function test_clock_can_be_overridden_with_custom_service() : void
{
$this->bootKernel([
'config' => static function (TestKernel $kernel) : void {
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'clock_service_id' => 'app.custom_clock',
]);
+ $kernel->addTestContainerConfigurator(static function (ContainerBuilder $container) : void {
+ $clockDefinition = new Definition(SystemClock::class);
+ $clockDefinition->setPublic(true);
+ $container->setDefinition('app.custom_clock', $clockDefinition);
+ });
},
]);
$container = $this->getContainer();
-
self::assertTrue($container->has('flow.telemetry.clock'));
- self::assertTrue($container->has('flow.telemetry.context_storage'));
- self::assertTrue($container->has('flow.telemetry.resource'));
- self::assertTrue($container->has('flow.telemetry'));
-
- self::assertInstanceOf(SystemClock::class, $container->get('flow.telemetry.clock'));
- self::assertInstanceOf(MemoryContextStorage::class, $container->get('flow.telemetry.context_storage'));
- self::assertInstanceOf(Resource::class, $container->get('flow.telemetry.resource'));
- self::assertInstanceOf(Telemetry::class, $container->get('flow.telemetry'));
-
- self::assertTrue($container->has('flow.telemetry.tracer_provider'));
- self::assertTrue($container->has('flow.telemetry.meter_provider'));
- self::assertTrue($container->has('flow.telemetry.logger_provider'));
-
- self::assertInstanceOf(TracerProvider::class, $container->get('flow.telemetry.tracer_provider'));
- self::assertInstanceOf(MeterProvider::class, $container->get('flow.telemetry.meter_provider'));
- self::assertInstanceOf(LoggerProvider::class, $container->get('flow.telemetry.logger_provider'));
-
- self::assertTrue($container->has('flow.telemetry.tracer_provider.processor'));
- self::assertInstanceOf(VoidSpanProcessor::class, $container->get('flow.telemetry.tracer_provider.processor'));
-
- self::assertTrue($container->has('flow.telemetry.meter_provider.processor'));
- self::assertInstanceOf(VoidMetricProcessor::class, $container->get('flow.telemetry.meter_provider.processor'));
-
- self::assertTrue($container->has('flow.telemetry.logger_provider.processor'));
- self::assertInstanceOf(VoidLogProcessor::class, $container->get('flow.telemetry.logger_provider.processor'));
}
- public function test_multiple_named_services_of_same_type() : void
+ public function test_composite_error_handler_is_registered_with_referenced_children() : void
{
$this->bootKernel([
'config' => static function (TestKernel $kernel) : void {
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
- 'tracers' => [
- 'database' => [
- 'version' => '1.0.0',
- ],
- 'http_client' => [
- 'version' => '2.0.0',
+ 'error_handlers' => [
+ 'default' => ['type' => 'error_log'],
+ 'silent' => ['type' => 'noop'],
+ 'fanout' => [
+ 'type' => 'composite',
+ 'handlers' => ['default', 'silent'],
],
],
]);
@@ -668,43 +126,51 @@ public function test_multiple_named_services_of_same_type() : void
]);
$container = $this->getContainer();
-
- self::assertTrue($container->has('flow.telemetry.database.tracer'));
- self::assertTrue($container->has('flow.telemetry.http_client.tracer'));
- self::assertInstanceOf(Tracer::class, $container->get('flow.telemetry.database.tracer'));
- self::assertInstanceOf(Tracer::class, $container->get('flow.telemetry.http_client.tracer'));
+ $composite = $container->get('flow.telemetry.error_handler.fanout');
+ self::assertInstanceOf(CompositeErrorHandler::class, $composite);
+ self::assertCount(2, $composite->handlers());
+ self::assertInstanceOf(ErrorLogHandler::class, $composite->handlers()[0]);
+ self::assertInstanceOf(NullErrorHandler::class, $composite->handlers()[1]);
}
- public function test_named_logger_is_registered_as_service() : void
+ public function test_composite_referencing_unknown_child_throws() : void
{
+ $this->expectException(RuntimeException::class);
+ $this->expectExceptionMessage('Unknown error_handler "ghost"');
+
$this->bootKernel([
'config' => static function (TestKernel $kernel) : void {
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
- 'loggers' => [
- 'audit' => [
- 'version' => '1.0.0',
- ],
+ 'error_handlers' => [
+ 'fanout' => ['type' => 'composite', 'handlers' => ['ghost']],
],
]);
},
]);
-
- $container = $this->getContainer();
-
- self::assertTrue($container->has('flow.telemetry.audit.logger'));
- self::assertInstanceOf(Logger::class, $container->get('flow.telemetry.audit.logger'));
}
- public function test_named_meter_is_registered_as_service() : void
+ public function test_composite_span_processor_with_named_exporters() : void
{
$this->bootKernel([
'config' => static function (TestKernel $kernel) : void {
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
- 'meters' => [
- 'etl_pipeline' => [
- 'version' => '1.0.0',
+ 'exporters' => [
+ 'memory' => ['memory' => null],
+ 'otlp' => [
+ 'otlp' => [
+ 'transport' => ['type' => 'curl', 'endpoint' => 'http://localhost:4318'],
+ ],
+ ],
+ ],
+ 'tracer_provider' => [
+ 'processor' => [
+ 'type' => 'composite',
+ 'processors' => [
+ ['type' => 'memory', 'exporter' => 'memory'],
+ ['type' => 'batching', 'exporter' => 'otlp', 'batch_size' => 256],
+ ],
],
],
]);
@@ -712,44 +178,48 @@ public function test_named_meter_is_registered_as_service() : void
]);
$container = $this->getContainer();
+ $processor = $container->get('flow.telemetry.tracer_provider.processor');
- self::assertTrue($container->has('flow.telemetry.etl_pipeline.meter'));
- self::assertInstanceOf(Meter::class, $container->get('flow.telemetry.etl_pipeline.meter'));
+ self::assertInstanceOf(CompositeSpanProcessor::class, $processor);
+ self::assertCount(2, $processor->processors());
+ self::assertInstanceOf(MemorySpanProcessor::class, $processor->processors()[0]);
+ self::assertInstanceOf(BatchingSpanProcessor::class, $processor->processors()[1]);
}
- public function test_named_tracer_is_registered_as_service() : void
+ public function test_console_exporter_is_registered() : void
{
$this->bootKernel([
'config' => static function (TestKernel $kernel) : void {
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
- 'tracers' => [
- 'database' => [
- 'version' => '2.0.0',
- ],
+ 'exporters' => [
+ 'console' => ['console' => null],
+ ],
+ 'logger_provider' => [
+ 'processor' => ['type' => 'batching', 'exporter' => 'console'],
],
]);
},
]);
$container = $this->getContainer();
-
- self::assertTrue($container->has('flow.telemetry.database.tracer'));
- self::assertInstanceOf(Tracer::class, $container->get('flow.telemetry.database.tracer'));
+ self::assertInstanceOf(ConsoleExporter::class, $container->get('flow.telemetry.exporter.console'));
}
- public function test_named_tracer_with_attributes() : void
+ public function test_curl_transport_is_built_inline_inside_otlp_exporter() : void
{
$this->bootKernel([
'config' => static function (TestKernel $kernel) : void {
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
- 'tracers' => [
- 'database' => [
- 'version' => '2.0.0',
- 'schema_url' => 'https://opentelemetry.io/schemas/1.20.0',
- 'attributes' => [
- 'db.system' => 'postgresql',
+ 'exporters' => [
+ 'otlp' => [
+ 'otlp' => [
+ 'transport' => [
+ 'type' => 'curl',
+ 'endpoint' => 'http://localhost:4318',
+ 'encoding' => 'protobuf',
+ ],
],
],
],
@@ -758,478 +228,632 @@ public function test_named_tracer_with_attributes() : void
]);
$container = $this->getContainer();
- $tracer = $container->get('flow.telemetry.database.tracer');
-
- self::assertInstanceOf(Tracer::class, $tracer);
+ self::assertInstanceOf(CurlTransport::class, $container->get('flow.telemetry.exporter.otlp.transport'));
+ self::assertInstanceOf(OTLPExporter::class, $container->get('flow.telemetry.exporter.otlp'));
}
- public function test_no_auto_alias_when_logger_is_already_an_alias_to_another_service() : void
+ public function test_custom_exporter_via_service() : void
{
$this->bootKernel([
'config' => static function (TestKernel $kernel) : void {
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => [
+ 'custom' => ['service' => ['id' => 'app.my_exporter']],
+ ],
+ 'tracer_provider' => [
+ 'processor' => ['type' => 'batching', 'exporter' => 'custom'],
+ ],
]);
$kernel->addTestContainerConfigurator(static function (ContainerBuilder $container) : void {
- $thirdPartyDefinition = new Definition(StubLogger::class);
- $thirdPartyDefinition->setPublic(true);
- $container->setDefinition('app.my_logger', $thirdPartyDefinition);
- $container->setAlias('logger', 'app.my_logger')->setPublic(true);
+ $definition = new Definition(VoidExporter::class);
+ $definition->setPublic(true);
+ $container->setDefinition('app.my_exporter', $definition);
});
},
]);
$container = $this->getContainer();
-
- self::assertSame(
- $container->get('app.my_logger'),
- $container->get('logger'),
- );
- self::assertInstanceOf(StubLogger::class, $container->get('logger'));
+ $exporter = $container->get('flow.telemetry.exporter.custom');
+ self::assertInstanceOf(VoidExporter::class, $exporter);
}
- public function test_no_auto_alias_when_logger_service_class_is_not_symfony_default() : void
+ public function test_custom_processor_via_service() : void
{
$this->bootKernel([
'config' => static function (TestKernel $kernel) : void {
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'tracer_provider' => [
+ 'processor' => ['type' => 'service', 'service_id' => 'app.my_span_processor'],
+ ],
]);
$kernel->addTestContainerConfigurator(static function (ContainerBuilder $container) : void {
- $loggerDefinition = new Definition(StubLogger::class);
- $loggerDefinition->setPublic(true);
- $container->setDefinition('logger', $loggerDefinition);
+ $definition = new Definition(VoidSpanProcessor::class);
+ $definition->setPublic(true);
+ $container->setDefinition('app.my_span_processor', $definition);
});
},
]);
$container = $this->getContainer();
-
- self::assertInstanceOf(StubLogger::class, $container->get('logger'));
+ self::assertInstanceOf(VoidSpanProcessor::class, $container->get('flow.telemetry.tracer_provider.processor'));
}
- public function test_otlp_availability_pass_sets_parameter_when_otlp_not_configured() : void
+ public function test_custom_transport_via_service() : void
{
$this->bootKernel([
'config' => static function (TestKernel $kernel) : void {
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => [
+ 'otlp' => [
+ 'otlp' => [
+ 'transport' => ['type' => 'service', 'service_id' => 'app.my_transport'],
+ ],
+ ],
+ ],
]);
+ $kernel->addTestContainerConfigurator(static function (ContainerBuilder $container) : void {
+ $definition = new Definition(StreamTransport::class);
+ $definition->setArgument('$destination', 'php://memory');
+ $definition->setPublic(true);
+ $container->setDefinition('app.my_transport', $definition);
+ });
},
]);
- self::assertTrue($this->getContainer()->hasParameter('flow.telemetry.otlp_available'));
+ $container = $this->getContainer();
+ self::assertInstanceOf(StreamTransport::class, $container->get('flow.telemetry.exporter.otlp.transport'));
}
- public function test_psr3_wrapper_service_is_a_telemetry_logger() : void
+ public function test_default_error_handler_is_registered_when_config_omits_error_handlers() : void
{
$this->bootKernel([
'config' => static function (TestKernel $kernel) : void {
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
- 'loggers' => ['app' => []],
]);
},
]);
- self::assertInstanceOf(TelemetryLogger::class, $this->getContainer()->get('flow.telemetry.app.logger.psr3'));
+ $container = $this->getContainer();
+ self::assertInstanceOf(ErrorLogHandler::class, $container->get('flow.telemetry.error_handler.default'));
}
- public function test_resource_caching_can_be_disabled() : void
+ public function test_default_telemetry_is_void() : void
{
$this->bootKernel([
'config' => static function (TestKernel $kernel) : void {
- $kernel->addTestExtensionConfig('flow_telemetry', [
- 'resource' => [
- 'detectors' => [
- 'static' => [
- 'cache' => ['enabled' => false],
- ],
- ],
- ],
- ]);
+ $kernel->addTestExtensionConfig('flow_telemetry', ['resource' => []]);
},
]);
$container = $this->getContainer();
-
- self::assertTrue($container->has('flow.telemetry.resource.detector'));
- self::assertTrue($container->has('flow.telemetry.resource.detector.static'));
+ self::assertInstanceOf(Telemetry::class, $container->get(Telemetry::class));
+ self::assertInstanceOf(VoidSpanProcessor::class, $container->get('flow.telemetry.tracer_provider.processor'));
+ self::assertInstanceOf(VoidMetricProcessor::class, $container->get('flow.telemetry.meter_provider.processor'));
+ self::assertInstanceOf(VoidLogProcessor::class, $container->get('flow.telemetry.logger_provider.processor'));
}
- public function test_resource_contains_custom_attributes() : void
+ public function test_error_log_handler_is_registered_with_configured_args() : void
{
$this->bootKernel([
'config' => static function (TestKernel $kernel) : void {
$kernel->addTestExtensionConfig('flow_telemetry', [
- 'resource' => [
- 'custom' => [
- 'service.name' => 'my-service',
- 'deployment.environment.name' => 'production',
- 'host.name' => 'server-01',
+ 'resource' => [],
+ 'error_handlers' => [
+ 'default' => [
+ 'type' => 'error_log',
+ 'message_type' => 'sapi',
+ 'expand_newlines' => true,
+ 'message_prefix' => '[custom]',
],
],
]);
},
]);
- /** @var resource $resource */
- $resource = $this->getContainer()->get('flow.telemetry.resource');
- self::assertSame('my-service', $resource->get('service.name'));
- self::assertSame('production', $resource->get('deployment.environment.name'));
- self::assertSame('server-01', $resource->get('host.name'));
+ $container = $this->getContainer();
+ self::assertInstanceOf(ErrorLogHandler::class, $container->get('flow.telemetry.error_handler.default'));
}
- public function test_resource_detectors_are_enabled_by_default() : void
+ public function test_minimal_otlp_setup_registers_three_providers_with_one_exporter() : void
{
$this->bootKernel([
'config' => static function (TestKernel $kernel) : void {
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => [
+ 'otlp' => [
+ 'otlp' => [
+ 'transport' => [
+ 'type' => 'curl',
+ 'endpoint' => 'http://localhost:4318',
+ 'encoding' => 'protobuf',
+ ],
+ ],
+ ],
+ ],
+ 'tracer_provider' => [
+ 'processor' => ['type' => 'batching', 'exporter' => 'otlp'],
+ ],
+ 'meter_provider' => [
+ 'processor' => ['type' => 'batching', 'exporter' => 'otlp'],
+ ],
+ 'logger_provider' => [
+ 'processor' => ['type' => 'batching', 'exporter' => 'otlp'],
+ ],
]);
},
]);
$container = $this->getContainer();
-
- self::assertTrue($container->has('flow.telemetry.resource.detector'));
- self::assertTrue($container->has('flow.telemetry.resource.detector.static'));
- self::assertTrue($container->has('flow.telemetry.resource.detector.dynamic'));
- self::assertTrue($container->has('flow.telemetry.resource.detector.os'));
- self::assertTrue($container->has('flow.telemetry.resource.detector.host'));
- self::assertTrue($container->has('flow.telemetry.resource.detector.process'));
- self::assertTrue($container->has('flow.telemetry.resource.detector.service'));
- self::assertTrue($container->has('flow.telemetry.resource.detector.deployment'));
- self::assertTrue($container->has('flow.telemetry.resource.detector.environment'));
-
- /** @var resource $resource */
- $resource = $container->get('flow.telemetry.resource');
- self::assertInstanceOf(Resource::class, $resource);
+ self::assertInstanceOf(BatchingSpanProcessor::class, $container->get('flow.telemetry.tracer_provider.processor'));
+ self::assertInstanceOf(BatchingMetricProcessor::class, $container->get('flow.telemetry.meter_provider.processor'));
+ self::assertInstanceOf(BatchingLogProcessor::class, $container->get('flow.telemetry.logger_provider.processor'));
+ self::assertInstanceOf(OTLPExporter::class, $container->get('flow.telemetry.exporter.otlp'));
}
- public function test_resource_detectors_can_be_disabled() : void
+ public function test_noop_error_handler_is_registered() : void
{
$this->bootKernel([
'config' => static function (TestKernel $kernel) : void {
$kernel->addTestExtensionConfig('flow_telemetry', [
- 'resource' => [
- 'detectors' => [
- 'enabled' => false,
- ],
- 'custom' => [
- 'service.name' => 'manual-service',
- ],
+ 'resource' => [],
+ 'error_handlers' => [
+ 'default' => ['type' => 'error_log'],
+ 'silent' => ['type' => 'noop'],
],
]);
},
]);
$container = $this->getContainer();
-
- self::assertFalse($container->has('flow.telemetry.resource.detector'));
- self::assertFalse($container->has('flow.telemetry.resource.detector.static'));
- self::assertFalse($container->has('flow.telemetry.resource.detector.dynamic'));
-
- /** @var resource $resource */
- $resource = $container->get('flow.telemetry.resource');
- self::assertSame('manual-service', $resource->get('service.name'));
+ self::assertInstanceOf(NullErrorHandler::class, $container->get('flow.telemetry.error_handler.silent'));
}
- public function test_resource_individual_detectors_can_be_disabled() : void
+ public function test_otlp_exporter_uses_named_error_handler() : void
{
- $this->bootKernel([
- 'config' => static function (TestKernel $kernel) : void {
- $kernel->addTestExtensionConfig('flow_telemetry', [
- 'resource' => [
- 'detectors' => [
- 'static' => [
- 'os' => ['enabled' => false],
- ],
- 'dynamic' => [
- 'process' => ['enabled' => false],
- ],
- ],
+ $container = new ContainerBuilder();
+ (new FlowTelemetryExtension())->load([[
+ 'resource' => [],
+ 'error_handlers' => [
+ 'default' => ['type' => 'error_log'],
+ 'silent' => ['type' => 'noop'],
+ ],
+ 'exporters' => [
+ 'otlp' => [
+ 'otlp' => [
+ 'error_handler' => 'silent',
+ 'transport' => ['type' => 'curl', 'endpoint' => 'http://localhost:4318'],
],
- ]);
- },
- ]);
-
- $container = $this->getContainer();
+ ],
+ ],
+ ]], $container);
- self::assertFalse($container->has('flow.telemetry.resource.detector.os'));
- self::assertTrue($container->has('flow.telemetry.resource.detector.host'));
- self::assertFalse($container->has('flow.telemetry.resource.detector.process'));
- self::assertTrue($container->has('flow.telemetry.resource.detector.service'));
- self::assertTrue($container->has('flow.telemetry.resource.detector.deployment'));
- self::assertTrue($container->has('flow.telemetry.resource.detector.environment'));
+ $definition = $container->getDefinition('flow.telemetry.exporter.otlp');
+ $errorHandlerArg = $definition->getArgument(1);
+ self::assertInstanceOf(Reference::class, $errorHandlerArg);
+ self::assertSame('flow.telemetry.error_handler.silent', (string) $errorHandlerArg);
}
- public function test_same_name_for_different_types_is_allowed() : void
+ public function test_otlp_transport_failover_inline_curl_with_stream_failover() : void
{
$this->bootKernel([
'config' => static function (TestKernel $kernel) : void {
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
- 'tracers' => [
- 'database' => ['version' => '1.0.0'],
- ],
- 'meters' => [
- 'database' => ['version' => '1.0.0'],
- ],
- 'loggers' => [
- 'database' => ['version' => '1.0.0'],
+ 'exporters' => [
+ 'otlp' => [
+ 'otlp' => [
+ 'transport' => [
+ 'type' => 'curl',
+ 'endpoint' => 'http://localhost:4318',
+ 'failover' => [
+ 'type' => 'stream',
+ 'endpoint' => 'php://memory',
+ ],
+ ],
+ ],
+ ],
],
]);
},
]);
$container = $this->getContainer();
-
- self::assertTrue($container->has('flow.telemetry.database.tracer'));
- self::assertTrue($container->has('flow.telemetry.database.meter'));
- self::assertTrue($container->has('flow.telemetry.database.logger'));
- self::assertInstanceOf(Tracer::class, $container->get('flow.telemetry.database.tracer'));
- self::assertInstanceOf(Meter::class, $container->get('flow.telemetry.database.meter'));
- self::assertInstanceOf(Logger::class, $container->get('flow.telemetry.database.logger'));
+ self::assertInstanceOf(CurlTransport::class, $container->get('flow.telemetry.exporter.otlp.transport'));
+ self::assertInstanceOf(StreamTransport::class, $container->get('flow.telemetry.exporter.otlp.failover.transport'));
}
- public function test_service_exporter_without_service_id_throws_exception() : void
+ public function test_otlp_transport_failover_inline_grpc_with_curl_failover() : void
{
- $this->expectException(RuntimeException::class);
- $this->expectExceptionMessage('service_id is required when exporter type is "service"');
+ if (!\extension_loaded('grpc')) {
+ self::markTestSkipped('ext-grpc is required');
+ }
$this->bootKernel([
'config' => static function (TestKernel $kernel) : void {
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
- 'tracer_provider' => [
- 'processor' => [
- 'type' => 'passthrough',
- 'exporter' => ['type' => 'service'],
+ 'exporters' => [
+ 'otlp' => [
+ 'otlp' => [
+ 'transport' => [
+ 'type' => 'grpc',
+ 'endpoint' => 'localhost:4317',
+ 'failover' => [
+ 'type' => 'curl',
+ 'endpoint' => 'http://localhost:4318',
+ ],
+ ],
+ ],
],
],
]);
},
]);
+
+ $container = $this->getContainer();
+ self::assertInstanceOf(GrpcTransport::class, $container->get('flow.telemetry.exporter.otlp.transport'));
+ self::assertInstanceOf(CurlTransport::class, $container->get('flow.telemetry.exporter.otlp.failover.transport'));
}
- public function test_service_processor_without_service_id_throws_exception() : void
+ public function test_processor_referencing_unknown_exporter_throws() : void
{
$this->expectException(RuntimeException::class);
- $this->expectExceptionMessage('service_id is required when processor type is "service"');
+ $this->expectExceptionMessage('references unknown exporter "missing"');
$this->bootKernel([
'config' => static function (TestKernel $kernel) : void {
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
'tracer_provider' => [
- 'processor' => ['type' => 'service'],
+ 'processor' => ['type' => 'batching', 'exporter' => 'missing'],
],
]);
},
]);
}
- public function test_service_sampler_without_service_id_throws_exception() : void
+ public function test_processor_uses_named_error_handler() : void
{
- $this->expectException(RuntimeException::class);
- $this->expectExceptionMessage('service_id is required when sampler type is "service"');
+ $container = new ContainerBuilder();
+ (new FlowTelemetryExtension())->load([[
+ 'resource' => [],
+ 'error_handlers' => [
+ 'default' => ['type' => 'error_log'],
+ 'silent' => ['type' => 'noop'],
+ ],
+ 'exporters' => [
+ 'memory' => ['memory' => null],
+ ],
+ 'logger_provider' => [
+ 'processor' => [
+ 'type' => 'batching',
+ 'exporter' => 'memory',
+ 'error_handler' => 'silent',
+ ],
+ ],
+ ]], $container);
- $this->bootKernel([
- 'config' => static function (TestKernel $kernel) : void {
- $kernel->addTestExtensionConfig('flow_telemetry', [
- 'resource' => [],
- 'tracer_provider' => [
- 'sampler' => ['type' => 'service'],
- ],
- ]);
- },
- ]);
+ $definition = $container->getDefinition('flow.telemetry.logger_provider.processor');
+ $errorHandlerArg = $definition->getArgument(2);
+ self::assertInstanceOf(Reference::class, $errorHandlerArg);
+ self::assertSame('flow.telemetry.error_handler.silent', (string) $errorHandlerArg);
}
- public function test_span_exporter_console_type() : void
+ public function test_provider_uses_named_error_handler() : void
{
- $this->bootKernel([
- 'config' => static function (TestKernel $kernel) : void {
- $kernel->addTestExtensionConfig('flow_telemetry', [
- 'resource' => [],
- 'tracer_provider' => ['processor' => ['type' => 'passthrough', 'exporter' => ['type' => 'console']]],
- ]);
- },
- ]);
+ $container = new ContainerBuilder();
+ (new FlowTelemetryExtension())->load([[
+ 'resource' => [],
+ 'error_handlers' => [
+ 'default' => ['type' => 'error_log'],
+ 'silent' => ['type' => 'noop'],
+ ],
+ 'logger_provider' => ['error_handler' => 'silent'],
+ ]], $container);
- self::assertInstanceOf(ConsoleSpanExporter::class, $this->getContainer()->get('flow.telemetry.tracer_provider.processor.exporter'));
+ $definition = $container->getDefinition('flow.telemetry.logger_provider');
+ $errorHandlerArg = $definition->getArgument('$errorHandler');
+ self::assertInstanceOf(Reference::class, $errorHandlerArg);
+ self::assertSame('flow.telemetry.error_handler.silent', (string) $errorHandlerArg);
}
- public function test_span_exporter_memory_type() : void
+ public function test_service_error_handler_creates_alias_to_user_service_id() : void
{
$this->bootKernel([
'config' => static function (TestKernel $kernel) : void {
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
- 'tracer_provider' => ['processor' => ['type' => 'passthrough', 'exporter' => ['type' => 'memory']]],
+ 'error_handlers' => [
+ 'default' => ['type' => 'error_log'],
+ 'custom' => ['type' => 'service', 'service_id' => 'app.my_handler'],
+ ],
]);
+ $kernel->addTestContainerConfigurator(static function (ContainerBuilder $container) : void {
+ $definition = new Definition(NullErrorHandler::class);
+ $definition->setPublic(true);
+ $container->setDefinition('app.my_handler', $definition);
+ });
},
]);
- self::assertInstanceOf(MemorySpanExporter::class, $this->getContainer()->get('flow.telemetry.tracer_provider.processor.exporter'));
+ $container = $this->getContainer();
+ self::assertSame(
+ $container->get('app.my_handler'),
+ $container->get('flow.telemetry.error_handler.custom'),
+ );
}
- public function test_span_exporter_void_type() : void
+ public function test_service_error_handler_missing_service_id_throws() : void
{
+ $this->expectException(RuntimeException::class);
+ $this->expectExceptionMessage('error_handler "custom" of type "service" requires a non-empty "service_id"');
+
$this->bootKernel([
'config' => static function (TestKernel $kernel) : void {
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
- 'tracer_provider' => ['processor' => ['type' => 'passthrough', 'exporter' => ['type' => 'void']]],
+ 'error_handlers' => [
+ 'custom' => ['type' => 'service'],
+ ],
]);
},
]);
-
- self::assertInstanceOf(VoidSpanExporter::class, $this->getContainer()->get('flow.telemetry.tracer_provider.processor.exporter'));
}
- public function test_span_processor_batching_type() : void
+ public function test_severity_filtering_wraps_batching_log_processor() : void
{
$this->bootKernel([
'config' => static function (TestKernel $kernel) : void {
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
- 'tracer_provider' => ['processor' => ['type' => 'batching', 'batch_size' => 100, 'exporter' => ['type' => 'void']]],
+ 'exporters' => [
+ 'otlp' => [
+ 'otlp' => [
+ 'transport' => ['type' => 'curl', 'endpoint' => 'http://localhost:4318'],
+ ],
+ ],
+ ],
+ 'logger_provider' => [
+ 'processor' => [
+ 'type' => 'severity_filtering',
+ 'minimum_severity' => 'warn',
+ 'inner_processor' => [
+ 'type' => 'batching',
+ 'exporter' => 'otlp',
+ 'batch_size' => 200,
+ ],
+ ],
+ ],
]);
},
]);
- self::assertInstanceOf(BatchingSpanProcessor::class, $this->getContainer()->get('flow.telemetry.tracer_provider.processor'));
+ $container = $this->getContainer();
+ self::assertInstanceOf(SeverityFilteringLogProcessor::class, $container->get('flow.telemetry.logger_provider.processor'));
}
- public function test_span_processor_memory_type() : void
+ public function test_stream_handler_is_registered_with_destination() : void
{
- $this->bootKernel([
- 'config' => static function (TestKernel $kernel) : void {
- $kernel->addTestExtensionConfig('flow_telemetry', [
- 'resource' => [],
- 'tracer_provider' => ['processor' => ['type' => 'memory', 'exporter' => ['type' => 'void']]],
- ]);
- },
- ]);
+ $destination = \sys_get_temp_dir() . '/flow-telemetry-test-' . \uniqid() . '.log';
+
+ try {
+ $this->bootKernel([
+ 'config' => static function (TestKernel $kernel) use ($destination) : void {
+ $kernel->addTestExtensionConfig('flow_telemetry', [
+ 'resource' => [],
+ 'error_handlers' => [
+ 'default' => ['type' => 'error_log'],
+ 'to_file' => [
+ 'type' => 'stream',
+ 'destination' => $destination,
+ 'create_directories' => false,
+ ],
+ ],
+ ]);
+ },
+ ]);
- self::assertInstanceOf(MemorySpanProcessor::class, $this->getContainer()->get('flow.telemetry.tracer_provider.processor'));
+ $container = $this->getContainer();
+ self::assertInstanceOf(StreamHandler::class, $container->get('flow.telemetry.error_handler.to_file'));
+ } finally {
+ if (\is_file($destination)) {
+ \unlink($destination);
+ }
+ }
}
- public function test_span_processor_passthrough_type() : void
+ public function test_stream_handler_missing_destination_throws() : void
{
+ $this->expectException(RuntimeException::class);
+ $this->expectExceptionMessage('error_handler "to_file" of type "stream" requires a non-empty "destination"');
+
$this->bootKernel([
'config' => static function (TestKernel $kernel) : void {
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
- 'tracer_provider' => ['processor' => ['type' => 'passthrough', 'exporter' => ['type' => 'void']]],
+ 'error_handlers' => [
+ 'to_file' => ['type' => 'stream'],
+ ],
]);
},
]);
-
- self::assertInstanceOf(PassThroughSpanProcessor::class, $this->getContainer()->get('flow.telemetry.tracer_provider.processor'));
}
- public function test_span_processor_void_type() : void
+ public function test_stream_transport_is_built_inline_for_file_path() : void
{
- $this->bootKernel([
- 'config' => static function (TestKernel $kernel) : void {
- $kernel->addTestExtensionConfig('flow_telemetry', [
- 'resource' => [],
- 'tracer_provider' => ['processor' => ['type' => 'void']],
- ]);
- },
- ]);
+ $path = \sys_get_temp_dir() . '/flow-otlp-bundle-' . \bin2hex(\random_bytes(4)) . '.jsonl';
+
+ try {
+ $this->bootKernel([
+ 'config' => static function (TestKernel $kernel) use ($path) : void {
+ $kernel->addTestExtensionConfig('flow_telemetry', [
+ 'resource' => [],
+ 'exporters' => [
+ 'otlp_stream' => [
+ 'otlp' => [
+ 'transport' => [
+ 'type' => 'stream',
+ 'endpoint' => $path,
+ 'file_permissions' => 0o640,
+ 'create_directories' => true,
+ ],
+ ],
+ ],
+ ],
+ ]);
+ },
+ ]);
- self::assertInstanceOf(VoidSpanProcessor::class, $this->getContainer()->get('flow.telemetry.tracer_provider.processor'));
+ $container = $this->getContainer();
+ self::assertInstanceOf(StreamTransport::class, $container->get('flow.telemetry.exporter.otlp_stream.transport'));
+ self::assertInstanceOf(OTLPExporter::class, $container->get('flow.telemetry.exporter.otlp_stream'));
+ } finally {
+ if (\is_file($path)) {
+ \unlink($path);
+ }
+ }
}
- public function test_tracer_provider_with_always_off_sampler() : void
+ #[TestWith(['php://stdout'])]
+ #[TestWith(['php://stderr'])]
+ public function test_stream_transport_is_built_inline_for_php_uri(string $endpoint) : void
{
$this->bootKernel([
- 'config' => static function (TestKernel $kernel) : void {
+ 'config' => static function (TestKernel $kernel) use ($endpoint) : void {
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
- 'tracer_provider' => [
- 'sampler' => ['type' => 'always_off'],
+ 'exporters' => [
+ 'otlp_stream' => [
+ 'otlp' => [
+ 'transport' => [
+ 'type' => 'stream',
+ 'endpoint' => $endpoint,
+ ],
+ ],
+ ],
],
]);
},
]);
- self::assertInstanceOf(AlwaysOffSampler::class, $this->getContainer()->get('flow.telemetry.tracer_provider.sampler'));
+ $container = $this->getContainer();
+ self::assertInstanceOf(StreamTransport::class, $container->get('flow.telemetry.exporter.otlp_stream.transport'));
+ self::assertInstanceOf(OTLPExporter::class, $container->get('flow.telemetry.exporter.otlp_stream'));
}
- public function test_tracer_provider_with_always_on_sampler() : void
+ public function test_syslog_handler_is_registered_with_facility_and_severity() : void
{
$this->bootKernel([
'config' => static function (TestKernel $kernel) : void {
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
- 'tracer_provider' => [
- 'sampler' => ['type' => 'always_on'],
+ 'error_handlers' => [
+ 'default' => ['type' => 'error_log'],
+ 'sys' => [
+ 'type' => 'syslog',
+ 'ident' => 'flow-test',
+ 'facility' => 'local3',
+ 'severity' => 'warning',
+ ],
],
]);
},
]);
$container = $this->getContainer();
-
- self::assertTrue($container->has('flow.telemetry.tracer_provider.sampler'));
- self::assertInstanceOf(AlwaysOnSampler::class, $container->get('flow.telemetry.tracer_provider.sampler'));
+ self::assertInstanceOf(SyslogHandler::class, $container->get('flow.telemetry.error_handler.sys'));
}
- public function test_tracer_provider_with_parent_based_sampler() : void
+ public function test_two_separate_otlp_backends() : void
{
$this->bootKernel([
'config' => static function (TestKernel $kernel) : void {
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => [
+ 'otlp_traces' => [
+ 'otlp' => [
+ 'transport' => [
+ 'type' => 'grpc',
+ 'endpoint' => 'http://traces:4317',
+ 'insecure' => true,
+ ],
+ ],
+ ],
+ 'otlp_metrics' => [
+ 'otlp' => [
+ 'transport' => ['type' => 'curl', 'endpoint' => 'http://metrics:4318'],
+ ],
+ ],
+ ],
'tracer_provider' => [
- 'sampler' => ['type' => 'parent_based'],
+ 'processor' => ['type' => 'batching', 'exporter' => 'otlp_traces'],
+ ],
+ 'meter_provider' => [
+ 'processor' => ['type' => 'batching', 'exporter' => 'otlp_metrics'],
],
]);
},
]);
- self::assertInstanceOf(ParentBasedSampler::class, $this->getContainer()->get('flow.telemetry.tracer_provider.sampler'));
+ $container = $this->getContainer();
+ self::assertInstanceOf(OTLPExporter::class, $container->get('flow.telemetry.exporter.otlp_traces'));
+ self::assertInstanceOf(OTLPExporter::class, $container->get('flow.telemetry.exporter.otlp_metrics'));
+
+ if (\extension_loaded('grpc')) {
+ self::assertInstanceOf(GrpcTransport::class, $container->get('flow.telemetry.exporter.otlp_traces.transport'));
+ }
+ self::assertInstanceOf(CurlTransport::class, $container->get('flow.telemetry.exporter.otlp_metrics.transport'));
}
- public function test_tracer_provider_with_trace_id_ratio_sampler() : void
+ public function test_udp_syslog_handler_is_registered_with_host_and_port() : void
{
$this->bootKernel([
'config' => static function (TestKernel $kernel) : void {
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
- 'tracer_provider' => [
- 'sampler' => [
- 'type' => 'trace_id_ratio',
- 'ratio' => 0.5,
+ 'error_handlers' => [
+ 'default' => ['type' => 'error_log'],
+ 'remote' => [
+ 'type' => 'udp_syslog',
+ 'host' => '192.0.2.1',
+ 'port' => 1514,
],
],
]);
},
]);
- self::assertInstanceOf(TraceIdRatioBasedSampler::class, $this->getContainer()->get('flow.telemetry.tracer_provider.sampler'));
+ $container = $this->getContainer();
+ self::assertInstanceOf(UdpSyslogHandler::class, $container->get('flow.telemetry.error_handler.remote'));
}
- public function test_user_defined_default_logger_config_overrides_auto_default() : void
+ public function test_unknown_error_handler_reference_throws() : void
{
+ $this->expectException(RuntimeException::class);
+ $this->expectExceptionMessage('Unknown error_handler "missing"');
+
$this->bootKernel([
'config' => static function (TestKernel $kernel) : void {
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
- 'loggers' => [
- 'default' => ['version' => '2.0.0'],
+ 'exporters' => [
+ 'memory' => ['memory' => null],
+ ],
+ 'logger_provider' => [
+ 'processor' => [
+ 'type' => 'batching',
+ 'exporter' => 'memory',
+ 'error_handler' => 'missing',
+ ],
],
]);
},
]);
-
- $telemetry = $this->getContainer()->get('flow.telemetry');
- self::assertInstanceOf(Telemetry::class, $telemetry);
- self::assertInstanceOf(Logger::class, $telemetry->logger('default', '2.0.0'));
}
}
diff --git a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/Instrumentation/Cache/TraceableCacheAdapterTest.php b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/Instrumentation/Cache/TraceableCacheAdapterTest.php
index 7eac83021..6e4bc2592 100644
--- a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/Instrumentation/Cache/TraceableCacheAdapterTest.php
+++ b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/Instrumentation/Cache/TraceableCacheAdapterTest.php
@@ -46,6 +46,7 @@ public function test_all_cache_pools_are_wrapped_when_enabled() : void
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'instrumentation' => [
'http_kernel' => false,
'console' => false,
@@ -74,6 +75,7 @@ public function test_decorator_not_registered_when_feature_disabled() : void
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'instrumentation' => [
'cache' => false,
],
@@ -102,6 +104,7 @@ public function test_excluded_pool_by_exact_id_is_not_wrapped() : void
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'instrumentation' => [
'http_kernel' => false,
'console' => false,
@@ -141,6 +144,7 @@ public function test_excluded_pool_by_regex_is_not_wrapped() : void
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'instrumentation' => [
'http_kernel' => false,
'console' => false,
@@ -173,10 +177,11 @@ public function test_get_item_records_metrics() : void
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'meter_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
@@ -231,10 +236,11 @@ public function test_get_items_records_metrics() : void
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'meter_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
@@ -286,10 +292,11 @@ public function test_get_records_hit_metric_on_cache_hit() : void
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'meter_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
@@ -340,10 +347,11 @@ public function test_get_records_miss_metric_on_cache_miss() : void
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'meter_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
@@ -387,10 +395,11 @@ public function test_has_item_records_hit_metric_when_exists() : void
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'meter_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
@@ -437,10 +446,11 @@ public function test_has_item_records_miss_metric_when_not_exists() : void
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'meter_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
@@ -486,6 +496,7 @@ public function test_tag_aware_adapters_are_wrapped_with_tag_aware_traceable() :
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'instrumentation' => [
'http_kernel' => false,
'console' => false,
@@ -513,10 +524,11 @@ public function test_tag_aware_cache_creates_span_for_invalidate_tags() : void
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
diff --git a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/Instrumentation/Console/ConsoleFlushSubscriberTest.php b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/Instrumentation/Console/ConsoleFlushSubscriberTest.php
index 7dec5448c..0e27ccba2 100644
--- a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/Instrumentation/Console/ConsoleFlushSubscriberTest.php
+++ b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/Instrumentation/Console/ConsoleFlushSubscriberTest.php
@@ -8,7 +8,7 @@
use Flow\Bridge\Symfony\TelemetryBundle\Tests\Fixtures\Command\TestCommand;
use Flow\Bridge\Symfony\TelemetryBundle\Tests\Fixtures\TestKernel;
use Flow\Bridge\Symfony\TelemetryBundle\Tests\Integration\KernelTestCase;
-use Flow\Telemetry\Provider\Memory\MemorySpanExporter;
+use Flow\Telemetry\Provider\Memory\MemoryExporter;
use PHPUnit\Framework\Attributes\CoversClass;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
@@ -40,11 +40,12 @@ public function test_flush_is_called_on_terminate() : void
]);
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'batching',
'batch_size' => 100,
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
@@ -69,8 +70,8 @@ public function test_flush_is_called_on_terminate() : void
self::assertSame(0, $exitCode);
$container = $this->getContainer();
- /** @var MemorySpanExporter $exporter */
- $exporter = $container->get('flow.telemetry.tracer_provider.processor.exporter');
+ /** @var MemoryExporter $exporter */
+ $exporter = $container->get('flow.telemetry.exporter.memory');
$spans = $exporter->spans();
self::assertCount(1, $spans, 'Spans should be exported after console terminate when flush is called');
@@ -91,11 +92,12 @@ public function test_flush_is_not_called_when_console_instrumentation_is_disable
]);
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'batching',
'batch_size' => 100,
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
@@ -118,8 +120,8 @@ public function test_flush_is_not_called_when_console_instrumentation_is_disable
$application->run($input, $output);
$container = $this->getContainer();
- /** @var MemorySpanExporter $exporter */
- $exporter = $container->get('flow.telemetry.tracer_provider.processor.exporter');
+ /** @var MemoryExporter $exporter */
+ $exporter = $container->get('flow.telemetry.exporter.memory');
$spans = $exporter->spans();
self::assertCount(0, $spans, 'No spans should be exported when instrumentation is disabled');
diff --git a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/Instrumentation/Console/ConsoleSpanSubscriberTest.php b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/Instrumentation/Console/ConsoleSpanSubscriberTest.php
index 6e7b32e48..7455833c8 100644
--- a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/Instrumentation/Console/ConsoleSpanSubscriberTest.php
+++ b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/Instrumentation/Console/ConsoleSpanSubscriberTest.php
@@ -41,10 +41,11 @@ public function test_does_not_trace_when_disabled() : void
]);
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
@@ -89,10 +90,11 @@ public function test_excludes_command_with_exact_match() : void
]);
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
@@ -140,10 +142,11 @@ public function test_excludes_command_with_regex_pattern() : void
]);
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
@@ -195,10 +198,11 @@ public function test_traces_failing_console_command() : void
]);
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
@@ -255,10 +259,11 @@ public function test_traces_successful_console_command() : void
]);
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
diff --git a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/Instrumentation/Doctrine/DBAL/TracingMiddlewareTest.php b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/Instrumentation/Doctrine/DBAL/TracingMiddlewareTest.php
index a6aaa305e..c1681a369 100644
--- a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/Instrumentation/Doctrine/DBAL/TracingMiddlewareTest.php
+++ b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/Instrumentation/Doctrine/DBAL/TracingMiddlewareTest.php
@@ -38,10 +38,11 @@ public function test_connection_span_includes_db_system_attribute() : void
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
@@ -96,6 +97,7 @@ public function test_dbal_middleware_is_not_registered_when_feature_disabled() :
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'instrumentation' => [
'http_kernel' => false,
'console' => false,
@@ -124,6 +126,7 @@ public function test_dbal_middleware_is_registered_for_multiple_connections() :
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'instrumentation' => [
'http_kernel' => false,
'console' => false,
@@ -152,6 +155,7 @@ public function test_dbal_middleware_is_registered_when_feature_enabled() : void
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'instrumentation' => [
'http_kernel' => false,
'console' => false,
@@ -181,6 +185,7 @@ public function test_excluded_connection_by_exact_name_is_not_traced() : void
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'instrumentation' => [
'http_kernel' => false,
'console' => false,
@@ -214,6 +219,7 @@ public function test_excluded_connections_by_regex_pattern_are_not_traced() : vo
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'instrumentation' => [
'http_kernel' => false,
'console' => false,
@@ -244,10 +250,11 @@ public function test_exec_creates_span_with_sql_attribute() : void
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
@@ -303,10 +310,11 @@ public function test_failed_transaction_creates_rollback_span() : void
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
@@ -371,10 +379,11 @@ public function test_long_sql_is_truncated_when_max_length_configured() : void
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
@@ -436,10 +445,11 @@ public function test_prepared_statement_creates_prepare_and_execute_spans() : vo
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
@@ -513,10 +523,11 @@ public function test_query_creates_span_with_sql_attribute() : void
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
@@ -573,10 +584,11 @@ public function test_query_error_creates_span_with_error_status() : void
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
@@ -646,10 +658,11 @@ public function test_sql_not_logged_when_log_sql_disabled() : void
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
@@ -706,10 +719,11 @@ public function test_transaction_creates_begin_commit_spans() : void
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
diff --git a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/Instrumentation/HttpClient/TracableHttpClientTest.php b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/Instrumentation/HttpClient/TracableHttpClientTest.php
index d15572223..08ead9143 100644
--- a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/Instrumentation/HttpClient/TracableHttpClientTest.php
+++ b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/Instrumentation/HttpClient/TracableHttpClientTest.php
@@ -46,6 +46,7 @@ public function test_all_tagged_clients_are_wrapped_with_telemetry_decorator() :
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'instrumentation' => [
'http_kernel' => false,
'console' => false,
@@ -75,6 +76,7 @@ public function test_decorator_not_registered_when_feature_disabled() : void
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'instrumentation' => [
'http_client' => false,
],
@@ -105,6 +107,7 @@ public function test_excluded_client_by_exact_id_is_not_wrapped() : void
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'instrumentation' => [
'http_kernel' => false,
'console' => false,
@@ -147,6 +150,7 @@ public function test_excluded_clients_by_regex_pattern_are_not_wrapped() : void
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'instrumentation' => [
'http_kernel' => false,
'console' => false,
@@ -180,10 +184,11 @@ public function test_wrapped_client_creates_error_span_for_http_error_response()
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
@@ -231,10 +236,11 @@ public function test_wrapped_client_creates_span_with_correct_attributes() : voi
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
@@ -289,10 +295,11 @@ public function test_wrapped_client_records_exception_and_creates_error_span() :
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
diff --git a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/Instrumentation/HttpKernel/HttpKernelFlushSubscriberTest.php b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/Instrumentation/HttpKernel/HttpKernelFlushSubscriberTest.php
index 4dcf4583d..05a6f826b 100644
--- a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/Instrumentation/HttpKernel/HttpKernelFlushSubscriberTest.php
+++ b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/Instrumentation/HttpKernel/HttpKernelFlushSubscriberTest.php
@@ -8,7 +8,7 @@
use Flow\Bridge\Symfony\TelemetryBundle\Tests\Fixtures\Controller\TestController;
use Flow\Bridge\Symfony\TelemetryBundle\Tests\Fixtures\TestKernel;
use Flow\Bridge\Symfony\TelemetryBundle\Tests\Integration\KernelTestCase;
-use Flow\Telemetry\Provider\Memory\MemorySpanExporter;
+use Flow\Telemetry\Provider\Memory\MemoryExporter;
use PHPUnit\Framework\Attributes\CoversClass;
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
use Symfony\Component\HttpFoundation\Request;
@@ -39,11 +39,12 @@ public function test_flush_is_called_on_terminate() : void
]);
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'batching',
'batch_size' => 100,
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
@@ -65,8 +66,8 @@ public function test_flush_is_called_on_terminate() : void
$request = Request::create('/test', 'GET');
$response = $kernel->handle($request);
- /** @var MemorySpanExporter $exporter */
- $exporter = $container->get('flow.telemetry.tracer_provider.processor.exporter');
+ /** @var MemoryExporter $exporter */
+ $exporter = $container->get('flow.telemetry.exporter.memory');
$spansBeforeTerminate = $exporter->spans();
self::assertCount(0, $spansBeforeTerminate, 'Spans should not be exported before terminate (batching)');
@@ -93,11 +94,12 @@ public function test_flush_is_not_called_when_http_kernel_instrumentation_is_dis
]);
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'batching',
'batch_size' => 100,
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
@@ -120,8 +122,8 @@ public function test_flush_is_not_called_when_http_kernel_instrumentation_is_dis
$response = $kernel->handle($request);
$kernel->terminate($request, $response);
- /** @var MemorySpanExporter $exporter */
- $exporter = $container->get('flow.telemetry.tracer_provider.processor.exporter');
+ /** @var MemoryExporter $exporter */
+ $exporter = $container->get('flow.telemetry.exporter.memory');
$spans = $exporter->spans();
self::assertCount(0, $spans, 'No spans should be exported when instrumentation is disabled');
diff --git a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/Instrumentation/HttpKernel/HttpKernelSpanSubscriberTest.php b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/Instrumentation/HttpKernel/HttpKernelSpanSubscriberTest.php
index c054981c9..5a70c4f6a 100644
--- a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/Instrumentation/HttpKernel/HttpKernelSpanSubscriberTest.php
+++ b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/Instrumentation/HttpKernel/HttpKernelSpanSubscriberTest.php
@@ -41,10 +41,11 @@ public function test_does_not_extract_context_when_propagation_disabled() : void
]);
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
@@ -103,10 +104,11 @@ public function test_does_not_trace_when_disabled() : void
]);
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
@@ -151,10 +153,11 @@ public function test_excludes_path_with_exact_match() : void
]);
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
@@ -210,10 +213,11 @@ public function test_excludes_path_with_method_filter() : void
]);
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
@@ -269,10 +273,11 @@ public function test_excludes_path_with_regex_pattern() : void
]);
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
@@ -333,10 +338,11 @@ public function test_extracts_context_from_traceparent_header() : void
]);
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
@@ -395,10 +401,11 @@ public function test_handles_missing_trace_headers_gracefully() : void
]);
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
@@ -452,10 +459,11 @@ public function test_traces_http_request_with_error_status() : void
]);
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
@@ -511,10 +519,11 @@ public function test_traces_successful_http_request() : void
]);
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
diff --git a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/Instrumentation/Messenger/TracingMiddlewareTest.php b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/Instrumentation/Messenger/TracingMiddlewareTest.php
index e6f608d96..a60a506a9 100644
--- a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/Instrumentation/Messenger/TracingMiddlewareTest.php
+++ b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/Instrumentation/Messenger/TracingMiddlewareTest.php
@@ -39,10 +39,11 @@ public function test_context_is_extracted_on_consume() : void
'config' => static function (TestKernel $kernel) : void {
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'propagator' => ['type' => 'w3c'],
@@ -107,10 +108,11 @@ public function test_context_is_injected_on_dispatch_when_context_propagation_en
'config' => static function (TestKernel $kernel) : void {
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'propagator' => ['type' => 'w3c'],
@@ -173,10 +175,11 @@ public function test_context_propagation_not_applied_when_disabled() : void
'config' => static function (TestKernel $kernel) : void {
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
@@ -219,10 +222,11 @@ public function test_handles_missing_stamp_on_consume_gracefully() : void
'config' => static function (TestKernel $kernel) : void {
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'propagator' => ['type' => 'w3c'],
@@ -275,6 +279,7 @@ public function test_middleware_not_registered_when_disabled() : void
'config' => static function (TestKernel $kernel) : void {
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'instrumentation' => [
'messenger' => [
'enabled' => false,
@@ -295,6 +300,7 @@ public function test_middleware_service_is_registered() : void
'config' => static function (TestKernel $kernel) : void {
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'instrumentation' => [
'messenger' => [
'enabled' => true,
@@ -316,10 +322,11 @@ public function test_producer_span_context_propagated_even_without_parent_contex
'config' => static function (TestKernel $kernel) : void {
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'propagator' => ['type' => 'w3c'],
@@ -376,6 +383,7 @@ public function test_propagator_baggage_only_configuration() : void
'config' => static function (TestKernel $kernel) : void {
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'propagator' => ['type' => 'baggage'],
]);
},
@@ -394,6 +402,7 @@ public function test_propagator_tracecontext_only_configuration() : void
'config' => static function (TestKernel $kernel) : void {
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'propagator' => ['type' => 'tracecontext'],
]);
},
@@ -412,6 +421,7 @@ public function test_propagator_w3c_configuration() : void
'config' => static function (TestKernel $kernel) : void {
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'propagator' => ['type' => 'w3c'],
]);
},
@@ -430,10 +440,11 @@ public function test_traces_message_dispatch() : void
'config' => static function (TestKernel $kernel) : void {
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
@@ -499,10 +510,11 @@ public function test_traces_message_with_exception() : void
'config' => static function (TestKernel $kernel) : void {
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
diff --git a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/Instrumentation/Psr18/Psr18ClientTelemetryPassTest.php b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/Instrumentation/Psr18/Psr18ClientTelemetryPassTest.php
index a1307b820..7a743777a 100644
--- a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/Instrumentation/Psr18/Psr18ClientTelemetryPassTest.php
+++ b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/Instrumentation/Psr18/Psr18ClientTelemetryPassTest.php
@@ -49,6 +49,7 @@ public function test_all_psr18_clients_are_wrapped_when_enabled() : void
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'instrumentation' => [
'http_kernel' => false,
'console' => false,
@@ -77,6 +78,7 @@ public function test_decorator_not_registered_when_feature_disabled() : void
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'instrumentation' => [
'psr18_client' => false,
],
@@ -105,6 +107,7 @@ public function test_excluded_client_by_exact_id_is_not_wrapped() : void
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'instrumentation' => [
'http_kernel' => false,
'console' => false,
@@ -144,6 +147,7 @@ public function test_excluded_clients_by_regex_pattern_are_not_wrapped() : void
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'instrumentation' => [
'http_kernel' => false,
'console' => false,
@@ -176,10 +180,11 @@ public function test_wrapped_client_creates_error_span_for_http_error_response()
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
@@ -226,10 +231,11 @@ public function test_wrapped_client_creates_span_with_correct_attributes() : voi
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
@@ -282,10 +288,11 @@ public function test_wrapped_client_records_exception_and_creates_error_span() :
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
diff --git a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/Instrumentation/Twig/TracingTwigExtensionTest.php b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/Instrumentation/Twig/TracingTwigExtensionTest.php
index 39c97fb18..cc0f6b135 100644
--- a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/Instrumentation/Twig/TracingTwigExtensionTest.php
+++ b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/Instrumentation/Twig/TracingTwigExtensionTest.php
@@ -31,10 +31,11 @@ public function test_does_not_trace_blocks_when_disabled() : void
'config' => static function (TestKernel $kernel) : void {
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
@@ -83,10 +84,11 @@ public function test_does_not_trace_excluded_templates() : void
'config' => static function (TestKernel $kernel) : void {
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
@@ -142,10 +144,11 @@ public function test_does_not_trace_excluded_templates_with_regex() : void
'config' => static function (TestKernel $kernel) : void {
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
@@ -204,10 +207,11 @@ public function test_does_not_trace_templates_when_disabled() : void
'config' => static function (TestKernel $kernel) : void {
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
@@ -255,10 +259,11 @@ public function test_excluded_template_cascades_to_children() : void
'config' => static function (TestKernel $kernel) : void {
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
@@ -319,6 +324,7 @@ public function test_extension_not_registered_when_disabled() : void
'config' => static function (TestKernel $kernel) : void {
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'instrumentation' => [
'twig' => false,
],
@@ -337,6 +343,7 @@ public function test_extension_service_is_registered() : void
'config' => static function (TestKernel $kernel) : void {
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'instrumentation' => [
'twig' => true,
],
@@ -356,10 +363,11 @@ public function test_traces_blocks_in_templates() : void
'config' => static function (TestKernel $kernel) : void {
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
@@ -418,10 +426,11 @@ public function test_traces_macros_when_enabled() : void
'config' => static function (TestKernel $kernel) : void {
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
@@ -478,10 +487,11 @@ public function test_traces_template_rendering() : void
'config' => static function (TestKernel $kernel) : void {
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
+ 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'memory',
- 'exporter' => ['type' => 'memory'],
+ 'exporter' => 'memory',
],
],
'instrumentation' => [
diff --git a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/DependencyInjection/ConfigurationTest.php b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/DependencyInjection/ConfigurationTest.php
index ee2800787..2febe833c 100644
--- a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/DependencyInjection/ConfigurationTest.php
+++ b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/DependencyInjection/ConfigurationTest.php
@@ -5,6 +5,7 @@
namespace Flow\Bridge\Symfony\TelemetryBundle\Tests\Unit\DependencyInjection;
use Flow\Bridge\Symfony\TelemetryBundle\DependencyInjection\Configuration;
+use PHPUnit\Framework\Attributes\TestWith;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
use Symfony\Component\Config\Definition\Processor;
@@ -31,21 +32,24 @@ public function test_clock_service_id_defaults_to_null() : void
self::assertNull($config['clock_service_id']);
}
- public function test_composite_processor_with_multiple_processors() : void
+ public function test_composite_span_processor_with_named_exporters() : void
{
$config = (new Processor())->processConfiguration(new Configuration(), [[
'resource' => [],
+ 'exporters' => [
+ 'memory' => ['memory' => null],
+ 'otlp' => [
+ 'otlp' => [
+ 'transport' => ['type' => 'curl', 'endpoint' => 'http://localhost:4318'],
+ ],
+ ],
+ ],
'tracer_provider' => [
'processor' => [
'type' => 'composite',
'processors' => [
- [
- 'type' => 'memory',
- ],
- [
- 'type' => 'batching',
- 'batch_size' => 100,
- ],
+ ['type' => 'memory', 'exporter' => 'memory'],
+ ['type' => 'batching', 'batch_size' => 100, 'exporter' => 'otlp'],
],
],
],
@@ -53,9 +57,20 @@ public function test_composite_processor_with_multiple_processors() : void
$processors = $config['tracer_provider']['processor']['processors'];
self::assertCount(2, $processors);
- self::assertSame('memory', $processors[0]['type']);
- self::assertSame('batching', $processors[1]['type']);
- self::assertSame(100, $processors[1]['batch_size']);
+ self::assertSame('memory', $processors[0]['exporter']);
+ self::assertSame('otlp', $processors[1]['exporter']);
+ }
+
+ public function test_console_exporter_no_options() : void
+ {
+ $config = (new Processor())->processConfiguration(new Configuration(), [[
+ 'resource' => [],
+ 'exporters' => [
+ 'console' => ['console' => null],
+ ],
+ ]]);
+
+ self::assertArrayHasKey('console', $config['exporters']['console']);
}
public function test_context_storage_can_use_custom_service() : void
@@ -83,587 +98,350 @@ public function test_context_storage_defaults_to_memory() : void
self::assertNull($config['context_storage']['service_id']);
}
- public function test_empty_tracers_meters_loggers_config() : void
+ public function test_empty_exporters_definition() : void
{
$config = (new Processor())->processConfiguration(new Configuration(), [[
'resource' => [],
- 'tracers' => [],
- 'meters' => [],
- 'loggers' => [],
]]);
- self::assertSame([], $config['tracers']);
- self::assertSame([], $config['meters']);
- self::assertSame([], $config['loggers']);
+ self::assertSame([], $config['exporters']);
}
- public function test_exporter_defaults_to_void() : void
+ public function test_error_handlers_can_define_multiple_named_entries() : void
{
$config = (new Processor())->processConfiguration(new Configuration(), [[
'resource' => [],
- 'tracer_provider' => [
- 'processor' => [
- 'type' => 'batching',
- ],
+ 'error_handlers' => [
+ 'default' => ['type' => 'error_log', 'message_type' => 'sapi'],
+ 'to_file' => ['type' => 'stream', 'destination' => '/var/log/flow.log'],
+ 'to_syslog' => ['type' => 'syslog', 'facility' => 'local0', 'severity' => 'warning'],
+ 'fanout' => ['type' => 'composite', 'handlers' => ['default', 'to_file']],
+ 'silent' => ['type' => 'noop'],
+ 'custom' => ['type' => 'service', 'service_id' => 'app.my_handler'],
],
]]);
- self::assertSame('void', $config['tracer_provider']['processor']['exporter']['type']);
+ self::assertSame('error_log', $config['error_handlers']['default']['type']);
+ self::assertSame('sapi', $config['error_handlers']['default']['message_type']);
+ self::assertSame('stream', $config['error_handlers']['to_file']['type']);
+ self::assertSame('/var/log/flow.log', $config['error_handlers']['to_file']['destination']);
+ self::assertSame('local0', $config['error_handlers']['to_syslog']['facility']);
+ self::assertSame('warning', $config['error_handlers']['to_syslog']['severity']);
+ self::assertSame(['default', 'to_file'], $config['error_handlers']['fanout']['handlers']);
+ self::assertSame('noop', $config['error_handlers']['silent']['type']);
+ self::assertSame('app.my_handler', $config['error_handlers']['custom']['service_id']);
}
- public function test_instrumentation_cache_can_be_enabled() : void
+ public function test_error_handlers_section_is_empty_by_default() : void
{
$config = (new Processor())->processConfiguration(new Configuration(), [[
'resource' => [],
- 'instrumentation' => [
- 'cache' => ['enabled' => true],
- ],
]]);
- self::assertTrue($config['instrumentation']['cache']['enabled']);
+ self::assertArrayHasKey('error_handlers', $config);
+ self::assertSame([], $config['error_handlers']);
}
- public function test_instrumentation_cache_defaults_to_disabled() : void
+ public function test_exporter_with_multiple_blocks_throws() : void
{
- $config = (new Processor())->processConfiguration(new Configuration(), [[
- 'resource' => [],
- ]]);
-
- self::assertArrayHasKey('instrumentation', $config);
- self::assertArrayHasKey('cache', $config['instrumentation']);
- self::assertFalse($config['instrumentation']['cache']['enabled']);
- self::assertSame([], $config['instrumentation']['cache']['exclude_pools']);
- }
+ $this->expectException(InvalidConfigurationException::class);
+ $this->expectExceptionMessage('Exporter must declare exactly one of');
- public function test_instrumentation_cache_exclude_pools() : void
- {
- $config = (new Processor())->processConfiguration(new Configuration(), [[
+ (new Processor())->processConfiguration(new Configuration(), [[
'resource' => [],
- 'instrumentation' => [
- 'cache' => [
- 'enabled' => true,
- 'exclude_pools' => ['cache.validator', 'cache.serializer', '/^cache\\.profiler\\..*/'],
+ 'exporters' => [
+ 'broken' => [
+ 'memory' => null,
+ 'console' => null,
],
],
]]);
-
- self::assertTrue($config['instrumentation']['cache']['enabled']);
- self::assertSame(['cache.validator', 'cache.serializer', '/^cache\\.profiler\\..*/'], $config['instrumentation']['cache']['exclude_pools']);
}
- public function test_instrumentation_can_be_enabled() : void
+ public function test_exporter_with_no_block_throws() : void
{
- $config = (new Processor())->processConfiguration(new Configuration(), [[
- 'resource' => [],
- 'instrumentation' => [
- 'http_kernel' => ['enabled' => true],
- 'console' => ['enabled' => true],
- 'messenger' => true,
- ],
- ]]);
-
- self::assertTrue($config['instrumentation']['http_kernel']['enabled']);
- self::assertTrue($config['instrumentation']['console']['enabled']);
- self::assertTrue($config['instrumentation']['messenger']['enabled']);
- }
+ $this->expectException(InvalidConfigurationException::class);
+ $this->expectExceptionMessage('Exporter must declare exactly one of');
- public function test_instrumentation_console_exclude_commands() : void
- {
- $config = (new Processor())->processConfiguration(new Configuration(), [[
+ (new Processor())->processConfiguration(new Configuration(), [[
'resource' => [],
- 'instrumentation' => [
- 'console' => [
- 'enabled' => true,
- 'exclude_commands' => ['cache:clear', 'debug:router'],
- ],
+ 'exporters' => [
+ 'broken' => [],
],
]]);
-
- self::assertTrue($config['instrumentation']['console']['enabled']);
- self::assertSame(['cache:clear', 'debug:router'], $config['instrumentation']['console']['exclude_commands']);
}
- public function test_instrumentation_dbal_can_be_enabled() : void
+ public function test_grpc_transport_accepts_timeout_ms() : void
{
$config = (new Processor())->processConfiguration(new Configuration(), [[
'resource' => [],
- 'instrumentation' => [
- 'dbal' => ['enabled' => true],
+ 'exporters' => [
+ 'otlp' => [
+ 'otlp' => [
+ 'transport' => [
+ 'type' => 'grpc',
+ 'endpoint' => 'localhost:4317',
+ 'timeout_ms' => 2000,
+ ],
+ ],
+ ],
],
]]);
- self::assertTrue($config['instrumentation']['dbal']['enabled']);
+ self::assertSame(2000, $config['exporters']['otlp']['otlp']['transport']['timeout_ms']);
}
- public function test_instrumentation_dbal_defaults_to_disabled() : void
+ public function test_grpc_transport_rejects_connect_timeout_ms() : void
{
- $config = (new Processor())->processConfiguration(new Configuration(), [[
- 'resource' => [],
- ]]);
-
- self::assertArrayHasKey('instrumentation', $config);
- self::assertArrayHasKey('dbal', $config['instrumentation']);
- self::assertFalse($config['instrumentation']['dbal']['enabled']);
- self::assertTrue($config['instrumentation']['dbal']['log_sql']);
- self::assertSame(1000, $config['instrumentation']['dbal']['max_sql_length']);
- self::assertSame([], $config['instrumentation']['dbal']['exclude_connections']);
- }
+ $this->expectException(InvalidConfigurationException::class);
+ $this->expectExceptionMessage('The "connect_timeout_ms" parameter is not supported when transport.type is "grpc"');
- public function test_instrumentation_dbal_exclude_connections() : void
- {
- $config = (new Processor())->processConfiguration(new Configuration(), [[
+ (new Processor())->processConfiguration(new Configuration(), [[
'resource' => [],
- 'instrumentation' => [
- 'dbal' => [
- 'enabled' => true,
- 'exclude_connections' => ['legacy', '/^debug_.*$/'],
+ 'exporters' => [
+ 'otlp' => [
+ 'otlp' => [
+ 'transport' => [
+ 'type' => 'grpc',
+ 'endpoint' => 'localhost:4317',
+ 'connect_timeout_ms' => 250,
+ ],
+ ],
],
],
]]);
-
- self::assertTrue($config['instrumentation']['dbal']['enabled']);
- self::assertSame(['legacy', '/^debug_.*$/'], $config['instrumentation']['dbal']['exclude_connections']);
}
- public function test_instrumentation_dbal_log_sql_can_be_disabled() : void
+ public function test_grpc_transport_rejects_encoding_field() : void
{
- $config = (new Processor())->processConfiguration(new Configuration(), [[
- 'resource' => [],
- 'instrumentation' => [
- 'dbal' => [
- 'enabled' => true,
- 'log_sql' => false,
- ],
- ],
- ]]);
-
- self::assertTrue($config['instrumentation']['dbal']['enabled']);
- self::assertFalse($config['instrumentation']['dbal']['log_sql']);
- }
+ $this->expectException(InvalidConfigurationException::class);
+ $this->expectExceptionMessage('"encoding" parameter is not supported when transport.type is "grpc"');
- public function test_instrumentation_dbal_max_sql_length_can_be_zero_for_unlimited() : void
- {
- $config = (new Processor())->processConfiguration(new Configuration(), [[
+ (new Processor())->processConfiguration(new Configuration(), [[
'resource' => [],
- 'instrumentation' => [
- 'dbal' => [
- 'enabled' => true,
- 'max_sql_length' => 0,
+ 'exporters' => [
+ 'otlp_grpc' => [
+ 'otlp' => [
+ 'transport' => [
+ 'type' => 'grpc',
+ 'endpoint' => 'localhost:4317',
+ 'encoding' => 'json',
+ ],
+ ],
],
],
]]);
-
- self::assertSame(0, $config['instrumentation']['dbal']['max_sql_length']);
}
- public function test_instrumentation_dbal_max_sql_length_configuration() : void
+ public function test_minimal_otlp_config() : void
{
$config = (new Processor())->processConfiguration(new Configuration(), [[
'resource' => [],
- 'instrumentation' => [
- 'dbal' => [
- 'enabled' => true,
- 'max_sql_length' => 500,
+ 'exporters' => [
+ 'otlp' => [
+ 'otlp' => [
+ 'transport' => [
+ 'type' => 'curl',
+ 'endpoint' => 'http://localhost:4318',
+ 'encoding' => 'protobuf',
+ ],
+ ],
],
],
- ]]);
-
- self::assertSame(500, $config['instrumentation']['dbal']['max_sql_length']);
- }
-
- public function test_instrumentation_defaults_to_all_disabled() : void
- {
- $config = (new Processor())->processConfiguration(new Configuration(), [[
- 'resource' => [],
- ]]);
-
- self::assertArrayHasKey('instrumentation', $config);
- self::assertFalse($config['instrumentation']['http_kernel']['enabled']);
- self::assertSame([], $config['instrumentation']['http_kernel']['exclude_paths']);
- self::assertFalse($config['instrumentation']['console']['enabled']);
- self::assertFalse($config['instrumentation']['messenger']['enabled']);
- }
-
- public function test_instrumentation_http_client_can_be_enabled() : void
- {
- $config = (new Processor())->processConfiguration(new Configuration(), [[
- 'resource' => [],
- 'instrumentation' => [
- 'http_client' => ['enabled' => true],
- ],
- ]]);
-
- self::assertTrue($config['instrumentation']['http_client']['enabled']);
- }
-
- public function test_instrumentation_http_client_defaults_to_disabled() : void
- {
- $config = (new Processor())->processConfiguration(new Configuration(), [[
- 'resource' => [],
- ]]);
-
- self::assertArrayHasKey('instrumentation', $config);
- self::assertArrayHasKey('http_client', $config['instrumentation']);
- self::assertFalse($config['instrumentation']['http_client']['enabled']);
- self::assertSame([], $config['instrumentation']['http_client']['exclude_clients']);
- }
-
- public function test_instrumentation_http_client_exclude_clients() : void
- {
- $config = (new Processor())->processConfiguration(new Configuration(), [[
- 'resource' => [],
- 'instrumentation' => [
- 'http_client' => [
- 'enabled' => true,
- 'exclude_clients' => ['internal.http_client', '/^debug\\..*$/'],
- ],
+ 'tracer_provider' => [
+ 'processor' => ['type' => 'batching', 'exporter' => 'otlp', 'batch_size' => 512],
],
]]);
- self::assertTrue($config['instrumentation']['http_client']['enabled']);
- self::assertSame(['internal.http_client', '/^debug\\..*$/'], $config['instrumentation']['http_client']['exclude_clients']);
+ self::assertSame('curl', $config['exporters']['otlp']['otlp']['transport']['type']);
+ self::assertSame('http://localhost:4318', $config['exporters']['otlp']['otlp']['transport']['endpoint']);
+ self::assertSame('protobuf', $config['exporters']['otlp']['otlp']['transport']['encoding']);
+ self::assertSame('otlp', $config['tracer_provider']['processor']['exporter']);
}
- public function test_instrumentation_http_kernel_exclude_paths() : void
+ public function test_otlp_exporter_error_handler_defaults_to_default() : void
{
$config = (new Processor())->processConfiguration(new Configuration(), [[
'resource' => [],
- 'instrumentation' => [
- 'http_kernel' => [
- 'enabled' => true,
- 'exclude_paths' => [
- ['path' => '/_wdt'],
- ['path' => '/_profiler', 'method' => 'GET'],
- ['path' => '/^\/_profiler.*/'],
+ 'exporters' => [
+ 'otlp' => [
+ 'otlp' => [
+ 'transport' => ['type' => 'curl', 'endpoint' => 'http://localhost:4318'],
],
],
],
]]);
- self::assertTrue($config['instrumentation']['http_kernel']['enabled']);
- self::assertSame([
- ['path' => '/_wdt', 'method' => null],
- ['path' => '/_profiler', 'method' => 'GET'],
- ['path' => '/^\/_profiler.*/', 'method' => null],
- ], $config['instrumentation']['http_kernel']['exclude_paths']);
+ self::assertSame('default', $config['exporters']['otlp']['otlp']['error_handler']);
}
- public function test_instrumentation_partial_config() : void
+ public function test_otlp_exporter_without_transport_block_throws() : void
{
- $config = (new Processor())->processConfiguration(new Configuration(), [[
- 'resource' => [],
- 'instrumentation' => [
- 'http_kernel' => ['enabled' => true],
- ],
- ]]);
-
- self::assertTrue($config['instrumentation']['http_kernel']['enabled']);
- self::assertFalse($config['instrumentation']['console']['enabled']);
- self::assertFalse($config['instrumentation']['messenger']['enabled']);
- }
+ $this->expectException(InvalidConfigurationException::class);
+ $this->expectExceptionMessage('OTLP exporter requires a "transport" configuration block');
- public function test_instrumentation_psr18_client_can_be_enabled() : void
- {
- $config = (new Processor())->processConfiguration(new Configuration(), [[
+ (new Processor())->processConfiguration(new Configuration(), [[
'resource' => [],
- 'instrumentation' => [
- 'psr18_client' => ['enabled' => true],
+ 'exporters' => [
+ 'broken' => [
+ 'otlp' => [],
+ ],
],
]]);
-
- self::assertTrue($config['instrumentation']['psr18_client']['enabled']);
}
- public function test_instrumentation_psr18_client_defaults_to_disabled() : void
+ public function test_processor_error_handler_defaults_to_default() : void
{
$config = (new Processor())->processConfiguration(new Configuration(), [[
'resource' => [],
]]);
- self::assertArrayHasKey('instrumentation', $config);
- self::assertArrayHasKey('psr18_client', $config['instrumentation']);
- self::assertFalse($config['instrumentation']['psr18_client']['enabled']);
- self::assertSame([], $config['instrumentation']['psr18_client']['exclude_clients']);
+ self::assertSame('default', $config['tracer_provider']['processor']['error_handler']);
+ self::assertSame('default', $config['meter_provider']['processor']['error_handler']);
+ self::assertSame('default', $config['logger_provider']['processor']['error_handler']);
}
- public function test_instrumentation_psr18_client_exclude_clients() : void
+ public function test_propagator_defaults_to_w3c() : void
{
$config = (new Processor())->processConfiguration(new Configuration(), [[
'resource' => [],
- 'instrumentation' => [
- 'psr18_client' => [
- 'enabled' => true,
- 'exclude_clients' => ['internal.psr18_client', '/^debug\\..*$/'],
- ],
- ],
]]);
- self::assertTrue($config['instrumentation']['psr18_client']['enabled']);
- self::assertSame(['internal.psr18_client', '/^debug\\..*$/'], $config['instrumentation']['psr18_client']['exclude_clients']);
+ self::assertSame('w3c', $config['propagator']['type']);
}
- public function test_instrumentation_twig_exclude_templates() : void
+ public function test_provider_error_handler_defaults_to_default() : void
{
$config = (new Processor())->processConfiguration(new Configuration(), [[
'resource' => [],
- 'instrumentation' => [
- 'twig' => [
- 'enabled' => true,
- 'exclude_templates' => ['@WebProfiler/Collector/time.html.twig', 'debug/exception.html.twig'],
- ],
- ],
]]);
- self::assertTrue($config['instrumentation']['twig']['enabled']);
- self::assertSame(['@WebProfiler/Collector/time.html.twig', 'debug/exception.html.twig'], $config['instrumentation']['twig']['exclude_templates']);
+ self::assertSame('default', $config['tracer_provider']['error_handler']);
+ self::assertSame('default', $config['meter_provider']['error_handler']);
+ self::assertSame('default', $config['logger_provider']['error_handler']);
}
- public function test_invalid_exporter_type_is_rejected() : void
+ public function test_service_exporter_requires_id() : void
{
$this->expectException(InvalidConfigurationException::class);
+ $this->expectExceptionMessage('id');
(new Processor())->processConfiguration(new Configuration(), [[
'resource' => [],
- 'tracer_provider' => [
- 'processor' => [
- 'type' => 'batching',
- 'exporter' => [
- 'type' => 'invalid_exporter',
- ],
+ 'exporters' => [
+ 'broken' => [
+ 'service' => [],
],
],
]]);
}
- public function test_invalid_processor_type_is_rejected() : void
+ public function test_service_exporter_with_id() : void
{
- $this->expectException(InvalidConfigurationException::class);
-
- (new Processor())->processConfiguration(new Configuration(), [[
+ $config = (new Processor())->processConfiguration(new Configuration(), [[
'resource' => [],
- 'tracer_provider' => [
- 'processor' => [
- 'type' => 'invalid_processor',
+ 'exporters' => [
+ 'datadog' => [
+ 'service' => ['id' => 'app.datadog_exporter'],
],
],
]]);
+
+ self::assertSame('app.datadog_exporter', $config['exporters']['datadog']['service']['id']);
}
- public function test_invalid_sampler_type_is_rejected() : void
+ public function test_severity_filtering_wraps_batching() : void
{
- $this->expectException(InvalidConfigurationException::class);
-
- (new Processor())->processConfiguration(new Configuration(), [[
+ $config = (new Processor())->processConfiguration(new Configuration(), [[
'resource' => [],
- 'tracer_provider' => [
- 'sampler' => [
- 'type' => 'invalid_sampler',
+ 'exporters' => [
+ 'otlp' => [
+ 'otlp' => [
+ 'transport' => ['type' => 'curl', 'endpoint' => 'http://localhost:4318'],
+ ],
],
],
- ]]);
- }
-
- public function test_invalid_severity_level_is_rejected() : void
- {
- $this->expectException(InvalidConfigurationException::class);
-
- (new Processor())->processConfiguration(new Configuration(), [[
- 'resource' => [],
'logger_provider' => [
'processor' => [
'type' => 'severity_filtering',
- 'minimum_severity' => 'invalid_level',
+ 'minimum_severity' => 'warn',
'inner_processor' => [
- 'type' => 'void',
- ],
- ],
- ],
- ]]);
- }
-
- public function test_logger_configuration() : void
- {
- $config = (new Processor())->processConfiguration(new Configuration(), [[
- 'resource' => [],
- 'loggers' => [
- 'audit' => [
- 'version' => '1.0.0',
- 'schema_url' => 'https://example.com/audit-schema/1.0',
- 'attributes' => [
- 'log.category' => 'audit',
- ],
- ],
- ],
- ]]);
-
- self::assertArrayHasKey('loggers', $config);
- self::assertArrayHasKey('audit', $config['loggers']);
- self::assertSame('1.0.0', $config['loggers']['audit']['version']);
- self::assertSame('https://example.com/audit-schema/1.0', $config['loggers']['audit']['schema_url']);
- self::assertSame(['log.category' => 'audit'], $config['loggers']['audit']['attributes']);
- }
-
- public function test_meter_configuration() : void
- {
- $config = (new Processor())->processConfiguration(new Configuration(), [[
- 'resource' => [],
- 'meters' => [
- 'etl_pipeline' => [
- 'version' => '1.0.0',
- 'attributes' => [
- 'flow.pipeline' => 'daily_import',
+ 'type' => 'batching',
+ 'exporter' => 'otlp',
+ 'batch_size' => 200,
],
],
],
]]);
- self::assertArrayHasKey('meters', $config);
- self::assertArrayHasKey('etl_pipeline', $config['meters']);
- self::assertSame('1.0.0', $config['meters']['etl_pipeline']['version']);
- self::assertNull($config['meters']['etl_pipeline']['schema_url']);
- self::assertSame(['flow.pipeline' => 'daily_import'], $config['meters']['etl_pipeline']['attributes']);
+ self::assertSame('severity_filtering', $config['logger_provider']['processor']['type']);
+ self::assertSame('warn', $config['logger_provider']['processor']['minimum_severity']);
+ self::assertSame('otlp', $config['logger_provider']['processor']['inner_processor']['exporter']);
}
- public function test_meter_provider_temporality_can_be_delta() : void
+ public function test_stream_transport_accepts_file_options() : void
{
$config = (new Processor())->processConfiguration(new Configuration(), [[
'resource' => [],
- 'meter_provider' => [
- 'temporality' => 'delta',
- ],
- ]]);
-
- self::assertSame('delta', $config['meter_provider']['temporality']);
- }
-
- public function test_meter_provider_temporality_defaults_to_cumulative() : void
- {
- $config = (new Processor())->processConfiguration(new Configuration(), [[
- 'resource' => [],
- 'meter_provider' => [],
- ]]);
-
- self::assertSame('cumulative', $config['meter_provider']['temporality']);
- }
-
- public function test_minimal_config_with_empty_resource() : void
- {
- $config = (new Processor())->processConfiguration(new Configuration(), [[
- 'resource' => [],
- ]]);
-
- self::assertArrayHasKey('resource', $config);
- self::assertSame([], $config['resource']['custom']);
- self::assertTrue($config['resource']['detectors']['enabled']);
- }
-
- public function test_multiple_named_items_of_same_type() : void
- {
- $config = (new Processor())->processConfiguration(new Configuration(), [[
- 'resource' => [],
- 'tracers' => [
- 'database' => [
- 'version' => '1.0.0',
- ],
- 'http_client' => [
- 'version' => '2.0.0',
- ],
- 'cache' => [],
- ],
- ]]);
-
- self::assertCount(3, $config['tracers']);
- self::assertSame('1.0.0', $config['tracers']['database']['version']);
- self::assertSame('2.0.0', $config['tracers']['http_client']['version']);
- self::assertSame('unknown', $config['tracers']['cache']['version']);
- }
-
- public function test_otlp_curl_transport_accepts_explicit_timeout() : void
- {
- $config = (new Processor())->processConfiguration(new Configuration(), [[
- 'resource' => [],
- 'tracer_provider' => [
- 'processor' => [
- 'exporter' => [
- 'type' => 'otlp',
- 'otlp' => [
- 'transport' => [
- 'type' => 'curl',
- 'endpoint' => 'http://otel:4318',
- 'timeout' => 60,
- ],
+ 'exporters' => [
+ 'otlp_stream' => [
+ 'otlp' => [
+ 'transport' => [
+ 'type' => 'stream',
+ 'endpoint' => '/var/log/otel/logs.jsonl',
+ 'file_permissions' => 0o640,
+ 'create_directories' => false,
],
],
],
],
]]);
- self::assertSame(60, $config['tracer_provider']['processor']['exporter']['otlp']['transport']['timeout']);
+ $transport = $config['exporters']['otlp_stream']['otlp']['transport'];
+ self::assertSame(0o640, $transport['file_permissions']);
+ self::assertFalse($transport['create_directories']);
}
- public function test_otlp_curl_transport_options() : void
+ #[TestWith(['/var/log/otel/logs.jsonl'])]
+ #[TestWith(['php://stdout'])]
+ #[TestWith(['php://stderr'])]
+ public function test_stream_transport_accepts_file_path_and_php_uri(string $endpoint) : void
{
$config = (new Processor())->processConfiguration(new Configuration(), [[
'resource' => [],
- 'tracer_provider' => [
- 'processor' => [
- 'type' => 'batching',
- 'exporter' => [
- 'type' => 'otlp',
- 'otlp' => [
- 'transport' => [
- 'type' => 'curl',
- 'endpoint' => 'http://localhost:4318',
- 'timeout' => 60,
- 'connect_timeout' => 5,
- 'compression' => true,
- 'follow_redirects' => false,
- 'max_redirects' => 5,
- 'proxy' => 'http://proxy:8080',
- 'ssl_verify_peer' => false,
- 'ssl_verify_host' => false,
- 'ssl_cert_path' => '/path/to/cert.pem',
- 'ssl_key_path' => '/path/to/key.pem',
- 'ca_info_path' => '/path/to/ca.pem',
- ],
+ 'exporters' => [
+ 'otlp_stream' => [
+ 'otlp' => [
+ 'transport' => [
+ 'type' => 'stream',
+ 'endpoint' => $endpoint,
],
],
],
],
]]);
- $transport = $config['tracer_provider']['processor']['exporter']['otlp']['transport'];
- self::assertSame('curl', $transport['type']);
- self::assertSame('http://localhost:4318', $transport['endpoint']);
- self::assertSame(60, $transport['timeout']);
- self::assertSame(5, $transport['connect_timeout']);
- self::assertTrue($transport['compression']);
- self::assertFalse($transport['follow_redirects']);
- self::assertSame(5, $transport['max_redirects']);
- self::assertSame('http://proxy:8080', $transport['proxy']);
- self::assertFalse($transport['ssl_verify_peer']);
- self::assertFalse($transport['ssl_verify_host']);
- self::assertSame('/path/to/cert.pem', $transport['ssl_cert_path']);
- self::assertSame('/path/to/key.pem', $transport['ssl_key_path']);
- self::assertSame('/path/to/ca.pem', $transport['ca_info_path']);
+ $transport = $config['exporters']['otlp_stream']['otlp']['transport'];
+ self::assertSame('stream', $transport['type']);
+ self::assertSame($endpoint, $transport['endpoint']);
+ self::assertSame(0644, $transport['file_permissions']);
+ self::assertTrue($transport['create_directories']);
}
- public function test_otlp_endpoint_is_required() : void
+ public function test_stream_transport_rejects_encoding_field() : void
{
$this->expectException(InvalidConfigurationException::class);
- $this->expectExceptionMessage('endpoint');
+ $this->expectExceptionMessage('"encoding" parameter is not supported when transport.type is "stream"');
(new Processor())->processConfiguration(new Configuration(), [[
'resource' => [],
- 'tracer_provider' => [
- 'processor' => [
- 'type' => 'batching',
- 'exporter' => [
- 'type' => 'otlp',
- 'otlp' => [
- 'transport' => [
- 'type' => 'curl',
- ],
+ 'exporters' => [
+ 'otlp_stream' => [
+ 'otlp' => [
+ 'transport' => [
+ 'type' => 'stream',
+ 'endpoint' => '/var/log/otel/logs.jsonl',
+ 'encoding' => 'protobuf',
],
],
],
@@ -671,48 +449,26 @@ public function test_otlp_endpoint_is_required() : void
]]);
}
- public function test_otlp_grpc_transport_accepts_config_without_timeout() : void
- {
- $config = (new Processor())->processConfiguration(new Configuration(), [[
- 'resource' => [],
- 'tracer_provider' => [
- 'processor' => [
- 'exporter' => [
- 'type' => 'otlp',
- 'otlp' => [
- 'transport' => [
- 'type' => 'grpc',
- 'endpoint' => 'otel:4317',
- 'insecure' => false,
- ],
- ],
- ],
- ],
- ],
- ]]);
-
- self::assertSame('grpc', $config['tracer_provider']['processor']['exporter']['otlp']['transport']['type']);
- self::assertSame('otel:4317', $config['tracer_provider']['processor']['exporter']['otlp']['transport']['endpoint']);
- self::assertFalse($config['tracer_provider']['processor']['exporter']['otlp']['transport']['insecure']);
- }
-
- public function test_otlp_grpc_transport_rejects_explicit_timeout() : void
+ #[TestWith(['timeout_ms', 2000])]
+ #[TestWith(['connect_timeout_ms', 500])]
+ #[TestWith(['compression', true])]
+ #[TestWith(['ssl_cert_path', '/etc/cert.pem'])]
+ #[TestWith(['headers', ['Authorization' => 'Bearer x']])]
+ #[TestWith(['insecure', true])]
+ public function test_stream_transport_rejects_http_specific_options(string $key, mixed $value) : void
{
$this->expectException(InvalidConfigurationException::class);
- $this->expectExceptionMessage('The "timeout" parameter is not supported when transport.type is "grpc"');
+ $this->expectExceptionMessage(\sprintf('The "%s" parameter is not supported when transport.type is "stream".', $key));
(new Processor())->processConfiguration(new Configuration(), [[
'resource' => [],
- 'tracer_provider' => [
- 'processor' => [
- 'exporter' => [
- 'type' => 'otlp',
- 'otlp' => [
- 'transport' => [
- 'type' => 'grpc',
- 'endpoint' => 'otel:4317',
- 'timeout' => 30,
- ],
+ 'exporters' => [
+ 'otlp_stream' => [
+ 'otlp' => [
+ 'transport' => [
+ 'type' => 'stream',
+ 'endpoint' => '/var/log/otel/logs.jsonl',
+ $key => $value,
],
],
],
@@ -720,71 +476,36 @@ public function test_otlp_grpc_transport_rejects_explicit_timeout() : void
]]);
}
- public function test_otlp_http_transport_with_psr_services() : void
+ public function test_stream_transport_requires_non_empty_endpoint() : void
{
- $config = (new Processor())->processConfiguration(new Configuration(), [[
- 'resource' => [],
- 'tracer_provider' => [
- 'processor' => [
- 'type' => 'batching',
- 'exporter' => [
- 'type' => 'otlp',
- 'otlp' => [
- 'transport' => [
- 'type' => 'http',
- 'endpoint' => 'http://localhost:4318',
- 'http_client_service_id' => 'app.http_client',
- 'request_factory_service_id' => 'app.request_factory',
- 'stream_factory_service_id' => 'app.stream_factory',
- ],
- ],
- ],
- ],
- ],
- ]]);
-
- $transport = $config['tracer_provider']['processor']['exporter']['otlp']['transport'];
- self::assertSame('http', $transport['type']);
- self::assertSame('app.http_client', $transport['http_client_service_id']);
- self::assertSame('app.request_factory', $transport['request_factory_service_id']);
- self::assertSame('app.stream_factory', $transport['stream_factory_service_id']);
- }
+ $this->expectException(InvalidConfigurationException::class);
+ $this->expectExceptionMessage('endpoint');
- public function test_otlp_serializer_defaults_to_json() : void
- {
- $config = (new Processor())->processConfiguration(new Configuration(), [[
+ (new Processor())->processConfiguration(new Configuration(), [[
'resource' => [],
- 'tracer_provider' => [
- 'processor' => [
- 'type' => 'batching',
- 'exporter' => [
- 'type' => 'otlp',
- 'otlp' => [
- 'transport' => [
- 'endpoint' => 'http://localhost:4318',
- ],
- ],
+ 'exporters' => [
+ 'otlp_stream' => [
+ 'otlp' => [
+ 'transport' => ['type' => 'stream'],
],
],
],
]]);
-
- $serializer = $config['tracer_provider']['processor']['exporter']['otlp']['transport']['serializer'];
- self::assertSame('json', $serializer['type']);
}
- public function test_otlp_transport_defaults() : void
+ public function test_transport_failover_block_is_accepted_under_curl_primary() : void
{
$config = (new Processor())->processConfiguration(new Configuration(), [[
'resource' => [],
- 'tracer_provider' => [
- 'processor' => [
- 'type' => 'batching',
- 'exporter' => [
- 'type' => 'otlp',
- 'otlp' => [
- 'transport' => [
- 'endpoint' => 'http://localhost:4318',
+ 'exporters' => [
+ 'otlp' => [
+ 'otlp' => [
+ 'transport' => [
+ 'type' => 'curl',
+ 'endpoint' => 'http://localhost:4318',
+ 'failover' => [
+ 'type' => 'stream',
+ 'endpoint' => 'php://memory',
],
],
],
@@ -792,309 +513,76 @@ public function test_otlp_transport_defaults() : void
],
]]);
- $transport = $config['tracer_provider']['processor']['exporter']['otlp']['transport'];
- self::assertSame('curl', $transport['type']);
- self::assertSame('http://localhost:4318', $transport['endpoint']);
- self::assertSame(30, $transport['timeout']);
- self::assertSame([], $transport['headers']);
- self::assertTrue($transport['insecure']);
+ self::assertSame('stream', $config['exporters']['otlp']['otlp']['transport']['failover']['type']);
+ self::assertSame('php://memory', $config['exporters']['otlp']['otlp']['transport']['failover']['endpoint']);
}
- public function test_processor_batch_size_default() : void
- {
- $config = (new Processor())->processConfiguration(new Configuration(), [[
- 'resource' => [],
- 'tracer_provider' => [
- 'processor' => [
- 'type' => 'batching',
- ],
- ],
- ]]);
-
- self::assertSame(512, $config['tracer_provider']['processor']['batch_size']);
- }
-
- public function test_processor_batch_size_minimum_validation() : void
+ public function test_transport_failover_rejected_for_stream_primary() : void
{
$this->expectException(InvalidConfigurationException::class);
+ $this->expectExceptionMessage('"failover" block is only supported for transport.type "curl" or "grpc"');
(new Processor())->processConfiguration(new Configuration(), [[
'resource' => [],
- 'tracer_provider' => [
- 'processor' => [
- 'type' => 'batching',
- 'batch_size' => 0,
- ],
- ],
- ]]);
- }
-
- public function test_processor_defaults_to_void() : void
- {
- $config = (new Processor())->processConfiguration(new Configuration(), [[
- 'resource' => [],
- 'tracer_provider' => [],
- ]]);
-
- self::assertSame('void', $config['tracer_provider']['processor']['type']);
- }
-
- public function test_providers_have_defaults() : void
- {
- $config = (new Processor())->processConfiguration(new Configuration(), [[
- 'resource' => [],
- ]]);
-
- self::assertArrayHasKey('tracer_provider', $config);
- self::assertArrayHasKey('meter_provider', $config);
- self::assertArrayHasKey('logger_provider', $config);
- self::assertSame('void', $config['tracer_provider']['processor']['type']);
- self::assertSame('void', $config['meter_provider']['processor']['type']);
- self::assertSame('void', $config['logger_provider']['processor']['type']);
- }
-
- public function test_resource_custom_attributes() : void
- {
- $config = (new Processor())->processConfiguration(new Configuration(), [[
- 'resource' => [
- 'custom' => [
- 'my.custom.attribute' => 'custom-value',
- 'another.attribute' => 123,
- ],
- ],
- ]]);
-
- self::assertSame([
- 'my.custom.attribute' => 'custom-value',
- 'another.attribute' => 123,
- ], $config['resource']['custom']);
- }
-
- public function test_resource_detectors_are_enabled_by_default() : void
- {
- $config = (new Processor())->processConfiguration(new Configuration(), [[
- 'resource' => [],
- ]]);
-
- self::assertTrue($config['resource']['detectors']['enabled']);
- self::assertTrue($config['resource']['detectors']['static']['cache']['enabled']);
- self::assertNull($config['resource']['detectors']['static']['cache']['path']);
- self::assertTrue($config['resource']['detectors']['static']['os']['enabled']);
- self::assertTrue($config['resource']['detectors']['static']['host']['enabled']);
- self::assertTrue($config['resource']['detectors']['static']['service']['enabled']);
- self::assertTrue($config['resource']['detectors']['static']['deployment']['enabled']);
- self::assertTrue($config['resource']['detectors']['static']['environment']['enabled']);
- self::assertTrue($config['resource']['detectors']['dynamic']['process']['enabled']);
- }
-
- public function test_resource_detectors_cache_can_be_configured() : void
- {
- $config = (new Processor())->processConfiguration(new Configuration(), [[
- 'resource' => [
- 'detectors' => [
- 'static' => [
- 'cache' => [
- 'enabled' => false,
- 'path' => '/var/cache/flow_telemetry_resource.cache',
+ 'exporters' => [
+ 'otlp' => [
+ 'otlp' => [
+ 'transport' => [
+ 'type' => 'stream',
+ 'endpoint' => 'php://memory',
+ 'failover' => [
+ 'type' => 'stream',
+ 'endpoint' => 'php://memory',
+ ],
],
],
],
],
]]);
-
- self::assertFalse($config['resource']['detectors']['static']['cache']['enabled']);
- self::assertSame('/var/cache/flow_telemetry_resource.cache', $config['resource']['detectors']['static']['cache']['path']);
}
- public function test_resource_detectors_can_be_disabled() : void
- {
- $config = (new Processor())->processConfiguration(new Configuration(), [[
- 'resource' => [
- 'detectors' => [
- 'enabled' => false,
- ],
- ],
- ]]);
-
- self::assertFalse($config['resource']['detectors']['enabled']);
- }
-
- public function test_resource_detectors_can_be_individually_disabled() : void
- {
- $config = (new Processor())->processConfiguration(new Configuration(), [[
- 'resource' => [
- 'detectors' => [
- 'static' => [
- 'os' => ['enabled' => false],
- 'host' => ['enabled' => false],
- 'service' => ['enabled' => false],
- 'deployment' => ['enabled' => false],
- 'environment' => ['enabled' => false],
- ],
- 'dynamic' => [
- 'process' => ['enabled' => false],
- ],
- ],
- ],
- ]]);
-
- self::assertTrue($config['resource']['detectors']['enabled']);
- self::assertFalse($config['resource']['detectors']['static']['os']['enabled']);
- self::assertFalse($config['resource']['detectors']['static']['host']['enabled']);
- self::assertFalse($config['resource']['detectors']['static']['service']['enabled']);
- self::assertFalse($config['resource']['detectors']['static']['deployment']['enabled']);
- self::assertFalse($config['resource']['detectors']['static']['environment']['enabled']);
- self::assertFalse($config['resource']['detectors']['dynamic']['process']['enabled']);
- }
-
- public function test_sampler_defaults_to_always_on() : void
- {
- $config = (new Processor())->processConfiguration(new Configuration(), [[
- 'resource' => [],
- 'tracer_provider' => [],
- ]]);
-
- self::assertSame('always_on', $config['tracer_provider']['sampler']['type']);
- }
-
- public function test_sampler_ratio_maximum_validation() : void
- {
- $this->expectException(InvalidConfigurationException::class);
-
- (new Processor())->processConfiguration(new Configuration(), [[
- 'resource' => [],
- 'tracer_provider' => [
- 'sampler' => [
- 'type' => 'trace_id_ratio',
- 'ratio' => 1.1,
- ],
- ],
- ]]);
- }
-
- public function test_sampler_ratio_minimum_validation() : void
- {
- $this->expectException(InvalidConfigurationException::class);
-
- (new Processor())->processConfiguration(new Configuration(), [[
- 'resource' => [],
- 'tracer_provider' => [
- 'sampler' => [
- 'type' => 'trace_id_ratio',
- 'ratio' => -0.1,
- ],
- ],
- ]]);
- }
-
- public function test_sampler_ratio_validation() : void
- {
- $config = (new Processor())->processConfiguration(new Configuration(), [[
- 'resource' => [],
- 'tracer_provider' => [
- 'sampler' => [
- 'type' => 'trace_id_ratio',
- 'ratio' => 0.5,
- ],
- ],
- ]]);
-
- self::assertSame(0.5, $config['tracer_provider']['sampler']['ratio']);
- }
-
- public function test_severity_filtering_is_only_available_for_log_processors() : void
+ public function test_transport_failover_rejects_nested_failover() : void
{
$this->expectException(InvalidConfigurationException::class);
+ $this->expectExceptionMessage('Unrecognized option "failover" under "flow_telemetry.exporters.otlp.otlp.transport.failover"');
(new Processor())->processConfiguration(new Configuration(), [[
'resource' => [],
- 'tracer_provider' => [
- 'processor' => [
- 'type' => 'severity_filtering',
- ],
- ],
- ]]);
- }
-
- public function test_severity_filtering_minimum_severity_default() : void
- {
- $config = (new Processor())->processConfiguration(new Configuration(), [[
- 'resource' => [],
- 'logger_provider' => [
- 'processor' => [
- 'type' => 'severity_filtering',
- 'inner_processor' => [
- 'type' => 'void',
- ],
- ],
- ],
- ]]);
-
- self::assertSame('info', $config['logger_provider']['processor']['minimum_severity']);
- }
-
- public function test_severity_filtering_processor_for_logs() : void
- {
- $config = (new Processor())->processConfiguration(new Configuration(), [[
- 'resource' => [],
- 'logger_provider' => [
- 'processor' => [
- 'type' => 'severity_filtering',
- 'minimum_severity' => 'warn',
- 'inner_processor' => [
- 'type' => 'batching',
- 'exporter' => ['type' => 'console'],
+ 'exporters' => [
+ 'otlp' => [
+ 'otlp' => [
+ 'transport' => [
+ 'type' => 'curl',
+ 'endpoint' => 'http://localhost:4318',
+ 'failover' => [
+ 'type' => 'stream',
+ 'endpoint' => 'php://memory',
+ 'failover' => [
+ 'type' => 'stream',
+ 'endpoint' => 'php://memory',
+ ],
+ ],
+ ],
],
],
],
]]);
-
- $processor = $config['logger_provider']['processor'];
- self::assertSame('severity_filtering', $processor['type']);
- self::assertSame('warn', $processor['minimum_severity']);
- self::assertSame('batching', $processor['inner_processor']['type']);
- self::assertSame('console', $processor['inner_processor']['exporter']['type']);
}
- public function test_tracer_configuration_with_all_options() : void
+ public function test_transport_service_type_inside_otlp() : void
{
$config = (new Processor())->processConfiguration(new Configuration(), [[
'resource' => [],
- 'tracers' => [
- 'database' => [
- 'version' => '2.0.0',
- 'schema_url' => 'https://opentelemetry.io/schemas/1.20.0',
- 'attributes' => [
- 'db.system' => 'postgresql',
- 'db.pool_size' => 10,
+ 'exporters' => [
+ 'custom_otlp' => [
+ 'otlp' => [
+ 'transport' => ['type' => 'service', 'service_id' => 'app.my_transport'],
],
],
],
]]);
- self::assertArrayHasKey('tracers', $config);
- self::assertArrayHasKey('database', $config['tracers']);
- self::assertSame('2.0.0', $config['tracers']['database']['version']);
- self::assertSame('https://opentelemetry.io/schemas/1.20.0', $config['tracers']['database']['schema_url']);
- self::assertSame([
- 'db.system' => 'postgresql',
- 'db.pool_size' => 10,
- ], $config['tracers']['database']['attributes']);
- }
-
- public function test_tracer_configuration_with_defaults() : void
- {
- $config = (new Processor())->processConfiguration(new Configuration(), [[
- 'resource' => [],
- 'tracers' => [
- 'http_client' => [],
- ],
- ]]);
-
- self::assertArrayHasKey('tracers', $config);
- self::assertArrayHasKey('http_client', $config['tracers']);
- self::assertSame('unknown', $config['tracers']['http_client']['version']);
- self::assertNull($config['tracers']['http_client']['schema_url']);
- self::assertSame([], $config['tracers']['http_client']['attributes']);
+ self::assertSame('service', $config['exporters']['custom_otlp']['otlp']['transport']['type']);
+ self::assertSame('app.my_transport', $config['exporters']['custom_otlp']['otlp']['transport']['service_id']);
}
}
diff --git a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/Instrumentation/Cache/TagAwareTraceableCacheAdapterTest.php b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/Instrumentation/Cache/TagAwareTraceableCacheAdapterTest.php
index 6f43a8b77..126cd3f16 100644
--- a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/Instrumentation/Cache/TagAwareTraceableCacheAdapterTest.php
+++ b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/Instrumentation/Cache/TagAwareTraceableCacheAdapterTest.php
@@ -9,7 +9,7 @@
use Flow\Telemetry\Logger\LoggerProvider;
use Flow\Telemetry\Meter\MeterProvider;
use Flow\Telemetry\Provider\Clock\SystemClock;
-use Flow\Telemetry\Provider\Memory\{MemorySpanExporter, MemorySpanProcessor};
+use Flow\Telemetry\Provider\Memory\{MemoryExporter, MemorySpanProcessor};
use Flow\Telemetry\Provider\Void\{VoidLogProcessor, VoidMetricProcessor};
use Flow\Telemetry\{Resource, Telemetry};
use Flow\Telemetry\Tracer\{SpanKind, TracerProvider};
@@ -24,7 +24,7 @@ final class TagAwareTraceableCacheAdapterTest extends TestCase
{
public function test_clear_creates_span_with_correct_attributes() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$adapter = new class implements TagAwareAdapterInterface {
@@ -96,7 +96,7 @@ public function saveDeferred(CacheItemInterface $item) : bool
public function test_clear_with_prefix_includes_prefix_attribute() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$adapter = new class implements TagAwareAdapterInterface {
@@ -164,7 +164,7 @@ public function saveDeferred(CacheItemInterface $item) : bool
public function test_delete_throws_when_adapter_does_not_implement_tag_aware_cache_interface() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$adapter = new class implements TagAwareAdapterInterface {
@@ -229,7 +229,7 @@ public function saveDeferred(CacheItemInterface $item) : bool
public function test_get_throws_when_adapter_does_not_implement_tag_aware_cache_interface() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$adapter = new class implements TagAwareAdapterInterface {
@@ -294,7 +294,7 @@ public function saveDeferred(CacheItemInterface $item) : bool
public function test_invalidate_tags_creates_span_with_tag_count() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$adapter = new class implements TagAwareAdapterInterface {
@@ -364,7 +364,7 @@ public function saveDeferred(CacheItemInterface $item) : bool
public function test_operation_records_exception_on_failure() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$adapter = new class implements TagAwareAdapterInterface {
@@ -441,7 +441,7 @@ public function saveDeferred(CacheItemInterface $item) : bool
public function test_operation_sets_error_status_on_failure() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$adapter = new class implements TagAwareAdapterInterface {
@@ -514,7 +514,7 @@ public function saveDeferred(CacheItemInterface $item) : bool
public function test_prune_delegates_to_adapter_when_pruneable() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$adapter = new class implements PruneableInterface, TagAwareAdapterInterface {
@@ -589,7 +589,7 @@ public function saveDeferred(CacheItemInterface $item) : bool
public function test_prune_returns_false_when_adapter_is_not_pruneable() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$adapter = new class implements TagAwareAdapterInterface {
@@ -654,7 +654,7 @@ public function saveDeferred(CacheItemInterface $item) : bool
public function test_reset_delegates_to_adapter_when_resettable() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$adapter = new class implements ResettableInterface, TagAwareAdapterInterface {
@@ -726,7 +726,7 @@ public function saveDeferred(CacheItemInterface $item) : bool
public function test_reset_does_nothing_when_adapter_is_not_resettable() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$adapter = new class implements TagAwareAdapterInterface {
diff --git a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/Instrumentation/Console/ConsoleSpanSubscriberTest.php b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/Instrumentation/Console/ConsoleSpanSubscriberTest.php
index 56d0cad8f..5c2be660c 100644
--- a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/Instrumentation/Console/ConsoleSpanSubscriberTest.php
+++ b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/Instrumentation/Console/ConsoleSpanSubscriberTest.php
@@ -9,7 +9,7 @@
use Flow\Telemetry\Logger\LoggerProvider;
use Flow\Telemetry\Meter\MeterProvider;
use Flow\Telemetry\Provider\Clock\SystemClock;
-use Flow\Telemetry\Provider\Memory\{MemorySpanExporter, MemorySpanProcessor};
+use Flow\Telemetry\Provider\Memory\{MemoryExporter, MemorySpanProcessor};
use Flow\Telemetry\Provider\Void\{VoidLogProcessor, VoidMetricProcessor};
use Flow\Telemetry\{Resource, Telemetry};
use Flow\Telemetry\Tracer\{SpanKind, TracerProvider};
@@ -25,7 +25,7 @@ final class ConsoleSpanSubscriberTest extends TestCase
{
public function test_exit_code_0_sets_ok_status() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$subscriber = new ConsoleSpanSubscriber($telemetry);
@@ -47,7 +47,7 @@ public function test_exit_code_0_sets_ok_status() : void
public function test_exit_code_nonzero_sets_error_status() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$subscriber = new ConsoleSpanSubscriber($telemetry);
@@ -85,7 +85,7 @@ public function test_get_subscribed_events_returns_correct_events() : void
public function test_matches_pattern_exact_match() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$subscriber = new ConsoleSpanSubscriber($telemetry, excludeCommands: ['cache:clear']);
@@ -100,7 +100,7 @@ public function test_matches_pattern_exact_match() : void
public function test_matches_pattern_regex_match() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$subscriber = new ConsoleSpanSubscriber($telemetry, excludeCommands: ['/^cache:.*/']);
@@ -115,7 +115,7 @@ public function test_matches_pattern_regex_match() : void
public function test_on_error_does_nothing_when_no_active_span() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$subscriber = new ConsoleSpanSubscriber($telemetry, excludeCommands: ['cache:clear']);
@@ -133,7 +133,7 @@ public function test_on_error_does_nothing_when_no_active_span() : void
public function test_on_error_records_exception() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$subscriber = new ConsoleSpanSubscriber($telemetry);
@@ -158,7 +158,7 @@ public function test_on_error_records_exception() : void
public function test_on_terminate_does_nothing_when_no_active_span() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$subscriber = new ConsoleSpanSubscriber($telemetry, excludeCommands: ['cache:clear']);
@@ -175,7 +175,7 @@ public function test_on_terminate_does_nothing_when_no_active_span() : void
public function test_should_trace_excludes_exact_matches() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$subscriber = new ConsoleSpanSubscriber($telemetry, excludeCommands: ['assets:install']);
@@ -190,7 +190,7 @@ public function test_should_trace_excludes_exact_matches() : void
public function test_should_trace_excludes_regex_matches() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$subscriber = new ConsoleSpanSubscriber($telemetry, excludeCommands: ['/^debug:/']);
@@ -205,7 +205,7 @@ public function test_should_trace_excludes_regex_matches() : void
public function test_should_trace_includes_non_excluded() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$subscriber = new ConsoleSpanSubscriber($telemetry, excludeCommands: ['cache:clear', '/^debug:/']);
@@ -224,7 +224,7 @@ public function test_should_trace_includes_non_excluded() : void
public function test_span_includes_command_class_attribute() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$subscriber = new ConsoleSpanSubscriber($telemetry);
@@ -243,7 +243,7 @@ public function test_span_includes_command_class_attribute() : void
public function test_span_includes_command_name_attribute() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$subscriber = new ConsoleSpanSubscriber($telemetry);
@@ -262,7 +262,7 @@ public function test_span_includes_command_name_attribute() : void
public function test_span_includes_exit_code_attribute() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$subscriber = new ConsoleSpanSubscriber($telemetry);
@@ -281,7 +281,7 @@ public function test_span_includes_exit_code_attribute() : void
public function test_span_kind_is_internal() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$subscriber = new ConsoleSpanSubscriber($telemetry);
@@ -300,7 +300,7 @@ public function test_span_kind_is_internal() : void
public function test_span_name_set_to_command_name() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$subscriber = new ConsoleSpanSubscriber($telemetry);
diff --git a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/Instrumentation/Doctrine/DBAL/TracingConnectionTest.php b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/Instrumentation/Doctrine/DBAL/TracingConnectionTest.php
index 132bc98f0..df54aee43 100644
--- a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/Instrumentation/Doctrine/DBAL/TracingConnectionTest.php
+++ b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/Instrumentation/Doctrine/DBAL/TracingConnectionTest.php
@@ -11,7 +11,7 @@
use Flow\Telemetry\Logger\LoggerProvider;
use Flow\Telemetry\Meter\MeterProvider;
use Flow\Telemetry\Provider\Clock\SystemClock;
-use Flow\Telemetry\Provider\Memory\{MemorySpanExporter, MemorySpanProcessor};
+use Flow\Telemetry\Provider\Memory\{MemoryExporter, MemorySpanProcessor};
use Flow\Telemetry\Provider\Void\{VoidLogProcessor, VoidMetricProcessor};
use Flow\Telemetry\{Resource, Telemetry};
use Flow\Telemetry\Tracer\TracerProvider;
@@ -30,7 +30,7 @@ protected function setUp() : void
public function test_prepare_uses_truncation() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$connection = $this->createMockConnection();
@@ -46,7 +46,7 @@ public function test_prepare_uses_truncation() : void
public function test_query_uses_truncation() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$connection = $this->createMockConnection();
@@ -62,7 +62,7 @@ public function test_query_uses_truncation() : void
public function test_sql_not_logged_when_log_sql_disabled() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$connection = $this->createMockConnection();
@@ -78,7 +78,7 @@ public function test_sql_not_logged_when_log_sql_disabled() : void
public function test_truncate_sql_exact_boundary_case() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$connection = $this->createMockConnection();
@@ -94,7 +94,7 @@ public function test_truncate_sql_exact_boundary_case() : void
public function test_truncate_sql_handles_multibyte_characters() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$connection = $this->createMockConnection();
@@ -113,7 +113,7 @@ public function test_truncate_sql_handles_multibyte_characters() : void
public function test_truncate_sql_returns_full_sql_when_max_length_negative() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$connection = $this->createMockConnection();
@@ -129,7 +129,7 @@ public function test_truncate_sql_returns_full_sql_when_max_length_negative() :
public function test_truncate_sql_returns_full_sql_when_max_length_zero() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$connection = $this->createMockConnection();
@@ -145,7 +145,7 @@ public function test_truncate_sql_returns_full_sql_when_max_length_zero() : void
public function test_truncate_sql_returns_sql_when_shorter_than_limit() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$connection = $this->createMockConnection();
@@ -161,7 +161,7 @@ public function test_truncate_sql_returns_sql_when_shorter_than_limit() : void
public function test_truncate_sql_truncates_and_appends_ellipsis() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$connection = $this->createMockConnection();
diff --git a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/Instrumentation/Doctrine/DBAL/TracingDriverTest.php b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/Instrumentation/Doctrine/DBAL/TracingDriverTest.php
index 48d7cbccd..283edab3f 100644
--- a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/Instrumentation/Doctrine/DBAL/TracingDriverTest.php
+++ b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/Instrumentation/Doctrine/DBAL/TracingDriverTest.php
@@ -13,7 +13,7 @@
use Flow\Telemetry\Logger\LoggerProvider;
use Flow\Telemetry\Meter\MeterProvider;
use Flow\Telemetry\Provider\Clock\SystemClock;
-use Flow\Telemetry\Provider\Memory\{MemorySpanExporter, MemorySpanProcessor};
+use Flow\Telemetry\Provider\Memory\{MemoryExporter, MemorySpanProcessor};
use Flow\Telemetry\Provider\Void\{VoidLogProcessor, VoidMetricProcessor};
use Flow\Telemetry\{Resource, Telemetry};
use Flow\Telemetry\Tracer\TracerProvider;
@@ -32,7 +32,7 @@ protected function setUp() : void
public function test_get_semantic_db_system_defaults_to_other_sql() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$platform = $this->createMock(AbstractPlatform::class);
@@ -49,7 +49,7 @@ public function test_get_semantic_db_system_defaults_to_other_sql() : void
public function test_get_semantic_db_system_detects_db2() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$platform = new DB2Platform();
@@ -66,7 +66,7 @@ public function test_get_semantic_db_system_detects_db2() : void
public function test_get_semantic_db_system_detects_mariadb_as_mysql() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$platform = new MariaDBPlatform();
@@ -83,7 +83,7 @@ public function test_get_semantic_db_system_detects_mariadb_as_mysql() : void
public function test_get_semantic_db_system_detects_mssql() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$platform = new SQLServerPlatform();
@@ -100,7 +100,7 @@ public function test_get_semantic_db_system_detects_mssql() : void
public function test_get_semantic_db_system_detects_mysql() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$platform = new MySQL80Platform();
@@ -117,7 +117,7 @@ public function test_get_semantic_db_system_detects_mysql() : void
public function test_get_semantic_db_system_detects_oracle() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$platform = new OraclePlatform();
@@ -134,7 +134,7 @@ public function test_get_semantic_db_system_detects_oracle() : void
public function test_get_semantic_db_system_detects_postgresql() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$platform = new PostgreSQLPlatform();
@@ -151,7 +151,7 @@ public function test_get_semantic_db_system_detects_postgresql() : void
public function test_get_semantic_db_system_detects_sqlite() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$platform = new SQLitePlatform();
@@ -168,7 +168,7 @@ public function test_get_semantic_db_system_detects_sqlite() : void
public function test_span_defaults_db_namespace_to_default() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$platform = new PostgreSQLPlatform();
@@ -185,7 +185,7 @@ public function test_span_defaults_db_namespace_to_default() : void
public function test_span_includes_connection_name() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$platform = new PostgreSQLPlatform();
@@ -202,7 +202,7 @@ public function test_span_includes_connection_name() : void
public function test_span_includes_db_namespace_from_params() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$platform = new PostgreSQLPlatform();
diff --git a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/Instrumentation/Doctrine/DBAL/V3/TracingConnectionTest.php b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/Instrumentation/Doctrine/DBAL/V3/TracingConnectionTest.php
index 754c93f73..501f0fb61 100644
--- a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/Instrumentation/Doctrine/DBAL/V3/TracingConnectionTest.php
+++ b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/Instrumentation/Doctrine/DBAL/V3/TracingConnectionTest.php
@@ -11,7 +11,7 @@
use Flow\Telemetry\Logger\LoggerProvider;
use Flow\Telemetry\Meter\MeterProvider;
use Flow\Telemetry\Provider\Clock\SystemClock;
-use Flow\Telemetry\Provider\Memory\{MemorySpanExporter, MemorySpanProcessor};
+use Flow\Telemetry\Provider\Memory\{MemoryExporter, MemorySpanProcessor};
use Flow\Telemetry\Provider\Void\{VoidLogProcessor, VoidMetricProcessor};
use Flow\Telemetry\{Resource, Telemetry};
use Flow\Telemetry\Tracer\TracerProvider;
@@ -30,7 +30,7 @@ protected function setUp() : void
public function test_prepare_uses_truncation() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$connection = $this->createMockConnection();
@@ -46,7 +46,7 @@ public function test_prepare_uses_truncation() : void
public function test_query_uses_truncation() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$connection = $this->createMockConnection();
@@ -62,7 +62,7 @@ public function test_query_uses_truncation() : void
public function test_sql_not_logged_when_log_sql_disabled() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$connection = $this->createMockConnection();
@@ -78,7 +78,7 @@ public function test_sql_not_logged_when_log_sql_disabled() : void
public function test_truncate_sql_exact_boundary_case() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$connection = $this->createMockConnection();
@@ -94,7 +94,7 @@ public function test_truncate_sql_exact_boundary_case() : void
public function test_truncate_sql_handles_multibyte_characters() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$connection = $this->createMockConnection();
@@ -113,7 +113,7 @@ public function test_truncate_sql_handles_multibyte_characters() : void
public function test_truncate_sql_returns_full_sql_when_max_length_negative() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$connection = $this->createMockConnection();
@@ -129,7 +129,7 @@ public function test_truncate_sql_returns_full_sql_when_max_length_negative() :
public function test_truncate_sql_returns_full_sql_when_max_length_zero() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$connection = $this->createMockConnection();
@@ -145,7 +145,7 @@ public function test_truncate_sql_returns_full_sql_when_max_length_zero() : void
public function test_truncate_sql_returns_sql_when_shorter_than_limit() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$connection = $this->createMockConnection();
@@ -161,7 +161,7 @@ public function test_truncate_sql_returns_sql_when_shorter_than_limit() : void
public function test_truncate_sql_truncates_and_appends_ellipsis() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$connection = $this->createMockConnection();
diff --git a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/Instrumentation/Doctrine/DBAL/V3/TracingDriverTest.php b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/Instrumentation/Doctrine/DBAL/V3/TracingDriverTest.php
index cc9804860..63bed99a0 100644
--- a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/Instrumentation/Doctrine/DBAL/V3/TracingDriverTest.php
+++ b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/Instrumentation/Doctrine/DBAL/V3/TracingDriverTest.php
@@ -14,7 +14,7 @@
use Flow\Telemetry\Logger\LoggerProvider;
use Flow\Telemetry\Meter\MeterProvider;
use Flow\Telemetry\Provider\Clock\SystemClock;
-use Flow\Telemetry\Provider\Memory\{MemorySpanExporter, MemorySpanProcessor};
+use Flow\Telemetry\Provider\Memory\{MemoryExporter, MemorySpanProcessor};
use Flow\Telemetry\Provider\Void\{VoidLogProcessor, VoidMetricProcessor};
use Flow\Telemetry\{Resource, Telemetry};
use Flow\Telemetry\Tracer\TracerProvider;
@@ -33,7 +33,7 @@ protected function setUp() : void
public function test_get_semantic_db_system_defaults_to_other_sql() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$platform = $this->createMock(AbstractPlatform::class);
@@ -50,7 +50,7 @@ public function test_get_semantic_db_system_defaults_to_other_sql() : void
public function test_get_semantic_db_system_detects_db2() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$platform = new DB2Platform();
@@ -67,7 +67,7 @@ public function test_get_semantic_db_system_detects_db2() : void
public function test_get_semantic_db_system_detects_mariadb_as_mysql() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$platform = new MariaDBPlatform();
@@ -84,7 +84,7 @@ public function test_get_semantic_db_system_detects_mariadb_as_mysql() : void
public function test_get_semantic_db_system_detects_mssql() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$platform = new SQLServerPlatform();
@@ -101,7 +101,7 @@ public function test_get_semantic_db_system_detects_mssql() : void
public function test_get_semantic_db_system_detects_mysql() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$platform = new MySQL80Platform();
@@ -118,7 +118,7 @@ public function test_get_semantic_db_system_detects_mysql() : void
public function test_get_semantic_db_system_detects_oracle() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$platform = new OraclePlatform();
@@ -135,7 +135,7 @@ public function test_get_semantic_db_system_detects_oracle() : void
public function test_get_semantic_db_system_detects_postgresql() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$platform = new PostgreSQLPlatform();
@@ -152,7 +152,7 @@ public function test_get_semantic_db_system_detects_postgresql() : void
public function test_get_semantic_db_system_detects_sqlite() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$platform = new SqlitePlatform();
@@ -169,7 +169,7 @@ public function test_get_semantic_db_system_detects_sqlite() : void
public function test_span_defaults_db_namespace_to_default() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$platform = new PostgreSQLPlatform();
@@ -186,7 +186,7 @@ public function test_span_defaults_db_namespace_to_default() : void
public function test_span_includes_connection_name() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$platform = new PostgreSQLPlatform();
@@ -203,7 +203,7 @@ public function test_span_includes_connection_name() : void
public function test_span_includes_db_namespace_from_params() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$platform = new PostgreSQLPlatform();
diff --git a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/Instrumentation/HttpClient/TracableHttpClientTest.php b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/Instrumentation/HttpClient/TracableHttpClientTest.php
index fa92d3246..7ee8f7b56 100644
--- a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/Instrumentation/HttpClient/TracableHttpClientTest.php
+++ b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/Instrumentation/HttpClient/TracableHttpClientTest.php
@@ -9,7 +9,7 @@
use Flow\Telemetry\Logger\LoggerProvider;
use Flow\Telemetry\Meter\MeterProvider;
use Flow\Telemetry\Provider\Clock\SystemClock;
-use Flow\Telemetry\Provider\Memory\{MemorySpanExporter, MemorySpanProcessor};
+use Flow\Telemetry\Provider\Memory\{MemoryExporter, MemorySpanProcessor};
use Flow\Telemetry\Provider\Void\{VoidLogProcessor, VoidMetricProcessor};
use Flow\Telemetry\{Resource, Telemetry};
use Flow\Telemetry\Tracer\{SpanKind, TracerProvider};
@@ -22,7 +22,7 @@ final class TracableHttpClientTest extends TestCase
{
public function test_request_defaults_host_to_unknown_when_missing() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$innerClient = $this->createMockHttpClient(200);
@@ -37,7 +37,7 @@ public function test_request_defaults_host_to_unknown_when_missing() : void
public function test_request_defaults_scheme_to_http_when_missing() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$innerClient = $this->createMockHttpClient(200);
@@ -52,7 +52,7 @@ public function test_request_defaults_scheme_to_http_when_missing() : void
public function test_request_extracts_host_from_url() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$innerClient = $this->createMockHttpClient(200);
@@ -67,7 +67,7 @@ public function test_request_extracts_host_from_url() : void
public function test_request_extracts_scheme_from_url() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$innerClient = new class implements HttpClientInterface {
@@ -131,7 +131,7 @@ public function withOptions(array $options) : static
public function test_request_includes_client_name_attribute() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$innerClient = $this->createMockHttpClient(200);
@@ -146,7 +146,7 @@ public function test_request_includes_client_name_attribute() : void
public function test_request_includes_http_status_code_attribute() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$innerClient = $this->createMockHttpClient(200);
@@ -161,7 +161,7 @@ public function test_request_includes_http_status_code_attribute() : void
public function test_request_includes_method_and_url_attributes() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$innerClient = $this->createMockHttpClient(200);
@@ -177,7 +177,7 @@ public function test_request_includes_method_and_url_attributes() : void
public function test_request_records_exception_on_failure() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$innerClient = new class implements HttpClientInterface {
@@ -226,7 +226,7 @@ public function withOptions(array $options) : static
public function test_request_sets_error_status_for_4xx_codes() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$innerClient = $this->createMockHttpClient(404);
@@ -245,7 +245,7 @@ public function test_request_sets_error_status_for_4xx_codes() : void
public function test_request_sets_error_status_for_5xx_codes() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$innerClient = $this->createMockHttpClient(500);
@@ -264,7 +264,7 @@ public function test_request_sets_error_status_for_5xx_codes() : void
public function test_request_sets_ok_status_for_2xx_codes() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$innerClient = $this->createMockHttpClient(201);
@@ -282,7 +282,7 @@ public function test_request_sets_ok_status_for_2xx_codes() : void
public function test_request_sets_ok_status_for_3xx_codes() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$innerClient = $this->createMockHttpClient(302);
@@ -300,7 +300,7 @@ public function test_request_sets_ok_status_for_3xx_codes() : void
public function test_request_span_name_includes_method_and_host() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$innerClient = $this->createMockHttpClient(200);
@@ -315,7 +315,7 @@ public function test_request_span_name_includes_method_and_host() : void
public function test_span_kind_is_client() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$innerClient = $this->createMockHttpClient(200);
@@ -330,7 +330,7 @@ public function test_span_kind_is_client() : void
public function test_stream_delegates_to_inner_client() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$mockResponse = $this->createMock(ResponseInterface::class);
@@ -399,7 +399,7 @@ public function withOptions(array $options) : static
public function test_with_options_creates_new_instance() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$innerClient = $this->createMockHttpClient(200);
diff --git a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/Instrumentation/Twig/TracingTwigExtensionTest.php b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/Instrumentation/Twig/TracingTwigExtensionTest.php
index 181d3373f..cc7b8e00d 100644
--- a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/Instrumentation/Twig/TracingTwigExtensionTest.php
+++ b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/Instrumentation/Twig/TracingTwigExtensionTest.php
@@ -9,7 +9,7 @@
use Flow\Telemetry\Logger\LoggerProvider;
use Flow\Telemetry\Meter\MeterProvider;
use Flow\Telemetry\Provider\Clock\SystemClock;
-use Flow\Telemetry\Provider\Memory\{MemorySpanExporter, MemorySpanProcessor};
+use Flow\Telemetry\Provider\Memory\{MemoryExporter, MemorySpanProcessor};
use Flow\Telemetry\Provider\Void\{VoidLogProcessor, VoidMetricProcessor};
use Flow\Telemetry\{Resource, Telemetry};
use Flow\Telemetry\Tracer\{SpanKind, TracerProvider};
@@ -23,7 +23,7 @@ final class TracingTwigExtensionTest extends TestCase
{
public function test_excluded_template_does_not_trace_nested_blocks() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$extension = new TracingTwigExtension(
@@ -46,7 +46,7 @@ public function test_excluded_template_does_not_trace_nested_blocks() : void
public function test_get_node_visitors_returns_profiler_node_visitor() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$extension = new TracingTwigExtension($telemetry);
@@ -59,7 +59,7 @@ public function test_get_node_visitors_returns_profiler_node_visitor() : void
public function test_get_span_name_with_block_profile_returns_formatted_string() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$extension = new TracingTwigExtension($telemetry, traceTemplates: true, traceBlocks: true);
@@ -75,7 +75,7 @@ public function test_get_span_name_with_block_profile_returns_formatted_string()
public function test_get_span_name_with_macro_profile_returns_formatted_string() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$extension = new TracingTwigExtension($telemetry, traceTemplates: true, traceMacros: true);
@@ -91,7 +91,7 @@ public function test_get_span_name_with_macro_profile_returns_formatted_string()
public function test_get_span_name_with_root_profile_returns_profile_name() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$extension = new TracingTwigExtension($telemetry);
@@ -107,7 +107,7 @@ public function test_get_span_name_with_root_profile_returns_profile_name() : vo
public function test_get_span_name_with_template_profile_returns_template_path() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$extension = new TracingTwigExtension($telemetry, traceTemplates: true);
@@ -123,7 +123,7 @@ public function test_get_span_name_with_template_profile_returns_template_path()
public function test_is_template_excluded_exact_match() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$extension = new TracingTwigExtension(
@@ -141,7 +141,7 @@ public function test_is_template_excluded_exact_match() : void
public function test_is_template_excluded_no_match_returns_false() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$extension = new TracingTwigExtension(
@@ -161,7 +161,7 @@ public function test_is_template_excluded_no_match_returns_false() : void
public function test_is_template_excluded_regex_match() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$extension = new TracingTwigExtension(
@@ -179,7 +179,7 @@ public function test_is_template_excluded_regex_match() : void
public function test_should_trace_respects_block_flag() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$extension = new TracingTwigExtension($telemetry, traceTemplates: true, traceBlocks: false);
@@ -193,7 +193,7 @@ public function test_should_trace_respects_block_flag() : void
public function test_should_trace_respects_macro_flag() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$extension = new TracingTwigExtension($telemetry, traceTemplates: true, traceMacros: false);
@@ -207,7 +207,7 @@ public function test_should_trace_respects_macro_flag() : void
public function test_should_trace_respects_template_flag() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$extension = new TracingTwigExtension($telemetry, traceTemplates: false);
@@ -221,7 +221,7 @@ public function test_should_trace_respects_template_flag() : void
public function test_span_has_correct_attributes_for_block() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$extension = new TracingTwigExtension($telemetry, traceTemplates: true, traceBlocks: true);
@@ -241,7 +241,7 @@ public function test_span_has_correct_attributes_for_block() : void
public function test_span_has_correct_attributes_for_template() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$extension = new TracingTwigExtension($telemetry, traceTemplates: true);
@@ -261,7 +261,7 @@ public function test_span_has_correct_attributes_for_template() : void
public function test_span_kind_is_internal() : void
{
- $spanProcessor = new MemorySpanProcessor(new MemorySpanExporter());
+ $spanProcessor = new MemorySpanProcessor(new MemoryExporter());
$telemetry = $this->createTelemetry($spanProcessor);
$extension = new TracingTwigExtension($telemetry);
diff --git a/src/bridge/telemetry/otlp/.gitattributes b/src/bridge/telemetry/otlp/.gitattributes
index e02097205..ad7f6f096 100644
--- a/src/bridge/telemetry/otlp/.gitattributes
+++ b/src/bridge/telemetry/otlp/.gitattributes
@@ -7,3 +7,6 @@
/.gitattributes export-ignore
/.gitignore export-ignore
+
+/.opentelemetry-proto export-ignore
+/bin export-ignore
diff --git a/src/bridge/telemetry/otlp/.gitignore b/src/bridge/telemetry/otlp/.gitignore
new file mode 100644
index 000000000..7c1de6588
--- /dev/null
+++ b/src/bridge/telemetry/otlp/.gitignore
@@ -0,0 +1 @@
+/.opentelemetry-proto/
diff --git a/src/bridge/telemetry/otlp/bin/generate-proto.sh b/src/bridge/telemetry/otlp/bin/generate-proto.sh
new file mode 100755
index 000000000..164547ecd
--- /dev/null
+++ b/src/bridge/telemetry/otlp/bin/generate-proto.sh
@@ -0,0 +1,75 @@
+#!/usr/bin/env bash
+#
+# Generate PHP classes (protobuf messages + gRPC service stubs) for the OTLP
+# wire format from the upstream open-telemetry/opentelemetry-proto repository.
+#
+# Output:
+# src/Opentelemetry/Proto/...
+# src/GPBMetadata/Opentelemetry/...
+#
+# Run from anywhere; paths are anchored to the package root.
+#
+# Requires: protoc, grpc_php_plugin, git
+# In this repo: nix-shell --arg with-protoc true
+
+set -euo pipefail
+
+PROTO_REPO_URL="https://github.com/open-telemetry/opentelemetry-proto.git"
+PROTO_REPO_REF="${PROTO_REPO_REF:-v1.10.0}"
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+PACKAGE_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
+CLONE_DIR="${PACKAGE_ROOT}/.opentelemetry-proto"
+OUT_DIR="${PACKAGE_ROOT}/src"
+
+require_tool() {
+ if ! command -v "$1" >/dev/null 2>&1; then
+ echo "error: required tool '$1' not found on PATH" >&2
+ echo " enter the dev shell first: nix-shell --arg with-protoc true" >&2
+ exit 1
+ fi
+}
+
+require_tool protoc
+require_tool grpc_php_plugin
+require_tool git
+
+echo "==> opentelemetry-proto ref: ${PROTO_REPO_REF}"
+
+if [ -d "${CLONE_DIR}/.git" ]; then
+ echo "==> refreshing existing clone at ${CLONE_DIR}"
+ git -C "${CLONE_DIR}" fetch --tags --depth 1 origin "${PROTO_REPO_REF}"
+ git -C "${CLONE_DIR}" -c advice.detachedHead=false checkout "${PROTO_REPO_REF}"
+else
+ echo "==> cloning ${PROTO_REPO_URL} into ${CLONE_DIR}"
+ git clone --depth 1 --branch "${PROTO_REPO_REF}" "${PROTO_REPO_URL}" "${CLONE_DIR}"
+fi
+
+echo "==> wiping previously generated trees"
+rm -rf "${OUT_DIR}/Opentelemetry" "${OUT_DIR}/GPBMetadata"
+mkdir -p "${OUT_DIR}"
+
+mapfile -t PROTO_FILES < <(find "${CLONE_DIR}/opentelemetry/proto" -type f -name '*.proto' | sort)
+
+if [ "${#PROTO_FILES[@]}" -eq 0 ]; then
+ echo "error: no .proto files found under ${CLONE_DIR}/opentelemetry/proto" >&2
+ exit 1
+fi
+
+echo "==> compiling ${#PROTO_FILES[@]} .proto files"
+protoc \
+ --proto_path="${CLONE_DIR}" \
+ --php_out="${OUT_DIR}" \
+ --grpc_out="${OUT_DIR}" \
+ --plugin=protoc-gen-grpc="$(command -v grpc_php_plugin)" \
+ "${PROTO_FILES[@]}"
+
+PHP_COUNT=$(find "${OUT_DIR}/Opentelemetry" "${OUT_DIR}/GPBMetadata" -type f -name '*.php' | wc -l | tr -d ' ')
+
+echo
+echo "==> done"
+echo " ref: ${PROTO_REPO_REF}"
+echo " php files: ${PHP_COUNT}"
+echo " output root: ${OUT_DIR}"
+echo " top-level:"
+find "${OUT_DIR}/Opentelemetry" "${OUT_DIR}/GPBMetadata" -mindepth 0 -maxdepth 1 -type d | sed 's|^| |'
diff --git a/src/bridge/telemetry/otlp/composer.json b/src/bridge/telemetry/otlp/composer.json
index 308683fd8..a7ef86697 100644
--- a/src/bridge/telemetry/otlp/composer.json
+++ b/src/bridge/telemetry/otlp/composer.json
@@ -16,27 +16,28 @@
"license": "MIT",
"require": {
"php": "~8.3.0 || ~8.4.0 || ~8.5.0",
- "flow-php/telemetry": "self.version",
- "psr/http-client": "^1.0",
- "psr/http-factory": "^1.0"
+ "flow-php/telemetry": "self.version"
},
"require-dev": {
- "grpc/grpc": "^1.74",
- "open-telemetry/gen-otlp-protobuf": "^1.8",
+ "ext-curl" : "*",
"google/protobuf": "^4.0 || ^5.0",
+ "grpc/grpc": "^1.74",
"nyholm/psr7": "^1.8",
+ "psr/http-client": "^1.0",
+ "psr/http-factory": "^1.0",
"symfony/http-client": "^6.4 || ^7.4 || ^8.0"
},
"suggest": {
"ext-grpc": "Required for gRPC transport",
- "google/protobuf": "Required for gRPC transport with binary protobuf encoding",
- "open-telemetry/gen-otlp-protobuf": "Generated PHP classes for OTLP protobuf messages (required for gRPC transport)"
+ "google/protobuf": "Required for gRPC transport with binary protobuf encoding"
},
"autoload": {
"psr-4": {
"Flow\\": [
"src/Flow"
- ]
+ ],
+ "Opentelemetry\\Proto\\": "src/Opentelemetry/Proto/",
+ "GPBMetadata\\Opentelemetry\\": "src/GPBMetadata/Opentelemetry/"
},
"files": [
"src/Flow/Bridge/Telemetry/OTLP/DSL/functions.php"
diff --git a/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/DSL/functions.php b/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/DSL/functions.php
index 272a03dac..d90f20801 100644
--- a/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/DSL/functions.php
+++ b/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/DSL/functions.php
@@ -4,32 +4,29 @@
namespace Flow\Bridge\Telemetry\OTLP\DSL;
-use Flow\Bridge\Telemetry\OTLP\Exporter\{OTLPLogExporter, OTLPMetricExporter, OTLPSpanExporter};
+use Flow\Bridge\Telemetry\OTLP\Exporter\OTLPExporter;
use Flow\Bridge\Telemetry\OTLP\Serializer\{JsonSerializer, ProtobufSerializer};
-use Flow\Bridge\Telemetry\OTLP\Transport\{CurlTransport, CurlTransportOptions, GrpcTransport, HttpTransport};
+use Flow\Bridge\Telemetry\OTLP\Transport\{CurlTransport, CurlTransportOptions, GrpcTransport, StreamTransport, Transport};
use Flow\ETL\Attribute\{DocumentationDSL, Module, Type as DSLType};
use Flow\Telemetry\Context\{ContextStorage, MemoryContextStorage};
+use Flow\Telemetry\ErrorHandler\{ErrorHandler, ErrorLogHandler};
use Flow\Telemetry\Logger\{LogProcessor, LoggerProvider};
use Flow\Telemetry\Meter\{AggregationTemporality, MetricProcessor};
use Flow\Telemetry\Meter\MeterProvider;
-use Flow\Telemetry\Serializer\Serializer;
use Flow\Telemetry\Tracer\Sampler\{AlwaysOnSampler, Sampler};
use Flow\Telemetry\Tracer\{SpanProcessor, TracerProvider};
-use Flow\Telemetry\Transport\Transport;
use Psr\Clock\ClockInterface;
-use Psr\Http\Client\ClientInterface;
-use Psr\Http\Message\{RequestFactoryInterface, StreamFactoryInterface};
/**
* Create a JSON serializer for OTLP.
*
* Returns a JsonSerializer that converts telemetry data to OTLP JSON wire format.
- * Use this with HttpTransport for JSON over HTTP.
+ * Use this with CurlTransport for JSON over HTTP.
*
* Example usage:
* ```php
* $serializer = otlp_json_serializer();
- * $transport = otlp_http_transport($client, $reqFactory, $streamFactory, $endpoint, $serializer);
+ * $transport = otlp_curl_transport($endpoint, $serializer);
* ```
*/
#[DocumentationDSL(module: Module::TELEMETRY_OTLP, type: DSLType::HELPER)]
@@ -42,16 +39,15 @@ function otlp_json_serializer() : JsonSerializer
* Create a Protobuf serializer for OTLP.
*
* Returns a ProtobufSerializer that converts telemetry data to OTLP Protobuf binary format.
- * Use this with HttpTransport for Protobuf over HTTP, or with GrpcTransport.
+ * Use this with CurlTransport for Protobuf over HTTP, or with GrpcTransport.
*
* Requires:
* - google/protobuf package
- * - open-telemetry/gen-otlp-protobuf package
*
* Example usage:
* ```php
* $serializer = otlp_protobuf_serializer();
- * $transport = otlp_http_transport($client, $reqFactory, $streamFactory, $endpoint, $serializer);
+ * $transport = otlp_curl_transport($endpoint, $serializer);
* ```
*/
#[DocumentationDSL(module: Module::TELEMETRY_OTLP, type: DSLType::HELPER)]
@@ -60,103 +56,38 @@ function otlp_protobuf_serializer() : ProtobufSerializer
return new ProtobufSerializer();
}
-/**
- * Create an HTTP transport for OTLP endpoints.
- *
- * Creates an HttpTransport configured to send telemetry data to an OTLP-compatible
- * endpoint using PSR-18 HTTP client. Supports both JSON and Protobuf formats.
- *
- * Example usage:
- * ```php
- * // JSON over HTTP
- * $transport = otlp_http_transport(
- * client: $client,
- * requestFactory: $psr17Factory,
- * streamFactory: $psr17Factory,
- * endpoint: 'http://localhost:4318',
- * serializer: otlp_json_serializer(),
- * );
- *
- * // Protobuf over HTTP
- * $transport = otlp_http_transport(
- * client: $client,
- * requestFactory: $psr17Factory,
- * streamFactory: $psr17Factory,
- * endpoint: 'http://localhost:4318',
- * serializer: otlp_protobuf_serializer(),
- * );
- * ```
- *
- * @param ClientInterface $client PSR-18 HTTP client
- * @param RequestFactoryInterface $requestFactory PSR-17 request factory
- * @param StreamFactoryInterface $streamFactory PSR-17 stream factory
- * @param string $endpoint OTLP endpoint URL (e.g., 'http://localhost:4318')
- * @param Serializer $serializer Serializer for encoding telemetry data (JSON or Protobuf)
- * @param array $headers Additional headers to include in requests
- */
-#[DocumentationDSL(module: Module::TELEMETRY_OTLP, type: DSLType::HELPER)]
-function otlp_http_transport(
- ClientInterface $client,
- RequestFactoryInterface $requestFactory,
- StreamFactoryInterface $streamFactory,
- string $endpoint,
- Serializer $serializer,
- array $headers = [],
-) : HttpTransport {
- return new HttpTransport($client, $requestFactory, $streamFactory, $endpoint, $serializer, $headers);
-}
-
/**
* Create a gRPC transport for OTLP endpoints.
*
* Creates a GrpcTransport configured to send telemetry data to an OTLP-compatible
- * endpoint using gRPC protocol with Protobuf serialization.
+ * endpoint using gRPC protocol with Protobuf serialization. OTLP/gRPC mandates
+ * Protobuf, so the serializer is built internally and not configurable.
*
* Requires:
* - ext-grpc PHP extension
* - google/protobuf package
- * - open-telemetry/gen-otlp-protobuf package
- *
- * Example usage:
- * ```php
- * $transport = otlp_grpc_transport(
- * endpoint: 'localhost:4317',
- * serializer: otlp_protobuf_serializer(),
- * );
- * ```
*
* @param string $endpoint gRPC endpoint (e.g., 'localhost:4317')
- * @param ProtobufSerializer $serializer Protobuf serializer for encoding telemetry data
* @param array $headers Additional headers (metadata) to include in requests
* @param bool $insecure Whether to use insecure channel credentials (default true for local dev)
+ * @param int $timeoutMs Per-call deadline in milliseconds (covers connect + send + receive)
+ * @param int $shutdownTimeoutMs Wall-clock budget for draining pending calls at shutdown
+ * @param ?Transport $failover Optional failover transport receiving prior batches when primary fails
*/
#[DocumentationDSL(module: Module::TELEMETRY_OTLP, type: DSLType::HELPER)]
function otlp_grpc_transport(
string $endpoint,
- ProtobufSerializer $serializer,
array $headers = [],
bool $insecure = true,
-) : GrpcTransport {
- return new GrpcTransport($endpoint, $serializer, $headers, $insecure);
+ int $timeoutMs = GrpcTransport::DEFAULT_TIMEOUT_MS,
+ int $shutdownTimeoutMs = GrpcTransport::DEFAULT_SHUTDOWN_TIMEOUT_MS,
+ ?Transport $failover = null,
+) : Transport {
+ return new GrpcTransport($endpoint, $headers, $insecure, $timeoutMs, $shutdownTimeoutMs, $failover);
}
/**
* Create curl transport options for OTLP.
- *
- * Returns a CurlTransportOptions builder for configuring curl transport settings
- * using a fluent interface.
- *
- * Example usage:
- * ```php
- * $options = otlp_curl_options()
- * ->withTimeout(60)
- * ->withConnectTimeout(15)
- * ->withHeader('Authorization', 'Bearer token')
- * ->withCompression()
- * ->withSslVerification(verifyPeer: true);
- *
- * $transport = otlp_curl_transport($endpoint, $serializer, $options);
- * ```
*/
#[DocumentationDSL(module: Module::TELEMETRY_OTLP, type: DSLType::HELPER)]
function otlp_curl_options() : CurlTransportOptions
@@ -168,105 +99,71 @@ function otlp_curl_options() : CurlTransportOptions
* Create an async curl transport for OTLP endpoints.
*
* Creates a CurlTransport that uses curl_multi for non-blocking I/O.
- * Unlike HttpTransport (PSR-18), this transport queues requests and executes
- * them asynchronously. Completed requests are processed on subsequent send()
- * calls or on shutdown().
+ * Requests are queued and executed asynchronously. OTLP/HTTP allows JSON
+ * or Protobuf encoding; defaults to JSON.
*
* Requires: ext-curl PHP extension
*
- * Example usage:
- * ```php
- * // JSON over HTTP (async) with default options
- * $transport = otlp_curl_transport(
- * endpoint: 'http://localhost:4318',
- * serializer: otlp_json_serializer(),
- * );
- *
- * // Protobuf over HTTP (async) with custom options
- * $transport = otlp_curl_transport(
- * endpoint: 'http://localhost:4318',
- * serializer: otlp_protobuf_serializer(),
- * options: otlp_curl_options()
- * ->withTimeout(60)
- * ->withHeader('Authorization', 'Bearer token')
- * ->withCompression(),
- * );
- * ```
- *
* @param string $endpoint OTLP endpoint URL (e.g., 'http://localhost:4318')
- * @param Serializer $serializer Serializer for encoding telemetry data (JSON or Protobuf)
+ * @param JsonSerializer|ProtobufSerializer $serializer Serializer for encoding telemetry data (JSON or Protobuf)
* @param CurlTransportOptions $options Transport configuration options
+ * @param ?Transport $failover Optional failover transport receiving prior batches when primary fails
*/
#[DocumentationDSL(module: Module::TELEMETRY_OTLP, type: DSLType::HELPER)]
function otlp_curl_transport(
string $endpoint,
- Serializer $serializer,
+ JsonSerializer|ProtobufSerializer $serializer = new JsonSerializer(),
CurlTransportOptions $options = new CurlTransportOptions(),
-) : CurlTransport {
- return new CurlTransport($endpoint, $serializer, $options);
+ ?Transport $failover = null,
+) : Transport {
+ return new CurlTransport($endpoint, $serializer, $options, $failover);
}
/**
- * Create an OTLP span exporter.
+ * Create a stream transport for OTLP that writes JSONL to a single destination.
*
- * Example usage:
- * ```php
- * $exporter = otlp_span_exporter($transport);
- * $processor = batching_span_processor($exporter);
- * ```
+ * Accepts an absolute file path or a php:// stream wrapper such as
+ * 'php://stdout', 'php://stderr', 'php://memory', or 'php://temp'. Each
+ * export() call appends one JSON Line under LOCK_EX so concurrent writers
+ * interleave at line boundaries. The $filePermissions and $createDirectories
+ * parameters apply only when the destination is a file path.
*
- * @param Transport $transport The transport for sending span data
+ * Per the OTLP File Exporter spec only JSON encoding is supported.
*/
#[DocumentationDSL(module: Module::TELEMETRY_OTLP, type: DSLType::HELPER)]
-function otlp_span_exporter(Transport $transport) : OTLPSpanExporter
-{
- return new OTLPSpanExporter($transport);
+function otlp_stream_transport(
+ string $destination,
+ int $filePermissions = 0644,
+ bool $createDirectories = true,
+) : Transport {
+ return new StreamTransport($destination, $filePermissions, $createDirectories);
}
/**
- * Create an OTLP metric exporter.
+ * Create an OTLP exporter that dispatches logs, metrics, and spans through a single transport.
*
* Example usage:
* ```php
- * $exporter = otlp_metric_exporter($transport);
- * $processor = batching_metric_processor($exporter);
+ * $exporter = otlp_exporter($transport);
+ * $spanProcessor = batching_span_processor($exporter);
+ * $metricProcessor = batching_metric_processor($exporter);
+ * $logProcessor = batching_log_processor($exporter);
* ```
*
- * @param Transport $transport The transport for sending metric data
+ * @param Transport $transport The transport for sending telemetry data
+ * @param ErrorHandler $errorHandler Handler for Throwables raised by the transport
*/
#[DocumentationDSL(module: Module::TELEMETRY_OTLP, type: DSLType::HELPER)]
-function otlp_metric_exporter(Transport $transport) : OTLPMetricExporter
-{
- return new OTLPMetricExporter($transport);
-}
-
-/**
- * Create an OTLP log exporter.
- *
- * Example usage:
- * ```php
- * $exporter = otlp_log_exporter($transport);
- * $processor = batching_log_processor($exporter);
- * ```
- *
- * @param Transport $transport The transport for sending log data
- */
-#[DocumentationDSL(module: Module::TELEMETRY_OTLP, type: DSLType::HELPER)]
-function otlp_log_exporter(Transport $transport) : OTLPLogExporter
-{
- return new OTLPLogExporter($transport);
+function otlp_exporter(
+ Transport $transport,
+ ErrorHandler $errorHandler = new ErrorLogHandler(),
+) : OTLPExporter {
+ return new OTLPExporter($transport, $errorHandler);
}
/**
* Create a tracer provider configured for OTLP export.
*
- * Example usage:
- * ```php
- * $processor = batching_span_processor(otlp_span_exporter($transport));
- * $provider = otlp_tracer_provider($processor, $clock);
- * $tracer = $provider->tracer($resource, 'my-service', '1.0.0');
- * ```
- *
* @param SpanProcessor $processor The processor for handling spans
* @param ClockInterface $clock The clock for timestamps
* @param Sampler $sampler The sampler for deciding whether to record spans
@@ -285,13 +182,6 @@ function otlp_tracer_provider(
/**
* Create a meter provider configured for OTLP export.
*
- * Example usage:
- * ```php
- * $processor = batching_metric_processor(otlp_metric_exporter($transport));
- * $provider = otlp_meter_provider($processor, $clock);
- * $meter = $provider->meter($resource, 'my-service', '1.0.0');
- * ```
- *
* @param MetricProcessor $processor The processor for handling metrics
* @param ClockInterface $clock The clock for timestamps
* @param AggregationTemporality $temporality The aggregation temporality for metrics
@@ -308,13 +198,6 @@ function otlp_meter_provider(
/**
* Create a logger provider configured for OTLP export.
*
- * Example usage:
- * ```php
- * $processor = batching_log_processor(otlp_log_exporter($transport));
- * $provider = otlp_logger_provider($processor, $clock);
- * $logger = $provider->logger($resource, 'my-service', '1.0.0');
- * ```
- *
* @param LogProcessor $processor The processor for handling log records
* @param ClockInterface $clock The clock for timestamps
* @param ContextStorage $contextStorage The context storage for propagating context
diff --git a/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/Exception/Exception.php b/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/Exception/Exception.php
deleted file mode 100644
index 1ae0213dc..000000000
--- a/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/Exception/Exception.php
+++ /dev/null
@@ -1,9 +0,0 @@
-count() === 0) {
+ return true;
+ }
+
+ try {
+ $this->transport->send($signal);
+
+ return true;
+ } catch (\Throwable $e) {
+ $this->errorHandler->handle($e);
+
+ return false;
+ }
+ }
+
+ public function shutdown() : void
+ {
+ try {
+ $this->transport->shutdown();
+ } catch (\Throwable $e) {
+ $this->errorHandler->handle($e);
+ }
+ }
+}
diff --git a/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/Exporter/OTLPLogExporter.php b/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/Exporter/OTLPLogExporter.php
deleted file mode 100644
index a207ca9be..000000000
--- a/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/Exporter/OTLPLogExporter.php
+++ /dev/null
@@ -1,54 +0,0 @@
-export($entries);
- * ```
- */
-final readonly class OTLPLogExporter implements LogExporter
-{
- public function __construct(
- private Transport $transport,
- ) {
- }
-
- /**
- * @param array $entries
- */
- public function export(array $entries) : bool
- {
- if (\count($entries) === 0) {
- return true;
- }
-
- try {
- $this->transport->sendLogs($entries);
-
- return true;
- } catch (\Throwable) {
- return false;
- }
- }
-
- /**
- * @return array
- */
- public function transports() : array
- {
- return [$this->transport];
- }
-}
diff --git a/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/Exporter/OTLPMetricExporter.php b/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/Exporter/OTLPMetricExporter.php
deleted file mode 100644
index 6610bb5f7..000000000
--- a/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/Exporter/OTLPMetricExporter.php
+++ /dev/null
@@ -1,54 +0,0 @@
-export($metrics);
- * ```
- */
-final readonly class OTLPMetricExporter implements MetricExporter
-{
- public function __construct(
- private Transport $transport,
- ) {
- }
-
- /**
- * @param array $metrics
- */
- public function export(array $metrics) : bool
- {
- if (\count($metrics) === 0) {
- return true;
- }
-
- try {
- $this->transport->sendMetrics($metrics);
-
- return true;
- } catch (\Throwable) {
- return false;
- }
- }
-
- /**
- * @return array
- */
- public function transports() : array
- {
- return [$this->transport];
- }
-}
diff --git a/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/Exporter/OTLPSpanExporter.php b/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/Exporter/OTLPSpanExporter.php
deleted file mode 100644
index ca48f9b86..000000000
--- a/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/Exporter/OTLPSpanExporter.php
+++ /dev/null
@@ -1,54 +0,0 @@
-export($spans);
- * ```
- */
-final readonly class OTLPSpanExporter implements SpanExporter
-{
- public function __construct(
- private Transport $transport,
- ) {
- }
-
- /**
- * @param array $spans
- */
- public function export(array $spans) : bool
- {
- if (\count($spans) === 0) {
- return true;
- }
-
- try {
- $this->transport->sendSpans($spans);
-
- return true;
- } catch (\Throwable) {
- return false;
- }
- }
-
- /**
- * @return array
- */
- public function transports() : array
- {
- return [$this->transport];
- }
-}
diff --git a/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/Serializer/GrpcSerializer.php b/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/Serializer/GrpcRequestFactory.php
similarity index 80%
rename from src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/Serializer/GrpcSerializer.php
rename to src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/Serializer/GrpcRequestFactory.php
index bc71f7194..f264dfab4 100644
--- a/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/Serializer/GrpcSerializer.php
+++ b/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/Serializer/GrpcRequestFactory.php
@@ -12,12 +12,12 @@
use Opentelemetry\Proto\Collector\Trace\V1\ExportTraceServiceRequest;
/**
- * Interface for gRPC serializers that create OTLP protobuf request objects.
+ * Builds OTLP protobuf request messages for gRPC transmission.
*
- * Unlike the Serializer interface which returns string payloads,
- * this interface returns protobuf request objects for direct gRPC transmission.
+ * Implementations return populated protobuf request objects that the gRPC
+ * transport hands directly to the generated service stubs.
*/
-interface GrpcSerializer
+interface GrpcRequestFactory
{
/**
* Create an ExportLogsServiceRequest for gRPC transport.
diff --git a/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/Serializer/JsonSerializer.php b/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/Serializer/JsonSerializer.php
index cf02c7c58..f237ea1ad 100644
--- a/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/Serializer/JsonSerializer.php
+++ b/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/Serializer/JsonSerializer.php
@@ -7,7 +7,6 @@
use Flow\Telemetry\{InstrumentationScope, Resource};
use Flow\Telemetry\Logger\LogEntry;
use Flow\Telemetry\Meter\{Exemplar, Metric, MetricType};
-use Flow\Telemetry\Serializer\Serializer;
use Flow\Telemetry\Tracer\{Span, SpanEvent, SpanKind, SpanLink, SpanStatusCode};
/**
@@ -17,7 +16,7 @@
*
* @see https://opentelemetry.io/docs/specs/otlp/#otlphttp-request
*/
-final class JsonSerializer implements Serializer
+final class JsonSerializer
{
/**
* @param array $entries
diff --git a/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/Serializer/ProtobufSerializer.php b/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/Serializer/ProtobufSerializer.php
index 8e981e4b3..f348b3f26 100644
--- a/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/Serializer/ProtobufSerializer.php
+++ b/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/Serializer/ProtobufSerializer.php
@@ -7,7 +7,6 @@
use Flow\Telemetry\{InstrumentationScope, Resource};
use Flow\Telemetry\Logger\{LogEntry, Severity};
use Flow\Telemetry\Meter\{Exemplar, Metric, MetricType};
-use Flow\Telemetry\Serializer\Serializer;
use Flow\Telemetry\Tracer\{Span, SpanEvent, SpanKind, SpanLink, SpanStatusCode};
use Opentelemetry\Proto\Collector\Logs\V1\ExportLogsServiceRequest;
use Opentelemetry\Proto\Collector\Metrics\V1\ExportMetricsServiceRequest;
@@ -22,14 +21,11 @@
use Opentelemetry\Proto\Trace\V1\Status\StatusCode;
/**
- * Protobuf serializer for OTLP wire format.
- *
- * Converts Flow Telemetry objects to OTLP Protobuf binary format.
- * Requires the grpc extension and open-telemetry/gen-otlp-protobuf package.
+ * Serializes Flow Telemetry objects to OTLP Protobuf wire format.
*
* @see https://opentelemetry.io/docs/specs/otlp/
*/
-final class ProtobufSerializer implements GrpcSerializer, Serializer
+final class ProtobufSerializer implements GrpcRequestFactory
{
public function __construct()
{
@@ -39,13 +35,6 @@ public function __construct()
. 'Install it via: composer require google/protobuf'
);
}
-
- if (!\class_exists('Opentelemetry\Proto\Collector\Trace\V1\ExportTraceServiceRequest')) {
- throw new \RuntimeException(
- 'The open-telemetry/gen-otlp-protobuf package is required for ProtobufSerializer. '
- . 'Install it via: composer require open-telemetry/gen-otlp-protobuf'
- );
- }
}
/**
@@ -74,15 +63,15 @@ public function createLogsRequest(array $entries) : ExportLogsServiceRequest
$logRecords[] = $this->createLogRecord($entry);
}
- $scopeLogs->setLogRecords($logRecords); // @phpstan-ignore argument.type
+ $scopeLogs->setLogRecords($logRecords);
$scopeLogsList[] = $scopeLogs;
}
- $resourceLogs->setScopeLogs($scopeLogsList); // @phpstan-ignore argument.type
+ $resourceLogs->setScopeLogs($scopeLogsList);
$resourceLogsList[] = $resourceLogs;
}
- $request->setResourceLogs($resourceLogsList); // @phpstan-ignore argument.type
+ $request->setResourceLogs($resourceLogsList);
return $request;
}
@@ -113,15 +102,15 @@ public function createMetricsRequest(array $metrics) : ExportMetricsServiceReque
$protoMetrics[] = $this->createProtoMetric($metric);
}
- $scopeMetrics->setMetrics($protoMetrics); // @phpstan-ignore argument.type
+ $scopeMetrics->setMetrics($protoMetrics);
$scopeMetricsList[] = $scopeMetrics;
}
- $resourceMetrics->setScopeMetrics($scopeMetricsList); // @phpstan-ignore argument.type
+ $resourceMetrics->setScopeMetrics($scopeMetricsList);
$resourceMetricsList[] = $resourceMetrics;
}
- $request->setResourceMetrics($resourceMetricsList); // @phpstan-ignore argument.type
+ $request->setResourceMetrics($resourceMetricsList);
return $request;
}
@@ -152,15 +141,15 @@ public function createSpansRequest(array $spans) : ExportTraceServiceRequest
$protoSpans[] = $this->createProtoSpan($span);
}
- $scopeSpans->setSpans($protoSpans); // @phpstan-ignore argument.type
+ $scopeSpans->setSpans($protoSpans);
$scopeSpansList[] = $scopeSpans;
}
- $resourceSpans->setScopeSpans($scopeSpansList); // @phpstan-ignore argument.type
+ $resourceSpans->setScopeSpans($scopeSpansList);
$resourceSpansList[] = $resourceSpans;
}
- $request->setResourceSpans($resourceSpansList); // @phpstan-ignore argument.type
+ $request->setResourceSpans($resourceSpansList);
return $request;
}
@@ -212,7 +201,7 @@ private function createAnyValue(string|int|float|bool|array $value) : AnyValue
$values[] = $this->createAnyValue($v);
}
- $arrayValue->setValues($values); // @phpstan-ignore argument.type
+ $arrayValue->setValues($values);
$anyValue->setArrayValue($arrayValue);
}
@@ -253,11 +242,11 @@ private function createHistogramDataPoint(Metric $metric) : HistogramDataPoint
\ARRAY_FILTER_USE_KEY,
);
- $dataPoint->setAttributes($this->createKeyValues($userAttributes)); // @phpstan-ignore argument.type
+ $dataPoint->setAttributes($this->createKeyValues($userAttributes));
$dataPoint->setCount($count);
$dataPoint->setSum(\is_int($sum) ? (float) $sum : $sum);
- $dataPoint->setBucketCounts($bucketCounts); // @phpstan-ignore argument.type
- $dataPoint->setExplicitBounds(\array_map(static fn (int|float $b) : float => (float) $b, $explicitBounds)); // @phpstan-ignore argument.type
+ $dataPoint->setBucketCounts($bucketCounts);
+ $dataPoint->setExplicitBounds(\array_map(static fn (int|float $b) : float => (float) $b, $explicitBounds));
if ($min !== null) {
$dataPoint->setMin(\is_int($min) ? (float) $min : $min);
@@ -274,7 +263,7 @@ private function createHistogramDataPoint(Metric $metric) : HistogramDataPoint
$exemplars[] = $this->createProtoExemplar($exemplar);
}
- $dataPoint->setExemplars($exemplars); // @phpstan-ignore argument.type
+ $dataPoint->setExemplars($exemplars);
}
return $dataPoint;
@@ -314,7 +303,7 @@ private function createLogRecord(LogEntry $entry) : LogRecord
$body->setStringValue($entry->record->body);
$logRecord->setBody($body);
- $logRecord->setAttributes($this->createKeyValues($entry->record->attributes->normalize())); // @phpstan-ignore argument.type
+ $logRecord->setAttributes($this->createKeyValues($entry->record->attributes->normalize()));
if ($entry->spanContext !== null && $entry->spanContext->isValid()) {
$logRecord->setTraceId(\hex2bin($entry->spanContext->traceId->toHex()) ?: '');
@@ -335,7 +324,7 @@ private function createNumberDataPoint(Metric $metric) : NumberDataPoint
$dataPoint->setStartTimeUnixNano($timestamp);
$dataPoint->setTimeUnixNano($timestamp);
- $dataPoint->setAttributes($this->createKeyValues($metric->attributes->normalize())); // @phpstan-ignore argument.type
+ $dataPoint->setAttributes($this->createKeyValues($metric->attributes->normalize()));
if (\is_int($metric->value)) {
$dataPoint->setAsInt($metric->value);
@@ -350,7 +339,7 @@ private function createNumberDataPoint(Metric $metric) : NumberDataPoint
$exemplars[] = $this->createProtoExemplar($exemplar);
}
- $dataPoint->setExemplars($exemplars); // @phpstan-ignore argument.type
+ $dataPoint->setExemplars($exemplars);
}
return $dataPoint;
@@ -369,7 +358,7 @@ private function createProtoExemplar(Exemplar $exemplar) : ProtoExemplar
$protoExemplar->setSpanId(\hex2bin($exemplar->spanId->toHex()) ?: '');
}
- $protoExemplar->setFilteredAttributes($this->createKeyValues($exemplar->filteredAttributes)); // @phpstan-ignore argument.type
+ $protoExemplar->setFilteredAttributes($this->createKeyValues($exemplar->filteredAttributes));
if (\is_int($exemplar->value)) {
$protoExemplar->setAsInt($exemplar->value);
@@ -404,7 +393,7 @@ private function createProtoMetric(Metric $metric) : ProtoMetric
private function createProtoResource(Resource $resource) : ProtoResource
{
$protoResource = new ProtoResource();
- $protoResource->setAttributes($this->createKeyValues($resource->all())); // @phpstan-ignore argument.type
+ $protoResource->setAttributes($this->createKeyValues($resource->all()));
return $protoResource;
}
@@ -419,7 +408,7 @@ private function createProtoScope(InstrumentationScope $scope) : ProtoInstrument
}
if ($scope->attributes->count() > 0) {
- $protoScope->setAttributes($this->createKeyValues($scope->attributes->normalize())); // @phpstan-ignore argument.type
+ $protoScope->setAttributes($this->createKeyValues($scope->attributes->normalize()));
}
return $protoScope;
@@ -435,16 +424,14 @@ private function createProtoSpan(Span $span) : ProtoSpan
$protoSpan->setName($span->name());
$protoSpan->setKind($this->mapSpanKind($span->kind()));
$protoSpan->setStartTimeUnixNano($this->toNanoseconds($span->startTime()));
- $protoSpan->setAttributes($this->createKeyValues($span->attributes())); // @phpstan-ignore argument.type
-
+ $protoSpan->setAttributes($this->createKeyValues($span->attributes()));
$events = [];
foreach ($span->events() as $event) {
$events[] = $this->createSpanEvent($event);
}
- $protoSpan->setEvents($events); // @phpstan-ignore argument.type
-
+ $protoSpan->setEvents($events);
$links = [];
foreach ($span->links() as $link) {
@@ -455,8 +442,7 @@ private function createProtoSpan(Span $span) : ProtoSpan
}
}
- $protoSpan->setLinks($links); // @phpstan-ignore argument.type
-
+ $protoSpan->setLinks($links);
$protoSpan->setStatus($this->createSpanStatus($span));
if ($context->parentSpanId !== null) {
@@ -481,7 +467,7 @@ private function createSpanEvent(SpanEvent $event) : Event
$protoEvent = new Event();
$protoEvent->setName($event->name());
$protoEvent->setTimeUnixNano($this->toNanoseconds($event->timestamp()));
- $protoEvent->setAttributes($this->createKeyValues($event->attributes())); // @phpstan-ignore argument.type
+ $protoEvent->setAttributes($this->createKeyValues($event->attributes()));
return $protoEvent;
}
@@ -495,7 +481,7 @@ private function createSpanLink(SpanLink $link) : ?Link
$protoLink = new Link();
$protoLink->setTraceId(\hex2bin($link->context->traceId->toHex()) ?: '');
$protoLink->setSpanId(\hex2bin($link->context->spanId->toHex()) ?: '');
- $protoLink->setAttributes($this->createKeyValues($link->attributes->normalize())); // @phpstan-ignore argument.type
+ $protoLink->setAttributes($this->createKeyValues($link->attributes->normalize()));
return $protoLink;
}
@@ -537,14 +523,10 @@ private function groupLogsByResource(array $entries) : array
foreach ($entries as $entry) {
$key = $this->resourceKey($entry->resource);
-
- if (!isset($grouped[$key])) {
- $grouped[$key] = [
- 'resource' => $entry->resource,
- 'entries' => [],
- ];
- }
-
+ $grouped[$key] ??= [
+ 'resource' => $entry->resource,
+ 'entries' => [],
+ ];
$grouped[$key]['entries'][] = $entry;
}
@@ -565,14 +547,10 @@ private function groupLogsByScope(array $entries) : array
foreach ($entries as $entry) {
$scope = $entry->scope;
$key = $scope->name . '@' . $scope->version;
-
- if (!isset($grouped[$key])) {
- $grouped[$key] = [
- 'scope' => $scope,
- 'entries' => [],
- ];
- }
-
+ $grouped[$key] ??= [
+ 'scope' => $scope,
+ 'entries' => [],
+ ];
$grouped[$key]['entries'][] = $entry;
}
@@ -592,14 +570,10 @@ private function groupMetricsByResource(array $metrics) : array
foreach ($metrics as $metric) {
$key = $this->resourceKey($metric->resource);
-
- if (!isset($grouped[$key])) {
- $grouped[$key] = [
- 'resource' => $metric->resource,
- 'metrics' => [],
- ];
- }
-
+ $grouped[$key] ??= [
+ 'resource' => $metric->resource,
+ 'metrics' => [],
+ ];
$grouped[$key]['metrics'][] = $metric;
}
@@ -620,14 +594,10 @@ private function groupMetricsByScope(array $metrics) : array
foreach ($metrics as $metric) {
$scope = $metric->scope;
$key = $scope->name . '@' . $scope->version;
-
- if (!isset($grouped[$key])) {
- $grouped[$key] = [
- 'scope' => $scope,
- 'metrics' => [],
- ];
- }
-
+ $grouped[$key] ??= [
+ 'scope' => $scope,
+ 'metrics' => [],
+ ];
$grouped[$key]['metrics'][] = $metric;
}
@@ -647,14 +617,10 @@ private function groupSpansByResource(array $spans) : array
foreach ($spans as $span) {
$key = $this->resourceKey($span->resource());
-
- if (!isset($grouped[$key])) {
- $grouped[$key] = [
- 'resource' => $span->resource(),
- 'spans' => [],
- ];
- }
-
+ $grouped[$key] ??= [
+ 'resource' => $span->resource(),
+ 'spans' => [],
+ ];
$grouped[$key]['spans'][] = $span;
}
@@ -675,14 +641,10 @@ private function groupSpansByScope(array $spans) : array
foreach ($spans as $span) {
$scope = $span->scope();
$key = $scope->name . '@' . $scope->version;
-
- if (!isset($grouped[$key])) {
- $grouped[$key] = [
- 'scope' => $scope,
- 'spans' => [],
- ];
- }
-
+ $grouped[$key] ??= [
+ 'scope' => $scope,
+ 'spans' => [],
+ ];
$grouped[$key]['spans'][] = $span;
}
@@ -736,8 +698,7 @@ private function setCounterData(ProtoMetric $protoMetric, Metric $metric, bool $
$sum->setAggregationTemporality($this->mapTemporality($metric));
$dataPoint = $this->createNumberDataPoint($metric);
- $sum->setDataPoints([$dataPoint]); // @phpstan-ignore argument.type
-
+ $sum->setDataPoints([$dataPoint]);
$protoMetric->setSum($sum);
return $protoMetric;
@@ -748,8 +709,7 @@ private function setGaugeData(ProtoMetric $protoMetric, Metric $metric) : ProtoM
$gauge = new Gauge();
$dataPoint = $this->createNumberDataPoint($metric);
- $gauge->setDataPoints([$dataPoint]); // @phpstan-ignore argument.type
-
+ $gauge->setDataPoints([$dataPoint]);
$protoMetric->setGauge($gauge);
return $protoMetric;
@@ -761,8 +721,7 @@ private function setHistogramData(ProtoMetric $protoMetric, Metric $metric) : Pr
$histogram->setAggregationTemporality($this->mapTemporality($metric));
$dataPoint = $this->createHistogramDataPoint($metric);
- $histogram->setDataPoints([$dataPoint]); // @phpstan-ignore argument.type
-
+ $histogram->setDataPoints([$dataPoint]);
$protoMetric->setHistogram($histogram);
return $protoMetric;
diff --git a/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/Transport/CurlTransport.php b/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/Transport/CurlTransport.php
index 4336f3f2f..03f47efd7 100644
--- a/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/Transport/CurlTransport.php
+++ b/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/Transport/CurlTransport.php
@@ -4,56 +4,37 @@
namespace Flow\Bridge\Telemetry\OTLP\Transport;
-use Flow\Bridge\Telemetry\OTLP\Serializer\JsonSerializer;
-use Flow\Telemetry\Logger\LogEntry;
-use Flow\Telemetry\Meter\Metric;
-use Flow\Telemetry\Serializer\Serializer;
-use Flow\Telemetry\Tracer\Span;
-use Flow\Telemetry\Transport\{Transport, TransportException};
+use Flow\Bridge\Telemetry\OTLP\Serializer\{JsonSerializer, ProtobufSerializer};
+use Flow\Telemetry\Signal\{SignalType, Signals};
/**
- * Asynchronous HTTP transport for OTLP using curl_multi.
+ * Asynchronous HTTP transport for OTLP using curl_multi for non-blocking I/O.
+ * Requests are queued and executed asynchronously, with results processed on
+ * subsequent send() calls or on shutdown().
*
- * Unlike HttpTransport (PSR-18), this transport uses curl_multi for non-blocking I/O.
- * Requests are queued and executed asynchronously, with results processed on subsequent
- * send() calls or on shutdown().
- *
- * Example usage:
- * ```php
- * $transport = new CurlTransport(
- * endpoint: 'http://localhost:4318',
- * serializer: new JsonSerializer(),
- * options: otlp_curl_options()
- * ->withTimeout(60)
- * ->withHeader('Authorization', 'Bearer token'),
- * );
- *
- * // Sends are non-blocking
- * $transport->sendSpans($spans);
- * $transport->sendMetrics($metrics);
- *
- * // Block until all pending sends complete
- * $transport->shutdown();
- * ```
+ * When $failover is set, prior failed batches are forwarded to it on the next
+ * send()/shutdown(); a FailoverTransportException is then thrown.
*/
final class CurlTransport implements Transport
{
+ /** @var list */
+ private array $deferredFailures = [];
+
+ /** @var list<\Throwable> */
+ private array $failures = [];
+
private bool $isShutdown = false;
private readonly \CurlMultiHandle $multiHandle;
- /** @var array */
- private array $pendingHandles = [];
+ /** @var array */
+ private array $pending = [];
- /**
- * @param string $endpoint Base OTLP HTTP endpoint URL (e.g., 'http://localhost:4318')
- * @param Serializer $serializer Serializer for encoding telemetry data
- * @param CurlTransportOptions $options Transport configuration options
- */
public function __construct(
private readonly string $endpoint,
- private readonly Serializer $serializer,
+ private readonly JsonSerializer|ProtobufSerializer $serializer = new JsonSerializer(),
private readonly CurlTransportOptions $options = new CurlTransportOptions(),
+ private readonly ?Transport $failover = null,
) {
if (!\extension_loaded('curl')) {
throw new \RuntimeException('ext-curl is required for CurlTransport');
@@ -62,28 +43,26 @@ public function __construct(
$this->multiHandle = \curl_multi_init();
}
- /**
- * @param array $entries
- */
- public function sendLogs(array $entries) : void
+ public function send(Signals $signal) : void
{
- $this->send('/v1/logs', $this->serializer->serializeLogs($entries), 'logs');
- }
+ [$path, $body, $signalName] = match ($signal->type) {
+ SignalType::LOGS => ['/v1/logs', $this->serializer->serializeLogs($signal->allLogs()), 'logs'],
+ SignalType::METRICS => ['/v1/metrics', $this->serializer->serializeMetrics($signal->allMetrics()), 'metrics'],
+ SignalType::TRACES => ['/v1/traces', $this->serializer->serializeSpans($signal->allSpans()), 'traces'],
+ };
+
+ if ($this->failover !== null) {
+ $this->drainCompleted();
+ }
- /**
- * @param array $metrics
- */
- public function sendMetrics(array $metrics) : void
- {
- $this->send('/v1/metrics', $this->serializer->serializeMetrics($metrics), 'metrics');
- }
+ $this->dispatch($path, $body, $signalName, $this->failover !== null ? $signal : null);
- /**
- * @param array $spans
- */
- public function sendSpans(array $spans) : void
- {
- $this->send('/v1/traces', $this->serializer->serializeSpans($spans), 'traces');
+ if ($this->failover !== null && $this->deferredFailures !== []) {
+ $snapshot = $this->deferredFailures;
+ $this->deferredFailures = [];
+
+ throw new FailoverTransportException($snapshot);
+ }
}
public function shutdown() : void
@@ -94,11 +73,63 @@ public function shutdown() : void
$this->isShutdown = true;
- $this->waitForCompletion();
+ $shutdownTimeoutMs = $this->options->shutdownTimeoutMs();
+ $shutdownDeadlineMicrotime = \microtime(true) + ($shutdownTimeoutMs / 1000);
+
+ // Extend per-handle deadline so slow-but-eventually-succeeds requests get the
+ // longer drain budget; the wall-clock cap below still bounds total shutdown time.
+ foreach ($this->pending as $entry) {
+ \curl_setopt($entry['handle'], \CURLOPT_TIMEOUT_MS, $shutdownTimeoutMs);
+ }
+
+ $this->waitForCompletion($shutdownDeadlineMicrotime);
- $this->processCompleted();
+ if ($this->failover === null) {
+ $this->processCompleted();
+ $this->markStillPendingAsShutdownTimedOut();
+ } else {
+ $this->drainCompleted();
+ $this->forwardStillPendingAsShutdownTimedOut();
+ }
\curl_multi_close($this->multiHandle);
+
+ $cascadeException = null;
+
+ if ($this->failover !== null) {
+ try {
+ $this->failover->shutdown();
+ } catch (\Throwable $e) {
+ $cascadeException = $e;
+ }
+ }
+
+ if ($this->deferredFailures !== []) {
+ $snapshot = $this->deferredFailures;
+ $this->deferredFailures = [];
+
+ throw new FailoverTransportException($snapshot);
+ }
+
+ if ($cascadeException !== null) {
+ throw new TransportException(
+ \sprintf('OTLP curl shutdown: failover shutdown failed: %s', $cascadeException->getMessage()),
+ 0,
+ $cascadeException,
+ );
+ }
+
+ if (\count($this->failures) === 0) {
+ return;
+ }
+
+ $first = $this->failures[0];
+ $count = \count($this->failures);
+ $message = $count === 1
+ ? \sprintf('OTLP curl shutdown: 1 export failed: %s', $first->getMessage())
+ : \sprintf('OTLP curl shutdown: %d exports failed; first error: %s', $count, $first->getMessage());
+
+ throw new TransportException($message, 0, $first);
}
/**
@@ -107,7 +138,10 @@ public function shutdown() : void
private function buildHeaders() : array
{
$headers = [
- 'Content-Type: ' . ($this->serializer instanceof JsonSerializer ? 'application/json' : 'application/x-protobuf'),
+ 'Content-Type: ' . match (true) {
+ $this->serializer instanceof JsonSerializer => 'application/json',
+ $this->serializer instanceof ProtobufSerializer => 'application/x-protobuf',
+ },
];
foreach ($this->options->headers() as $name => $value) {
@@ -117,23 +151,15 @@ private function buildHeaders() : array
return $headers;
}
- private function processCompleted() : void
+ private function buildShutdownTimeoutError() : TransportException
{
- $running = 0;
- \curl_multi_exec($this->multiHandle, $running);
-
- while ($info = \curl_multi_info_read($this->multiHandle)) {
- /** @var \CurlHandle $ch */
- $ch = $info['handle'];
- $id = (int) $ch;
-
- \curl_multi_remove_handle($this->multiHandle, $ch);
- \curl_close($ch);
- unset($this->pendingHandles[$id]);
- }
+ return new TransportException(\sprintf(
+ 'OTLP curl shutdown: request still pending when configured shutdown_timeout=%dms expired',
+ $this->options->shutdownTimeoutMs(),
+ ));
}
- private function send(string $path, string $body, string $signalName) : void
+ private function dispatch(string $path, string $body, string $signalName, ?Signals $signalsToRetain) : void
{
if ($this->isShutdown) {
throw new TransportException('Cannot send after shutdown');
@@ -157,14 +183,140 @@ private function send(string $path, string $body, string $signalName) : void
throw new TransportException(\sprintf('Failed to add curl handle for %s: %s', $signalName, \curl_multi_strerror($result)));
}
- $this->pendingHandles[(int) $ch] = $ch;
+ $this->pending[(int) $ch] = ['handle' => $ch, 'signals' => $signalsToRetain];
+
+ if ($this->failover === null) {
+ $this->processCompleted();
+ }
+ }
+
+ private function drainCompleted() : void
+ {
+ foreach ($this->iterateCompleted() as $item) {
+ if ($item['primaryError'] === null) {
+ continue;
+ }
+
+ $failoverError = null;
+
+ if ($item['entry'] !== null && $item['entry']['signals'] !== null && $this->failover !== null) {
+ try {
+ $this->failover->send($item['entry']['signals']);
+ } catch (\Throwable $e) {
+ $failoverError = $e;
+ }
+ }
+
+ $this->deferredFailures[] = ['primary' => $item['primaryError'], 'failover' => $failoverError];
+ }
+ }
+
+ private function forwardStillPendingAsShutdownTimedOut() : void
+ {
+ if ($this->failover === null) {
+ return;
+ }
+
+ foreach ($this->iterateStillPending() as $item) {
+ $failoverError = null;
+
+ if ($item['entry']['signals'] !== null) {
+ try {
+ $this->failover->send($item['entry']['signals']);
+ } catch (\Throwable $e) {
+ $failoverError = $e;
+ }
+ }
+
+ $this->deferredFailures[] = ['primary' => $item['primaryError'], 'failover' => $failoverError];
+ }
+ }
+
+ /**
+ * @return \Generator
+ */
+ private function iterateCompleted() : \Generator
+ {
+ $running = 0;
+ \curl_multi_exec($this->multiHandle, $running);
+
+ while ($info = \curl_multi_info_read($this->multiHandle)) {
+ /** @var \CurlHandle $ch */
+ $ch = $info['handle'];
+ $id = (int) $ch;
+
+ $errno = $info['result'];
+ $httpCode = (int) \curl_getinfo($ch, \CURLINFO_HTTP_CODE);
+ $effectiveUrl = (string) \curl_getinfo($ch, \CURLINFO_EFFECTIVE_URL);
+ $urlForMessage = $effectiveUrl !== '' ? $effectiveUrl : 'unknown url';
+ $totalTimeMs = (int) \round((float) \curl_getinfo($ch, \CURLINFO_TOTAL_TIME) * 1000);
+ $connectTimeMs = (int) \round((float) \curl_getinfo($ch, \CURLINFO_CONNECT_TIME) * 1000);
+
+ $primaryError = null;
+
+ if ($errno !== \CURLE_OK) {
+ $primaryError = new TransportException(\sprintf(
+ 'curl error %d (%s) for %s; elapsed connect=%dms total=%dms (configured connect_timeout=%dms, timeout=%dms)',
+ $errno,
+ \curl_strerror($errno) ?? 'unknown',
+ $urlForMessage,
+ $connectTimeMs,
+ $totalTimeMs,
+ $this->options->connectTimeoutMs(),
+ $this->options->timeoutMs(),
+ ));
+ } elseif ($httpCode < 200 || $httpCode >= 300) {
+ $primaryError = new TransportException(\sprintf(
+ 'HTTP %d from %s after %dms',
+ $httpCode,
+ $urlForMessage,
+ $totalTimeMs,
+ ));
+ }
+
+ $entry = $this->pending[$id] ?? null;
+
+ \curl_multi_remove_handle($this->multiHandle, $ch);
+ \curl_close($ch);
+ unset($this->pending[$id]);
+
+ yield ['primaryError' => $primaryError, 'entry' => $entry];
+ }
+ }
+
+ /**
+ * @return \Generator
+ */
+ private function iterateStillPending() : \Generator
+ {
+ foreach ($this->pending as $id => $entry) {
+ \curl_multi_remove_handle($this->multiHandle, $entry['handle']);
+ \curl_close($entry['handle']);
+ unset($this->pending[$id]);
+
+ yield ['primaryError' => $this->buildShutdownTimeoutError(), 'entry' => $entry];
+ }
+ }
+
+ private function markStillPendingAsShutdownTimedOut() : void
+ {
+ foreach ($this->iterateStillPending() as $item) {
+ $this->failures[] = $item['primaryError'];
+ }
+ }
- $this->processCompleted();
+ private function processCompleted() : void
+ {
+ foreach ($this->iterateCompleted() as $item) {
+ if ($item['primaryError'] !== null) {
+ $this->failures[] = $item['primaryError'];
+ }
+ }
}
- private function waitForCompletion() : void
+ private function waitForCompletion(?float $deadlineMicrotime = null) : void
{
- if (\count($this->pendingHandles) === 0) {
+ if (\count($this->pending) === 0) {
return;
}
@@ -175,7 +327,19 @@ private function waitForCompletion() : void
} while ($status === CURLM_CALL_MULTI_PERFORM);
while ($running > 0 && $status === CURLM_OK) {
- if (\curl_multi_select($this->multiHandle, 1.0) === -1) {
+ $selectTimeout = 1.0;
+
+ if ($deadlineMicrotime !== null) {
+ $remaining = $deadlineMicrotime - \microtime(true);
+
+ if ($remaining <= 0.0) {
+ return;
+ }
+
+ $selectTimeout = \min(1.0, $remaining);
+ }
+
+ if (\curl_multi_select($this->multiHandle, $selectTimeout) === -1) {
\usleep(1000);
}
diff --git a/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/Transport/CurlTransportOptions.php b/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/Transport/CurlTransportOptions.php
index d66b7252d..24a2d75d1 100644
--- a/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/Transport/CurlTransportOptions.php
+++ b/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/Transport/CurlTransportOptions.php
@@ -12,8 +12,8 @@
* Example usage:
* ```php
* $options = otlp_curl_options()
- * ->withTimeout(60)
- * ->withConnectTimeout(15)
+ * ->withTimeout(2000)
+ * ->withConnectTimeout(500)
* ->withHeader('Authorization', 'Bearer token')
* ->withCompression()
* ->withSslVerification(verifyPeer: true);
@@ -23,11 +23,17 @@
*/
final class CurlTransportOptions
{
+ public const int DEFAULT_CONNECT_TIMEOUT_MS = 250;
+
+ public const int DEFAULT_SHUTDOWN_TIMEOUT_MS = 5000;
+
+ public const int DEFAULT_TIMEOUT_MS = 250;
+
private ?string $caInfoPath = null;
private bool $compression = false;
- private int $connectTimeout = 10;
+ private int $connectTimeoutMs = self::DEFAULT_CONNECT_TIMEOUT_MS;
private bool $followRedirects = true;
@@ -38,6 +44,8 @@ final class CurlTransportOptions
private ?string $proxy = null;
+ private int $shutdownTimeoutMs = self::DEFAULT_SHUTDOWN_TIMEOUT_MS;
+
private ?string $sslCertPath = null;
private ?string $sslKeyPath = null;
@@ -46,7 +54,7 @@ final class CurlTransportOptions
private bool $sslVerifyPeer = true;
- private int $timeout = 30;
+ private int $timeoutMs = self::DEFAULT_TIMEOUT_MS;
public function caInfoPath() : ?string
{
@@ -58,9 +66,9 @@ public function compression() : bool
return $this->compression;
}
- public function connectTimeout() : int
+ public function connectTimeoutMs() : int
{
- return $this->connectTimeout;
+ return $this->connectTimeoutMs;
}
public function followRedirects() : bool
@@ -86,6 +94,11 @@ public function proxy() : ?string
return $this->proxy;
}
+ public function shutdownTimeoutMs() : int
+ {
+ return $this->shutdownTimeoutMs;
+ }
+
public function sslCertPath() : ?string
{
return $this->sslCertPath;
@@ -106,9 +119,9 @@ public function sslVerifyPeer() : bool
return $this->sslVerifyPeer;
}
- public function timeout() : int
+ public function timeoutMs() : int
{
- return $this->timeout;
+ return $this->timeoutMs;
}
/**
@@ -127,8 +140,8 @@ public function toCurlOptions(string $url, string $body, array $headers) : array
\CURLOPT_POST => true,
\CURLOPT_POSTFIELDS => $body,
\CURLOPT_RETURNTRANSFER => true,
- \CURLOPT_TIMEOUT => $this->timeout,
- \CURLOPT_CONNECTTIMEOUT => $this->connectTimeout,
+ \CURLOPT_TIMEOUT_MS => $this->timeoutMs,
+ \CURLOPT_CONNECTTIMEOUT_MS => $this->connectTimeoutMs,
\CURLOPT_HTTPHEADER => $headers,
\CURLOPT_FOLLOWLOCATION => $this->followRedirects,
\CURLOPT_MAXREDIRS => $this->maxRedirects,
@@ -187,17 +200,17 @@ public function withCompression(bool $enabled = true) : self
}
/**
- * Set the connection timeout.
+ * Set the connection timeout in milliseconds.
*
- * @param int $seconds Maximum time in seconds to wait for connection
+ * @param int $milliseconds Maximum time in milliseconds to wait for the TCP/TLS connection
*/
- public function withConnectTimeout(int $seconds) : self
+ public function withConnectTimeout(int $milliseconds) : self
{
- if ($seconds < 0) {
+ if ($milliseconds < 0) {
throw new \InvalidArgumentException('Connect timeout must be non-negative');
}
- $this->connectTimeout = $seconds;
+ $this->connectTimeoutMs = $milliseconds;
return $this;
}
@@ -259,6 +272,26 @@ public function withProxy(string $proxy) : self
return $this;
}
+ /**
+ * Set the wall-clock budget for draining pending requests at shutdown.
+ *
+ * Requests still pending after this deadline are abandoned and reported as failed
+ * (forwarded to a configured failover transport, otherwise aggregated into the
+ * shutdown TransportException). Steady-state flush() is unaffected by this knob.
+ *
+ * @param int $milliseconds Maximum drain wall-clock at shutdown
+ */
+ public function withShutdownTimeout(int $milliseconds) : self
+ {
+ if ($milliseconds < 0) {
+ throw new \InvalidArgumentException('Shutdown timeout must be non-negative');
+ }
+
+ $this->shutdownTimeoutMs = $milliseconds;
+
+ return $this;
+ }
+
/**
* Set SSL client certificate.
*
@@ -288,17 +321,17 @@ public function withSslVerification(bool $verifyPeer, bool $verifyHost = true) :
}
/**
- * Set the request timeout.
+ * Set the request timeout in milliseconds.
*
- * @param int $seconds Maximum time in seconds for the entire request
+ * @param int $milliseconds Maximum time in milliseconds for the entire request (connect + send + receive)
*/
- public function withTimeout(int $seconds) : self
+ public function withTimeout(int $milliseconds) : self
{
- if ($seconds < 0) {
+ if ($milliseconds < 0) {
throw new \InvalidArgumentException('Timeout must be non-negative');
}
- $this->timeout = $seconds;
+ $this->timeoutMs = $milliseconds;
return $this;
}
diff --git a/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/Transport/FailoverTransportException.php b/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/Transport/FailoverTransportException.php
new file mode 100644
index 000000000..05a02298d
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/Transport/FailoverTransportException.php
@@ -0,0 +1,40 @@
+ $failures
+ */
+ public function __construct(public readonly array $failures)
+ {
+ $count = \count($failures);
+ $first = $failures[0];
+ $absorbed = \count(\array_filter($failures, static fn (array $f) : bool => $f['failover'] === null));
+ $lost = $count - $absorbed;
+
+ $message = \sprintf(
+ 'OTLP transport failover: %d primary failure(s) (%d absorbed by failover, %d lost); first primary error: %s%s',
+ $count,
+ $absorbed,
+ $lost,
+ $first['primary']->getMessage(),
+ $first['failover'] !== null
+ ? \sprintf('; first failover error: %s', $first['failover']->getMessage())
+ : '',
+ );
+
+ parent::__construct($message, 0, $first['primary']);
+ }
+}
diff --git a/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/Transport/GrpcTransport.php b/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/Transport/GrpcTransport.php
index a006995db..bf785875f 100644
--- a/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/Transport/GrpcTransport.php
+++ b/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/Transport/GrpcTransport.php
@@ -4,51 +4,62 @@
namespace Flow\Bridge\Telemetry\OTLP\Transport;
-use Flow\Bridge\Telemetry\OTLP\Serializer\GrpcSerializer;
-use Flow\Telemetry\Logger\LogEntry;
-use Flow\Telemetry\Meter\Metric;
-use Flow\Telemetry\Tracer\Span;
-use Flow\Telemetry\Transport\{Transport, TransportException};
-use Grpc\ChannelCredentials;
+use const Grpc\{STATUS_ABORTED, STATUS_ALREADY_EXISTS, STATUS_CANCELLED, STATUS_DATA_LOSS, STATUS_DEADLINE_EXCEEDED, STATUS_FAILED_PRECONDITION, STATUS_INTERNAL, STATUS_INVALID_ARGUMENT, STATUS_NOT_FOUND, STATUS_OK, STATUS_OUT_OF_RANGE, STATUS_PERMISSION_DENIED, STATUS_RESOURCE_EXHAUSTED, STATUS_UNAUTHENTICATED, STATUS_UNAVAILABLE, STATUS_UNIMPLEMENTED, STATUS_UNKNOWN};
+use Flow\Bridge\Telemetry\OTLP\Serializer\{GrpcRequestFactory, ProtobufSerializer};
+use Flow\Telemetry\Signal\{SignalType, Signals};
+use Google\Protobuf\Internal\Message;
+use Grpc\{ChannelCredentials, UnaryCall};
use Opentelemetry\Proto\Collector\Logs\V1\LogsServiceClient;
use Opentelemetry\Proto\Collector\Metrics\V1\MetricsServiceClient;
use Opentelemetry\Proto\Collector\Trace\V1\TraceServiceClient;
/**
- * gRPC transport for OTLP using the grpc PHP extension.
+ * Asynchronous gRPC transport for OTLP using the grpc PHP extension.
*
- * Sends telemetry data as protobuf over gRPC to OTLP-compatible endpoints.
- * Requires the grpc PHP extension and google/protobuf + open-telemetry/gen-otlp-protobuf packages.
+ * Sends are non-blocking: each Export() returns a UnaryCall whose wait()
+ * is deferred until shutdown(). Requires the grpc PHP extension and
+ * google/protobuf package.
*
- * Example usage:
- * ```php
- * $transport = new GrpcTransport(
- * endpoint: 'localhost:4317',
- * serializer: new ProtobufSerializer(),
- * );
- *
- * $transport->sendSpans($spans);
- * ```
+ * When $failover is set, prior failed batches are forwarded to it on the next
+ * send()/shutdown(); a FailoverTransportException is then thrown.
*/
final class GrpcTransport implements Transport
{
+ public const int DEFAULT_SHUTDOWN_TIMEOUT_MS = 5000;
+
+ public const int DEFAULT_TIMEOUT_MS = 250;
+
+ /** @var list */
+ private array $deferredFailures = [];
+
+ private bool $isShutdown = false;
+
private ?LogsServiceClient $logsClient = null;
private ?MetricsServiceClient $metricsClient = null;
+ /** @var list, signals: ?Signals}> */
+ private array $pending = [];
+
+ private readonly GrpcRequestFactory $requestFactory;
+
private ?TraceServiceClient $tracesClient = null;
/**
* @param string $endpoint gRPC endpoint (e.g., 'localhost:4317')
- * @param GrpcSerializer $serializer gRPC serializer for creating request messages
* @param array $headers Additional headers (metadata) to include in requests
* @param bool $insecure Whether to use insecure channel credentials (default true for local dev)
+ * @param int $timeoutMs Per-call deadline in milliseconds (covers connect + send + receive); gRPC has no separate connect timeout
+ * @param int $shutdownTimeoutMs Wall-clock budget for draining pending calls at shutdown; remaining calls are cancelled
+ * @param ?Transport $failover Optional failover transport receiving prior batches when primary fails
*/
public function __construct(
private readonly string $endpoint,
- private readonly GrpcSerializer $serializer,
private readonly array $headers = [],
private readonly bool $insecure = true,
+ private readonly int $timeoutMs = self::DEFAULT_TIMEOUT_MS,
+ private readonly int $shutdownTimeoutMs = self::DEFAULT_SHUTDOWN_TIMEOUT_MS,
+ private readonly ?Transport $failover = null,
) {
if (!\extension_loaded('grpc')) {
throw new \RuntimeException(
@@ -56,62 +67,99 @@ public function __construct(
. 'Install it via: pecl install grpc'
);
}
- }
- /**
- * @param array $entries
- */
- public function sendLogs(array $entries) : void
- {
- $request = $this->serializer->createLogsRequest($entries);
- $metadata = $this->buildMetadata();
+ if ($timeoutMs < 0) {
+ throw new \InvalidArgumentException('Timeout must be non-negative');
+ }
- [$result, $status] = $this->getLogsClient()->Export($request, $metadata)->wait();
+ if ($shutdownTimeoutMs < 0) {
+ throw new \InvalidArgumentException('Shutdown timeout must be non-negative');
+ }
- $this->checkStatus($status, 'logs');
+ $this->requestFactory = new ProtobufSerializer();
}
- /**
- * @param array $metrics
- */
- public function sendMetrics(array $metrics) : void
+ public function send(Signals $signal) : void
{
- $request = $this->serializer->createMetricsRequest($metrics);
- $metadata = $this->buildMetadata();
+ if ($this->isShutdown) {
+ throw new TransportException('Cannot send after shutdown');
+ }
- [$result, $status] = $this->getMetricsClient()->Export($request, $metadata)->wait();
+ if ($this->failover !== null) {
+ $this->drainPending();
+ }
- $this->checkStatus($status, 'metrics');
+ $callOptions = ['timeout' => $this->timeoutMs * 1000];
+
+ $call = match ($signal->type) {
+ SignalType::LOGS => $this->getLogsClient()->Export(
+ $this->requestFactory->createLogsRequest($signal->allLogs()),
+ $this->buildMetadata(),
+ $callOptions,
+ ),
+ SignalType::METRICS => $this->getMetricsClient()->Export(
+ $this->requestFactory->createMetricsRequest($signal->allMetrics()),
+ $this->buildMetadata(),
+ $callOptions,
+ ),
+ SignalType::TRACES => $this->getTracesClient()->Export(
+ $this->requestFactory->createSpansRequest($signal->allSpans()),
+ $this->buildMetadata(),
+ $callOptions,
+ ),
+ };
+
+ $this->pending[] = ['call' => $call, 'signals' => $this->failover !== null ? $signal : null];
+
+ if ($this->failover !== null && $this->deferredFailures !== []) {
+ $snapshot = $this->deferredFailures;
+ $this->deferredFailures = [];
+
+ throw new FailoverTransportException($snapshot);
+ }
}
- /**
- * @param array $spans
- */
- public function sendSpans(array $spans) : void
+ public function shutdown() : void
{
- $request = $this->serializer->createSpansRequest($spans);
- $metadata = $this->buildMetadata();
+ if ($this->isShutdown) {
+ return;
+ }
- [$result, $status] = $this->getTracesClient()->Export($request, $metadata)->wait();
+ $this->isShutdown = true;
- $this->checkStatus($status, 'traces');
- }
+ $shutdownDeadlineMicrotime = \microtime(true) + ($this->shutdownTimeoutMs / 1000);
- public function shutdown() : void
- {
- if ($this->tracesClient !== null) {
- $this->tracesClient->close();
- $this->tracesClient = null;
+ if ($this->failover === null) {
+ $this->shutdownWithoutFailover($shutdownDeadlineMicrotime);
+
+ return;
}
- if ($this->metricsClient !== null) {
- $this->metricsClient->close();
- $this->metricsClient = null;
+ $this->drainPending($shutdownDeadlineMicrotime);
+
+ $this->closeClients();
+
+ $cascadeException = null;
+
+ try {
+ $this->failover->shutdown();
+ } catch (\Throwable $e) {
+ $cascadeException = $e;
}
- if ($this->logsClient !== null) {
- $this->logsClient->close();
- $this->logsClient = null;
+ if ($this->deferredFailures !== []) {
+ $snapshot = $this->deferredFailures;
+ $this->deferredFailures = [];
+
+ throw new FailoverTransportException($snapshot);
+ }
+
+ if ($cascadeException !== null) {
+ throw new TransportException(
+ \sprintf('OTLP gRPC shutdown: failover shutdown failed: %s', $cascadeException->getMessage()),
+ 0,
+ $cascadeException,
+ );
}
}
@@ -129,20 +177,29 @@ private function buildMetadata() : array
return $metadata;
}
- private function checkStatus(object $status, string $signalName) : void
+ private function buildShutdownTimeoutError() : TransportException
{
- $statusCode = $status->code ?? -1;
- $statusDetails = $status->details ?? 'Unknown error';
+ return new TransportException(\sprintf(
+ 'OTLP gRPC shutdown: call cancelled when configured shutdown_timeout=%dms expired',
+ $this->shutdownTimeoutMs,
+ ));
+ }
- if ($statusCode !== 0) {
- throw new TransportException(
- \sprintf(
- 'gRPC export failed for %s: %s (code: %d)',
- $signalName,
- $statusDetails,
- $statusCode
- )
- );
+ private function closeClients() : void
+ {
+ if ($this->tracesClient !== null) {
+ $this->tracesClient->close();
+ $this->tracesClient = null;
+ }
+
+ if ($this->metricsClient !== null) {
+ $this->metricsClient->close();
+ $this->metricsClient = null;
+ }
+
+ if ($this->logsClient !== null) {
+ $this->logsClient->close();
+ $this->logsClient = null;
}
}
@@ -155,6 +212,27 @@ private function createChannel() : mixed
return ChannelCredentials::createSsl();
}
+ private function drainPending(?float $deadlineMicrotime = null) : void
+ {
+ foreach ($this->iteratePending($deadlineMicrotime) as $item) {
+ if ($item['primaryError'] === null) {
+ continue;
+ }
+
+ $failoverError = null;
+
+ if ($item['entry']['signals'] !== null && $this->failover !== null) {
+ try {
+ $this->failover->send($item['entry']['signals']);
+ } catch (\Throwable $e) {
+ $failoverError = $e;
+ }
+ }
+
+ $this->deferredFailures[] = ['primary' => $item['primaryError'], 'failover' => $failoverError];
+ }
+ }
+
private function getLogsClient() : LogsServiceClient
{
if ($this->logsClient === null) {
@@ -190,4 +268,91 @@ private function getTracesClient() : TraceServiceClient
return $this->tracesClient;
}
+
+ /**
+ * @return \Generator, signals: ?Signals}}>
+ */
+ private function iteratePending(?float $deadlineMicrotime = null) : \Generator
+ {
+ $pending = $this->pending;
+ $this->pending = [];
+
+ foreach ($pending as $entry) {
+ if ($deadlineMicrotime !== null && \microtime(true) >= $deadlineMicrotime) {
+ $entry['call']->cancel();
+ yield ['primaryError' => $this->buildShutdownTimeoutError(), 'entry' => $entry];
+
+ continue;
+ }
+
+ $primaryError = null;
+
+ try {
+ [, $status] = $entry['call']->wait();
+
+ if ($status->code !== STATUS_OK) {
+ $primaryError = new TransportException(\sprintf(
+ 'gRPC status %d (%s): %s',
+ $status->code,
+ self::grpcStatusName($status->code),
+ $status->details ?? '',
+ ));
+ }
+ } catch (\Throwable $e) {
+ $primaryError = $e;
+ }
+
+ yield ['primaryError' => $primaryError, 'entry' => $entry];
+ }
+ }
+
+ private function shutdownWithoutFailover(?float $deadlineMicrotime) : void
+ {
+ /** @var list<\Throwable> $failures */
+ $failures = [];
+
+ foreach ($this->iteratePending($deadlineMicrotime) as $item) {
+ if ($item['primaryError'] !== null) {
+ $failures[] = $item['primaryError'];
+ }
+ }
+
+ $this->closeClients();
+
+ if (\count($failures) === 0) {
+ return;
+ }
+
+ $first = $failures[0];
+ $count = \count($failures);
+ $message = $count === 1
+ ? \sprintf('OTLP gRPC shutdown: 1 export failed: %s', $first->getMessage())
+ : \sprintf('OTLP gRPC shutdown: %d exports failed; first error: %s', $count, $first->getMessage());
+
+ throw new TransportException($message, 0, $first);
+ }
+
+ private static function grpcStatusName(int $code) : string
+ {
+ return match ($code) {
+ STATUS_OK => 'OK',
+ STATUS_CANCELLED => 'CANCELLED',
+ STATUS_UNKNOWN => 'UNKNOWN',
+ STATUS_INVALID_ARGUMENT => 'INVALID_ARGUMENT',
+ STATUS_DEADLINE_EXCEEDED => 'DEADLINE_EXCEEDED',
+ STATUS_NOT_FOUND => 'NOT_FOUND',
+ STATUS_ALREADY_EXISTS => 'ALREADY_EXISTS',
+ STATUS_PERMISSION_DENIED => 'PERMISSION_DENIED',
+ STATUS_UNAUTHENTICATED => 'UNAUTHENTICATED',
+ STATUS_RESOURCE_EXHAUSTED => 'RESOURCE_EXHAUSTED',
+ STATUS_FAILED_PRECONDITION => 'FAILED_PRECONDITION',
+ STATUS_ABORTED => 'ABORTED',
+ STATUS_OUT_OF_RANGE => 'OUT_OF_RANGE',
+ STATUS_UNIMPLEMENTED => 'UNIMPLEMENTED',
+ STATUS_INTERNAL => 'INTERNAL',
+ STATUS_UNAVAILABLE => 'UNAVAILABLE',
+ STATUS_DATA_LOSS => 'DATA_LOSS',
+ default => 'UNKNOWN',
+ };
+ }
}
diff --git a/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/Transport/HttpTransport.php b/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/Transport/HttpTransport.php
deleted file mode 100644
index 0af89f883..000000000
--- a/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/Transport/HttpTransport.php
+++ /dev/null
@@ -1,120 +0,0 @@
-sendSpans($spans);
- * ```
- */
-final readonly class HttpTransport implements Transport
-{
- /**
- * @param ClientInterface $httpClient PSR-18 HTTP client
- * @param RequestFactoryInterface $requestFactory PSR-17 request factory
- * @param StreamFactoryInterface $streamFactory PSR-17 stream factory
- * @param string $endpoint Base OTLP HTTP endpoint URL (e.g., 'http://localhost:4318')
- * @param Serializer $serializer Serializer for encoding telemetry data
- * @param array $headers Additional headers to include in requests
- */
- public function __construct(
- private ClientInterface $httpClient,
- private RequestFactoryInterface $requestFactory,
- private StreamFactoryInterface $streamFactory,
- private string $endpoint,
- private Serializer $serializer,
- private array $headers = [],
- ) {
- }
-
- /**
- * @param array $entries
- */
- public function sendLogs(array $entries) : void
- {
- $this->send('/v1/logs', $this->serializer->serializeLogs($entries), 'logs');
- }
-
- /**
- * @param array $metrics
- */
- public function sendMetrics(array $metrics) : void
- {
- $this->send('/v1/metrics', $this->serializer->serializeMetrics($metrics), 'metrics');
- }
-
- /**
- * @param array $spans
- */
- public function sendSpans(array $spans) : void
- {
- $this->send('/v1/traces', $this->serializer->serializeSpans($spans), 'traces');
- }
-
- public function shutdown() : void
- {
- }
-
- private function send(string $path, string $body, string $signalName) : void
- {
- $url = \rtrim($this->endpoint, '/') . $path;
-
- $request = $this->requestFactory->createRequest('POST', $url)
- ->withHeader('Content-Type', $this->serializer instanceof JsonSerializer ? 'application/json' : 'application/x-protobuf');
-
- foreach ($this->headers as $name => $value) {
- $request = $request->withHeader($name, $value);
- }
-
- $request = $request->withBody($this->streamFactory->createStream($body));
-
- try {
- $response = $this->httpClient->sendRequest($request);
- } catch (ClientExceptionInterface $e) {
- throw new TransportException(
- \sprintf('Failed to send %s to OTLP endpoint: %s', $signalName, $e->getMessage()),
- previous: $e
- );
- }
-
- $statusCode = $response->getStatusCode();
-
- if ($statusCode >= 400) {
- $responseBody = (string) $response->getBody();
-
- throw new TransportException(
- \sprintf(
- 'OTLP endpoint returned HTTP %d for %s: %s',
- $statusCode,
- $signalName,
- $responseBody
- )
- );
- }
- }
-}
diff --git a/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/Transport/StreamTransport.php b/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/Transport/StreamTransport.php
new file mode 100644
index 000000000..3365f84cf
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/Transport/StreamTransport.php
@@ -0,0 +1,167 @@
+ 0777) {
+ throw new \InvalidArgumentException('File permissions must be between 0 and 0777');
+ }
+
+ $isStreamWrapper = \str_starts_with($destination, 'php://');
+ $existedBefore = !$isStreamWrapper && \is_file($destination);
+
+ if (!$isStreamWrapper && $createDirectories) {
+ $directory = \dirname($destination);
+
+ if (!\is_dir($directory)) {
+ $created = $this->captureError(static fn () : bool => \mkdir($directory, 0755, true));
+
+ if ($created === false && !\is_dir($directory)) {
+ throw new TransportException(\sprintf(
+ 'Failed to create directory "%s" for StreamTransport destination: %s',
+ $directory,
+ $this->errorMessage ?? 'unknown error',
+ ));
+ }
+ }
+ }
+
+ $handle = $this->captureError(static fn () => \fopen($destination, 'a+b'));
+
+ if (!\is_resource($handle)) {
+ throw new TransportException(\sprintf(
+ 'Failed to open OTLP stream "%s": %s',
+ $destination,
+ $this->errorMessage ?? 'unknown error',
+ ));
+ }
+
+ $this->stream = $handle;
+ \stream_set_chunk_size($this->stream, self::STREAM_CHUNK_SIZE);
+ $this->serializer = new JsonSerializer();
+
+ if (!$isStreamWrapper && !$existedBefore) {
+ @\chmod($destination, $filePermissions);
+ }
+ }
+
+ public function send(Signals $signal) : void
+ {
+ if ($this->isShutdown) {
+ throw new TransportException('Cannot send after shutdown');
+ }
+
+ if ($signal->count() === 0) {
+ return;
+ }
+
+ $payload = match ($signal->type) {
+ SignalType::LOGS => $this->serializer->serializeLogs($signal->allLogs()),
+ SignalType::METRICS => $this->serializer->serializeMetrics($signal->allMetrics()),
+ SignalType::TRACES => $this->serializer->serializeSpans($signal->allSpans()),
+ } . "\n";
+
+ $stream = $this->stream;
+
+ @\flock($stream, \LOCK_EX);
+
+ try {
+ $written = $this->captureError(static fn () : false|int => \fwrite($stream, $payload));
+ } finally {
+ @\flock($stream, \LOCK_UN);
+ }
+
+ if (!\is_int($written)) {
+ throw new TransportException(\sprintf(
+ 'Failed to write OTLP payload to "%s": %s',
+ $this->destination,
+ $this->errorMessage ?? 'unknown error',
+ ));
+ }
+
+ if ($written < \strlen($payload)) {
+ throw new TransportException(\sprintf(
+ 'Partial write to OTLP stream "%s": wrote %d of %d bytes',
+ $this->destination,
+ $written,
+ \strlen($payload),
+ ));
+ }
+ }
+
+ public function shutdown() : void
+ {
+ if ($this->isShutdown) {
+ return;
+ }
+
+ @\fclose($this->stream);
+ $this->isShutdown = true;
+ }
+
+ /**
+ * @return resource
+ */
+ public function stream()
+ {
+ return $this->stream;
+ }
+
+ private function captureError(\Closure $operation) : mixed
+ {
+ $this->errorMessage = null;
+ \set_error_handler(function (int $code, string $message) : bool {
+ $this->errorMessage = \preg_replace('{^\w+\(.*?\): }', '', $message);
+
+ return true;
+ });
+
+ try {
+ return $operation();
+ } finally {
+ \restore_error_handler();
+ }
+ }
+}
diff --git a/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/Transport/Transport.php b/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/Transport/Transport.php
new file mode 100644
index 000000000..e225a2777
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Flow/Bridge/Telemetry/OTLP/Transport/Transport.php
@@ -0,0 +1,35 @@
+internalAddGeneratedFile(
+ "\x0A\xCA\x05\x0A8opentelemetry/proto/collector/logs/v1/logs_service.proto\x12%opentelemetry.proto.collector.logs.v1\"\\\x0A\x18ExportLogsServiceRequest\x12@\x0A\x0Dresource_logs\x18\x01 \x03(\x0B2).opentelemetry.proto.logs.v1.ResourceLogs\"u\x0A\x19ExportLogsServiceResponse\x12X\x0A\x0Fpartial_success\x18\x01 \x01(\x0B2?.opentelemetry.proto.collector.logs.v1.ExportLogsPartialSuccess\"O\x0A\x18ExportLogsPartialSuccess\x12\x1C\x0A\x14rejected_log_records\x18\x01 \x01(\x03\x12\x15\x0A\x0Derror_message\x18\x02 \x01(\x092\x9D\x01\x0A\x0BLogsService\x12\x8D\x01\x0A\x06Export\x12?.opentelemetry.proto.collector.logs.v1.ExportLogsServiceRequest\x1A@.opentelemetry.proto.collector.logs.v1.ExportLogsServiceResponse\"\x00B\x98\x01\x0A(io.opentelemetry.proto.collector.logs.v1B\x10LogsServiceProtoP\x01Z0go.opentelemetry.io/proto/otlp/collector/logs/v1\xAA\x02%OpenTelemetry.Proto.Collector.Logs.V1b\x06proto3"
+ , true);
+
+ static::$is_initialized = true;
+ }
+}
+
diff --git a/src/bridge/telemetry/otlp/src/GPBMetadata/Opentelemetry/Proto/Collector/Metrics/V1/MetricsService.php b/src/bridge/telemetry/otlp/src/GPBMetadata/Opentelemetry/Proto/Collector/Metrics/V1/MetricsService.php
new file mode 100644
index 000000000..a9e6a7211
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/GPBMetadata/Opentelemetry/Proto/Collector/Metrics/V1/MetricsService.php
@@ -0,0 +1,26 @@
+internalAddGeneratedFile(
+ "\x0A\x86\x06\x0A>opentelemetry/proto/collector/metrics/v1/metrics_service.proto\x12(opentelemetry.proto.collector.metrics.v1\"h\x0A\x1BExportMetricsServiceRequest\x12I\x0A\x10resource_metrics\x18\x01 \x03(\x0B2/.opentelemetry.proto.metrics.v1.ResourceMetrics\"~\x0A\x1CExportMetricsServiceResponse\x12^\x0A\x0Fpartial_success\x18\x01 \x01(\x0B2E.opentelemetry.proto.collector.metrics.v1.ExportMetricsPartialSuccess\"R\x0A\x1BExportMetricsPartialSuccess\x12\x1C\x0A\x14rejected_data_points\x18\x01 \x01(\x03\x12\x15\x0A\x0Derror_message\x18\x02 \x01(\x092\xAC\x01\x0A\x0EMetricsService\x12\x99\x01\x0A\x06Export\x12E.opentelemetry.proto.collector.metrics.v1.ExportMetricsServiceRequest\x1AF.opentelemetry.proto.collector.metrics.v1.ExportMetricsServiceResponse\"\x00B\xA4\x01\x0A+io.opentelemetry.proto.collector.metrics.v1B\x13MetricsServiceProtoP\x01Z3go.opentelemetry.io/proto/otlp/collector/metrics/v1\xAA\x02(OpenTelemetry.Proto.Collector.Metrics.V1b\x06proto3"
+ , true);
+
+ static::$is_initialized = true;
+ }
+}
+
diff --git a/src/bridge/telemetry/otlp/src/GPBMetadata/Opentelemetry/Proto/Collector/Profiles/V1Development/ProfilesService.php b/src/bridge/telemetry/otlp/src/GPBMetadata/Opentelemetry/Proto/Collector/Profiles/V1Development/ProfilesService.php
new file mode 100644
index 000000000..9ad21a49c
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/GPBMetadata/Opentelemetry/Proto/Collector/Profiles/V1Development/ProfilesService.php
@@ -0,0 +1,26 @@
+internalAddGeneratedFile(
+ "\x0A\xD0\x07\x0AKopentelemetry/proto/collector/profiles/v1development/profiles_service.proto\x124opentelemetry.proto.collector.profiles.v1development\"\xCB\x01\x0A\x1CExportProfilesServiceRequest\x12W\x0A\x11resource_profiles\x18\x01 \x03(\x0B2<.opentelemetry.proto.profiles.v1development.ResourceProfiles\x12R\x0A\x0Adictionary\x18\x02 \x01(\x0B2>.opentelemetry.proto.profiles.v1development.ProfilesDictionary\"\x8C\x01\x0A\x1DExportProfilesServiceResponse\x12k\x0A\x0Fpartial_success\x18\x01 \x01(\x0B2R.opentelemetry.proto.collector.profiles.v1development.ExportProfilesPartialSuccess\"P\x0A\x1CExportProfilesPartialSuccess\x12\x19\x0A\x11rejected_profiles\x18\x01 \x01(\x03\x12\x15\x0A\x0Derror_message\x18\x02 \x01(\x092\xC7\x01\x0A\x0FProfilesService\x12\xB3\x01\x0A\x06Export\x12R.opentelemetry.proto.collector.profiles.v1development.ExportProfilesServiceRequest\x1AS.opentelemetry.proto.collector.profiles.v1development.ExportProfilesServiceResponse\"\x00B\xC9\x01\x0A7io.opentelemetry.proto.collector.profiles.v1developmentB\x14ProfilesServiceProtoP\x01Z?go.opentelemetry.io/proto/otlp/collector/profiles/v1development\xAA\x024OpenTelemetry.Proto.Collector.Profiles.V1Developmentb\x06proto3"
+ , true);
+
+ static::$is_initialized = true;
+ }
+}
+
diff --git a/src/bridge/telemetry/otlp/src/GPBMetadata/Opentelemetry/Proto/Collector/Trace/V1/TraceService.php b/src/bridge/telemetry/otlp/src/GPBMetadata/Opentelemetry/Proto/Collector/Trace/V1/TraceService.php
new file mode 100644
index 000000000..5517f82bc
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/GPBMetadata/Opentelemetry/Proto/Collector/Trace/V1/TraceService.php
@@ -0,0 +1,26 @@
+internalAddGeneratedFile(
+ "\x0A\xD8\x05\x0A:opentelemetry/proto/collector/trace/v1/trace_service.proto\x12&opentelemetry.proto.collector.trace.v1\"`\x0A\x19ExportTraceServiceRequest\x12C\x0A\x0Eresource_spans\x18\x01 \x03(\x0B2+.opentelemetry.proto.trace.v1.ResourceSpans\"x\x0A\x1AExportTraceServiceResponse\x12Z\x0A\x0Fpartial_success\x18\x01 \x01(\x0B2A.opentelemetry.proto.collector.trace.v1.ExportTracePartialSuccess\"J\x0A\x19ExportTracePartialSuccess\x12\x16\x0A\x0Erejected_spans\x18\x01 \x01(\x03\x12\x15\x0A\x0Derror_message\x18\x02 \x01(\x092\xA2\x01\x0A\x0CTraceService\x12\x91\x01\x0A\x06Export\x12A.opentelemetry.proto.collector.trace.v1.ExportTraceServiceRequest\x1AB.opentelemetry.proto.collector.trace.v1.ExportTraceServiceResponse\"\x00B\x9C\x01\x0A)io.opentelemetry.proto.collector.trace.v1B\x11TraceServiceProtoP\x01Z1go.opentelemetry.io/proto/otlp/collector/trace/v1\xAA\x02&OpenTelemetry.Proto.Collector.Trace.V1b\x06proto3"
+ , true);
+
+ static::$is_initialized = true;
+ }
+}
+
diff --git a/src/bridge/telemetry/otlp/src/GPBMetadata/Opentelemetry/Proto/Common/V1/Common.php b/src/bridge/telemetry/otlp/src/GPBMetadata/Opentelemetry/Proto/Common/V1/Common.php
new file mode 100644
index 000000000..b7b93f5eb
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/GPBMetadata/Opentelemetry/Proto/Common/V1/Common.php
@@ -0,0 +1,25 @@
+internalAddGeneratedFile(
+ "\x0A\xE8\x07\x0A*opentelemetry/proto/common/v1/common.proto\x12\x1Dopentelemetry.proto.common.v1\"\xAD\x02\x0A\x08AnyValue\x12\x16\x0A\x0Cstring_value\x18\x01 \x01(\x09H\x00\x12\x14\x0A\x0Abool_value\x18\x02 \x01(\x08H\x00\x12\x13\x0A\x09int_value\x18\x03 \x01(\x03H\x00\x12\x16\x0A\x0Cdouble_value\x18\x04 \x01(\x01H\x00\x12@\x0A\x0Barray_value\x18\x05 \x01(\x0B2).opentelemetry.proto.common.v1.ArrayValueH\x00\x12C\x0A\x0Ckvlist_value\x18\x06 \x01(\x0B2+.opentelemetry.proto.common.v1.KeyValueListH\x00\x12\x15\x0A\x0Bbytes_value\x18\x07 \x01(\x0CH\x00\x12\x1F\x0A\x15string_value_strindex\x18\x08 \x01(\x05H\x00B\x07\x0A\x05value\"E\x0A\x0AArrayValue\x127\x0A\x06values\x18\x01 \x03(\x0B2'.opentelemetry.proto.common.v1.AnyValue\"G\x0A\x0CKeyValueList\x127\x0A\x06values\x18\x01 \x03(\x0B2'.opentelemetry.proto.common.v1.KeyValue\"e\x0A\x08KeyValue\x12\x0B\x0A\x03key\x18\x01 \x01(\x09\x126\x0A\x05value\x18\x02 \x01(\x0B2'.opentelemetry.proto.common.v1.AnyValue\x12\x14\x0A\x0Ckey_strindex\x18\x03 \x01(\x05\"\x94\x01\x0A\x14InstrumentationScope\x12\x0C\x0A\x04name\x18\x01 \x01(\x09\x12\x0F\x0A\x07version\x18\x02 \x01(\x09\x12;\x0A\x0Aattributes\x18\x03 \x03(\x0B2'.opentelemetry.proto.common.v1.KeyValue\x12 \x0A\x18dropped_attributes_count\x18\x04 \x01(\x0D\"X\x0A\x09EntityRef\x12\x12\x0A\x0Aschema_url\x18\x01 \x01(\x09\x12\x0C\x0A\x04type\x18\x02 \x01(\x09\x12\x0F\x0A\x07id_keys\x18\x03 \x03(\x09\x12\x18\x0A\x10description_keys\x18\x04 \x03(\x09B{\x0A io.opentelemetry.proto.common.v1B\x0BCommonProtoP\x01Z(go.opentelemetry.io/proto/otlp/common/v1\xAA\x02\x1DOpenTelemetry.Proto.Common.V1b\x06proto3"
+ , true);
+
+ static::$is_initialized = true;
+ }
+}
+
diff --git a/src/bridge/telemetry/otlp/src/GPBMetadata/Opentelemetry/Proto/Logs/V1/Logs.php b/src/bridge/telemetry/otlp/src/GPBMetadata/Opentelemetry/Proto/Logs/V1/Logs.php
new file mode 100644
index 000000000..645e3111c
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/GPBMetadata/Opentelemetry/Proto/Logs/V1/Logs.php
@@ -0,0 +1,27 @@
+internalAddGeneratedFile(
+ "\x0A\xB0\x0E\x0A&opentelemetry/proto/logs/v1/logs.proto\x12\x1Bopentelemetry.proto.logs.v1\x1A.opentelemetry/proto/resource/v1/resource.proto\"L\x0A\x08LogsData\x12@\x0A\x0Dresource_logs\x18\x01 \x03(\x0B2).opentelemetry.proto.logs.v1.ResourceLogs\"\xA3\x01\x0A\x0CResourceLogs\x12;\x0A\x08resource\x18\x01 \x01(\x0B2).opentelemetry.proto.resource.v1.Resource\x12:\x0A\x0Ascope_logs\x18\x02 \x03(\x0B2&.opentelemetry.proto.logs.v1.ScopeLogs\x12\x12\x0A\x0Aschema_url\x18\x03 \x01(\x09J\x06\x08\xE8\x07\x10\xE9\x07\"\xA0\x01\x0A\x09ScopeLogs\x12B\x0A\x05scope\x18\x01 \x01(\x0B23.opentelemetry.proto.common.v1.InstrumentationScope\x12;\x0A\x0Blog_records\x18\x02 \x03(\x0B2&.opentelemetry.proto.logs.v1.LogRecord\x12\x12\x0A\x0Aschema_url\x18\x03 \x01(\x09\"\x83\x03\x0A\x09LogRecord\x12\x16\x0A\x0Etime_unix_nano\x18\x01 \x01(\x06\x12\x1F\x0A\x17observed_time_unix_nano\x18\x0B \x01(\x06\x12D\x0A\x0Fseverity_number\x18\x02 \x01(\x0E2+.opentelemetry.proto.logs.v1.SeverityNumber\x12\x15\x0A\x0Dseverity_text\x18\x03 \x01(\x09\x125\x0A\x04body\x18\x05 \x01(\x0B2'.opentelemetry.proto.common.v1.AnyValue\x12;\x0A\x0Aattributes\x18\x06 \x03(\x0B2'.opentelemetry.proto.common.v1.KeyValue\x12 \x0A\x18dropped_attributes_count\x18\x07 \x01(\x0D\x12\x0D\x0A\x05flags\x18\x08 \x01(\x07\x12\x10\x0A\x08trace_id\x18\x09 \x01(\x0C\x12\x0F\x0A\x07span_id\x18\x0A \x01(\x0C\x12\x12\x0A\x0Aevent_name\x18\x0C \x01(\x09J\x04\x08\x04\x10\x05*\xC3\x05\x0A\x0ESeverityNumber\x12\x1F\x0A\x1BSEVERITY_NUMBER_UNSPECIFIED\x10\x00\x12\x19\x0A\x15SEVERITY_NUMBER_TRACE\x10\x01\x12\x1A\x0A\x16SEVERITY_NUMBER_TRACE2\x10\x02\x12\x1A\x0A\x16SEVERITY_NUMBER_TRACE3\x10\x03\x12\x1A\x0A\x16SEVERITY_NUMBER_TRACE4\x10\x04\x12\x19\x0A\x15SEVERITY_NUMBER_DEBUG\x10\x05\x12\x1A\x0A\x16SEVERITY_NUMBER_DEBUG2\x10\x06\x12\x1A\x0A\x16SEVERITY_NUMBER_DEBUG3\x10\x07\x12\x1A\x0A\x16SEVERITY_NUMBER_DEBUG4\x10\x08\x12\x18\x0A\x14SEVERITY_NUMBER_INFO\x10\x09\x12\x19\x0A\x15SEVERITY_NUMBER_INFO2\x10\x0A\x12\x19\x0A\x15SEVERITY_NUMBER_INFO3\x10\x0B\x12\x19\x0A\x15SEVERITY_NUMBER_INFO4\x10\x0C\x12\x18\x0A\x14SEVERITY_NUMBER_WARN\x10\x0D\x12\x19\x0A\x15SEVERITY_NUMBER_WARN2\x10\x0E\x12\x19\x0A\x15SEVERITY_NUMBER_WARN3\x10\x0F\x12\x19\x0A\x15SEVERITY_NUMBER_WARN4\x10\x10\x12\x19\x0A\x15SEVERITY_NUMBER_ERROR\x10\x11\x12\x1A\x0A\x16SEVERITY_NUMBER_ERROR2\x10\x12\x12\x1A\x0A\x16SEVERITY_NUMBER_ERROR3\x10\x13\x12\x1A\x0A\x16SEVERITY_NUMBER_ERROR4\x10\x14\x12\x19\x0A\x15SEVERITY_NUMBER_FATAL\x10\x15\x12\x1A\x0A\x16SEVERITY_NUMBER_FATAL2\x10\x16\x12\x1A\x0A\x16SEVERITY_NUMBER_FATAL3\x10\x17\x12\x1A\x0A\x16SEVERITY_NUMBER_FATAL4\x10\x18*Y\x0A\x0ELogRecordFlags\x12\x1F\x0A\x1BLOG_RECORD_FLAGS_DO_NOT_USE\x10\x00\x12&\x0A!LOG_RECORD_FLAGS_TRACE_FLAGS_MASK\x10\xFF\x01Bs\x0A\x1Eio.opentelemetry.proto.logs.v1B\x09LogsProtoP\x01Z&go.opentelemetry.io/proto/otlp/logs/v1\xAA\x02\x1BOpenTelemetry.Proto.Logs.V1b\x06proto3"
+ , true);
+
+ static::$is_initialized = true;
+ }
+}
+
diff --git a/src/bridge/telemetry/otlp/src/GPBMetadata/Opentelemetry/Proto/Metrics/V1/Metrics.php b/src/bridge/telemetry/otlp/src/GPBMetadata/Opentelemetry/Proto/Metrics/V1/Metrics.php
new file mode 100644
index 000000000..1477368d3
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/GPBMetadata/Opentelemetry/Proto/Metrics/V1/Metrics.php
@@ -0,0 +1,27 @@
+internalAddGeneratedFile(
+ "\x0A\xA3\x1E\x0A,opentelemetry/proto/metrics/v1/metrics.proto\x12\x1Eopentelemetry.proto.metrics.v1\x1A.opentelemetry/proto/resource/v1/resource.proto\"X\x0A\x0BMetricsData\x12I\x0A\x10resource_metrics\x18\x01 \x03(\x0B2/.opentelemetry.proto.metrics.v1.ResourceMetrics\"\xAF\x01\x0A\x0FResourceMetrics\x12;\x0A\x08resource\x18\x01 \x01(\x0B2).opentelemetry.proto.resource.v1.Resource\x12C\x0A\x0Dscope_metrics\x18\x02 \x03(\x0B2,.opentelemetry.proto.metrics.v1.ScopeMetrics\x12\x12\x0A\x0Aschema_url\x18\x03 \x01(\x09J\x06\x08\xE8\x07\x10\xE9\x07\"\x9F\x01\x0A\x0CScopeMetrics\x12B\x0A\x05scope\x18\x01 \x01(\x0B23.opentelemetry.proto.common.v1.InstrumentationScope\x127\x0A\x07metrics\x18\x02 \x03(\x0B2&.opentelemetry.proto.metrics.v1.Metric\x12\x12\x0A\x0Aschema_url\x18\x03 \x01(\x09\"\xCD\x03\x0A\x06Metric\x12\x0C\x0A\x04name\x18\x01 \x01(\x09\x12\x13\x0A\x0Bdescription\x18\x02 \x01(\x09\x12\x0C\x0A\x04unit\x18\x03 \x01(\x09\x126\x0A\x05gauge\x18\x05 \x01(\x0B2%.opentelemetry.proto.metrics.v1.GaugeH\x00\x122\x0A\x03sum\x18\x07 \x01(\x0B2#.opentelemetry.proto.metrics.v1.SumH\x00\x12>\x0A\x09histogram\x18\x09 \x01(\x0B2).opentelemetry.proto.metrics.v1.HistogramH\x00\x12U\x0A\x15exponential_histogram\x18\x0A \x01(\x0B24.opentelemetry.proto.metrics.v1.ExponentialHistogramH\x00\x12:\x0A\x07summary\x18\x0B \x01(\x0B2'.opentelemetry.proto.metrics.v1.SummaryH\x00\x129\x0A\x08metadata\x18\x0C \x03(\x0B2'.opentelemetry.proto.common.v1.KeyValueB\x06\x0A\x04dataJ\x04\x08\x04\x10\x05J\x04\x08\x06\x10\x07J\x04\x08\x08\x10\x09\"M\x0A\x05Gauge\x12D\x0A\x0Bdata_points\x18\x01 \x03(\x0B2/.opentelemetry.proto.metrics.v1.NumberDataPoint\"\xBA\x01\x0A\x03Sum\x12D\x0A\x0Bdata_points\x18\x01 \x03(\x0B2/.opentelemetry.proto.metrics.v1.NumberDataPoint\x12W\x0A\x17aggregation_temporality\x18\x02 \x01(\x0E26.opentelemetry.proto.metrics.v1.AggregationTemporality\x12\x14\x0A\x0Cis_monotonic\x18\x03 \x01(\x08\"\xAD\x01\x0A\x09Histogram\x12G\x0A\x0Bdata_points\x18\x01 \x03(\x0B22.opentelemetry.proto.metrics.v1.HistogramDataPoint\x12W\x0A\x17aggregation_temporality\x18\x02 \x01(\x0E26.opentelemetry.proto.metrics.v1.AggregationTemporality\"\xC3\x01\x0A\x14ExponentialHistogram\x12R\x0A\x0Bdata_points\x18\x01 \x03(\x0B2=.opentelemetry.proto.metrics.v1.ExponentialHistogramDataPoint\x12W\x0A\x17aggregation_temporality\x18\x02 \x01(\x0E26.opentelemetry.proto.metrics.v1.AggregationTemporality\"P\x0A\x07Summary\x12E\x0A\x0Bdata_points\x18\x01 \x03(\x0B20.opentelemetry.proto.metrics.v1.SummaryDataPoint\"\x86\x02\x0A\x0FNumberDataPoint\x12;\x0A\x0Aattributes\x18\x07 \x03(\x0B2'.opentelemetry.proto.common.v1.KeyValue\x12\x1C\x0A\x14start_time_unix_nano\x18\x02 \x01(\x06\x12\x16\x0A\x0Etime_unix_nano\x18\x03 \x01(\x06\x12\x13\x0A\x09as_double\x18\x04 \x01(\x01H\x00\x12\x10\x0A\x06as_int\x18\x06 \x01(\x10H\x00\x12;\x0A\x09exemplars\x18\x05 \x03(\x0B2(.opentelemetry.proto.metrics.v1.Exemplar\x12\x0D\x0A\x05flags\x18\x08 \x01(\x0DB\x07\x0A\x05valueJ\x04\x08\x01\x10\x02\"\xE6\x02\x0A\x12HistogramDataPoint\x12;\x0A\x0Aattributes\x18\x09 \x03(\x0B2'.opentelemetry.proto.common.v1.KeyValue\x12\x1C\x0A\x14start_time_unix_nano\x18\x02 \x01(\x06\x12\x16\x0A\x0Etime_unix_nano\x18\x03 \x01(\x06\x12\x0D\x0A\x05count\x18\x04 \x01(\x06\x12\x10\x0A\x03sum\x18\x05 \x01(\x01H\x00\x88\x01\x01\x12\x15\x0A\x0Dbucket_counts\x18\x06 \x03(\x06\x12\x17\x0A\x0Fexplicit_bounds\x18\x07 \x03(\x01\x12;\x0A\x09exemplars\x18\x08 \x03(\x0B2(.opentelemetry.proto.metrics.v1.Exemplar\x12\x0D\x0A\x05flags\x18\x0A \x01(\x0D\x12\x10\x0A\x03min\x18\x0B \x01(\x01H\x01\x88\x01\x01\x12\x10\x0A\x03max\x18\x0C \x01(\x01H\x02\x88\x01\x01B\x06\x0A\x04_sumB\x06\x0A\x04_minB\x06\x0A\x04_maxJ\x04\x08\x01\x10\x02\"\xDA\x04\x0A\x1DExponentialHistogramDataPoint\x12;\x0A\x0Aattributes\x18\x01 \x03(\x0B2'.opentelemetry.proto.common.v1.KeyValue\x12\x1C\x0A\x14start_time_unix_nano\x18\x02 \x01(\x06\x12\x16\x0A\x0Etime_unix_nano\x18\x03 \x01(\x06\x12\x0D\x0A\x05count\x18\x04 \x01(\x06\x12\x10\x0A\x03sum\x18\x05 \x01(\x01H\x00\x88\x01\x01\x12\x0D\x0A\x05scale\x18\x06 \x01(\x11\x12\x12\x0A\x0Azero_count\x18\x07 \x01(\x06\x12W\x0A\x08positive\x18\x08 \x01(\x0B2E.opentelemetry.proto.metrics.v1.ExponentialHistogramDataPoint.Buckets\x12W\x0A\x08negative\x18\x09 \x01(\x0B2E.opentelemetry.proto.metrics.v1.ExponentialHistogramDataPoint.Buckets\x12\x0D\x0A\x05flags\x18\x0A \x01(\x0D\x12;\x0A\x09exemplars\x18\x0B \x03(\x0B2(.opentelemetry.proto.metrics.v1.Exemplar\x12\x10\x0A\x03min\x18\x0C \x01(\x01H\x01\x88\x01\x01\x12\x10\x0A\x03max\x18\x0D \x01(\x01H\x02\x88\x01\x01\x12\x16\x0A\x0Ezero_threshold\x18\x0E \x01(\x01\x1A0\x0A\x07Buckets\x12\x0E\x0A\x06offset\x18\x01 \x01(\x11\x12\x15\x0A\x0Dbucket_counts\x18\x02 \x03(\x04B\x06\x0A\x04_sumB\x06\x0A\x04_minB\x06\x0A\x04_max\"\xC5\x02\x0A\x10SummaryDataPoint\x12;\x0A\x0Aattributes\x18\x07 \x03(\x0B2'.opentelemetry.proto.common.v1.KeyValue\x12\x1C\x0A\x14start_time_unix_nano\x18\x02 \x01(\x06\x12\x16\x0A\x0Etime_unix_nano\x18\x03 \x01(\x06\x12\x0D\x0A\x05count\x18\x04 \x01(\x06\x12\x0B\x0A\x03sum\x18\x05 \x01(\x01\x12Y\x0A\x0Fquantile_values\x18\x06 \x03(\x0B2@.opentelemetry.proto.metrics.v1.SummaryDataPoint.ValueAtQuantile\x12\x0D\x0A\x05flags\x18\x08 \x01(\x0D\x1A2\x0A\x0FValueAtQuantile\x12\x10\x0A\x08quantile\x18\x01 \x01(\x01\x12\x0D\x0A\x05value\x18\x02 \x01(\x01J\x04\x08\x01\x10\x02\"\xC1\x01\x0A\x08Exemplar\x12D\x0A\x13filtered_attributes\x18\x07 \x03(\x0B2'.opentelemetry.proto.common.v1.KeyValue\x12\x16\x0A\x0Etime_unix_nano\x18\x02 \x01(\x06\x12\x13\x0A\x09as_double\x18\x03 \x01(\x01H\x00\x12\x10\x0A\x06as_int\x18\x06 \x01(\x10H\x00\x12\x0F\x0A\x07span_id\x18\x04 \x01(\x0C\x12\x10\x0A\x08trace_id\x18\x05 \x01(\x0CB\x07\x0A\x05valueJ\x04\x08\x01\x10\x02*\x8C\x01\x0A\x16AggregationTemporality\x12'\x0A#AGGREGATION_TEMPORALITY_UNSPECIFIED\x10\x00\x12!\x0A\x1DAGGREGATION_TEMPORALITY_DELTA\x10\x01\x12&\x0A\"AGGREGATION_TEMPORALITY_CUMULATIVE\x10\x02*^\x0A\x0EDataPointFlags\x12\x1F\x0A\x1BDATA_POINT_FLAGS_DO_NOT_USE\x10\x00\x12+\x0A'DATA_POINT_FLAGS_NO_RECORDED_VALUE_MASK\x10\x01B\x7F\x0A!io.opentelemetry.proto.metrics.v1B\x0CMetricsProtoP\x01Z)go.opentelemetry.io/proto/otlp/metrics/v1\xAA\x02\x1EOpenTelemetry.Proto.Metrics.V1b\x06proto3"
+ , true);
+
+ static::$is_initialized = true;
+ }
+}
+
diff --git a/src/bridge/telemetry/otlp/src/GPBMetadata/Opentelemetry/Proto/Profiles/V1Development/Profiles.php b/src/bridge/telemetry/otlp/src/GPBMetadata/Opentelemetry/Proto/Profiles/V1Development/Profiles.php
new file mode 100644
index 000000000..9f5843b6e
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/GPBMetadata/Opentelemetry/Proto/Profiles/V1Development/Profiles.php
@@ -0,0 +1,27 @@
+internalAddGeneratedFile(
+ "\x0A\xE2\x14\x0A9opentelemetry/proto/profiles/v1development/profiles.proto\x12*opentelemetry.proto.profiles.v1development\x1A.opentelemetry/proto/resource/v1/resource.proto\"\xF6\x03\x0A\x12ProfilesDictionary\x12J\x0A\x0Dmapping_table\x18\x01 \x03(\x0B23.opentelemetry.proto.profiles.v1development.Mapping\x12L\x0A\x0Elocation_table\x18\x02 \x03(\x0B24.opentelemetry.proto.profiles.v1development.Location\x12L\x0A\x0Efunction_table\x18\x03 \x03(\x0B24.opentelemetry.proto.profiles.v1development.Function\x12D\x0A\x0Alink_table\x18\x04 \x03(\x0B20.opentelemetry.proto.profiles.v1development.Link\x12\x14\x0A\x0Cstring_table\x18\x05 \x03(\x09\x12T\x0A\x0Fattribute_table\x18\x06 \x03(\x0B2;.opentelemetry.proto.profiles.v1development.KeyValueAndUnit\x12F\x0A\x0Bstack_table\x18\x07 \x03(\x0B21.opentelemetry.proto.profiles.v1development.Stack\"\xBB\x01\x0A\x0CProfilesData\x12W\x0A\x11resource_profiles\x18\x01 \x03(\x0B2<.opentelemetry.proto.profiles.v1development.ResourceProfiles\x12R\x0A\x0Adictionary\x18\x02 \x01(\x0B2>.opentelemetry.proto.profiles.v1development.ProfilesDictionary\"\xBE\x01\x0A\x10ResourceProfiles\x12;\x0A\x08resource\x18\x01 \x01(\x0B2).opentelemetry.proto.resource.v1.Resource\x12Q\x0A\x0Escope_profiles\x18\x02 \x03(\x0B29.opentelemetry.proto.profiles.v1development.ScopeProfiles\x12\x12\x0A\x0Aschema_url\x18\x03 \x01(\x09J\x06\x08\xE8\x07\x10\xE9\x07\"\xAE\x01\x0A\x0DScopeProfiles\x12B\x0A\x05scope\x18\x01 \x01(\x0B23.opentelemetry.proto.common.v1.InstrumentationScope\x12E\x0A\x08profiles\x18\x02 \x03(\x0B23.opentelemetry.proto.profiles.v1development.Profile\x12\x12\x0A\x0Aschema_url\x18\x03 \x01(\x09\"\xB1\x03\x0A\x07Profile\x12J\x0A\x0Bsample_type\x18\x01 \x01(\x0B25.opentelemetry.proto.profiles.v1development.ValueType\x12C\x0A\x07samples\x18\x02 \x03(\x0B22.opentelemetry.proto.profiles.v1development.Sample\x12\x16\x0A\x0Etime_unix_nano\x18\x03 \x01(\x06\x12\x15\x0A\x0Dduration_nano\x18\x04 \x01(\x04\x12J\x0A\x0Bperiod_type\x18\x05 \x01(\x0B25.opentelemetry.proto.profiles.v1development.ValueType\x12\x0E\x0A\x06period\x18\x06 \x01(\x03\x12\x12\x0A\x0Aprofile_id\x18\x07 \x01(\x0C\x12 \x0A\x18dropped_attributes_count\x18\x08 \x01(\x0D\x12\x1F\x0A\x17original_payload_format\x18\x09 \x01(\x09\x12\x18\x0A\x10original_payload\x18\x0A \x01(\x0C\x12\x19\x0A\x11attribute_indices\x18\x0B \x03(\x05\")\x0A\x04Link\x12\x10\x0A\x08trace_id\x18\x01 \x01(\x0C\x12\x0F\x0A\x07span_id\x18\x02 \x01(\x0C\"9\x0A\x09ValueType\x12\x15\x0A\x0Dtype_strindex\x18\x01 \x01(\x05\x12\x15\x0A\x0Dunit_strindex\x18\x02 \x01(\x05\"z\x0A\x06Sample\x12\x13\x0A\x0Bstack_index\x18\x01 \x01(\x05\x12\x19\x0A\x11attribute_indices\x18\x02 \x03(\x05\x12\x12\x0A\x0Alink_index\x18\x03 \x01(\x05\x12\x0E\x0A\x06values\x18\x04 \x03(\x03\x12\x1C\x0A\x14timestamps_unix_nano\x18\x05 \x03(\x06\"\x80\x01\x0A\x07Mapping\x12\x14\x0A\x0Cmemory_start\x18\x01 \x01(\x04\x12\x14\x0A\x0Cmemory_limit\x18\x02 \x01(\x04\x12\x13\x0A\x0Bfile_offset\x18\x03 \x01(\x04\x12\x19\x0A\x11filename_strindex\x18\x04 \x01(\x05\x12\x19\x0A\x11attribute_indices\x18\x05 \x03(\x05\"!\x0A\x05Stack\x12\x18\x0A\x10location_indices\x18\x01 \x03(\x05\"\x8E\x01\x0A\x08Location\x12\x15\x0A\x0Dmapping_index\x18\x01 \x01(\x05\x12\x0F\x0A\x07address\x18\x02 \x01(\x04\x12?\x0A\x05lines\x18\x03 \x03(\x0B20.opentelemetry.proto.profiles.v1development.Line\x12\x19\x0A\x11attribute_indices\x18\x04 \x03(\x05\"<\x0A\x04Line\x12\x16\x0A\x0Efunction_index\x18\x01 \x01(\x05\x12\x0C\x0A\x04line\x18\x02 \x01(\x03\x12\x0E\x0A\x06column\x18\x03 \x01(\x03\"n\x0A\x08Function\x12\x15\x0A\x0Dname_strindex\x18\x01 \x01(\x05\x12\x1C\x0A\x14system_name_strindex\x18\x02 \x01(\x05\x12\x19\x0A\x11filename_strindex\x18\x03 \x01(\x05\x12\x12\x0A\x0Astart_line\x18\x04 \x01(\x03\"v\x0A\x0FKeyValueAndUnit\x12\x14\x0A\x0Ckey_strindex\x18\x01 \x01(\x05\x126\x0A\x05value\x18\x02 \x01(\x0B2'.opentelemetry.proto.common.v1.AnyValue\x12\x15\x0A\x0Dunit_strindex\x18\x03 \x01(\x05B\xA4\x01\x0A-io.opentelemetry.proto.profiles.v1developmentB\x0DProfilesProtoP\x01Z5go.opentelemetry.io/proto/otlp/profiles/v1development\xAA\x02*OpenTelemetry.Proto.Profiles.V1Developmentb\x06proto3"
+ , true);
+
+ static::$is_initialized = true;
+ }
+}
+
diff --git a/src/bridge/telemetry/otlp/src/GPBMetadata/Opentelemetry/Proto/Resource/V1/Resource.php b/src/bridge/telemetry/otlp/src/GPBMetadata/Opentelemetry/Proto/Resource/V1/Resource.php
new file mode 100644
index 000000000..e42b22b62
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/GPBMetadata/Opentelemetry/Proto/Resource/V1/Resource.php
@@ -0,0 +1,26 @@
+internalAddGeneratedFile(
+ "\x0A\x8A\x03\x0A.opentelemetry/proto/resource/v1/resource.proto\x12\x1Fopentelemetry.proto.resource.v1\"\xA8\x01\x0A\x08Resource\x12;\x0A\x0Aattributes\x18\x01 \x03(\x0B2'.opentelemetry.proto.common.v1.KeyValue\x12 \x0A\x18dropped_attributes_count\x18\x02 \x01(\x0D\x12=\x0A\x0Bentity_refs\x18\x03 \x03(\x0B2(.opentelemetry.proto.common.v1.EntityRefB\x83\x01\x0A\"io.opentelemetry.proto.resource.v1B\x0DResourceProtoP\x01Z*go.opentelemetry.io/proto/otlp/resource/v1\xAA\x02\x1FOpenTelemetry.Proto.Resource.V1b\x06proto3"
+ , true);
+
+ static::$is_initialized = true;
+ }
+}
+
diff --git a/src/bridge/telemetry/otlp/src/GPBMetadata/Opentelemetry/Proto/Trace/V1/Trace.php b/src/bridge/telemetry/otlp/src/GPBMetadata/Opentelemetry/Proto/Trace/V1/Trace.php
new file mode 100644
index 000000000..5319bcfcf
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/GPBMetadata/Opentelemetry/Proto/Trace/V1/Trace.php
@@ -0,0 +1,27 @@
+internalAddGeneratedFile(
+ "\x0A\xE7\x0F\x0A(opentelemetry/proto/trace/v1/trace.proto\x12\x1Copentelemetry.proto.trace.v1\x1A.opentelemetry/proto/resource/v1/resource.proto\"Q\x0A\x0ATracesData\x12C\x0A\x0Eresource_spans\x18\x01 \x03(\x0B2+.opentelemetry.proto.trace.v1.ResourceSpans\"\xA7\x01\x0A\x0DResourceSpans\x12;\x0A\x08resource\x18\x01 \x01(\x0B2).opentelemetry.proto.resource.v1.Resource\x12=\x0A\x0Bscope_spans\x18\x02 \x03(\x0B2(.opentelemetry.proto.trace.v1.ScopeSpans\x12\x12\x0A\x0Aschema_url\x18\x03 \x01(\x09J\x06\x08\xE8\x07\x10\xE9\x07\"\x97\x01\x0A\x0AScopeSpans\x12B\x0A\x05scope\x18\x01 \x01(\x0B23.opentelemetry.proto.common.v1.InstrumentationScope\x121\x0A\x05spans\x18\x02 \x03(\x0B2\".opentelemetry.proto.trace.v1.Span\x12\x12\x0A\x0Aschema_url\x18\x03 \x01(\x09\"\x84\x08\x0A\x04Span\x12\x10\x0A\x08trace_id\x18\x01 \x01(\x0C\x12\x0F\x0A\x07span_id\x18\x02 \x01(\x0C\x12\x13\x0A\x0Btrace_state\x18\x03 \x01(\x09\x12\x16\x0A\x0Eparent_span_id\x18\x04 \x01(\x0C\x12\x0D\x0A\x05flags\x18\x10 \x01(\x07\x12\x0C\x0A\x04name\x18\x05 \x01(\x09\x129\x0A\x04kind\x18\x06 \x01(\x0E2+.opentelemetry.proto.trace.v1.Span.SpanKind\x12\x1C\x0A\x14start_time_unix_nano\x18\x07 \x01(\x06\x12\x1A\x0A\x12end_time_unix_nano\x18\x08 \x01(\x06\x12;\x0A\x0Aattributes\x18\x09 \x03(\x0B2'.opentelemetry.proto.common.v1.KeyValue\x12 \x0A\x18dropped_attributes_count\x18\x0A \x01(\x0D\x128\x0A\x06events\x18\x0B \x03(\x0B2(.opentelemetry.proto.trace.v1.Span.Event\x12\x1C\x0A\x14dropped_events_count\x18\x0C \x01(\x0D\x126\x0A\x05links\x18\x0D \x03(\x0B2'.opentelemetry.proto.trace.v1.Span.Link\x12\x1B\x0A\x13dropped_links_count\x18\x0E \x01(\x0D\x124\x0A\x06status\x18\x0F \x01(\x0B2\$.opentelemetry.proto.trace.v1.Status\x1A\x8C\x01\x0A\x05Event\x12\x16\x0A\x0Etime_unix_nano\x18\x01 \x01(\x06\x12\x0C\x0A\x04name\x18\x02 \x01(\x09\x12;\x0A\x0Aattributes\x18\x03 \x03(\x0B2'.opentelemetry.proto.common.v1.KeyValue\x12 \x0A\x18dropped_attributes_count\x18\x04 \x01(\x0D\x1A\xAC\x01\x0A\x04Link\x12\x10\x0A\x08trace_id\x18\x01 \x01(\x0C\x12\x0F\x0A\x07span_id\x18\x02 \x01(\x0C\x12\x13\x0A\x0Btrace_state\x18\x03 \x01(\x09\x12;\x0A\x0Aattributes\x18\x04 \x03(\x0B2'.opentelemetry.proto.common.v1.KeyValue\x12 \x0A\x18dropped_attributes_count\x18\x05 \x01(\x0D\x12\x0D\x0A\x05flags\x18\x06 \x01(\x07\"\x99\x01\x0A\x08SpanKind\x12\x19\x0A\x15SPAN_KIND_UNSPECIFIED\x10\x00\x12\x16\x0A\x12SPAN_KIND_INTERNAL\x10\x01\x12\x14\x0A\x10SPAN_KIND_SERVER\x10\x02\x12\x14\x0A\x10SPAN_KIND_CLIENT\x10\x03\x12\x16\x0A\x12SPAN_KIND_PRODUCER\x10\x04\x12\x16\x0A\x12SPAN_KIND_CONSUMER\x10\x05\"\xAE\x01\x0A\x06Status\x12\x0F\x0A\x07message\x18\x02 \x01(\x09\x12=\x0A\x04code\x18\x03 \x01(\x0E2/.opentelemetry.proto.trace.v1.Status.StatusCode\"N\x0A\x0AStatusCode\x12\x15\x0A\x11STATUS_CODE_UNSET\x10\x00\x12\x12\x0A\x0ESTATUS_CODE_OK\x10\x01\x12\x15\x0A\x11STATUS_CODE_ERROR\x10\x02J\x04\x08\x01\x10\x02*\x9C\x01\x0A\x09SpanFlags\x12\x19\x0A\x15SPAN_FLAGS_DO_NOT_USE\x10\x00\x12 \x0A\x1BSPAN_FLAGS_TRACE_FLAGS_MASK\x10\xFF\x01\x12*\x0A%SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK\x10\x80\x02\x12&\x0A!SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK\x10\x80\x04Bw\x0A\x1Fio.opentelemetry.proto.trace.v1B\x0ATraceProtoP\x01Z'go.opentelemetry.io/proto/otlp/trace/v1\xAA\x02\x1COpenTelemetry.Proto.Trace.V1b\x06proto3"
+ , true);
+
+ static::$is_initialized = true;
+ }
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Collector/Logs/V1/ExportLogsPartialSuccess.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Collector/Logs/V1/ExportLogsPartialSuccess.php
new file mode 100644
index 000000000..d5fcc0254
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Collector/Logs/V1/ExportLogsPartialSuccess.php
@@ -0,0 +1,128 @@
+opentelemetry.proto.collector.logs.v1.ExportLogsPartialSuccess
+ */
+class ExportLogsPartialSuccess extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * The number of rejected log records.
+ * A `rejected_` field holding a `0` value indicates that the
+ * request was fully accepted.
+ *
+ * Generated from protobuf field int64 rejected_log_records = 1;
+ */
+ protected $rejected_log_records = 0;
+ /**
+ * A developer-facing human-readable message in English. It should be used
+ * either to explain why the server rejected parts of the data during a partial
+ * success or to convey warnings/suggestions during a full success. The message
+ * should offer guidance on how users can address such issues.
+ * error_message is an optional field. An error_message with an empty value
+ * is equivalent to it not being set.
+ *
+ * Generated from protobuf field string error_message = 2;
+ */
+ protected $error_message = '';
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type int|string $rejected_log_records
+ * The number of rejected log records.
+ * A `rejected_` field holding a `0` value indicates that the
+ * request was fully accepted.
+ * @type string $error_message
+ * A developer-facing human-readable message in English. It should be used
+ * either to explain why the server rejected parts of the data during a partial
+ * success or to convey warnings/suggestions during a full success. The message
+ * should offer guidance on how users can address such issues.
+ * error_message is an optional field. An error_message with an empty value
+ * is equivalent to it not being set.
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Collector\Logs\V1\LogsService::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * The number of rejected log records.
+ * A `rejected_` field holding a `0` value indicates that the
+ * request was fully accepted.
+ *
+ * Generated from protobuf field int64 rejected_log_records = 1;
+ * @return int|string
+ */
+ public function getRejectedLogRecords()
+ {
+ return $this->rejected_log_records;
+ }
+
+ /**
+ * The number of rejected log records.
+ * A `rejected_` field holding a `0` value indicates that the
+ * request was fully accepted.
+ *
+ * Generated from protobuf field int64 rejected_log_records = 1;
+ * @param int|string $var
+ * @return $this
+ */
+ public function setRejectedLogRecords(int|string $var)
+ {
+ GPBUtil::checkInt64($var);
+ $this->rejected_log_records = $var;
+
+ return $this;
+ }
+
+ /**
+ * A developer-facing human-readable message in English. It should be used
+ * either to explain why the server rejected parts of the data during a partial
+ * success or to convey warnings/suggestions during a full success. The message
+ * should offer guidance on how users can address such issues.
+ * error_message is an optional field. An error_message with an empty value
+ * is equivalent to it not being set.
+ *
+ * Generated from protobuf field string error_message = 2;
+ * @return string
+ */
+ public function getErrorMessage()
+ {
+ return $this->error_message;
+ }
+
+ /**
+ * A developer-facing human-readable message in English. It should be used
+ * either to explain why the server rejected parts of the data during a partial
+ * success or to convey warnings/suggestions during a full success. The message
+ * should offer guidance on how users can address such issues.
+ * error_message is an optional field. An error_message with an empty value
+ * is equivalent to it not being set.
+ *
+ * Generated from protobuf field string error_message = 2;
+ * @param string $var
+ * @return $this
+ */
+ public function setErrorMessage(string $var)
+ {
+ GPBUtil::checkString($var, true);
+ $this->error_message = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Collector/Logs/V1/ExportLogsServiceRequest.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Collector/Logs/V1/ExportLogsServiceRequest.php
new file mode 100644
index 000000000..e4d96cc6d
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Collector/Logs/V1/ExportLogsServiceRequest.php
@@ -0,0 +1,82 @@
+opentelemetry.proto.collector.logs.v1.ExportLogsServiceRequest
+ */
+class ExportLogsServiceRequest extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * An array of ResourceLogs.
+ * For data coming from a single resource this array will typically contain one
+ * element. Intermediary nodes (such as OpenTelemetry Collector) that receive
+ * data from multiple origins typically batch the data before forwarding further and
+ * in that case this array will contain multiple elements.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.logs.v1.ResourceLogs resource_logs = 1;
+ */
+ private $resource_logs;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type \Opentelemetry\Proto\Logs\V1\ResourceLogs[] $resource_logs
+ * An array of ResourceLogs.
+ * For data coming from a single resource this array will typically contain one
+ * element. Intermediary nodes (such as OpenTelemetry Collector) that receive
+ * data from multiple origins typically batch the data before forwarding further and
+ * in that case this array will contain multiple elements.
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Collector\Logs\V1\LogsService::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * An array of ResourceLogs.
+ * For data coming from a single resource this array will typically contain one
+ * element. Intermediary nodes (such as OpenTelemetry Collector) that receive
+ * data from multiple origins typically batch the data before forwarding further and
+ * in that case this array will contain multiple elements.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.logs.v1.ResourceLogs resource_logs = 1;
+ * @return RepeatedField<\Opentelemetry\Proto\Logs\V1\ResourceLogs>
+ */
+ public function getResourceLogs()
+ {
+ return $this->resource_logs;
+ }
+
+ /**
+ * An array of ResourceLogs.
+ * For data coming from a single resource this array will typically contain one
+ * element. Intermediary nodes (such as OpenTelemetry Collector) that receive
+ * data from multiple origins typically batch the data before forwarding further and
+ * in that case this array will contain multiple elements.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.logs.v1.ResourceLogs resource_logs = 1;
+ * @param \Opentelemetry\Proto\Logs\V1\ResourceLogs[] $var
+ * @return $this
+ */
+ public function setResourceLogs(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Opentelemetry\Proto\Logs\V1\ResourceLogs::class);
+ $this->resource_logs = $arr;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Collector/Logs/V1/ExportLogsServiceResponse.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Collector/Logs/V1/ExportLogsServiceResponse.php
new file mode 100644
index 000000000..f198bb6af
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Collector/Logs/V1/ExportLogsServiceResponse.php
@@ -0,0 +1,119 @@
+opentelemetry.proto.collector.logs.v1.ExportLogsServiceResponse
+ */
+class ExportLogsServiceResponse extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * The details of a partially successful export request.
+ * If the request is only partially accepted
+ * (i.e. when the server accepts only parts of the data and rejects the rest)
+ * the server MUST initialize the `partial_success` field and MUST
+ * set the `rejected_` with the number of items it rejected.
+ * Servers MAY also make use of the `partial_success` field to convey
+ * warnings/suggestions to senders even when the request was fully accepted.
+ * In such cases, the `rejected_` MUST have a value of `0` and
+ * the `error_message` MUST be non-empty.
+ * A `partial_success` message with an empty value (rejected_ = 0 and
+ * `error_message` = "") is equivalent to it not being set/present. Senders
+ * SHOULD interpret it the same way as in the full success case.
+ *
+ * Generated from protobuf field .opentelemetry.proto.collector.logs.v1.ExportLogsPartialSuccess partial_success = 1;
+ */
+ protected $partial_success = null;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type \Opentelemetry\Proto\Collector\Logs\V1\ExportLogsPartialSuccess $partial_success
+ * The details of a partially successful export request.
+ * If the request is only partially accepted
+ * (i.e. when the server accepts only parts of the data and rejects the rest)
+ * the server MUST initialize the `partial_success` field and MUST
+ * set the `rejected_` with the number of items it rejected.
+ * Servers MAY also make use of the `partial_success` field to convey
+ * warnings/suggestions to senders even when the request was fully accepted.
+ * In such cases, the `rejected_` MUST have a value of `0` and
+ * the `error_message` MUST be non-empty.
+ * A `partial_success` message with an empty value (rejected_ = 0 and
+ * `error_message` = "") is equivalent to it not being set/present. Senders
+ * SHOULD interpret it the same way as in the full success case.
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Collector\Logs\V1\LogsService::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * The details of a partially successful export request.
+ * If the request is only partially accepted
+ * (i.e. when the server accepts only parts of the data and rejects the rest)
+ * the server MUST initialize the `partial_success` field and MUST
+ * set the `rejected_` with the number of items it rejected.
+ * Servers MAY also make use of the `partial_success` field to convey
+ * warnings/suggestions to senders even when the request was fully accepted.
+ * In such cases, the `rejected_` MUST have a value of `0` and
+ * the `error_message` MUST be non-empty.
+ * A `partial_success` message with an empty value (rejected_ = 0 and
+ * `error_message` = "") is equivalent to it not being set/present. Senders
+ * SHOULD interpret it the same way as in the full success case.
+ *
+ * Generated from protobuf field .opentelemetry.proto.collector.logs.v1.ExportLogsPartialSuccess partial_success = 1;
+ * @return \Opentelemetry\Proto\Collector\Logs\V1\ExportLogsPartialSuccess|null
+ */
+ public function getPartialSuccess()
+ {
+ return $this->partial_success;
+ }
+
+ public function hasPartialSuccess()
+ {
+ return isset($this->partial_success);
+ }
+
+ public function clearPartialSuccess()
+ {
+ unset($this->partial_success);
+ }
+
+ /**
+ * The details of a partially successful export request.
+ * If the request is only partially accepted
+ * (i.e. when the server accepts only parts of the data and rejects the rest)
+ * the server MUST initialize the `partial_success` field and MUST
+ * set the `rejected_` with the number of items it rejected.
+ * Servers MAY also make use of the `partial_success` field to convey
+ * warnings/suggestions to senders even when the request was fully accepted.
+ * In such cases, the `rejected_` MUST have a value of `0` and
+ * the `error_message` MUST be non-empty.
+ * A `partial_success` message with an empty value (rejected_ = 0 and
+ * `error_message` = "") is equivalent to it not being set/present. Senders
+ * SHOULD interpret it the same way as in the full success case.
+ *
+ * Generated from protobuf field .opentelemetry.proto.collector.logs.v1.ExportLogsPartialSuccess partial_success = 1;
+ * @param \Opentelemetry\Proto\Collector\Logs\V1\ExportLogsPartialSuccess $var
+ * @return $this
+ */
+ public function setPartialSuccess(\Opentelemetry\Proto\Collector\Logs\V1\ExportLogsPartialSuccess|null $var)
+ {
+ $this->partial_success = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Collector/Logs/V1/LogsServiceClient.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Collector/Logs/V1/LogsServiceClient.php
new file mode 100644
index 000000000..8e4ec7d4a
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Collector/Logs/V1/LogsServiceClient.php
@@ -0,0 +1,51 @@
+
+ */
+ public function Export(\Opentelemetry\Proto\Collector\Logs\V1\ExportLogsServiceRequest $argument,
+ $metadata = [], $options = []) {
+ return $this->_simpleRequest('/opentelemetry.proto.collector.logs.v1.LogsService/Export',
+ $argument,
+ ['\Opentelemetry\Proto\Collector\Logs\V1\ExportLogsServiceResponse', 'decode'],
+ $metadata, $options);
+ }
+
+}
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Collector/Metrics/V1/ExportMetricsPartialSuccess.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Collector/Metrics/V1/ExportMetricsPartialSuccess.php
new file mode 100644
index 000000000..232b40221
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Collector/Metrics/V1/ExportMetricsPartialSuccess.php
@@ -0,0 +1,128 @@
+opentelemetry.proto.collector.metrics.v1.ExportMetricsPartialSuccess
+ */
+class ExportMetricsPartialSuccess extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * The number of rejected data points.
+ * A `rejected_` field holding a `0` value indicates that the
+ * request was fully accepted.
+ *
+ * Generated from protobuf field int64 rejected_data_points = 1;
+ */
+ protected $rejected_data_points = 0;
+ /**
+ * A developer-facing human-readable message in English. It should be used
+ * either to explain why the server rejected parts of the data during a partial
+ * success or to convey warnings/suggestions during a full success. The message
+ * should offer guidance on how users can address such issues.
+ * error_message is an optional field. An error_message with an empty value
+ * is equivalent to it not being set.
+ *
+ * Generated from protobuf field string error_message = 2;
+ */
+ protected $error_message = '';
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type int|string $rejected_data_points
+ * The number of rejected data points.
+ * A `rejected_` field holding a `0` value indicates that the
+ * request was fully accepted.
+ * @type string $error_message
+ * A developer-facing human-readable message in English. It should be used
+ * either to explain why the server rejected parts of the data during a partial
+ * success or to convey warnings/suggestions during a full success. The message
+ * should offer guidance on how users can address such issues.
+ * error_message is an optional field. An error_message with an empty value
+ * is equivalent to it not being set.
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Collector\Metrics\V1\MetricsService::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * The number of rejected data points.
+ * A `rejected_` field holding a `0` value indicates that the
+ * request was fully accepted.
+ *
+ * Generated from protobuf field int64 rejected_data_points = 1;
+ * @return int|string
+ */
+ public function getRejectedDataPoints()
+ {
+ return $this->rejected_data_points;
+ }
+
+ /**
+ * The number of rejected data points.
+ * A `rejected_` field holding a `0` value indicates that the
+ * request was fully accepted.
+ *
+ * Generated from protobuf field int64 rejected_data_points = 1;
+ * @param int|string $var
+ * @return $this
+ */
+ public function setRejectedDataPoints(int|string $var)
+ {
+ GPBUtil::checkInt64($var);
+ $this->rejected_data_points = $var;
+
+ return $this;
+ }
+
+ /**
+ * A developer-facing human-readable message in English. It should be used
+ * either to explain why the server rejected parts of the data during a partial
+ * success or to convey warnings/suggestions during a full success. The message
+ * should offer guidance on how users can address such issues.
+ * error_message is an optional field. An error_message with an empty value
+ * is equivalent to it not being set.
+ *
+ * Generated from protobuf field string error_message = 2;
+ * @return string
+ */
+ public function getErrorMessage()
+ {
+ return $this->error_message;
+ }
+
+ /**
+ * A developer-facing human-readable message in English. It should be used
+ * either to explain why the server rejected parts of the data during a partial
+ * success or to convey warnings/suggestions during a full success. The message
+ * should offer guidance on how users can address such issues.
+ * error_message is an optional field. An error_message with an empty value
+ * is equivalent to it not being set.
+ *
+ * Generated from protobuf field string error_message = 2;
+ * @param string $var
+ * @return $this
+ */
+ public function setErrorMessage(string $var)
+ {
+ GPBUtil::checkString($var, true);
+ $this->error_message = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Collector/Metrics/V1/ExportMetricsServiceRequest.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Collector/Metrics/V1/ExportMetricsServiceRequest.php
new file mode 100644
index 000000000..ec1fd58f2
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Collector/Metrics/V1/ExportMetricsServiceRequest.php
@@ -0,0 +1,82 @@
+opentelemetry.proto.collector.metrics.v1.ExportMetricsServiceRequest
+ */
+class ExportMetricsServiceRequest extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * An array of ResourceMetrics.
+ * For data coming from a single resource this array will typically contain one
+ * element. Intermediary nodes (such as OpenTelemetry Collector) that receive
+ * data from multiple origins typically batch the data before forwarding further and
+ * in that case this array will contain multiple elements.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.metrics.v1.ResourceMetrics resource_metrics = 1;
+ */
+ private $resource_metrics;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type \Opentelemetry\Proto\Metrics\V1\ResourceMetrics[] $resource_metrics
+ * An array of ResourceMetrics.
+ * For data coming from a single resource this array will typically contain one
+ * element. Intermediary nodes (such as OpenTelemetry Collector) that receive
+ * data from multiple origins typically batch the data before forwarding further and
+ * in that case this array will contain multiple elements.
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Collector\Metrics\V1\MetricsService::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * An array of ResourceMetrics.
+ * For data coming from a single resource this array will typically contain one
+ * element. Intermediary nodes (such as OpenTelemetry Collector) that receive
+ * data from multiple origins typically batch the data before forwarding further and
+ * in that case this array will contain multiple elements.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.metrics.v1.ResourceMetrics resource_metrics = 1;
+ * @return RepeatedField<\Opentelemetry\Proto\Metrics\V1\ResourceMetrics>
+ */
+ public function getResourceMetrics()
+ {
+ return $this->resource_metrics;
+ }
+
+ /**
+ * An array of ResourceMetrics.
+ * For data coming from a single resource this array will typically contain one
+ * element. Intermediary nodes (such as OpenTelemetry Collector) that receive
+ * data from multiple origins typically batch the data before forwarding further and
+ * in that case this array will contain multiple elements.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.metrics.v1.ResourceMetrics resource_metrics = 1;
+ * @param \Opentelemetry\Proto\Metrics\V1\ResourceMetrics[] $var
+ * @return $this
+ */
+ public function setResourceMetrics(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Opentelemetry\Proto\Metrics\V1\ResourceMetrics::class);
+ $this->resource_metrics = $arr;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Collector/Metrics/V1/ExportMetricsServiceResponse.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Collector/Metrics/V1/ExportMetricsServiceResponse.php
new file mode 100644
index 000000000..65ce4e1bf
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Collector/Metrics/V1/ExportMetricsServiceResponse.php
@@ -0,0 +1,119 @@
+opentelemetry.proto.collector.metrics.v1.ExportMetricsServiceResponse
+ */
+class ExportMetricsServiceResponse extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * The details of a partially successful export request.
+ * If the request is only partially accepted
+ * (i.e. when the server accepts only parts of the data and rejects the rest)
+ * the server MUST initialize the `partial_success` field and MUST
+ * set the `rejected_` with the number of items it rejected.
+ * Servers MAY also make use of the `partial_success` field to convey
+ * warnings/suggestions to senders even when the request was fully accepted.
+ * In such cases, the `rejected_` MUST have a value of `0` and
+ * the `error_message` MUST be non-empty.
+ * A `partial_success` message with an empty value (rejected_ = 0 and
+ * `error_message` = "") is equivalent to it not being set/present. Senders
+ * SHOULD interpret it the same way as in the full success case.
+ *
+ * Generated from protobuf field .opentelemetry.proto.collector.metrics.v1.ExportMetricsPartialSuccess partial_success = 1;
+ */
+ protected $partial_success = null;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type \Opentelemetry\Proto\Collector\Metrics\V1\ExportMetricsPartialSuccess $partial_success
+ * The details of a partially successful export request.
+ * If the request is only partially accepted
+ * (i.e. when the server accepts only parts of the data and rejects the rest)
+ * the server MUST initialize the `partial_success` field and MUST
+ * set the `rejected_` with the number of items it rejected.
+ * Servers MAY also make use of the `partial_success` field to convey
+ * warnings/suggestions to senders even when the request was fully accepted.
+ * In such cases, the `rejected_` MUST have a value of `0` and
+ * the `error_message` MUST be non-empty.
+ * A `partial_success` message with an empty value (rejected_ = 0 and
+ * `error_message` = "") is equivalent to it not being set/present. Senders
+ * SHOULD interpret it the same way as in the full success case.
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Collector\Metrics\V1\MetricsService::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * The details of a partially successful export request.
+ * If the request is only partially accepted
+ * (i.e. when the server accepts only parts of the data and rejects the rest)
+ * the server MUST initialize the `partial_success` field and MUST
+ * set the `rejected_` with the number of items it rejected.
+ * Servers MAY also make use of the `partial_success` field to convey
+ * warnings/suggestions to senders even when the request was fully accepted.
+ * In such cases, the `rejected_` MUST have a value of `0` and
+ * the `error_message` MUST be non-empty.
+ * A `partial_success` message with an empty value (rejected_ = 0 and
+ * `error_message` = "") is equivalent to it not being set/present. Senders
+ * SHOULD interpret it the same way as in the full success case.
+ *
+ * Generated from protobuf field .opentelemetry.proto.collector.metrics.v1.ExportMetricsPartialSuccess partial_success = 1;
+ * @return \Opentelemetry\Proto\Collector\Metrics\V1\ExportMetricsPartialSuccess|null
+ */
+ public function getPartialSuccess()
+ {
+ return $this->partial_success;
+ }
+
+ public function hasPartialSuccess()
+ {
+ return isset($this->partial_success);
+ }
+
+ public function clearPartialSuccess()
+ {
+ unset($this->partial_success);
+ }
+
+ /**
+ * The details of a partially successful export request.
+ * If the request is only partially accepted
+ * (i.e. when the server accepts only parts of the data and rejects the rest)
+ * the server MUST initialize the `partial_success` field and MUST
+ * set the `rejected_` with the number of items it rejected.
+ * Servers MAY also make use of the `partial_success` field to convey
+ * warnings/suggestions to senders even when the request was fully accepted.
+ * In such cases, the `rejected_` MUST have a value of `0` and
+ * the `error_message` MUST be non-empty.
+ * A `partial_success` message with an empty value (rejected_ = 0 and
+ * `error_message` = "") is equivalent to it not being set/present. Senders
+ * SHOULD interpret it the same way as in the full success case.
+ *
+ * Generated from protobuf field .opentelemetry.proto.collector.metrics.v1.ExportMetricsPartialSuccess partial_success = 1;
+ * @param \Opentelemetry\Proto\Collector\Metrics\V1\ExportMetricsPartialSuccess $var
+ * @return $this
+ */
+ public function setPartialSuccess(\Opentelemetry\Proto\Collector\Metrics\V1\ExportMetricsPartialSuccess|null $var)
+ {
+ $this->partial_success = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Collector/Metrics/V1/MetricsServiceClient.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Collector/Metrics/V1/MetricsServiceClient.php
new file mode 100644
index 000000000..896f8534e
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Collector/Metrics/V1/MetricsServiceClient.php
@@ -0,0 +1,51 @@
+
+ */
+ public function Export(\Opentelemetry\Proto\Collector\Metrics\V1\ExportMetricsServiceRequest $argument,
+ $metadata = [], $options = []) {
+ return $this->_simpleRequest('/opentelemetry.proto.collector.metrics.v1.MetricsService/Export',
+ $argument,
+ ['\Opentelemetry\Proto\Collector\Metrics\V1\ExportMetricsServiceResponse', 'decode'],
+ $metadata, $options);
+ }
+
+}
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Collector/Profiles/V1development/ExportProfilesPartialSuccess.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Collector/Profiles/V1development/ExportProfilesPartialSuccess.php
new file mode 100644
index 000000000..6e4ad26df
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Collector/Profiles/V1development/ExportProfilesPartialSuccess.php
@@ -0,0 +1,128 @@
+opentelemetry.proto.collector.profiles.v1development.ExportProfilesPartialSuccess
+ */
+class ExportProfilesPartialSuccess extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * The number of rejected profiles.
+ * A `rejected_` field holding a `0` value indicates that the
+ * request was fully accepted.
+ *
+ * Generated from protobuf field int64 rejected_profiles = 1;
+ */
+ protected $rejected_profiles = 0;
+ /**
+ * A developer-facing human-readable message in English. It should be used
+ * either to explain why the server rejected parts of the data during a partial
+ * success or to convey warnings/suggestions during a full success. The message
+ * should offer guidance on how users can address such issues.
+ * error_message is an optional field. An error_message with an empty value
+ * is equivalent to it not being set.
+ *
+ * Generated from protobuf field string error_message = 2;
+ */
+ protected $error_message = '';
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type int|string $rejected_profiles
+ * The number of rejected profiles.
+ * A `rejected_` field holding a `0` value indicates that the
+ * request was fully accepted.
+ * @type string $error_message
+ * A developer-facing human-readable message in English. It should be used
+ * either to explain why the server rejected parts of the data during a partial
+ * success or to convey warnings/suggestions during a full success. The message
+ * should offer guidance on how users can address such issues.
+ * error_message is an optional field. An error_message with an empty value
+ * is equivalent to it not being set.
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Collector\Profiles\V1Development\ProfilesService::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * The number of rejected profiles.
+ * A `rejected_` field holding a `0` value indicates that the
+ * request was fully accepted.
+ *
+ * Generated from protobuf field int64 rejected_profiles = 1;
+ * @return int|string
+ */
+ public function getRejectedProfiles()
+ {
+ return $this->rejected_profiles;
+ }
+
+ /**
+ * The number of rejected profiles.
+ * A `rejected_` field holding a `0` value indicates that the
+ * request was fully accepted.
+ *
+ * Generated from protobuf field int64 rejected_profiles = 1;
+ * @param int|string $var
+ * @return $this
+ */
+ public function setRejectedProfiles(int|string $var)
+ {
+ GPBUtil::checkInt64($var);
+ $this->rejected_profiles = $var;
+
+ return $this;
+ }
+
+ /**
+ * A developer-facing human-readable message in English. It should be used
+ * either to explain why the server rejected parts of the data during a partial
+ * success or to convey warnings/suggestions during a full success. The message
+ * should offer guidance on how users can address such issues.
+ * error_message is an optional field. An error_message with an empty value
+ * is equivalent to it not being set.
+ *
+ * Generated from protobuf field string error_message = 2;
+ * @return string
+ */
+ public function getErrorMessage()
+ {
+ return $this->error_message;
+ }
+
+ /**
+ * A developer-facing human-readable message in English. It should be used
+ * either to explain why the server rejected parts of the data during a partial
+ * success or to convey warnings/suggestions during a full success. The message
+ * should offer guidance on how users can address such issues.
+ * error_message is an optional field. An error_message with an empty value
+ * is equivalent to it not being set.
+ *
+ * Generated from protobuf field string error_message = 2;
+ * @param string $var
+ * @return $this
+ */
+ public function setErrorMessage(string $var)
+ {
+ GPBUtil::checkString($var, true);
+ $this->error_message = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Collector/Profiles/V1development/ExportProfilesServiceRequest.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Collector/Profiles/V1development/ExportProfilesServiceRequest.php
new file mode 100644
index 000000000..4adf4ac70
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Collector/Profiles/V1development/ExportProfilesServiceRequest.php
@@ -0,0 +1,125 @@
+opentelemetry.proto.collector.profiles.v1development.ExportProfilesServiceRequest
+ */
+class ExportProfilesServiceRequest extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * An array of ResourceProfiles.
+ * For data coming from a single resource this array will typically contain one
+ * element. Intermediary nodes (such as OpenTelemetry Collector) that receive
+ * data from multiple origins typically batch the data before forwarding further and
+ * in that case this array will contain multiple elements.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.profiles.v1development.ResourceProfiles resource_profiles = 1;
+ */
+ private $resource_profiles;
+ /**
+ * The reference table containing all data shared by profiles across the message being sent.
+ *
+ * Generated from protobuf field .opentelemetry.proto.profiles.v1development.ProfilesDictionary dictionary = 2;
+ */
+ protected $dictionary = null;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type \Opentelemetry\Proto\Profiles\V1development\ResourceProfiles[] $resource_profiles
+ * An array of ResourceProfiles.
+ * For data coming from a single resource this array will typically contain one
+ * element. Intermediary nodes (such as OpenTelemetry Collector) that receive
+ * data from multiple origins typically batch the data before forwarding further and
+ * in that case this array will contain multiple elements.
+ * @type \Opentelemetry\Proto\Profiles\V1development\ProfilesDictionary $dictionary
+ * The reference table containing all data shared by profiles across the message being sent.
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Collector\Profiles\V1Development\ProfilesService::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * An array of ResourceProfiles.
+ * For data coming from a single resource this array will typically contain one
+ * element. Intermediary nodes (such as OpenTelemetry Collector) that receive
+ * data from multiple origins typically batch the data before forwarding further and
+ * in that case this array will contain multiple elements.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.profiles.v1development.ResourceProfiles resource_profiles = 1;
+ * @return RepeatedField<\Opentelemetry\Proto\Profiles\V1development\ResourceProfiles>
+ */
+ public function getResourceProfiles()
+ {
+ return $this->resource_profiles;
+ }
+
+ /**
+ * An array of ResourceProfiles.
+ * For data coming from a single resource this array will typically contain one
+ * element. Intermediary nodes (such as OpenTelemetry Collector) that receive
+ * data from multiple origins typically batch the data before forwarding further and
+ * in that case this array will contain multiple elements.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.profiles.v1development.ResourceProfiles resource_profiles = 1;
+ * @param \Opentelemetry\Proto\Profiles\V1development\ResourceProfiles[] $var
+ * @return $this
+ */
+ public function setResourceProfiles(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Opentelemetry\Proto\Profiles\V1development\ResourceProfiles::class);
+ $this->resource_profiles = $arr;
+
+ return $this;
+ }
+
+ /**
+ * The reference table containing all data shared by profiles across the message being sent.
+ *
+ * Generated from protobuf field .opentelemetry.proto.profiles.v1development.ProfilesDictionary dictionary = 2;
+ * @return \Opentelemetry\Proto\Profiles\V1development\ProfilesDictionary|null
+ */
+ public function getDictionary()
+ {
+ return $this->dictionary;
+ }
+
+ public function hasDictionary()
+ {
+ return isset($this->dictionary);
+ }
+
+ public function clearDictionary()
+ {
+ unset($this->dictionary);
+ }
+
+ /**
+ * The reference table containing all data shared by profiles across the message being sent.
+ *
+ * Generated from protobuf field .opentelemetry.proto.profiles.v1development.ProfilesDictionary dictionary = 2;
+ * @param \Opentelemetry\Proto\Profiles\V1development\ProfilesDictionary $var
+ * @return $this
+ */
+ public function setDictionary(\Opentelemetry\Proto\Profiles\V1development\ProfilesDictionary|null $var)
+ {
+ $this->dictionary = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Collector/Profiles/V1development/ExportProfilesServiceResponse.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Collector/Profiles/V1development/ExportProfilesServiceResponse.php
new file mode 100644
index 000000000..ef7c1daba
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Collector/Profiles/V1development/ExportProfilesServiceResponse.php
@@ -0,0 +1,119 @@
+opentelemetry.proto.collector.profiles.v1development.ExportProfilesServiceResponse
+ */
+class ExportProfilesServiceResponse extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * The details of a partially successful export request.
+ * If the request is only partially accepted
+ * (i.e. when the server accepts only parts of the data and rejects the rest)
+ * the server MUST initialize the `partial_success` field and MUST
+ * set the `rejected_` with the number of items it rejected.
+ * Servers MAY also make use of the `partial_success` field to convey
+ * warnings/suggestions to senders even when the request was fully accepted.
+ * In such cases, the `rejected_` MUST have a value of `0` and
+ * the `error_message` MUST be non-empty.
+ * A `partial_success` message with an empty value (rejected_ = 0 and
+ * `error_message` = "") is equivalent to it not being set/present. Senders
+ * SHOULD interpret it the same way as in the full success case.
+ *
+ * Generated from protobuf field .opentelemetry.proto.collector.profiles.v1development.ExportProfilesPartialSuccess partial_success = 1;
+ */
+ protected $partial_success = null;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type \Opentelemetry\Proto\Collector\Profiles\V1development\ExportProfilesPartialSuccess $partial_success
+ * The details of a partially successful export request.
+ * If the request is only partially accepted
+ * (i.e. when the server accepts only parts of the data and rejects the rest)
+ * the server MUST initialize the `partial_success` field and MUST
+ * set the `rejected_` with the number of items it rejected.
+ * Servers MAY also make use of the `partial_success` field to convey
+ * warnings/suggestions to senders even when the request was fully accepted.
+ * In such cases, the `rejected_` MUST have a value of `0` and
+ * the `error_message` MUST be non-empty.
+ * A `partial_success` message with an empty value (rejected_ = 0 and
+ * `error_message` = "") is equivalent to it not being set/present. Senders
+ * SHOULD interpret it the same way as in the full success case.
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Collector\Profiles\V1Development\ProfilesService::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * The details of a partially successful export request.
+ * If the request is only partially accepted
+ * (i.e. when the server accepts only parts of the data and rejects the rest)
+ * the server MUST initialize the `partial_success` field and MUST
+ * set the `rejected_` with the number of items it rejected.
+ * Servers MAY also make use of the `partial_success` field to convey
+ * warnings/suggestions to senders even when the request was fully accepted.
+ * In such cases, the `rejected_` MUST have a value of `0` and
+ * the `error_message` MUST be non-empty.
+ * A `partial_success` message with an empty value (rejected_ = 0 and
+ * `error_message` = "") is equivalent to it not being set/present. Senders
+ * SHOULD interpret it the same way as in the full success case.
+ *
+ * Generated from protobuf field .opentelemetry.proto.collector.profiles.v1development.ExportProfilesPartialSuccess partial_success = 1;
+ * @return \Opentelemetry\Proto\Collector\Profiles\V1development\ExportProfilesPartialSuccess|null
+ */
+ public function getPartialSuccess()
+ {
+ return $this->partial_success;
+ }
+
+ public function hasPartialSuccess()
+ {
+ return isset($this->partial_success);
+ }
+
+ public function clearPartialSuccess()
+ {
+ unset($this->partial_success);
+ }
+
+ /**
+ * The details of a partially successful export request.
+ * If the request is only partially accepted
+ * (i.e. when the server accepts only parts of the data and rejects the rest)
+ * the server MUST initialize the `partial_success` field and MUST
+ * set the `rejected_` with the number of items it rejected.
+ * Servers MAY also make use of the `partial_success` field to convey
+ * warnings/suggestions to senders even when the request was fully accepted.
+ * In such cases, the `rejected_` MUST have a value of `0` and
+ * the `error_message` MUST be non-empty.
+ * A `partial_success` message with an empty value (rejected_ = 0 and
+ * `error_message` = "") is equivalent to it not being set/present. Senders
+ * SHOULD interpret it the same way as in the full success case.
+ *
+ * Generated from protobuf field .opentelemetry.proto.collector.profiles.v1development.ExportProfilesPartialSuccess partial_success = 1;
+ * @param \Opentelemetry\Proto\Collector\Profiles\V1development\ExportProfilesPartialSuccess $var
+ * @return $this
+ */
+ public function setPartialSuccess(\Opentelemetry\Proto\Collector\Profiles\V1development\ExportProfilesPartialSuccess|null $var)
+ {
+ $this->partial_success = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Collector/Profiles/V1development/ProfilesServiceClient.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Collector/Profiles/V1development/ProfilesServiceClient.php
new file mode 100644
index 000000000..c75e9e7a9
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Collector/Profiles/V1development/ProfilesServiceClient.php
@@ -0,0 +1,50 @@
+
+ */
+ public function Export(\Opentelemetry\Proto\Collector\Profiles\V1development\ExportProfilesServiceRequest $argument,
+ $metadata = [], $options = []) {
+ return $this->_simpleRequest('/opentelemetry.proto.collector.profiles.v1development.ProfilesService/Export',
+ $argument,
+ ['\Opentelemetry\Proto\Collector\Profiles\V1development\ExportProfilesServiceResponse', 'decode'],
+ $metadata, $options);
+ }
+
+}
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Collector/Trace/V1/ExportTracePartialSuccess.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Collector/Trace/V1/ExportTracePartialSuccess.php
new file mode 100644
index 000000000..40a62edd8
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Collector/Trace/V1/ExportTracePartialSuccess.php
@@ -0,0 +1,128 @@
+opentelemetry.proto.collector.trace.v1.ExportTracePartialSuccess
+ */
+class ExportTracePartialSuccess extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * The number of rejected spans.
+ * A `rejected_` field holding a `0` value indicates that the
+ * request was fully accepted.
+ *
+ * Generated from protobuf field int64 rejected_spans = 1;
+ */
+ protected $rejected_spans = 0;
+ /**
+ * A developer-facing human-readable message in English. It should be used
+ * either to explain why the server rejected parts of the data during a partial
+ * success or to convey warnings/suggestions during a full success. The message
+ * should offer guidance on how users can address such issues.
+ * error_message is an optional field. An error_message with an empty value
+ * is equivalent to it not being set.
+ *
+ * Generated from protobuf field string error_message = 2;
+ */
+ protected $error_message = '';
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type int|string $rejected_spans
+ * The number of rejected spans.
+ * A `rejected_` field holding a `0` value indicates that the
+ * request was fully accepted.
+ * @type string $error_message
+ * A developer-facing human-readable message in English. It should be used
+ * either to explain why the server rejected parts of the data during a partial
+ * success or to convey warnings/suggestions during a full success. The message
+ * should offer guidance on how users can address such issues.
+ * error_message is an optional field. An error_message with an empty value
+ * is equivalent to it not being set.
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Collector\Trace\V1\TraceService::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * The number of rejected spans.
+ * A `rejected_` field holding a `0` value indicates that the
+ * request was fully accepted.
+ *
+ * Generated from protobuf field int64 rejected_spans = 1;
+ * @return int|string
+ */
+ public function getRejectedSpans()
+ {
+ return $this->rejected_spans;
+ }
+
+ /**
+ * The number of rejected spans.
+ * A `rejected_` field holding a `0` value indicates that the
+ * request was fully accepted.
+ *
+ * Generated from protobuf field int64 rejected_spans = 1;
+ * @param int|string $var
+ * @return $this
+ */
+ public function setRejectedSpans(int|string $var)
+ {
+ GPBUtil::checkInt64($var);
+ $this->rejected_spans = $var;
+
+ return $this;
+ }
+
+ /**
+ * A developer-facing human-readable message in English. It should be used
+ * either to explain why the server rejected parts of the data during a partial
+ * success or to convey warnings/suggestions during a full success. The message
+ * should offer guidance on how users can address such issues.
+ * error_message is an optional field. An error_message with an empty value
+ * is equivalent to it not being set.
+ *
+ * Generated from protobuf field string error_message = 2;
+ * @return string
+ */
+ public function getErrorMessage()
+ {
+ return $this->error_message;
+ }
+
+ /**
+ * A developer-facing human-readable message in English. It should be used
+ * either to explain why the server rejected parts of the data during a partial
+ * success or to convey warnings/suggestions during a full success. The message
+ * should offer guidance on how users can address such issues.
+ * error_message is an optional field. An error_message with an empty value
+ * is equivalent to it not being set.
+ *
+ * Generated from protobuf field string error_message = 2;
+ * @param string $var
+ * @return $this
+ */
+ public function setErrorMessage(string $var)
+ {
+ GPBUtil::checkString($var, true);
+ $this->error_message = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Collector/Trace/V1/ExportTraceServiceRequest.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Collector/Trace/V1/ExportTraceServiceRequest.php
new file mode 100644
index 000000000..2c849922c
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Collector/Trace/V1/ExportTraceServiceRequest.php
@@ -0,0 +1,82 @@
+opentelemetry.proto.collector.trace.v1.ExportTraceServiceRequest
+ */
+class ExportTraceServiceRequest extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * An array of ResourceSpans.
+ * For data coming from a single resource this array will typically contain one
+ * element. Intermediary nodes (such as OpenTelemetry Collector) that receive
+ * data from multiple origins typically batch the data before forwarding further and
+ * in that case this array will contain multiple elements.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.trace.v1.ResourceSpans resource_spans = 1;
+ */
+ private $resource_spans;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type \Opentelemetry\Proto\Trace\V1\ResourceSpans[] $resource_spans
+ * An array of ResourceSpans.
+ * For data coming from a single resource this array will typically contain one
+ * element. Intermediary nodes (such as OpenTelemetry Collector) that receive
+ * data from multiple origins typically batch the data before forwarding further and
+ * in that case this array will contain multiple elements.
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Collector\Trace\V1\TraceService::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * An array of ResourceSpans.
+ * For data coming from a single resource this array will typically contain one
+ * element. Intermediary nodes (such as OpenTelemetry Collector) that receive
+ * data from multiple origins typically batch the data before forwarding further and
+ * in that case this array will contain multiple elements.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.trace.v1.ResourceSpans resource_spans = 1;
+ * @return RepeatedField<\Opentelemetry\Proto\Trace\V1\ResourceSpans>
+ */
+ public function getResourceSpans()
+ {
+ return $this->resource_spans;
+ }
+
+ /**
+ * An array of ResourceSpans.
+ * For data coming from a single resource this array will typically contain one
+ * element. Intermediary nodes (such as OpenTelemetry Collector) that receive
+ * data from multiple origins typically batch the data before forwarding further and
+ * in that case this array will contain multiple elements.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.trace.v1.ResourceSpans resource_spans = 1;
+ * @param \Opentelemetry\Proto\Trace\V1\ResourceSpans[] $var
+ * @return $this
+ */
+ public function setResourceSpans(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Opentelemetry\Proto\Trace\V1\ResourceSpans::class);
+ $this->resource_spans = $arr;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Collector/Trace/V1/ExportTraceServiceResponse.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Collector/Trace/V1/ExportTraceServiceResponse.php
new file mode 100644
index 000000000..df8e75fd1
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Collector/Trace/V1/ExportTraceServiceResponse.php
@@ -0,0 +1,119 @@
+opentelemetry.proto.collector.trace.v1.ExportTraceServiceResponse
+ */
+class ExportTraceServiceResponse extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * The details of a partially successful export request.
+ * If the request is only partially accepted
+ * (i.e. when the server accepts only parts of the data and rejects the rest)
+ * the server MUST initialize the `partial_success` field and MUST
+ * set the `rejected_` with the number of items it rejected.
+ * Servers MAY also make use of the `partial_success` field to convey
+ * warnings/suggestions to senders even when the request was fully accepted.
+ * In such cases, the `rejected_` MUST have a value of `0` and
+ * the `error_message` MUST be non-empty.
+ * A `partial_success` message with an empty value (rejected_ = 0 and
+ * `error_message` = "") is equivalent to it not being set/present. Senders
+ * SHOULD interpret it the same way as in the full success case.
+ *
+ * Generated from protobuf field .opentelemetry.proto.collector.trace.v1.ExportTracePartialSuccess partial_success = 1;
+ */
+ protected $partial_success = null;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type \Opentelemetry\Proto\Collector\Trace\V1\ExportTracePartialSuccess $partial_success
+ * The details of a partially successful export request.
+ * If the request is only partially accepted
+ * (i.e. when the server accepts only parts of the data and rejects the rest)
+ * the server MUST initialize the `partial_success` field and MUST
+ * set the `rejected_` with the number of items it rejected.
+ * Servers MAY also make use of the `partial_success` field to convey
+ * warnings/suggestions to senders even when the request was fully accepted.
+ * In such cases, the `rejected_` MUST have a value of `0` and
+ * the `error_message` MUST be non-empty.
+ * A `partial_success` message with an empty value (rejected_ = 0 and
+ * `error_message` = "") is equivalent to it not being set/present. Senders
+ * SHOULD interpret it the same way as in the full success case.
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Collector\Trace\V1\TraceService::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * The details of a partially successful export request.
+ * If the request is only partially accepted
+ * (i.e. when the server accepts only parts of the data and rejects the rest)
+ * the server MUST initialize the `partial_success` field and MUST
+ * set the `rejected_` with the number of items it rejected.
+ * Servers MAY also make use of the `partial_success` field to convey
+ * warnings/suggestions to senders even when the request was fully accepted.
+ * In such cases, the `rejected_` MUST have a value of `0` and
+ * the `error_message` MUST be non-empty.
+ * A `partial_success` message with an empty value (rejected_ = 0 and
+ * `error_message` = "") is equivalent to it not being set/present. Senders
+ * SHOULD interpret it the same way as in the full success case.
+ *
+ * Generated from protobuf field .opentelemetry.proto.collector.trace.v1.ExportTracePartialSuccess partial_success = 1;
+ * @return \Opentelemetry\Proto\Collector\Trace\V1\ExportTracePartialSuccess|null
+ */
+ public function getPartialSuccess()
+ {
+ return $this->partial_success;
+ }
+
+ public function hasPartialSuccess()
+ {
+ return isset($this->partial_success);
+ }
+
+ public function clearPartialSuccess()
+ {
+ unset($this->partial_success);
+ }
+
+ /**
+ * The details of a partially successful export request.
+ * If the request is only partially accepted
+ * (i.e. when the server accepts only parts of the data and rejects the rest)
+ * the server MUST initialize the `partial_success` field and MUST
+ * set the `rejected_` with the number of items it rejected.
+ * Servers MAY also make use of the `partial_success` field to convey
+ * warnings/suggestions to senders even when the request was fully accepted.
+ * In such cases, the `rejected_` MUST have a value of `0` and
+ * the `error_message` MUST be non-empty.
+ * A `partial_success` message with an empty value (rejected_ = 0 and
+ * `error_message` = "") is equivalent to it not being set/present. Senders
+ * SHOULD interpret it the same way as in the full success case.
+ *
+ * Generated from protobuf field .opentelemetry.proto.collector.trace.v1.ExportTracePartialSuccess partial_success = 1;
+ * @param \Opentelemetry\Proto\Collector\Trace\V1\ExportTracePartialSuccess $var
+ * @return $this
+ */
+ public function setPartialSuccess(\Opentelemetry\Proto\Collector\Trace\V1\ExportTracePartialSuccess|null $var)
+ {
+ $this->partial_success = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Collector/Trace/V1/TraceServiceClient.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Collector/Trace/V1/TraceServiceClient.php
new file mode 100644
index 000000000..882382543
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Collector/Trace/V1/TraceServiceClient.php
@@ -0,0 +1,51 @@
+
+ */
+ public function Export(\Opentelemetry\Proto\Collector\Trace\V1\ExportTraceServiceRequest $argument,
+ $metadata = [], $options = []) {
+ return $this->_simpleRequest('/opentelemetry.proto.collector.trace.v1.TraceService/Export',
+ $argument,
+ ['\Opentelemetry\Proto\Collector\Trace\V1\ExportTraceServiceResponse', 'decode'],
+ $metadata, $options);
+ }
+
+}
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Common/V1/AnyValue.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Common/V1/AnyValue.php
new file mode 100644
index 000000000..a869b4731
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Common/V1/AnyValue.php
@@ -0,0 +1,291 @@
+opentelemetry.proto.common.v1.AnyValue
+ */
+class AnyValue extends \Google\Protobuf\Internal\Message
+{
+ protected $value;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type string $string_value
+ * @type bool $bool_value
+ * @type int|string $int_value
+ * @type float $double_value
+ * @type \Opentelemetry\Proto\Common\V1\ArrayValue $array_value
+ * @type \Opentelemetry\Proto\Common\V1\KeyValueList $kvlist_value
+ * @type string $bytes_value
+ * @type int $string_value_strindex
+ * Reference to the string value in ProfilesDictionary.string_table.
+ * Note: This is currently used exclusively in the Profiling signal.
+ * Implementers of OTLP receivers for signals other than Profiling should
+ * treat the presence of this value as a non-fatal issue.
+ * Log an error or warning indicating an unexpected field intended for the
+ * Profiling signal and process the data as if this value were absent or
+ * empty, ignoring its semantic content for the non-Profiling signal.
+ * Status: [Development]
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Common\V1\Common::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * Generated from protobuf field string string_value = 1;
+ * @return string
+ */
+ public function getStringValue()
+ {
+ return $this->readOneof(1);
+ }
+
+ public function hasStringValue()
+ {
+ return $this->hasOneof(1);
+ }
+
+ /**
+ * Generated from protobuf field string string_value = 1;
+ * @param string $var
+ * @return $this
+ */
+ public function setStringValue(string $var)
+ {
+ GPBUtil::checkString($var, true);
+ $this->writeOneof(1, $var);
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field bool bool_value = 2;
+ * @return bool
+ */
+ public function getBoolValue()
+ {
+ return $this->readOneof(2);
+ }
+
+ public function hasBoolValue()
+ {
+ return $this->hasOneof(2);
+ }
+
+ /**
+ * Generated from protobuf field bool bool_value = 2;
+ * @param bool $var
+ * @return $this
+ */
+ public function setBoolValue(bool $var)
+ {
+ $this->writeOneof(2, $var);
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field int64 int_value = 3;
+ * @return int|string
+ */
+ public function getIntValue()
+ {
+ return $this->readOneof(3);
+ }
+
+ public function hasIntValue()
+ {
+ return $this->hasOneof(3);
+ }
+
+ /**
+ * Generated from protobuf field int64 int_value = 3;
+ * @param int|string $var
+ * @return $this
+ */
+ public function setIntValue(int|string $var)
+ {
+ GPBUtil::checkInt64($var);
+ $this->writeOneof(3, $var);
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field double double_value = 4;
+ * @return float
+ */
+ public function getDoubleValue()
+ {
+ return $this->readOneof(4);
+ }
+
+ public function hasDoubleValue()
+ {
+ return $this->hasOneof(4);
+ }
+
+ /**
+ * Generated from protobuf field double double_value = 4;
+ * @param float $var
+ * @return $this
+ */
+ public function setDoubleValue(float $var)
+ {
+ $this->writeOneof(4, $var);
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field .opentelemetry.proto.common.v1.ArrayValue array_value = 5;
+ * @return \Opentelemetry\Proto\Common\V1\ArrayValue|null
+ */
+ public function getArrayValue()
+ {
+ return $this->readOneof(5);
+ }
+
+ public function hasArrayValue()
+ {
+ return $this->hasOneof(5);
+ }
+
+ /**
+ * Generated from protobuf field .opentelemetry.proto.common.v1.ArrayValue array_value = 5;
+ * @param \Opentelemetry\Proto\Common\V1\ArrayValue $var
+ * @return $this
+ */
+ public function setArrayValue(\Opentelemetry\Proto\Common\V1\ArrayValue|null $var)
+ {
+ $this->writeOneof(5, $var);
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field .opentelemetry.proto.common.v1.KeyValueList kvlist_value = 6;
+ * @return \Opentelemetry\Proto\Common\V1\KeyValueList|null
+ */
+ public function getKvlistValue()
+ {
+ return $this->readOneof(6);
+ }
+
+ public function hasKvlistValue()
+ {
+ return $this->hasOneof(6);
+ }
+
+ /**
+ * Generated from protobuf field .opentelemetry.proto.common.v1.KeyValueList kvlist_value = 6;
+ * @param \Opentelemetry\Proto\Common\V1\KeyValueList $var
+ * @return $this
+ */
+ public function setKvlistValue(\Opentelemetry\Proto\Common\V1\KeyValueList|null $var)
+ {
+ $this->writeOneof(6, $var);
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field bytes bytes_value = 7;
+ * @return string
+ */
+ public function getBytesValue()
+ {
+ return $this->readOneof(7);
+ }
+
+ public function hasBytesValue()
+ {
+ return $this->hasOneof(7);
+ }
+
+ /**
+ * Generated from protobuf field bytes bytes_value = 7;
+ * @param string $var
+ * @return $this
+ */
+ public function setBytesValue(string $var)
+ {
+ GPBUtil::checkString($var, false);
+ $this->writeOneof(7, $var);
+
+ return $this;
+ }
+
+ /**
+ * Reference to the string value in ProfilesDictionary.string_table.
+ * Note: This is currently used exclusively in the Profiling signal.
+ * Implementers of OTLP receivers for signals other than Profiling should
+ * treat the presence of this value as a non-fatal issue.
+ * Log an error or warning indicating an unexpected field intended for the
+ * Profiling signal and process the data as if this value were absent or
+ * empty, ignoring its semantic content for the non-Profiling signal.
+ * Status: [Development]
+ *
+ * Generated from protobuf field int32 string_value_strindex = 8;
+ * @return int
+ */
+ public function getStringValueStrindex()
+ {
+ return $this->readOneof(8);
+ }
+
+ public function hasStringValueStrindex()
+ {
+ return $this->hasOneof(8);
+ }
+
+ /**
+ * Reference to the string value in ProfilesDictionary.string_table.
+ * Note: This is currently used exclusively in the Profiling signal.
+ * Implementers of OTLP receivers for signals other than Profiling should
+ * treat the presence of this value as a non-fatal issue.
+ * Log an error or warning indicating an unexpected field intended for the
+ * Profiling signal and process the data as if this value were absent or
+ * empty, ignoring its semantic content for the non-Profiling signal.
+ * Status: [Development]
+ *
+ * Generated from protobuf field int32 string_value_strindex = 8;
+ * @param int $var
+ * @return $this
+ */
+ public function setStringValueStrindex(int $var)
+ {
+ GPBUtil::checkInt32($var);
+ $this->writeOneof(8, $var);
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getValue()
+ {
+ return $this->whichOneof("value");
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Common/V1/ArrayValue.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Common/V1/ArrayValue.php
new file mode 100644
index 000000000..3503ecdde
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Common/V1/ArrayValue.php
@@ -0,0 +1,69 @@
+opentelemetry.proto.common.v1.ArrayValue
+ */
+class ArrayValue extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * Array of values. The array may be empty (contain 0 elements).
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.common.v1.AnyValue values = 1;
+ */
+ private $values;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type \Opentelemetry\Proto\Common\V1\AnyValue[] $values
+ * Array of values. The array may be empty (contain 0 elements).
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Common\V1\Common::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * Array of values. The array may be empty (contain 0 elements).
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.common.v1.AnyValue values = 1;
+ * @return RepeatedField<\Opentelemetry\Proto\Common\V1\AnyValue>
+ */
+ public function getValues()
+ {
+ return $this->values;
+ }
+
+ /**
+ * Array of values. The array may be empty (contain 0 elements).
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.common.v1.AnyValue values = 1;
+ * @param \Opentelemetry\Proto\Common\V1\AnyValue[] $var
+ * @return $this
+ */
+ public function setValues(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Opentelemetry\Proto\Common\V1\AnyValue::class);
+ $this->values = $arr;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Common/V1/EntityRef.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Common/V1/EntityRef.php
new file mode 100644
index 000000000..532c9e0b5
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Common/V1/EntityRef.php
@@ -0,0 +1,228 @@
+opentelemetry.proto.common.v1.EntityRef
+ */
+class EntityRef extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * The Schema URL, if known. This is the identifier of the Schema that the entity data
+ * is recorded in. To learn more about Schema URL see
+ * https://opentelemetry.io/docs/specs/otel/schemas/#schema-url
+ * This schema_url applies to the data in this message and to the Resource attributes
+ * referenced by id_keys and description_keys.
+ * TODO: discuss if we are happy with this somewhat complicated definition of what
+ * the schema_url applies to.
+ * This field obsoletes the schema_url field in ResourceMetrics/ResourceSpans/ResourceLogs.
+ *
+ * Generated from protobuf field string schema_url = 1;
+ */
+ protected $schema_url = '';
+ /**
+ * Defines the type of the entity. MUST not change during the lifetime of the entity.
+ * For example: "service" or "host". This field is required and MUST not be empty
+ * for valid entities.
+ *
+ * Generated from protobuf field string type = 2;
+ */
+ protected $type = '';
+ /**
+ * Attribute Keys that identify the entity.
+ * MUST not change during the lifetime of the entity. The Id must contain at least one attribute.
+ * These keys MUST exist in the containing {message}.attributes.
+ *
+ * Generated from protobuf field repeated string id_keys = 3;
+ */
+ private $id_keys;
+ /**
+ * Descriptive (non-identifying) attribute keys of the entity.
+ * MAY change over the lifetime of the entity. MAY be empty.
+ * These attribute keys are not part of entity's identity.
+ * These keys MUST exist in the containing {message}.attributes.
+ *
+ * Generated from protobuf field repeated string description_keys = 4;
+ */
+ private $description_keys;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type string $schema_url
+ * The Schema URL, if known. This is the identifier of the Schema that the entity data
+ * is recorded in. To learn more about Schema URL see
+ * https://opentelemetry.io/docs/specs/otel/schemas/#schema-url
+ * This schema_url applies to the data in this message and to the Resource attributes
+ * referenced by id_keys and description_keys.
+ * TODO: discuss if we are happy with this somewhat complicated definition of what
+ * the schema_url applies to.
+ * This field obsoletes the schema_url field in ResourceMetrics/ResourceSpans/ResourceLogs.
+ * @type string $type
+ * Defines the type of the entity. MUST not change during the lifetime of the entity.
+ * For example: "service" or "host". This field is required and MUST not be empty
+ * for valid entities.
+ * @type string[] $id_keys
+ * Attribute Keys that identify the entity.
+ * MUST not change during the lifetime of the entity. The Id must contain at least one attribute.
+ * These keys MUST exist in the containing {message}.attributes.
+ * @type string[] $description_keys
+ * Descriptive (non-identifying) attribute keys of the entity.
+ * MAY change over the lifetime of the entity. MAY be empty.
+ * These attribute keys are not part of entity's identity.
+ * These keys MUST exist in the containing {message}.attributes.
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Common\V1\Common::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * The Schema URL, if known. This is the identifier of the Schema that the entity data
+ * is recorded in. To learn more about Schema URL see
+ * https://opentelemetry.io/docs/specs/otel/schemas/#schema-url
+ * This schema_url applies to the data in this message and to the Resource attributes
+ * referenced by id_keys and description_keys.
+ * TODO: discuss if we are happy with this somewhat complicated definition of what
+ * the schema_url applies to.
+ * This field obsoletes the schema_url field in ResourceMetrics/ResourceSpans/ResourceLogs.
+ *
+ * Generated from protobuf field string schema_url = 1;
+ * @return string
+ */
+ public function getSchemaUrl()
+ {
+ return $this->schema_url;
+ }
+
+ /**
+ * The Schema URL, if known. This is the identifier of the Schema that the entity data
+ * is recorded in. To learn more about Schema URL see
+ * https://opentelemetry.io/docs/specs/otel/schemas/#schema-url
+ * This schema_url applies to the data in this message and to the Resource attributes
+ * referenced by id_keys and description_keys.
+ * TODO: discuss if we are happy with this somewhat complicated definition of what
+ * the schema_url applies to.
+ * This field obsoletes the schema_url field in ResourceMetrics/ResourceSpans/ResourceLogs.
+ *
+ * Generated from protobuf field string schema_url = 1;
+ * @param string $var
+ * @return $this
+ */
+ public function setSchemaUrl(string $var)
+ {
+ GPBUtil::checkString($var, true);
+ $this->schema_url = $var;
+
+ return $this;
+ }
+
+ /**
+ * Defines the type of the entity. MUST not change during the lifetime of the entity.
+ * For example: "service" or "host". This field is required and MUST not be empty
+ * for valid entities.
+ *
+ * Generated from protobuf field string type = 2;
+ * @return string
+ */
+ public function getType()
+ {
+ return $this->type;
+ }
+
+ /**
+ * Defines the type of the entity. MUST not change during the lifetime of the entity.
+ * For example: "service" or "host". This field is required and MUST not be empty
+ * for valid entities.
+ *
+ * Generated from protobuf field string type = 2;
+ * @param string $var
+ * @return $this
+ */
+ public function setType(string $var)
+ {
+ GPBUtil::checkString($var, true);
+ $this->type = $var;
+
+ return $this;
+ }
+
+ /**
+ * Attribute Keys that identify the entity.
+ * MUST not change during the lifetime of the entity. The Id must contain at least one attribute.
+ * These keys MUST exist in the containing {message}.attributes.
+ *
+ * Generated from protobuf field repeated string id_keys = 3;
+ * @return RepeatedField
+ */
+ public function getIdKeys()
+ {
+ return $this->id_keys;
+ }
+
+ /**
+ * Attribute Keys that identify the entity.
+ * MUST not change during the lifetime of the entity. The Id must contain at least one attribute.
+ * These keys MUST exist in the containing {message}.attributes.
+ *
+ * Generated from protobuf field repeated string id_keys = 3;
+ * @param string[] $var
+ * @return $this
+ */
+ public function setIdKeys(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+ $this->id_keys = $arr;
+
+ return $this;
+ }
+
+ /**
+ * Descriptive (non-identifying) attribute keys of the entity.
+ * MAY change over the lifetime of the entity. MAY be empty.
+ * These attribute keys are not part of entity's identity.
+ * These keys MUST exist in the containing {message}.attributes.
+ *
+ * Generated from protobuf field repeated string description_keys = 4;
+ * @return RepeatedField
+ */
+ public function getDescriptionKeys()
+ {
+ return $this->description_keys;
+ }
+
+ /**
+ * Descriptive (non-identifying) attribute keys of the entity.
+ * MAY change over the lifetime of the entity. MAY be empty.
+ * These attribute keys are not part of entity's identity.
+ * These keys MUST exist in the containing {message}.attributes.
+ *
+ * Generated from protobuf field repeated string description_keys = 4;
+ * @param string[] $var
+ * @return $this
+ */
+ public function setDescriptionKeys(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+ $this->description_keys = $arr;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Common/V1/InstrumentationScope.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Common/V1/InstrumentationScope.php
new file mode 100644
index 000000000..54313c0ba
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Common/V1/InstrumentationScope.php
@@ -0,0 +1,199 @@
+opentelemetry.proto.common.v1.InstrumentationScope
+ */
+class InstrumentationScope extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * A name denoting the Instrumentation scope.
+ * An empty instrumentation scope name means the name is unknown.
+ *
+ * Generated from protobuf field string name = 1;
+ */
+ protected $name = '';
+ /**
+ * Defines the version of the instrumentation scope.
+ * An empty instrumentation scope version means the version is unknown.
+ *
+ * Generated from protobuf field string version = 2;
+ */
+ protected $version = '';
+ /**
+ * Additional attributes that describe the scope. [Optional].
+ * Attribute keys MUST be unique (it is not allowed to have more than one
+ * attribute with the same key).
+ * The behavior of software that receives duplicated keys can be unpredictable.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.common.v1.KeyValue attributes = 3;
+ */
+ private $attributes;
+ /**
+ * The number of attributes that were discarded. Attributes
+ * can be discarded because their keys are too long or because there are too many
+ * attributes. If this value is 0, then no attributes were dropped.
+ *
+ * Generated from protobuf field uint32 dropped_attributes_count = 4;
+ */
+ protected $dropped_attributes_count = 0;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type string $name
+ * A name denoting the Instrumentation scope.
+ * An empty instrumentation scope name means the name is unknown.
+ * @type string $version
+ * Defines the version of the instrumentation scope.
+ * An empty instrumentation scope version means the version is unknown.
+ * @type \Opentelemetry\Proto\Common\V1\KeyValue[] $attributes
+ * Additional attributes that describe the scope. [Optional].
+ * Attribute keys MUST be unique (it is not allowed to have more than one
+ * attribute with the same key).
+ * The behavior of software that receives duplicated keys can be unpredictable.
+ * @type int $dropped_attributes_count
+ * The number of attributes that were discarded. Attributes
+ * can be discarded because their keys are too long or because there are too many
+ * attributes. If this value is 0, then no attributes were dropped.
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Common\V1\Common::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * A name denoting the Instrumentation scope.
+ * An empty instrumentation scope name means the name is unknown.
+ *
+ * Generated from protobuf field string name = 1;
+ * @return string
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * A name denoting the Instrumentation scope.
+ * An empty instrumentation scope name means the name is unknown.
+ *
+ * Generated from protobuf field string name = 1;
+ * @param string $var
+ * @return $this
+ */
+ public function setName(string $var)
+ {
+ GPBUtil::checkString($var, true);
+ $this->name = $var;
+
+ return $this;
+ }
+
+ /**
+ * Defines the version of the instrumentation scope.
+ * An empty instrumentation scope version means the version is unknown.
+ *
+ * Generated from protobuf field string version = 2;
+ * @return string
+ */
+ public function getVersion()
+ {
+ return $this->version;
+ }
+
+ /**
+ * Defines the version of the instrumentation scope.
+ * An empty instrumentation scope version means the version is unknown.
+ *
+ * Generated from protobuf field string version = 2;
+ * @param string $var
+ * @return $this
+ */
+ public function setVersion(string $var)
+ {
+ GPBUtil::checkString($var, true);
+ $this->version = $var;
+
+ return $this;
+ }
+
+ /**
+ * Additional attributes that describe the scope. [Optional].
+ * Attribute keys MUST be unique (it is not allowed to have more than one
+ * attribute with the same key).
+ * The behavior of software that receives duplicated keys can be unpredictable.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.common.v1.KeyValue attributes = 3;
+ * @return RepeatedField<\Opentelemetry\Proto\Common\V1\KeyValue>
+ */
+ public function getAttributes()
+ {
+ return $this->attributes;
+ }
+
+ /**
+ * Additional attributes that describe the scope. [Optional].
+ * Attribute keys MUST be unique (it is not allowed to have more than one
+ * attribute with the same key).
+ * The behavior of software that receives duplicated keys can be unpredictable.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.common.v1.KeyValue attributes = 3;
+ * @param \Opentelemetry\Proto\Common\V1\KeyValue[] $var
+ * @return $this
+ */
+ public function setAttributes(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Opentelemetry\Proto\Common\V1\KeyValue::class);
+ $this->attributes = $arr;
+
+ return $this;
+ }
+
+ /**
+ * The number of attributes that were discarded. Attributes
+ * can be discarded because their keys are too long or because there are too many
+ * attributes. If this value is 0, then no attributes were dropped.
+ *
+ * Generated from protobuf field uint32 dropped_attributes_count = 4;
+ * @return int
+ */
+ public function getDroppedAttributesCount()
+ {
+ return $this->dropped_attributes_count;
+ }
+
+ /**
+ * The number of attributes that were discarded. Attributes
+ * can be discarded because their keys are too long or because there are too many
+ * attributes. If this value is 0, then no attributes were dropped.
+ *
+ * Generated from protobuf field uint32 dropped_attributes_count = 4;
+ * @param int $var
+ * @return $this
+ */
+ public function setDroppedAttributesCount(int $var)
+ {
+ GPBUtil::checkUint32($var);
+ $this->dropped_attributes_count = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Common/V1/KeyValue.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Common/V1/KeyValue.php
new file mode 100644
index 000000000..8e8d9df52
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Common/V1/KeyValue.php
@@ -0,0 +1,182 @@
+opentelemetry.proto.common.v1.KeyValue
+ */
+class KeyValue extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * The key name of the pair.
+ * key_ref MUST NOT be set if key is used.
+ *
+ * Generated from protobuf field string key = 1;
+ */
+ protected $key = '';
+ /**
+ * The value of the pair.
+ *
+ * Generated from protobuf field .opentelemetry.proto.common.v1.AnyValue value = 2;
+ */
+ protected $value = null;
+ /**
+ * Reference to the string key in ProfilesDictionary.string_table.
+ * key MUST NOT be set if key_strindex is used.
+ * Note: This is currently used exclusively in the Profiling signal.
+ * Implementers of OTLP receivers for signals other than Profiling should
+ * treat the presence of this key as a non-fatal issue.
+ * Log an error or warning indicating an unexpected field intended for the
+ * Profiling signal and process the data as if this value were absent or
+ * empty, ignoring its semantic content for the non-Profiling signal.
+ * Status: [Development]
+ *
+ * Generated from protobuf field int32 key_strindex = 3;
+ */
+ protected $key_strindex = 0;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type string $key
+ * The key name of the pair.
+ * key_ref MUST NOT be set if key is used.
+ * @type \Opentelemetry\Proto\Common\V1\AnyValue $value
+ * The value of the pair.
+ * @type int $key_strindex
+ * Reference to the string key in ProfilesDictionary.string_table.
+ * key MUST NOT be set if key_strindex is used.
+ * Note: This is currently used exclusively in the Profiling signal.
+ * Implementers of OTLP receivers for signals other than Profiling should
+ * treat the presence of this key as a non-fatal issue.
+ * Log an error or warning indicating an unexpected field intended for the
+ * Profiling signal and process the data as if this value were absent or
+ * empty, ignoring its semantic content for the non-Profiling signal.
+ * Status: [Development]
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Common\V1\Common::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * The key name of the pair.
+ * key_ref MUST NOT be set if key is used.
+ *
+ * Generated from protobuf field string key = 1;
+ * @return string
+ */
+ public function getKey()
+ {
+ return $this->key;
+ }
+
+ /**
+ * The key name of the pair.
+ * key_ref MUST NOT be set if key is used.
+ *
+ * Generated from protobuf field string key = 1;
+ * @param string $var
+ * @return $this
+ */
+ public function setKey(string $var)
+ {
+ GPBUtil::checkString($var, true);
+ $this->key = $var;
+
+ return $this;
+ }
+
+ /**
+ * The value of the pair.
+ *
+ * Generated from protobuf field .opentelemetry.proto.common.v1.AnyValue value = 2;
+ * @return \Opentelemetry\Proto\Common\V1\AnyValue|null
+ */
+ public function getValue()
+ {
+ return $this->value;
+ }
+
+ public function hasValue()
+ {
+ return isset($this->value);
+ }
+
+ public function clearValue()
+ {
+ unset($this->value);
+ }
+
+ /**
+ * The value of the pair.
+ *
+ * Generated from protobuf field .opentelemetry.proto.common.v1.AnyValue value = 2;
+ * @param \Opentelemetry\Proto\Common\V1\AnyValue $var
+ * @return $this
+ */
+ public function setValue(\Opentelemetry\Proto\Common\V1\AnyValue|null $var)
+ {
+ $this->value = $var;
+
+ return $this;
+ }
+
+ /**
+ * Reference to the string key in ProfilesDictionary.string_table.
+ * key MUST NOT be set if key_strindex is used.
+ * Note: This is currently used exclusively in the Profiling signal.
+ * Implementers of OTLP receivers for signals other than Profiling should
+ * treat the presence of this key as a non-fatal issue.
+ * Log an error or warning indicating an unexpected field intended for the
+ * Profiling signal and process the data as if this value were absent or
+ * empty, ignoring its semantic content for the non-Profiling signal.
+ * Status: [Development]
+ *
+ * Generated from protobuf field int32 key_strindex = 3;
+ * @return int
+ */
+ public function getKeyStrindex()
+ {
+ return $this->key_strindex;
+ }
+
+ /**
+ * Reference to the string key in ProfilesDictionary.string_table.
+ * key MUST NOT be set if key_strindex is used.
+ * Note: This is currently used exclusively in the Profiling signal.
+ * Implementers of OTLP receivers for signals other than Profiling should
+ * treat the presence of this key as a non-fatal issue.
+ * Log an error or warning indicating an unexpected field intended for the
+ * Profiling signal and process the data as if this value were absent or
+ * empty, ignoring its semantic content for the non-Profiling signal.
+ * Status: [Development]
+ *
+ * Generated from protobuf field int32 key_strindex = 3;
+ * @param int $var
+ * @return $this
+ */
+ public function setKeyStrindex(int $var)
+ {
+ GPBUtil::checkInt32($var);
+ $this->key_strindex = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Common/V1/KeyValueList.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Common/V1/KeyValueList.php
new file mode 100644
index 000000000..eaa31fac4
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Common/V1/KeyValueList.php
@@ -0,0 +1,88 @@
+opentelemetry.proto.common.v1.KeyValueList
+ */
+class KeyValueList extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * A collection of key/value pairs of key-value pairs. The list may be empty (may
+ * contain 0 elements).
+ * The keys MUST be unique (it is not allowed to have more than one
+ * value with the same key).
+ * The behavior of software that receives duplicated keys can be unpredictable.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.common.v1.KeyValue values = 1;
+ */
+ private $values;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type \Opentelemetry\Proto\Common\V1\KeyValue[] $values
+ * A collection of key/value pairs of key-value pairs. The list may be empty (may
+ * contain 0 elements).
+ * The keys MUST be unique (it is not allowed to have more than one
+ * value with the same key).
+ * The behavior of software that receives duplicated keys can be unpredictable.
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Common\V1\Common::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * A collection of key/value pairs of key-value pairs. The list may be empty (may
+ * contain 0 elements).
+ * The keys MUST be unique (it is not allowed to have more than one
+ * value with the same key).
+ * The behavior of software that receives duplicated keys can be unpredictable.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.common.v1.KeyValue values = 1;
+ * @return RepeatedField<\Opentelemetry\Proto\Common\V1\KeyValue>
+ */
+ public function getValues()
+ {
+ return $this->values;
+ }
+
+ /**
+ * A collection of key/value pairs of key-value pairs. The list may be empty (may
+ * contain 0 elements).
+ * The keys MUST be unique (it is not allowed to have more than one
+ * value with the same key).
+ * The behavior of software that receives duplicated keys can be unpredictable.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.common.v1.KeyValue values = 1;
+ * @param \Opentelemetry\Proto\Common\V1\KeyValue[] $var
+ * @return $this
+ */
+ public function setValues(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Opentelemetry\Proto\Common\V1\KeyValue::class);
+ $this->values = $arr;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Logs/V1/LogRecord.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Logs/V1/LogRecord.php
new file mode 100644
index 000000000..d4ad82fb7
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Logs/V1/LogRecord.php
@@ -0,0 +1,603 @@
+opentelemetry.proto.logs.v1.LogRecord
+ */
+class LogRecord extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * time_unix_nano is the time when the event occurred.
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970.
+ * Value of 0 indicates unknown or missing timestamp.
+ *
+ * Generated from protobuf field fixed64 time_unix_nano = 1;
+ */
+ protected $time_unix_nano = 0;
+ /**
+ * Time when the event was observed by the collection system.
+ * For events that originate in OpenTelemetry (e.g. using OpenTelemetry Logging SDK)
+ * this timestamp is typically set at the generation time and is equal to Timestamp.
+ * For events originating externally and collected by OpenTelemetry (e.g. using
+ * Collector) this is the time when OpenTelemetry's code observed the event measured
+ * by the clock of the OpenTelemetry code. This field MUST be set once the event is
+ * observed by OpenTelemetry.
+ * For converting OpenTelemetry log data to formats that support only one timestamp or
+ * when receiving OpenTelemetry log data by recipients that support only one timestamp
+ * internally the following logic is recommended:
+ * - Use time_unix_nano if it is present, otherwise use observed_time_unix_nano.
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970.
+ * Value of 0 indicates unknown or missing timestamp.
+ *
+ * Generated from protobuf field fixed64 observed_time_unix_nano = 11;
+ */
+ protected $observed_time_unix_nano = 0;
+ /**
+ * Numerical value of the severity, normalized to values described in Log Data Model.
+ * [Optional].
+ *
+ * Generated from protobuf field .opentelemetry.proto.logs.v1.SeverityNumber severity_number = 2;
+ */
+ protected $severity_number = 0;
+ /**
+ * The severity text (also known as log level). The original string representation as
+ * it is known at the source. [Optional].
+ *
+ * Generated from protobuf field string severity_text = 3;
+ */
+ protected $severity_text = '';
+ /**
+ * A value containing the body of the log record. Can be for example a human-readable
+ * string message (including multi-line) describing the event in a free form or it can
+ * be a structured data composed of arrays and maps of other values. [Optional].
+ *
+ * Generated from protobuf field .opentelemetry.proto.common.v1.AnyValue body = 5;
+ */
+ protected $body = null;
+ /**
+ * Additional attributes that describe the specific event occurrence. [Optional].
+ * Attribute keys MUST be unique (it is not allowed to have more than one
+ * attribute with the same key).
+ * The behavior of software that receives duplicated keys can be unpredictable.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.common.v1.KeyValue attributes = 6;
+ */
+ private $attributes;
+ /**
+ * Generated from protobuf field uint32 dropped_attributes_count = 7;
+ */
+ protected $dropped_attributes_count = 0;
+ /**
+ * Flags, a bit field. 8 least significant bits are the trace flags as
+ * defined in W3C Trace Context specification. 24 most significant bits are reserved
+ * and must be set to 0. Readers must not assume that 24 most significant bits
+ * will be zero and must correctly mask the bits when reading 8-bit trace flag (use
+ * flags & LOG_RECORD_FLAGS_TRACE_FLAGS_MASK). [Optional].
+ *
+ * Generated from protobuf field fixed32 flags = 8;
+ */
+ protected $flags = 0;
+ /**
+ * A unique identifier for a trace. All logs from the same trace share
+ * the same `trace_id`. The ID is a 16-byte array. An ID with all zeroes OR
+ * of length other than 16 bytes is considered invalid (empty string in OTLP/JSON
+ * is zero-length and thus is also invalid).
+ * This field is optional.
+ * The receivers SHOULD assume that the log record is not associated with a
+ * trace if any of the following is true:
+ * - the field is not present,
+ * - the field contains an invalid value.
+ *
+ * Generated from protobuf field bytes trace_id = 9;
+ */
+ protected $trace_id = '';
+ /**
+ * A unique identifier for a span within a trace, assigned when the span
+ * is created. The ID is an 8-byte array. An ID with all zeroes OR of length
+ * other than 8 bytes is considered invalid (empty string in OTLP/JSON
+ * is zero-length and thus is also invalid).
+ * This field is optional. If the sender specifies a valid span_id then it SHOULD also
+ * specify a valid trace_id.
+ * The receivers SHOULD assume that the log record is not associated with a
+ * span if any of the following is true:
+ * - the field is not present,
+ * - the field contains an invalid value.
+ *
+ * Generated from protobuf field bytes span_id = 10;
+ */
+ protected $span_id = '';
+ /**
+ * A unique identifier of event category/type.
+ * All events with the same event_name are expected to conform to the same
+ * schema for both their attributes and their body.
+ * Recommended to be fully qualified and short (no longer than 256 characters).
+ * Presence of event_name on the log record identifies this record
+ * as an event.
+ * [Optional].
+ *
+ * Generated from protobuf field string event_name = 12;
+ */
+ protected $event_name = '';
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type int|string $time_unix_nano
+ * time_unix_nano is the time when the event occurred.
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970.
+ * Value of 0 indicates unknown or missing timestamp.
+ * @type int|string $observed_time_unix_nano
+ * Time when the event was observed by the collection system.
+ * For events that originate in OpenTelemetry (e.g. using OpenTelemetry Logging SDK)
+ * this timestamp is typically set at the generation time and is equal to Timestamp.
+ * For events originating externally and collected by OpenTelemetry (e.g. using
+ * Collector) this is the time when OpenTelemetry's code observed the event measured
+ * by the clock of the OpenTelemetry code. This field MUST be set once the event is
+ * observed by OpenTelemetry.
+ * For converting OpenTelemetry log data to formats that support only one timestamp or
+ * when receiving OpenTelemetry log data by recipients that support only one timestamp
+ * internally the following logic is recommended:
+ * - Use time_unix_nano if it is present, otherwise use observed_time_unix_nano.
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970.
+ * Value of 0 indicates unknown or missing timestamp.
+ * @type int $severity_number
+ * Numerical value of the severity, normalized to values described in Log Data Model.
+ * [Optional].
+ * @type string $severity_text
+ * The severity text (also known as log level). The original string representation as
+ * it is known at the source. [Optional].
+ * @type \Opentelemetry\Proto\Common\V1\AnyValue $body
+ * A value containing the body of the log record. Can be for example a human-readable
+ * string message (including multi-line) describing the event in a free form or it can
+ * be a structured data composed of arrays and maps of other values. [Optional].
+ * @type \Opentelemetry\Proto\Common\V1\KeyValue[] $attributes
+ * Additional attributes that describe the specific event occurrence. [Optional].
+ * Attribute keys MUST be unique (it is not allowed to have more than one
+ * attribute with the same key).
+ * The behavior of software that receives duplicated keys can be unpredictable.
+ * @type int $dropped_attributes_count
+ * @type int $flags
+ * Flags, a bit field. 8 least significant bits are the trace flags as
+ * defined in W3C Trace Context specification. 24 most significant bits are reserved
+ * and must be set to 0. Readers must not assume that 24 most significant bits
+ * will be zero and must correctly mask the bits when reading 8-bit trace flag (use
+ * flags & LOG_RECORD_FLAGS_TRACE_FLAGS_MASK). [Optional].
+ * @type string $trace_id
+ * A unique identifier for a trace. All logs from the same trace share
+ * the same `trace_id`. The ID is a 16-byte array. An ID with all zeroes OR
+ * of length other than 16 bytes is considered invalid (empty string in OTLP/JSON
+ * is zero-length and thus is also invalid).
+ * This field is optional.
+ * The receivers SHOULD assume that the log record is not associated with a
+ * trace if any of the following is true:
+ * - the field is not present,
+ * - the field contains an invalid value.
+ * @type string $span_id
+ * A unique identifier for a span within a trace, assigned when the span
+ * is created. The ID is an 8-byte array. An ID with all zeroes OR of length
+ * other than 8 bytes is considered invalid (empty string in OTLP/JSON
+ * is zero-length and thus is also invalid).
+ * This field is optional. If the sender specifies a valid span_id then it SHOULD also
+ * specify a valid trace_id.
+ * The receivers SHOULD assume that the log record is not associated with a
+ * span if any of the following is true:
+ * - the field is not present,
+ * - the field contains an invalid value.
+ * @type string $event_name
+ * A unique identifier of event category/type.
+ * All events with the same event_name are expected to conform to the same
+ * schema for both their attributes and their body.
+ * Recommended to be fully qualified and short (no longer than 256 characters).
+ * Presence of event_name on the log record identifies this record
+ * as an event.
+ * [Optional].
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Logs\V1\Logs::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * time_unix_nano is the time when the event occurred.
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970.
+ * Value of 0 indicates unknown or missing timestamp.
+ *
+ * Generated from protobuf field fixed64 time_unix_nano = 1;
+ * @return int|string
+ */
+ public function getTimeUnixNano()
+ {
+ return $this->time_unix_nano;
+ }
+
+ /**
+ * time_unix_nano is the time when the event occurred.
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970.
+ * Value of 0 indicates unknown or missing timestamp.
+ *
+ * Generated from protobuf field fixed64 time_unix_nano = 1;
+ * @param int|string $var
+ * @return $this
+ */
+ public function setTimeUnixNano(int|string $var)
+ {
+ GPBUtil::checkUint64($var);
+ $this->time_unix_nano = $var;
+
+ return $this;
+ }
+
+ /**
+ * Time when the event was observed by the collection system.
+ * For events that originate in OpenTelemetry (e.g. using OpenTelemetry Logging SDK)
+ * this timestamp is typically set at the generation time and is equal to Timestamp.
+ * For events originating externally and collected by OpenTelemetry (e.g. using
+ * Collector) this is the time when OpenTelemetry's code observed the event measured
+ * by the clock of the OpenTelemetry code. This field MUST be set once the event is
+ * observed by OpenTelemetry.
+ * For converting OpenTelemetry log data to formats that support only one timestamp or
+ * when receiving OpenTelemetry log data by recipients that support only one timestamp
+ * internally the following logic is recommended:
+ * - Use time_unix_nano if it is present, otherwise use observed_time_unix_nano.
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970.
+ * Value of 0 indicates unknown or missing timestamp.
+ *
+ * Generated from protobuf field fixed64 observed_time_unix_nano = 11;
+ * @return int|string
+ */
+ public function getObservedTimeUnixNano()
+ {
+ return $this->observed_time_unix_nano;
+ }
+
+ /**
+ * Time when the event was observed by the collection system.
+ * For events that originate in OpenTelemetry (e.g. using OpenTelemetry Logging SDK)
+ * this timestamp is typically set at the generation time and is equal to Timestamp.
+ * For events originating externally and collected by OpenTelemetry (e.g. using
+ * Collector) this is the time when OpenTelemetry's code observed the event measured
+ * by the clock of the OpenTelemetry code. This field MUST be set once the event is
+ * observed by OpenTelemetry.
+ * For converting OpenTelemetry log data to formats that support only one timestamp or
+ * when receiving OpenTelemetry log data by recipients that support only one timestamp
+ * internally the following logic is recommended:
+ * - Use time_unix_nano if it is present, otherwise use observed_time_unix_nano.
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970.
+ * Value of 0 indicates unknown or missing timestamp.
+ *
+ * Generated from protobuf field fixed64 observed_time_unix_nano = 11;
+ * @param int|string $var
+ * @return $this
+ */
+ public function setObservedTimeUnixNano(int|string $var)
+ {
+ GPBUtil::checkUint64($var);
+ $this->observed_time_unix_nano = $var;
+
+ return $this;
+ }
+
+ /**
+ * Numerical value of the severity, normalized to values described in Log Data Model.
+ * [Optional].
+ *
+ * Generated from protobuf field .opentelemetry.proto.logs.v1.SeverityNumber severity_number = 2;
+ * @return int one of the values in {@see \Opentelemetry\Proto\Logs\V1\SeverityNumber}
+ */
+ public function getSeverityNumber()
+ {
+ return $this->severity_number;
+ }
+
+ /**
+ * Numerical value of the severity, normalized to values described in Log Data Model.
+ * [Optional].
+ *
+ * Generated from protobuf field .opentelemetry.proto.logs.v1.SeverityNumber severity_number = 2;
+ * @param int $var one of the values in {@see \Opentelemetry\Proto\Logs\V1\SeverityNumber}
+ * @return $this
+ */
+ public function setSeverityNumber(int $var)
+ {
+ GPBUtil::checkEnum($var, \Opentelemetry\Proto\Logs\V1\SeverityNumber::class);
+ $this->severity_number = $var;
+
+ return $this;
+ }
+
+ /**
+ * The severity text (also known as log level). The original string representation as
+ * it is known at the source. [Optional].
+ *
+ * Generated from protobuf field string severity_text = 3;
+ * @return string
+ */
+ public function getSeverityText()
+ {
+ return $this->severity_text;
+ }
+
+ /**
+ * The severity text (also known as log level). The original string representation as
+ * it is known at the source. [Optional].
+ *
+ * Generated from protobuf field string severity_text = 3;
+ * @param string $var
+ * @return $this
+ */
+ public function setSeverityText(string $var)
+ {
+ GPBUtil::checkString($var, true);
+ $this->severity_text = $var;
+
+ return $this;
+ }
+
+ /**
+ * A value containing the body of the log record. Can be for example a human-readable
+ * string message (including multi-line) describing the event in a free form or it can
+ * be a structured data composed of arrays and maps of other values. [Optional].
+ *
+ * Generated from protobuf field .opentelemetry.proto.common.v1.AnyValue body = 5;
+ * @return \Opentelemetry\Proto\Common\V1\AnyValue|null
+ */
+ public function getBody()
+ {
+ return $this->body;
+ }
+
+ public function hasBody()
+ {
+ return isset($this->body);
+ }
+
+ public function clearBody()
+ {
+ unset($this->body);
+ }
+
+ /**
+ * A value containing the body of the log record. Can be for example a human-readable
+ * string message (including multi-line) describing the event in a free form or it can
+ * be a structured data composed of arrays and maps of other values. [Optional].
+ *
+ * Generated from protobuf field .opentelemetry.proto.common.v1.AnyValue body = 5;
+ * @param \Opentelemetry\Proto\Common\V1\AnyValue $var
+ * @return $this
+ */
+ public function setBody(\Opentelemetry\Proto\Common\V1\AnyValue|null $var)
+ {
+ $this->body = $var;
+
+ return $this;
+ }
+
+ /**
+ * Additional attributes that describe the specific event occurrence. [Optional].
+ * Attribute keys MUST be unique (it is not allowed to have more than one
+ * attribute with the same key).
+ * The behavior of software that receives duplicated keys can be unpredictable.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.common.v1.KeyValue attributes = 6;
+ * @return RepeatedField<\Opentelemetry\Proto\Common\V1\KeyValue>
+ */
+ public function getAttributes()
+ {
+ return $this->attributes;
+ }
+
+ /**
+ * Additional attributes that describe the specific event occurrence. [Optional].
+ * Attribute keys MUST be unique (it is not allowed to have more than one
+ * attribute with the same key).
+ * The behavior of software that receives duplicated keys can be unpredictable.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.common.v1.KeyValue attributes = 6;
+ * @param \Opentelemetry\Proto\Common\V1\KeyValue[] $var
+ * @return $this
+ */
+ public function setAttributes(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Opentelemetry\Proto\Common\V1\KeyValue::class);
+ $this->attributes = $arr;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field uint32 dropped_attributes_count = 7;
+ * @return int
+ */
+ public function getDroppedAttributesCount()
+ {
+ return $this->dropped_attributes_count;
+ }
+
+ /**
+ * Generated from protobuf field uint32 dropped_attributes_count = 7;
+ * @param int $var
+ * @return $this
+ */
+ public function setDroppedAttributesCount(int $var)
+ {
+ GPBUtil::checkUint32($var);
+ $this->dropped_attributes_count = $var;
+
+ return $this;
+ }
+
+ /**
+ * Flags, a bit field. 8 least significant bits are the trace flags as
+ * defined in W3C Trace Context specification. 24 most significant bits are reserved
+ * and must be set to 0. Readers must not assume that 24 most significant bits
+ * will be zero and must correctly mask the bits when reading 8-bit trace flag (use
+ * flags & LOG_RECORD_FLAGS_TRACE_FLAGS_MASK). [Optional].
+ *
+ * Generated from protobuf field fixed32 flags = 8;
+ * @return int
+ */
+ public function getFlags()
+ {
+ return $this->flags;
+ }
+
+ /**
+ * Flags, a bit field. 8 least significant bits are the trace flags as
+ * defined in W3C Trace Context specification. 24 most significant bits are reserved
+ * and must be set to 0. Readers must not assume that 24 most significant bits
+ * will be zero and must correctly mask the bits when reading 8-bit trace flag (use
+ * flags & LOG_RECORD_FLAGS_TRACE_FLAGS_MASK). [Optional].
+ *
+ * Generated from protobuf field fixed32 flags = 8;
+ * @param int $var
+ * @return $this
+ */
+ public function setFlags(int $var)
+ {
+ GPBUtil::checkUint32($var);
+ $this->flags = $var;
+
+ return $this;
+ }
+
+ /**
+ * A unique identifier for a trace. All logs from the same trace share
+ * the same `trace_id`. The ID is a 16-byte array. An ID with all zeroes OR
+ * of length other than 16 bytes is considered invalid (empty string in OTLP/JSON
+ * is zero-length and thus is also invalid).
+ * This field is optional.
+ * The receivers SHOULD assume that the log record is not associated with a
+ * trace if any of the following is true:
+ * - the field is not present,
+ * - the field contains an invalid value.
+ *
+ * Generated from protobuf field bytes trace_id = 9;
+ * @return string
+ */
+ public function getTraceId()
+ {
+ return $this->trace_id;
+ }
+
+ /**
+ * A unique identifier for a trace. All logs from the same trace share
+ * the same `trace_id`. The ID is a 16-byte array. An ID with all zeroes OR
+ * of length other than 16 bytes is considered invalid (empty string in OTLP/JSON
+ * is zero-length and thus is also invalid).
+ * This field is optional.
+ * The receivers SHOULD assume that the log record is not associated with a
+ * trace if any of the following is true:
+ * - the field is not present,
+ * - the field contains an invalid value.
+ *
+ * Generated from protobuf field bytes trace_id = 9;
+ * @param string $var
+ * @return $this
+ */
+ public function setTraceId(string $var)
+ {
+ GPBUtil::checkString($var, false);
+ $this->trace_id = $var;
+
+ return $this;
+ }
+
+ /**
+ * A unique identifier for a span within a trace, assigned when the span
+ * is created. The ID is an 8-byte array. An ID with all zeroes OR of length
+ * other than 8 bytes is considered invalid (empty string in OTLP/JSON
+ * is zero-length and thus is also invalid).
+ * This field is optional. If the sender specifies a valid span_id then it SHOULD also
+ * specify a valid trace_id.
+ * The receivers SHOULD assume that the log record is not associated with a
+ * span if any of the following is true:
+ * - the field is not present,
+ * - the field contains an invalid value.
+ *
+ * Generated from protobuf field bytes span_id = 10;
+ * @return string
+ */
+ public function getSpanId()
+ {
+ return $this->span_id;
+ }
+
+ /**
+ * A unique identifier for a span within a trace, assigned when the span
+ * is created. The ID is an 8-byte array. An ID with all zeroes OR of length
+ * other than 8 bytes is considered invalid (empty string in OTLP/JSON
+ * is zero-length and thus is also invalid).
+ * This field is optional. If the sender specifies a valid span_id then it SHOULD also
+ * specify a valid trace_id.
+ * The receivers SHOULD assume that the log record is not associated with a
+ * span if any of the following is true:
+ * - the field is not present,
+ * - the field contains an invalid value.
+ *
+ * Generated from protobuf field bytes span_id = 10;
+ * @param string $var
+ * @return $this
+ */
+ public function setSpanId(string $var)
+ {
+ GPBUtil::checkString($var, false);
+ $this->span_id = $var;
+
+ return $this;
+ }
+
+ /**
+ * A unique identifier of event category/type.
+ * All events with the same event_name are expected to conform to the same
+ * schema for both their attributes and their body.
+ * Recommended to be fully qualified and short (no longer than 256 characters).
+ * Presence of event_name on the log record identifies this record
+ * as an event.
+ * [Optional].
+ *
+ * Generated from protobuf field string event_name = 12;
+ * @return string
+ */
+ public function getEventName()
+ {
+ return $this->event_name;
+ }
+
+ /**
+ * A unique identifier of event category/type.
+ * All events with the same event_name are expected to conform to the same
+ * schema for both their attributes and their body.
+ * Recommended to be fully qualified and short (no longer than 256 characters).
+ * Presence of event_name on the log record identifies this record
+ * as an event.
+ * [Optional].
+ *
+ * Generated from protobuf field string event_name = 12;
+ * @param string $var
+ * @return $this
+ */
+ public function setEventName(string $var)
+ {
+ GPBUtil::checkString($var, true);
+ $this->event_name = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Logs/V1/LogRecordFlags.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Logs/V1/LogRecordFlags.php
new file mode 100644
index 000000000..b63c14b1d
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Logs/V1/LogRecordFlags.php
@@ -0,0 +1,61 @@
+opentelemetry.proto.logs.v1.LogRecordFlags
+ */
+class LogRecordFlags
+{
+ /**
+ * The zero value for the enum. Should not be used for comparisons.
+ * Instead use bitwise "and" with the appropriate mask as shown above.
+ *
+ * Generated from protobuf enum LOG_RECORD_FLAGS_DO_NOT_USE = 0;
+ */
+ const LOG_RECORD_FLAGS_DO_NOT_USE = 0;
+ /**
+ * Bits 0-7 are used for trace flags.
+ *
+ * Generated from protobuf enum LOG_RECORD_FLAGS_TRACE_FLAGS_MASK = 255;
+ */
+ const LOG_RECORD_FLAGS_TRACE_FLAGS_MASK = 255;
+
+ private static $valueToName = [
+ self::LOG_RECORD_FLAGS_DO_NOT_USE => 'LOG_RECORD_FLAGS_DO_NOT_USE',
+ self::LOG_RECORD_FLAGS_TRACE_FLAGS_MASK => 'LOG_RECORD_FLAGS_TRACE_FLAGS_MASK',
+ ];
+
+ public static function name($value)
+ {
+ if (!isset(self::$valueToName[$value])) {
+ throw new UnexpectedValueException(sprintf(
+ 'Enum %s has no name defined for value %s', __CLASS__, $value));
+ }
+ return self::$valueToName[$value];
+ }
+
+
+ public static function value($name)
+ {
+ $const = __CLASS__ . '::' . strtoupper($name);
+ if (!defined($const)) {
+ throw new UnexpectedValueException(sprintf(
+ 'Enum %s has no value defined for name %s', __CLASS__, $name));
+ }
+ return constant($const);
+ }
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Logs/V1/LogsData.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Logs/V1/LogsData.php
new file mode 100644
index 000000000..413d0b35e
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Logs/V1/LogsData.php
@@ -0,0 +1,91 @@
+opentelemetry.proto.logs.v1.LogsData
+ */
+class LogsData extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * An array of ResourceLogs.
+ * For data coming from a single resource this array will typically contain
+ * one element. Intermediary nodes that receive data from multiple origins
+ * typically batch the data before forwarding further and in that case this
+ * array will contain multiple elements.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.logs.v1.ResourceLogs resource_logs = 1;
+ */
+ private $resource_logs;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type \Opentelemetry\Proto\Logs\V1\ResourceLogs[] $resource_logs
+ * An array of ResourceLogs.
+ * For data coming from a single resource this array will typically contain
+ * one element. Intermediary nodes that receive data from multiple origins
+ * typically batch the data before forwarding further and in that case this
+ * array will contain multiple elements.
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Logs\V1\Logs::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * An array of ResourceLogs.
+ * For data coming from a single resource this array will typically contain
+ * one element. Intermediary nodes that receive data from multiple origins
+ * typically batch the data before forwarding further and in that case this
+ * array will contain multiple elements.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.logs.v1.ResourceLogs resource_logs = 1;
+ * @return RepeatedField<\Opentelemetry\Proto\Logs\V1\ResourceLogs>
+ */
+ public function getResourceLogs()
+ {
+ return $this->resource_logs;
+ }
+
+ /**
+ * An array of ResourceLogs.
+ * For data coming from a single resource this array will typically contain
+ * one element. Intermediary nodes that receive data from multiple origins
+ * typically batch the data before forwarding further and in that case this
+ * array will contain multiple elements.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.logs.v1.ResourceLogs resource_logs = 1;
+ * @param \Opentelemetry\Proto\Logs\V1\ResourceLogs[] $var
+ * @return $this
+ */
+ public function setResourceLogs(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Opentelemetry\Proto\Logs\V1\ResourceLogs::class);
+ $this->resource_logs = $arr;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Logs/V1/ResourceLogs.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Logs/V1/ResourceLogs.php
new file mode 100644
index 000000000..985e9af9f
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Logs/V1/ResourceLogs.php
@@ -0,0 +1,169 @@
+opentelemetry.proto.logs.v1.ResourceLogs
+ */
+class ResourceLogs extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * The resource for the logs in this message.
+ * If this field is not set then resource info is unknown.
+ *
+ * Generated from protobuf field .opentelemetry.proto.resource.v1.Resource resource = 1;
+ */
+ protected $resource = null;
+ /**
+ * A list of ScopeLogs that originate from a resource.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.logs.v1.ScopeLogs scope_logs = 2;
+ */
+ private $scope_logs;
+ /**
+ * The Schema URL, if known. This is the identifier of the Schema that the resource data
+ * is recorded in. Notably, the last part of the URL path is the version number of the
+ * schema: http[s]://server[:port]/path/. To learn more about Schema URL see
+ * https://opentelemetry.io/docs/specs/otel/schemas/#schema-url
+ * This schema_url applies to the data in the "resource" field. It does not apply
+ * to the data in the "scope_logs" field which have their own schema_url field.
+ *
+ * Generated from protobuf field string schema_url = 3;
+ */
+ protected $schema_url = '';
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type \Opentelemetry\Proto\Resource\V1\Resource $resource
+ * The resource for the logs in this message.
+ * If this field is not set then resource info is unknown.
+ * @type \Opentelemetry\Proto\Logs\V1\ScopeLogs[] $scope_logs
+ * A list of ScopeLogs that originate from a resource.
+ * @type string $schema_url
+ * The Schema URL, if known. This is the identifier of the Schema that the resource data
+ * is recorded in. Notably, the last part of the URL path is the version number of the
+ * schema: http[s]://server[:port]/path/. To learn more about Schema URL see
+ * https://opentelemetry.io/docs/specs/otel/schemas/#schema-url
+ * This schema_url applies to the data in the "resource" field. It does not apply
+ * to the data in the "scope_logs" field which have their own schema_url field.
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Logs\V1\Logs::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * The resource for the logs in this message.
+ * If this field is not set then resource info is unknown.
+ *
+ * Generated from protobuf field .opentelemetry.proto.resource.v1.Resource resource = 1;
+ * @return \Opentelemetry\Proto\Resource\V1\Resource|null
+ */
+ public function getResource()
+ {
+ return $this->resource;
+ }
+
+ public function hasResource()
+ {
+ return isset($this->resource);
+ }
+
+ public function clearResource()
+ {
+ unset($this->resource);
+ }
+
+ /**
+ * The resource for the logs in this message.
+ * If this field is not set then resource info is unknown.
+ *
+ * Generated from protobuf field .opentelemetry.proto.resource.v1.Resource resource = 1;
+ * @param \Opentelemetry\Proto\Resource\V1\Resource $var
+ * @return $this
+ */
+ public function setResource(\Opentelemetry\Proto\Resource\V1\Resource|null $var)
+ {
+ $this->resource = $var;
+
+ return $this;
+ }
+
+ /**
+ * A list of ScopeLogs that originate from a resource.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.logs.v1.ScopeLogs scope_logs = 2;
+ * @return RepeatedField<\Opentelemetry\Proto\Logs\V1\ScopeLogs>
+ */
+ public function getScopeLogs()
+ {
+ return $this->scope_logs;
+ }
+
+ /**
+ * A list of ScopeLogs that originate from a resource.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.logs.v1.ScopeLogs scope_logs = 2;
+ * @param \Opentelemetry\Proto\Logs\V1\ScopeLogs[] $var
+ * @return $this
+ */
+ public function setScopeLogs(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Opentelemetry\Proto\Logs\V1\ScopeLogs::class);
+ $this->scope_logs = $arr;
+
+ return $this;
+ }
+
+ /**
+ * The Schema URL, if known. This is the identifier of the Schema that the resource data
+ * is recorded in. Notably, the last part of the URL path is the version number of the
+ * schema: http[s]://server[:port]/path/. To learn more about Schema URL see
+ * https://opentelemetry.io/docs/specs/otel/schemas/#schema-url
+ * This schema_url applies to the data in the "resource" field. It does not apply
+ * to the data in the "scope_logs" field which have their own schema_url field.
+ *
+ * Generated from protobuf field string schema_url = 3;
+ * @return string
+ */
+ public function getSchemaUrl()
+ {
+ return $this->schema_url;
+ }
+
+ /**
+ * The Schema URL, if known. This is the identifier of the Schema that the resource data
+ * is recorded in. Notably, the last part of the URL path is the version number of the
+ * schema: http[s]://server[:port]/path/. To learn more about Schema URL see
+ * https://opentelemetry.io/docs/specs/otel/schemas/#schema-url
+ * This schema_url applies to the data in the "resource" field. It does not apply
+ * to the data in the "scope_logs" field which have their own schema_url field.
+ *
+ * Generated from protobuf field string schema_url = 3;
+ * @param string $var
+ * @return $this
+ */
+ public function setSchemaUrl(string $var)
+ {
+ GPBUtil::checkString($var, true);
+ $this->schema_url = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Logs/V1/ScopeLogs.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Logs/V1/ScopeLogs.php
new file mode 100644
index 000000000..aeb944464
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Logs/V1/ScopeLogs.php
@@ -0,0 +1,173 @@
+opentelemetry.proto.logs.v1.ScopeLogs
+ */
+class ScopeLogs extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * The instrumentation scope information for the logs in this message.
+ * Semantically when InstrumentationScope isn't set, it is equivalent with
+ * an empty instrumentation scope name (unknown).
+ *
+ * Generated from protobuf field .opentelemetry.proto.common.v1.InstrumentationScope scope = 1;
+ */
+ protected $scope = null;
+ /**
+ * A list of log records.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.logs.v1.LogRecord log_records = 2;
+ */
+ private $log_records;
+ /**
+ * The Schema URL, if known. This is the identifier of the Schema that the log data
+ * is recorded in. Notably, the last part of the URL path is the version number of the
+ * schema: http[s]://server[:port]/path/. To learn more about Schema URL see
+ * https://opentelemetry.io/docs/specs/otel/schemas/#schema-url
+ * This schema_url applies to the data in the "scope" field and all logs in the
+ * "log_records" field.
+ *
+ * Generated from protobuf field string schema_url = 3;
+ */
+ protected $schema_url = '';
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type \Opentelemetry\Proto\Common\V1\InstrumentationScope $scope
+ * The instrumentation scope information for the logs in this message.
+ * Semantically when InstrumentationScope isn't set, it is equivalent with
+ * an empty instrumentation scope name (unknown).
+ * @type \Opentelemetry\Proto\Logs\V1\LogRecord[] $log_records
+ * A list of log records.
+ * @type string $schema_url
+ * The Schema URL, if known. This is the identifier of the Schema that the log data
+ * is recorded in. Notably, the last part of the URL path is the version number of the
+ * schema: http[s]://server[:port]/path/. To learn more about Schema URL see
+ * https://opentelemetry.io/docs/specs/otel/schemas/#schema-url
+ * This schema_url applies to the data in the "scope" field and all logs in the
+ * "log_records" field.
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Logs\V1\Logs::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * The instrumentation scope information for the logs in this message.
+ * Semantically when InstrumentationScope isn't set, it is equivalent with
+ * an empty instrumentation scope name (unknown).
+ *
+ * Generated from protobuf field .opentelemetry.proto.common.v1.InstrumentationScope scope = 1;
+ * @return \Opentelemetry\Proto\Common\V1\InstrumentationScope|null
+ */
+ public function getScope()
+ {
+ return $this->scope;
+ }
+
+ public function hasScope()
+ {
+ return isset($this->scope);
+ }
+
+ public function clearScope()
+ {
+ unset($this->scope);
+ }
+
+ /**
+ * The instrumentation scope information for the logs in this message.
+ * Semantically when InstrumentationScope isn't set, it is equivalent with
+ * an empty instrumentation scope name (unknown).
+ *
+ * Generated from protobuf field .opentelemetry.proto.common.v1.InstrumentationScope scope = 1;
+ * @param \Opentelemetry\Proto\Common\V1\InstrumentationScope $var
+ * @return $this
+ */
+ public function setScope(\Opentelemetry\Proto\Common\V1\InstrumentationScope|null $var)
+ {
+ $this->scope = $var;
+
+ return $this;
+ }
+
+ /**
+ * A list of log records.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.logs.v1.LogRecord log_records = 2;
+ * @return RepeatedField<\Opentelemetry\Proto\Logs\V1\LogRecord>
+ */
+ public function getLogRecords()
+ {
+ return $this->log_records;
+ }
+
+ /**
+ * A list of log records.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.logs.v1.LogRecord log_records = 2;
+ * @param \Opentelemetry\Proto\Logs\V1\LogRecord[] $var
+ * @return $this
+ */
+ public function setLogRecords(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Opentelemetry\Proto\Logs\V1\LogRecord::class);
+ $this->log_records = $arr;
+
+ return $this;
+ }
+
+ /**
+ * The Schema URL, if known. This is the identifier of the Schema that the log data
+ * is recorded in. Notably, the last part of the URL path is the version number of the
+ * schema: http[s]://server[:port]/path/. To learn more about Schema URL see
+ * https://opentelemetry.io/docs/specs/otel/schemas/#schema-url
+ * This schema_url applies to the data in the "scope" field and all logs in the
+ * "log_records" field.
+ *
+ * Generated from protobuf field string schema_url = 3;
+ * @return string
+ */
+ public function getSchemaUrl()
+ {
+ return $this->schema_url;
+ }
+
+ /**
+ * The Schema URL, if known. This is the identifier of the Schema that the log data
+ * is recorded in. Notably, the last part of the URL path is the version number of the
+ * schema: http[s]://server[:port]/path/. To learn more about Schema URL see
+ * https://opentelemetry.io/docs/specs/otel/schemas/#schema-url
+ * This schema_url applies to the data in the "scope" field and all logs in the
+ * "log_records" field.
+ *
+ * Generated from protobuf field string schema_url = 3;
+ * @param string $var
+ * @return $this
+ */
+ public function setSchemaUrl(string $var)
+ {
+ GPBUtil::checkString($var, true);
+ $this->schema_url = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Logs/V1/SeverityNumber.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Logs/V1/SeverityNumber.php
new file mode 100644
index 000000000..3f5ca7f69
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Logs/V1/SeverityNumber.php
@@ -0,0 +1,166 @@
+opentelemetry.proto.logs.v1.SeverityNumber
+ */
+class SeverityNumber
+{
+ /**
+ * Generated from protobuf enum SEVERITY_NUMBER_UNSPECIFIED = 0;
+ */
+ const SEVERITY_NUMBER_UNSPECIFIED = 0;
+ /**
+ * Generated from protobuf enum SEVERITY_NUMBER_TRACE = 1;
+ */
+ const SEVERITY_NUMBER_TRACE = 1;
+ /**
+ * Generated from protobuf enum SEVERITY_NUMBER_TRACE2 = 2;
+ */
+ const SEVERITY_NUMBER_TRACE2 = 2;
+ /**
+ * Generated from protobuf enum SEVERITY_NUMBER_TRACE3 = 3;
+ */
+ const SEVERITY_NUMBER_TRACE3 = 3;
+ /**
+ * Generated from protobuf enum SEVERITY_NUMBER_TRACE4 = 4;
+ */
+ const SEVERITY_NUMBER_TRACE4 = 4;
+ /**
+ * Generated from protobuf enum SEVERITY_NUMBER_DEBUG = 5;
+ */
+ const SEVERITY_NUMBER_DEBUG = 5;
+ /**
+ * Generated from protobuf enum SEVERITY_NUMBER_DEBUG2 = 6;
+ */
+ const SEVERITY_NUMBER_DEBUG2 = 6;
+ /**
+ * Generated from protobuf enum SEVERITY_NUMBER_DEBUG3 = 7;
+ */
+ const SEVERITY_NUMBER_DEBUG3 = 7;
+ /**
+ * Generated from protobuf enum SEVERITY_NUMBER_DEBUG4 = 8;
+ */
+ const SEVERITY_NUMBER_DEBUG4 = 8;
+ /**
+ * Generated from protobuf enum SEVERITY_NUMBER_INFO = 9;
+ */
+ const SEVERITY_NUMBER_INFO = 9;
+ /**
+ * Generated from protobuf enum SEVERITY_NUMBER_INFO2 = 10;
+ */
+ const SEVERITY_NUMBER_INFO2 = 10;
+ /**
+ * Generated from protobuf enum SEVERITY_NUMBER_INFO3 = 11;
+ */
+ const SEVERITY_NUMBER_INFO3 = 11;
+ /**
+ * Generated from protobuf enum SEVERITY_NUMBER_INFO4 = 12;
+ */
+ const SEVERITY_NUMBER_INFO4 = 12;
+ /**
+ * Generated from protobuf enum SEVERITY_NUMBER_WARN = 13;
+ */
+ const SEVERITY_NUMBER_WARN = 13;
+ /**
+ * Generated from protobuf enum SEVERITY_NUMBER_WARN2 = 14;
+ */
+ const SEVERITY_NUMBER_WARN2 = 14;
+ /**
+ * Generated from protobuf enum SEVERITY_NUMBER_WARN3 = 15;
+ */
+ const SEVERITY_NUMBER_WARN3 = 15;
+ /**
+ * Generated from protobuf enum SEVERITY_NUMBER_WARN4 = 16;
+ */
+ const SEVERITY_NUMBER_WARN4 = 16;
+ /**
+ * Generated from protobuf enum SEVERITY_NUMBER_ERROR = 17;
+ */
+ const SEVERITY_NUMBER_ERROR = 17;
+ /**
+ * Generated from protobuf enum SEVERITY_NUMBER_ERROR2 = 18;
+ */
+ const SEVERITY_NUMBER_ERROR2 = 18;
+ /**
+ * Generated from protobuf enum SEVERITY_NUMBER_ERROR3 = 19;
+ */
+ const SEVERITY_NUMBER_ERROR3 = 19;
+ /**
+ * Generated from protobuf enum SEVERITY_NUMBER_ERROR4 = 20;
+ */
+ const SEVERITY_NUMBER_ERROR4 = 20;
+ /**
+ * Generated from protobuf enum SEVERITY_NUMBER_FATAL = 21;
+ */
+ const SEVERITY_NUMBER_FATAL = 21;
+ /**
+ * Generated from protobuf enum SEVERITY_NUMBER_FATAL2 = 22;
+ */
+ const SEVERITY_NUMBER_FATAL2 = 22;
+ /**
+ * Generated from protobuf enum SEVERITY_NUMBER_FATAL3 = 23;
+ */
+ const SEVERITY_NUMBER_FATAL3 = 23;
+ /**
+ * Generated from protobuf enum SEVERITY_NUMBER_FATAL4 = 24;
+ */
+ const SEVERITY_NUMBER_FATAL4 = 24;
+
+ private static $valueToName = [
+ self::SEVERITY_NUMBER_UNSPECIFIED => 'SEVERITY_NUMBER_UNSPECIFIED',
+ self::SEVERITY_NUMBER_TRACE => 'SEVERITY_NUMBER_TRACE',
+ self::SEVERITY_NUMBER_TRACE2 => 'SEVERITY_NUMBER_TRACE2',
+ self::SEVERITY_NUMBER_TRACE3 => 'SEVERITY_NUMBER_TRACE3',
+ self::SEVERITY_NUMBER_TRACE4 => 'SEVERITY_NUMBER_TRACE4',
+ self::SEVERITY_NUMBER_DEBUG => 'SEVERITY_NUMBER_DEBUG',
+ self::SEVERITY_NUMBER_DEBUG2 => 'SEVERITY_NUMBER_DEBUG2',
+ self::SEVERITY_NUMBER_DEBUG3 => 'SEVERITY_NUMBER_DEBUG3',
+ self::SEVERITY_NUMBER_DEBUG4 => 'SEVERITY_NUMBER_DEBUG4',
+ self::SEVERITY_NUMBER_INFO => 'SEVERITY_NUMBER_INFO',
+ self::SEVERITY_NUMBER_INFO2 => 'SEVERITY_NUMBER_INFO2',
+ self::SEVERITY_NUMBER_INFO3 => 'SEVERITY_NUMBER_INFO3',
+ self::SEVERITY_NUMBER_INFO4 => 'SEVERITY_NUMBER_INFO4',
+ self::SEVERITY_NUMBER_WARN => 'SEVERITY_NUMBER_WARN',
+ self::SEVERITY_NUMBER_WARN2 => 'SEVERITY_NUMBER_WARN2',
+ self::SEVERITY_NUMBER_WARN3 => 'SEVERITY_NUMBER_WARN3',
+ self::SEVERITY_NUMBER_WARN4 => 'SEVERITY_NUMBER_WARN4',
+ self::SEVERITY_NUMBER_ERROR => 'SEVERITY_NUMBER_ERROR',
+ self::SEVERITY_NUMBER_ERROR2 => 'SEVERITY_NUMBER_ERROR2',
+ self::SEVERITY_NUMBER_ERROR3 => 'SEVERITY_NUMBER_ERROR3',
+ self::SEVERITY_NUMBER_ERROR4 => 'SEVERITY_NUMBER_ERROR4',
+ self::SEVERITY_NUMBER_FATAL => 'SEVERITY_NUMBER_FATAL',
+ self::SEVERITY_NUMBER_FATAL2 => 'SEVERITY_NUMBER_FATAL2',
+ self::SEVERITY_NUMBER_FATAL3 => 'SEVERITY_NUMBER_FATAL3',
+ self::SEVERITY_NUMBER_FATAL4 => 'SEVERITY_NUMBER_FATAL4',
+ ];
+
+ public static function name($value)
+ {
+ if (!isset(self::$valueToName[$value])) {
+ throw new UnexpectedValueException(sprintf(
+ 'Enum %s has no name defined for value %s', __CLASS__, $value));
+ }
+ return self::$valueToName[$value];
+ }
+
+
+ public static function value($name)
+ {
+ $const = __CLASS__ . '::' . strtoupper($name);
+ if (!defined($const)) {
+ throw new UnexpectedValueException(sprintf(
+ 'Enum %s has no value defined for name %s', __CLASS__, $name));
+ }
+ return constant($const);
+ }
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/AggregationTemporality.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/AggregationTemporality.php
new file mode 100644
index 000000000..66d084039
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/AggregationTemporality.php
@@ -0,0 +1,115 @@
+opentelemetry.proto.metrics.v1.AggregationTemporality
+ */
+class AggregationTemporality
+{
+ /**
+ * UNSPECIFIED is the default AggregationTemporality, it MUST not be used.
+ *
+ * Generated from protobuf enum AGGREGATION_TEMPORALITY_UNSPECIFIED = 0;
+ */
+ const AGGREGATION_TEMPORALITY_UNSPECIFIED = 0;
+ /**
+ * DELTA is an AggregationTemporality for a metric aggregator which reports
+ * changes since last report time. Successive metrics contain aggregation of
+ * values from continuous and non-overlapping intervals.
+ * The values for a DELTA metric are based only on the time interval
+ * associated with one measurement cycle. There is no dependency on
+ * previous measurements like is the case for CUMULATIVE metrics.
+ * For example, consider a system measuring the number of requests that
+ * it receives and reports the sum of these requests every second as a
+ * DELTA metric:
+ * 1. The system starts receiving at time=t_0.
+ * 2. A request is received, the system measures 1 request.
+ * 3. A request is received, the system measures 1 request.
+ * 4. A request is received, the system measures 1 request.
+ * 5. The 1 second collection cycle ends. A metric is exported for the
+ * number of requests received over the interval of time t_0 to
+ * t_0+1 with a value of 3.
+ * 6. A request is received, the system measures 1 request.
+ * 7. A request is received, the system measures 1 request.
+ * 8. The 1 second collection cycle ends. A metric is exported for the
+ * number of requests received over the interval of time t_0+1 to
+ * t_0+2 with a value of 2.
+ *
+ * Generated from protobuf enum AGGREGATION_TEMPORALITY_DELTA = 1;
+ */
+ const AGGREGATION_TEMPORALITY_DELTA = 1;
+ /**
+ * CUMULATIVE is an AggregationTemporality for a metric aggregator which
+ * reports changes since a fixed start time. This means that current values
+ * of a CUMULATIVE metric depend on all previous measurements since the
+ * start time. Because of this, the sender is required to retain this state
+ * in some form. If this state is lost or invalidated, the CUMULATIVE metric
+ * values MUST be reset and a new fixed start time following the last
+ * reported measurement time sent MUST be used.
+ * For example, consider a system measuring the number of requests that
+ * it receives and reports the sum of these requests every second as a
+ * CUMULATIVE metric:
+ * 1. The system starts receiving at time=t_0.
+ * 2. A request is received, the system measures 1 request.
+ * 3. A request is received, the system measures 1 request.
+ * 4. A request is received, the system measures 1 request.
+ * 5. The 1 second collection cycle ends. A metric is exported for the
+ * number of requests received over the interval of time t_0 to
+ * t_0+1 with a value of 3.
+ * 6. A request is received, the system measures 1 request.
+ * 7. A request is received, the system measures 1 request.
+ * 8. The 1 second collection cycle ends. A metric is exported for the
+ * number of requests received over the interval of time t_0 to
+ * t_0+2 with a value of 5.
+ * 9. The system experiences a fault and loses state.
+ * 10. The system recovers and resumes receiving at time=t_1.
+ * 11. A request is received, the system measures 1 request.
+ * 12. The 1 second collection cycle ends. A metric is exported for the
+ * number of requests received over the interval of time t_1 to
+ * t_0+1 with a value of 1.
+ * Note: Even though, when reporting changes since last report time, using
+ * CUMULATIVE is valid, it is not recommended. This may cause problems for
+ * systems that do not use start_time to determine when the aggregation
+ * value was reset (e.g. Prometheus).
+ *
+ * Generated from protobuf enum AGGREGATION_TEMPORALITY_CUMULATIVE = 2;
+ */
+ const AGGREGATION_TEMPORALITY_CUMULATIVE = 2;
+
+ private static $valueToName = [
+ self::AGGREGATION_TEMPORALITY_UNSPECIFIED => 'AGGREGATION_TEMPORALITY_UNSPECIFIED',
+ self::AGGREGATION_TEMPORALITY_DELTA => 'AGGREGATION_TEMPORALITY_DELTA',
+ self::AGGREGATION_TEMPORALITY_CUMULATIVE => 'AGGREGATION_TEMPORALITY_CUMULATIVE',
+ ];
+
+ public static function name($value)
+ {
+ if (!isset(self::$valueToName[$value])) {
+ throw new UnexpectedValueException(sprintf(
+ 'Enum %s has no name defined for value %s', __CLASS__, $value));
+ }
+ return self::$valueToName[$value];
+ }
+
+
+ public static function value($name)
+ {
+ $const = __CLASS__ . '::' . strtoupper($name);
+ if (!defined($const)) {
+ throw new UnexpectedValueException(sprintf(
+ 'Enum %s has no value defined for name %s', __CLASS__, $name));
+ }
+ return constant($const);
+ }
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/DataPointFlags.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/DataPointFlags.php
new file mode 100644
index 000000000..9415ed5a2
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/DataPointFlags.php
@@ -0,0 +1,62 @@
+opentelemetry.proto.metrics.v1.DataPointFlags
+ */
+class DataPointFlags
+{
+ /**
+ * The zero value for the enum. Should not be used for comparisons.
+ * Instead use bitwise "and" with the appropriate mask as shown above.
+ *
+ * Generated from protobuf enum DATA_POINT_FLAGS_DO_NOT_USE = 0;
+ */
+ const DATA_POINT_FLAGS_DO_NOT_USE = 0;
+ /**
+ * This DataPoint is valid but has no recorded value. This value
+ * SHOULD be used to reflect explicitly missing data in a series, as
+ * for an equivalent to the Prometheus "staleness marker".
+ *
+ * Generated from protobuf enum DATA_POINT_FLAGS_NO_RECORDED_VALUE_MASK = 1;
+ */
+ const DATA_POINT_FLAGS_NO_RECORDED_VALUE_MASK = 1;
+
+ private static $valueToName = [
+ self::DATA_POINT_FLAGS_DO_NOT_USE => 'DATA_POINT_FLAGS_DO_NOT_USE',
+ self::DATA_POINT_FLAGS_NO_RECORDED_VALUE_MASK => 'DATA_POINT_FLAGS_NO_RECORDED_VALUE_MASK',
+ ];
+
+ public static function name($value)
+ {
+ if (!isset(self::$valueToName[$value])) {
+ throw new UnexpectedValueException(sprintf(
+ 'Enum %s has no name defined for value %s', __CLASS__, $value));
+ }
+ return self::$valueToName[$value];
+ }
+
+
+ public static function value($name)
+ {
+ $const = __CLASS__ . '::' . strtoupper($name);
+ if (!defined($const)) {
+ throw new UnexpectedValueException(sprintf(
+ 'Enum %s has no value defined for name %s', __CLASS__, $name));
+ }
+ return constant($const);
+ }
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/Exemplar.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/Exemplar.php
new file mode 100644
index 000000000..0b11ebe49
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/Exemplar.php
@@ -0,0 +1,269 @@
+opentelemetry.proto.metrics.v1.Exemplar
+ */
+class Exemplar extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * The set of key/value pairs that were filtered out by the aggregator, but
+ * recorded alongside the original measurement. Only key/value pairs that were
+ * filtered out by the aggregator should be included
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.common.v1.KeyValue filtered_attributes = 7;
+ */
+ private $filtered_attributes;
+ /**
+ * time_unix_nano is the exact time when this exemplar was recorded
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January
+ * 1970.
+ *
+ * Generated from protobuf field fixed64 time_unix_nano = 2;
+ */
+ protected $time_unix_nano = 0;
+ /**
+ * (Optional) Span ID of the exemplar trace.
+ * span_id may be missing if the measurement is not recorded inside a trace
+ * or if the trace is not sampled.
+ *
+ * Generated from protobuf field bytes span_id = 4;
+ */
+ protected $span_id = '';
+ /**
+ * (Optional) Trace ID of the exemplar trace.
+ * trace_id may be missing if the measurement is not recorded inside a trace
+ * or if the trace is not sampled.
+ *
+ * Generated from protobuf field bytes trace_id = 5;
+ */
+ protected $trace_id = '';
+ protected $value;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type \Opentelemetry\Proto\Common\V1\KeyValue[] $filtered_attributes
+ * The set of key/value pairs that were filtered out by the aggregator, but
+ * recorded alongside the original measurement. Only key/value pairs that were
+ * filtered out by the aggregator should be included
+ * @type int|string $time_unix_nano
+ * time_unix_nano is the exact time when this exemplar was recorded
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January
+ * 1970.
+ * @type float $as_double
+ * @type int|string $as_int
+ * @type string $span_id
+ * (Optional) Span ID of the exemplar trace.
+ * span_id may be missing if the measurement is not recorded inside a trace
+ * or if the trace is not sampled.
+ * @type string $trace_id
+ * (Optional) Trace ID of the exemplar trace.
+ * trace_id may be missing if the measurement is not recorded inside a trace
+ * or if the trace is not sampled.
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Metrics\V1\Metrics::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * The set of key/value pairs that were filtered out by the aggregator, but
+ * recorded alongside the original measurement. Only key/value pairs that were
+ * filtered out by the aggregator should be included
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.common.v1.KeyValue filtered_attributes = 7;
+ * @return RepeatedField<\Opentelemetry\Proto\Common\V1\KeyValue>
+ */
+ public function getFilteredAttributes()
+ {
+ return $this->filtered_attributes;
+ }
+
+ /**
+ * The set of key/value pairs that were filtered out by the aggregator, but
+ * recorded alongside the original measurement. Only key/value pairs that were
+ * filtered out by the aggregator should be included
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.common.v1.KeyValue filtered_attributes = 7;
+ * @param \Opentelemetry\Proto\Common\V1\KeyValue[] $var
+ * @return $this
+ */
+ public function setFilteredAttributes(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Opentelemetry\Proto\Common\V1\KeyValue::class);
+ $this->filtered_attributes = $arr;
+
+ return $this;
+ }
+
+ /**
+ * time_unix_nano is the exact time when this exemplar was recorded
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January
+ * 1970.
+ *
+ * Generated from protobuf field fixed64 time_unix_nano = 2;
+ * @return int|string
+ */
+ public function getTimeUnixNano()
+ {
+ return $this->time_unix_nano;
+ }
+
+ /**
+ * time_unix_nano is the exact time when this exemplar was recorded
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January
+ * 1970.
+ *
+ * Generated from protobuf field fixed64 time_unix_nano = 2;
+ * @param int|string $var
+ * @return $this
+ */
+ public function setTimeUnixNano(int|string $var)
+ {
+ GPBUtil::checkUint64($var);
+ $this->time_unix_nano = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field double as_double = 3;
+ * @return float
+ */
+ public function getAsDouble()
+ {
+ return $this->readOneof(3);
+ }
+
+ public function hasAsDouble()
+ {
+ return $this->hasOneof(3);
+ }
+
+ /**
+ * Generated from protobuf field double as_double = 3;
+ * @param float $var
+ * @return $this
+ */
+ public function setAsDouble(float $var)
+ {
+ $this->writeOneof(3, $var);
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field sfixed64 as_int = 6;
+ * @return int|string
+ */
+ public function getAsInt()
+ {
+ return $this->readOneof(6);
+ }
+
+ public function hasAsInt()
+ {
+ return $this->hasOneof(6);
+ }
+
+ /**
+ * Generated from protobuf field sfixed64 as_int = 6;
+ * @param int|string $var
+ * @return $this
+ */
+ public function setAsInt(int|string $var)
+ {
+ GPBUtil::checkInt64($var);
+ $this->writeOneof(6, $var);
+
+ return $this;
+ }
+
+ /**
+ * (Optional) Span ID of the exemplar trace.
+ * span_id may be missing if the measurement is not recorded inside a trace
+ * or if the trace is not sampled.
+ *
+ * Generated from protobuf field bytes span_id = 4;
+ * @return string
+ */
+ public function getSpanId()
+ {
+ return $this->span_id;
+ }
+
+ /**
+ * (Optional) Span ID of the exemplar trace.
+ * span_id may be missing if the measurement is not recorded inside a trace
+ * or if the trace is not sampled.
+ *
+ * Generated from protobuf field bytes span_id = 4;
+ * @param string $var
+ * @return $this
+ */
+ public function setSpanId(string $var)
+ {
+ GPBUtil::checkString($var, false);
+ $this->span_id = $var;
+
+ return $this;
+ }
+
+ /**
+ * (Optional) Trace ID of the exemplar trace.
+ * trace_id may be missing if the measurement is not recorded inside a trace
+ * or if the trace is not sampled.
+ *
+ * Generated from protobuf field bytes trace_id = 5;
+ * @return string
+ */
+ public function getTraceId()
+ {
+ return $this->trace_id;
+ }
+
+ /**
+ * (Optional) Trace ID of the exemplar trace.
+ * trace_id may be missing if the measurement is not recorded inside a trace
+ * or if the trace is not sampled.
+ *
+ * Generated from protobuf field bytes trace_id = 5;
+ * @param string $var
+ * @return $this
+ */
+ public function setTraceId(string $var)
+ {
+ GPBUtil::checkString($var, false);
+ $this->trace_id = $var;
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getValue()
+ {
+ return $this->whichOneof("value");
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/ExponentialHistogram.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/ExponentialHistogram.php
new file mode 100644
index 000000000..955f1f073
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/ExponentialHistogram.php
@@ -0,0 +1,111 @@
+opentelemetry.proto.metrics.v1.ExponentialHistogram
+ */
+class ExponentialHistogram extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * The time series data points.
+ * Note: Multiple time series may be included (same timestamp, different attributes).
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.metrics.v1.ExponentialHistogramDataPoint data_points = 1;
+ */
+ private $data_points;
+ /**
+ * aggregation_temporality describes if the aggregator reports delta changes
+ * since last report time, or cumulative changes since a fixed start time.
+ *
+ * Generated from protobuf field .opentelemetry.proto.metrics.v1.AggregationTemporality aggregation_temporality = 2;
+ */
+ protected $aggregation_temporality = 0;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type \Opentelemetry\Proto\Metrics\V1\ExponentialHistogramDataPoint[] $data_points
+ * The time series data points.
+ * Note: Multiple time series may be included (same timestamp, different attributes).
+ * @type int $aggregation_temporality
+ * aggregation_temporality describes if the aggregator reports delta changes
+ * since last report time, or cumulative changes since a fixed start time.
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Metrics\V1\Metrics::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * The time series data points.
+ * Note: Multiple time series may be included (same timestamp, different attributes).
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.metrics.v1.ExponentialHistogramDataPoint data_points = 1;
+ * @return RepeatedField<\Opentelemetry\Proto\Metrics\V1\ExponentialHistogramDataPoint>
+ */
+ public function getDataPoints()
+ {
+ return $this->data_points;
+ }
+
+ /**
+ * The time series data points.
+ * Note: Multiple time series may be included (same timestamp, different attributes).
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.metrics.v1.ExponentialHistogramDataPoint data_points = 1;
+ * @param \Opentelemetry\Proto\Metrics\V1\ExponentialHistogramDataPoint[] $var
+ * @return $this
+ */
+ public function setDataPoints(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Opentelemetry\Proto\Metrics\V1\ExponentialHistogramDataPoint::class);
+ $this->data_points = $arr;
+
+ return $this;
+ }
+
+ /**
+ * aggregation_temporality describes if the aggregator reports delta changes
+ * since last report time, or cumulative changes since a fixed start time.
+ *
+ * Generated from protobuf field .opentelemetry.proto.metrics.v1.AggregationTemporality aggregation_temporality = 2;
+ * @return int one of the values in {@see \Opentelemetry\Proto\Metrics\V1\AggregationTemporality}
+ */
+ public function getAggregationTemporality()
+ {
+ return $this->aggregation_temporality;
+ }
+
+ /**
+ * aggregation_temporality describes if the aggregator reports delta changes
+ * since last report time, or cumulative changes since a fixed start time.
+ *
+ * Generated from protobuf field .opentelemetry.proto.metrics.v1.AggregationTemporality aggregation_temporality = 2;
+ * @param int $var one of the values in {@see \Opentelemetry\Proto\Metrics\V1\AggregationTemporality}
+ * @return $this
+ */
+ public function setAggregationTemporality(int $var)
+ {
+ GPBUtil::checkEnum($var, \Opentelemetry\Proto\Metrics\V1\AggregationTemporality::class);
+ $this->aggregation_temporality = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/ExponentialHistogramDataPoint.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/ExponentialHistogramDataPoint.php
new file mode 100644
index 000000000..99a11e618
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/ExponentialHistogramDataPoint.php
@@ -0,0 +1,717 @@
+opentelemetry.proto.metrics.v1.ExponentialHistogramDataPoint
+ */
+class ExponentialHistogramDataPoint extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * The set of key/value pairs that uniquely identify the timeseries from
+ * where this point belongs. The list may be empty (may contain 0 elements).
+ * Attribute keys MUST be unique (it is not allowed to have more than one
+ * attribute with the same key).
+ * The behavior of software that receives duplicated keys can be unpredictable.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.common.v1.KeyValue attributes = 1;
+ */
+ private $attributes;
+ /**
+ * StartTimeUnixNano is optional but strongly encouraged, see the
+ * the detailed comments above Metric.
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January
+ * 1970.
+ *
+ * Generated from protobuf field fixed64 start_time_unix_nano = 2;
+ */
+ protected $start_time_unix_nano = 0;
+ /**
+ * TimeUnixNano is required, see the detailed comments above Metric.
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January
+ * 1970.
+ *
+ * Generated from protobuf field fixed64 time_unix_nano = 3;
+ */
+ protected $time_unix_nano = 0;
+ /**
+ * The number of values in the population. Must be
+ * non-negative. This value must be equal to the sum of the "bucket_counts"
+ * values in the positive and negative Buckets plus the "zero_count" field.
+ *
+ * Generated from protobuf field fixed64 count = 4;
+ */
+ protected $count = 0;
+ /**
+ * The sum of the values in the population. If count is zero then this field
+ * must be zero.
+ * Note: Sum should only be filled out when measuring non-negative discrete
+ * events, and is assumed to be monotonic over the values of these events.
+ * Negative events *can* be recorded, but sum should not be filled out when
+ * doing so. This is specifically to enforce compatibility w/ OpenMetrics,
+ * see: https://github.com/prometheus/OpenMetrics/blob/v1.0.0/specification/OpenMetrics.md#histogram
+ *
+ * Generated from protobuf field optional double sum = 5;
+ */
+ protected $sum = null;
+ /**
+ * scale describes the resolution of the histogram. Boundaries are
+ * located at powers of the base, where:
+ * base = (2^(2^-scale))
+ * The histogram bucket identified by `index`, a signed integer,
+ * contains values that are greater than (base^index) and
+ * less than or equal to (base^(index+1)).
+ * The positive and negative ranges of the histogram are expressed
+ * separately. Negative values are mapped by their absolute value
+ * into the negative range using the same scale as the positive range.
+ * scale is not restricted by the protocol, as the permissible
+ * values depend on the range of the data.
+ *
+ * Generated from protobuf field sint32 scale = 6;
+ */
+ protected $scale = 0;
+ /**
+ * The count of values that are either exactly zero or
+ * within the region considered zero by the instrumentation at the
+ * tolerated degree of precision. This bucket stores values that
+ * cannot be expressed using the standard exponential formula as
+ * well as values that have been rounded to zero.
+ * Implementations MAY consider the zero bucket to have probability
+ * mass equal to (zero_count / count).
+ *
+ * Generated from protobuf field fixed64 zero_count = 7;
+ */
+ protected $zero_count = 0;
+ /**
+ * positive carries the positive range of exponential bucket counts.
+ *
+ * Generated from protobuf field .opentelemetry.proto.metrics.v1.ExponentialHistogramDataPoint.Buckets positive = 8;
+ */
+ protected $positive = null;
+ /**
+ * negative carries the negative range of exponential bucket counts.
+ *
+ * Generated from protobuf field .opentelemetry.proto.metrics.v1.ExponentialHistogramDataPoint.Buckets negative = 9;
+ */
+ protected $negative = null;
+ /**
+ * Flags that apply to this specific data point. See DataPointFlags
+ * for the available flags and their meaning.
+ *
+ * Generated from protobuf field uint32 flags = 10;
+ */
+ protected $flags = 0;
+ /**
+ * (Optional) List of exemplars collected from
+ * measurements that were used to form the data point
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.metrics.v1.Exemplar exemplars = 11;
+ */
+ private $exemplars;
+ /**
+ * The minimum value over (start_time, end_time].
+ *
+ * Generated from protobuf field optional double min = 12;
+ */
+ protected $min = null;
+ /**
+ * The maximum value over (start_time, end_time].
+ *
+ * Generated from protobuf field optional double max = 13;
+ */
+ protected $max = null;
+ /**
+ * ZeroThreshold may be optionally set to convey the width of the zero
+ * region. Where the zero region is defined as the closed interval
+ * [-ZeroThreshold, ZeroThreshold].
+ * When ZeroThreshold is 0, zero count bucket stores values that cannot be
+ * expressed using the standard exponential formula as well as values that
+ * have been rounded to zero.
+ *
+ * Generated from protobuf field double zero_threshold = 14;
+ */
+ protected $zero_threshold = 0.0;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type \Opentelemetry\Proto\Common\V1\KeyValue[] $attributes
+ * The set of key/value pairs that uniquely identify the timeseries from
+ * where this point belongs. The list may be empty (may contain 0 elements).
+ * Attribute keys MUST be unique (it is not allowed to have more than one
+ * attribute with the same key).
+ * The behavior of software that receives duplicated keys can be unpredictable.
+ * @type int|string $start_time_unix_nano
+ * StartTimeUnixNano is optional but strongly encouraged, see the
+ * the detailed comments above Metric.
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January
+ * 1970.
+ * @type int|string $time_unix_nano
+ * TimeUnixNano is required, see the detailed comments above Metric.
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January
+ * 1970.
+ * @type int|string $count
+ * The number of values in the population. Must be
+ * non-negative. This value must be equal to the sum of the "bucket_counts"
+ * values in the positive and negative Buckets plus the "zero_count" field.
+ * @type float $sum
+ * The sum of the values in the population. If count is zero then this field
+ * must be zero.
+ * Note: Sum should only be filled out when measuring non-negative discrete
+ * events, and is assumed to be monotonic over the values of these events.
+ * Negative events *can* be recorded, but sum should not be filled out when
+ * doing so. This is specifically to enforce compatibility w/ OpenMetrics,
+ * see: https://github.com/prometheus/OpenMetrics/blob/v1.0.0/specification/OpenMetrics.md#histogram
+ * @type int $scale
+ * scale describes the resolution of the histogram. Boundaries are
+ * located at powers of the base, where:
+ * base = (2^(2^-scale))
+ * The histogram bucket identified by `index`, a signed integer,
+ * contains values that are greater than (base^index) and
+ * less than or equal to (base^(index+1)).
+ * The positive and negative ranges of the histogram are expressed
+ * separately. Negative values are mapped by their absolute value
+ * into the negative range using the same scale as the positive range.
+ * scale is not restricted by the protocol, as the permissible
+ * values depend on the range of the data.
+ * @type int|string $zero_count
+ * The count of values that are either exactly zero or
+ * within the region considered zero by the instrumentation at the
+ * tolerated degree of precision. This bucket stores values that
+ * cannot be expressed using the standard exponential formula as
+ * well as values that have been rounded to zero.
+ * Implementations MAY consider the zero bucket to have probability
+ * mass equal to (zero_count / count).
+ * @type \Opentelemetry\Proto\Metrics\V1\ExponentialHistogramDataPoint\Buckets $positive
+ * positive carries the positive range of exponential bucket counts.
+ * @type \Opentelemetry\Proto\Metrics\V1\ExponentialHistogramDataPoint\Buckets $negative
+ * negative carries the negative range of exponential bucket counts.
+ * @type int $flags
+ * Flags that apply to this specific data point. See DataPointFlags
+ * for the available flags and their meaning.
+ * @type \Opentelemetry\Proto\Metrics\V1\Exemplar[] $exemplars
+ * (Optional) List of exemplars collected from
+ * measurements that were used to form the data point
+ * @type float $min
+ * The minimum value over (start_time, end_time].
+ * @type float $max
+ * The maximum value over (start_time, end_time].
+ * @type float $zero_threshold
+ * ZeroThreshold may be optionally set to convey the width of the zero
+ * region. Where the zero region is defined as the closed interval
+ * [-ZeroThreshold, ZeroThreshold].
+ * When ZeroThreshold is 0, zero count bucket stores values that cannot be
+ * expressed using the standard exponential formula as well as values that
+ * have been rounded to zero.
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Metrics\V1\Metrics::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * The set of key/value pairs that uniquely identify the timeseries from
+ * where this point belongs. The list may be empty (may contain 0 elements).
+ * Attribute keys MUST be unique (it is not allowed to have more than one
+ * attribute with the same key).
+ * The behavior of software that receives duplicated keys can be unpredictable.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.common.v1.KeyValue attributes = 1;
+ * @return RepeatedField<\Opentelemetry\Proto\Common\V1\KeyValue>
+ */
+ public function getAttributes()
+ {
+ return $this->attributes;
+ }
+
+ /**
+ * The set of key/value pairs that uniquely identify the timeseries from
+ * where this point belongs. The list may be empty (may contain 0 elements).
+ * Attribute keys MUST be unique (it is not allowed to have more than one
+ * attribute with the same key).
+ * The behavior of software that receives duplicated keys can be unpredictable.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.common.v1.KeyValue attributes = 1;
+ * @param \Opentelemetry\Proto\Common\V1\KeyValue[] $var
+ * @return $this
+ */
+ public function setAttributes(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Opentelemetry\Proto\Common\V1\KeyValue::class);
+ $this->attributes = $arr;
+
+ return $this;
+ }
+
+ /**
+ * StartTimeUnixNano is optional but strongly encouraged, see the
+ * the detailed comments above Metric.
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January
+ * 1970.
+ *
+ * Generated from protobuf field fixed64 start_time_unix_nano = 2;
+ * @return int|string
+ */
+ public function getStartTimeUnixNano()
+ {
+ return $this->start_time_unix_nano;
+ }
+
+ /**
+ * StartTimeUnixNano is optional but strongly encouraged, see the
+ * the detailed comments above Metric.
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January
+ * 1970.
+ *
+ * Generated from protobuf field fixed64 start_time_unix_nano = 2;
+ * @param int|string $var
+ * @return $this
+ */
+ public function setStartTimeUnixNano(int|string $var)
+ {
+ GPBUtil::checkUint64($var);
+ $this->start_time_unix_nano = $var;
+
+ return $this;
+ }
+
+ /**
+ * TimeUnixNano is required, see the detailed comments above Metric.
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January
+ * 1970.
+ *
+ * Generated from protobuf field fixed64 time_unix_nano = 3;
+ * @return int|string
+ */
+ public function getTimeUnixNano()
+ {
+ return $this->time_unix_nano;
+ }
+
+ /**
+ * TimeUnixNano is required, see the detailed comments above Metric.
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January
+ * 1970.
+ *
+ * Generated from protobuf field fixed64 time_unix_nano = 3;
+ * @param int|string $var
+ * @return $this
+ */
+ public function setTimeUnixNano(int|string $var)
+ {
+ GPBUtil::checkUint64($var);
+ $this->time_unix_nano = $var;
+
+ return $this;
+ }
+
+ /**
+ * The number of values in the population. Must be
+ * non-negative. This value must be equal to the sum of the "bucket_counts"
+ * values in the positive and negative Buckets plus the "zero_count" field.
+ *
+ * Generated from protobuf field fixed64 count = 4;
+ * @return int|string
+ */
+ public function getCount()
+ {
+ return $this->count;
+ }
+
+ /**
+ * The number of values in the population. Must be
+ * non-negative. This value must be equal to the sum of the "bucket_counts"
+ * values in the positive and negative Buckets plus the "zero_count" field.
+ *
+ * Generated from protobuf field fixed64 count = 4;
+ * @param int|string $var
+ * @return $this
+ */
+ public function setCount(int|string $var)
+ {
+ GPBUtil::checkUint64($var);
+ $this->count = $var;
+
+ return $this;
+ }
+
+ /**
+ * The sum of the values in the population. If count is zero then this field
+ * must be zero.
+ * Note: Sum should only be filled out when measuring non-negative discrete
+ * events, and is assumed to be monotonic over the values of these events.
+ * Negative events *can* be recorded, but sum should not be filled out when
+ * doing so. This is specifically to enforce compatibility w/ OpenMetrics,
+ * see: https://github.com/prometheus/OpenMetrics/blob/v1.0.0/specification/OpenMetrics.md#histogram
+ *
+ * Generated from protobuf field optional double sum = 5;
+ * @return float
+ */
+ public function getSum()
+ {
+ return isset($this->sum) ? $this->sum : 0.0;
+ }
+
+ public function hasSum()
+ {
+ return isset($this->sum);
+ }
+
+ public function clearSum()
+ {
+ unset($this->sum);
+ }
+
+ /**
+ * The sum of the values in the population. If count is zero then this field
+ * must be zero.
+ * Note: Sum should only be filled out when measuring non-negative discrete
+ * events, and is assumed to be monotonic over the values of these events.
+ * Negative events *can* be recorded, but sum should not be filled out when
+ * doing so. This is specifically to enforce compatibility w/ OpenMetrics,
+ * see: https://github.com/prometheus/OpenMetrics/blob/v1.0.0/specification/OpenMetrics.md#histogram
+ *
+ * Generated from protobuf field optional double sum = 5;
+ * @param float $var
+ * @return $this
+ */
+ public function setSum(float $var)
+ {
+ $this->sum = $var;
+
+ return $this;
+ }
+
+ /**
+ * scale describes the resolution of the histogram. Boundaries are
+ * located at powers of the base, where:
+ * base = (2^(2^-scale))
+ * The histogram bucket identified by `index`, a signed integer,
+ * contains values that are greater than (base^index) and
+ * less than or equal to (base^(index+1)).
+ * The positive and negative ranges of the histogram are expressed
+ * separately. Negative values are mapped by their absolute value
+ * into the negative range using the same scale as the positive range.
+ * scale is not restricted by the protocol, as the permissible
+ * values depend on the range of the data.
+ *
+ * Generated from protobuf field sint32 scale = 6;
+ * @return int
+ */
+ public function getScale()
+ {
+ return $this->scale;
+ }
+
+ /**
+ * scale describes the resolution of the histogram. Boundaries are
+ * located at powers of the base, where:
+ * base = (2^(2^-scale))
+ * The histogram bucket identified by `index`, a signed integer,
+ * contains values that are greater than (base^index) and
+ * less than or equal to (base^(index+1)).
+ * The positive and negative ranges of the histogram are expressed
+ * separately. Negative values are mapped by their absolute value
+ * into the negative range using the same scale as the positive range.
+ * scale is not restricted by the protocol, as the permissible
+ * values depend on the range of the data.
+ *
+ * Generated from protobuf field sint32 scale = 6;
+ * @param int $var
+ * @return $this
+ */
+ public function setScale(int $var)
+ {
+ GPBUtil::checkInt32($var);
+ $this->scale = $var;
+
+ return $this;
+ }
+
+ /**
+ * The count of values that are either exactly zero or
+ * within the region considered zero by the instrumentation at the
+ * tolerated degree of precision. This bucket stores values that
+ * cannot be expressed using the standard exponential formula as
+ * well as values that have been rounded to zero.
+ * Implementations MAY consider the zero bucket to have probability
+ * mass equal to (zero_count / count).
+ *
+ * Generated from protobuf field fixed64 zero_count = 7;
+ * @return int|string
+ */
+ public function getZeroCount()
+ {
+ return $this->zero_count;
+ }
+
+ /**
+ * The count of values that are either exactly zero or
+ * within the region considered zero by the instrumentation at the
+ * tolerated degree of precision. This bucket stores values that
+ * cannot be expressed using the standard exponential formula as
+ * well as values that have been rounded to zero.
+ * Implementations MAY consider the zero bucket to have probability
+ * mass equal to (zero_count / count).
+ *
+ * Generated from protobuf field fixed64 zero_count = 7;
+ * @param int|string $var
+ * @return $this
+ */
+ public function setZeroCount(int|string $var)
+ {
+ GPBUtil::checkUint64($var);
+ $this->zero_count = $var;
+
+ return $this;
+ }
+
+ /**
+ * positive carries the positive range of exponential bucket counts.
+ *
+ * Generated from protobuf field .opentelemetry.proto.metrics.v1.ExponentialHistogramDataPoint.Buckets positive = 8;
+ * @return \Opentelemetry\Proto\Metrics\V1\ExponentialHistogramDataPoint\Buckets|null
+ */
+ public function getPositive()
+ {
+ return $this->positive;
+ }
+
+ public function hasPositive()
+ {
+ return isset($this->positive);
+ }
+
+ public function clearPositive()
+ {
+ unset($this->positive);
+ }
+
+ /**
+ * positive carries the positive range of exponential bucket counts.
+ *
+ * Generated from protobuf field .opentelemetry.proto.metrics.v1.ExponentialHistogramDataPoint.Buckets positive = 8;
+ * @param \Opentelemetry\Proto\Metrics\V1\ExponentialHistogramDataPoint\Buckets $var
+ * @return $this
+ */
+ public function setPositive(\Opentelemetry\Proto\Metrics\V1\ExponentialHistogramDataPoint\Buckets|null $var)
+ {
+ $this->positive = $var;
+
+ return $this;
+ }
+
+ /**
+ * negative carries the negative range of exponential bucket counts.
+ *
+ * Generated from protobuf field .opentelemetry.proto.metrics.v1.ExponentialHistogramDataPoint.Buckets negative = 9;
+ * @return \Opentelemetry\Proto\Metrics\V1\ExponentialHistogramDataPoint\Buckets|null
+ */
+ public function getNegative()
+ {
+ return $this->negative;
+ }
+
+ public function hasNegative()
+ {
+ return isset($this->negative);
+ }
+
+ public function clearNegative()
+ {
+ unset($this->negative);
+ }
+
+ /**
+ * negative carries the negative range of exponential bucket counts.
+ *
+ * Generated from protobuf field .opentelemetry.proto.metrics.v1.ExponentialHistogramDataPoint.Buckets negative = 9;
+ * @param \Opentelemetry\Proto\Metrics\V1\ExponentialHistogramDataPoint\Buckets $var
+ * @return $this
+ */
+ public function setNegative(\Opentelemetry\Proto\Metrics\V1\ExponentialHistogramDataPoint\Buckets|null $var)
+ {
+ $this->negative = $var;
+
+ return $this;
+ }
+
+ /**
+ * Flags that apply to this specific data point. See DataPointFlags
+ * for the available flags and their meaning.
+ *
+ * Generated from protobuf field uint32 flags = 10;
+ * @return int
+ */
+ public function getFlags()
+ {
+ return $this->flags;
+ }
+
+ /**
+ * Flags that apply to this specific data point. See DataPointFlags
+ * for the available flags and their meaning.
+ *
+ * Generated from protobuf field uint32 flags = 10;
+ * @param int $var
+ * @return $this
+ */
+ public function setFlags(int $var)
+ {
+ GPBUtil::checkUint32($var);
+ $this->flags = $var;
+
+ return $this;
+ }
+
+ /**
+ * (Optional) List of exemplars collected from
+ * measurements that were used to form the data point
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.metrics.v1.Exemplar exemplars = 11;
+ * @return RepeatedField<\Opentelemetry\Proto\Metrics\V1\Exemplar>
+ */
+ public function getExemplars()
+ {
+ return $this->exemplars;
+ }
+
+ /**
+ * (Optional) List of exemplars collected from
+ * measurements that were used to form the data point
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.metrics.v1.Exemplar exemplars = 11;
+ * @param \Opentelemetry\Proto\Metrics\V1\Exemplar[] $var
+ * @return $this
+ */
+ public function setExemplars(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Opentelemetry\Proto\Metrics\V1\Exemplar::class);
+ $this->exemplars = $arr;
+
+ return $this;
+ }
+
+ /**
+ * The minimum value over (start_time, end_time].
+ *
+ * Generated from protobuf field optional double min = 12;
+ * @return float
+ */
+ public function getMin()
+ {
+ return isset($this->min) ? $this->min : 0.0;
+ }
+
+ public function hasMin()
+ {
+ return isset($this->min);
+ }
+
+ public function clearMin()
+ {
+ unset($this->min);
+ }
+
+ /**
+ * The minimum value over (start_time, end_time].
+ *
+ * Generated from protobuf field optional double min = 12;
+ * @param float $var
+ * @return $this
+ */
+ public function setMin(float $var)
+ {
+ $this->min = $var;
+
+ return $this;
+ }
+
+ /**
+ * The maximum value over (start_time, end_time].
+ *
+ * Generated from protobuf field optional double max = 13;
+ * @return float
+ */
+ public function getMax()
+ {
+ return isset($this->max) ? $this->max : 0.0;
+ }
+
+ public function hasMax()
+ {
+ return isset($this->max);
+ }
+
+ public function clearMax()
+ {
+ unset($this->max);
+ }
+
+ /**
+ * The maximum value over (start_time, end_time].
+ *
+ * Generated from protobuf field optional double max = 13;
+ * @param float $var
+ * @return $this
+ */
+ public function setMax(float $var)
+ {
+ $this->max = $var;
+
+ return $this;
+ }
+
+ /**
+ * ZeroThreshold may be optionally set to convey the width of the zero
+ * region. Where the zero region is defined as the closed interval
+ * [-ZeroThreshold, ZeroThreshold].
+ * When ZeroThreshold is 0, zero count bucket stores values that cannot be
+ * expressed using the standard exponential formula as well as values that
+ * have been rounded to zero.
+ *
+ * Generated from protobuf field double zero_threshold = 14;
+ * @return float
+ */
+ public function getZeroThreshold()
+ {
+ return $this->zero_threshold;
+ }
+
+ /**
+ * ZeroThreshold may be optionally set to convey the width of the zero
+ * region. Where the zero region is defined as the closed interval
+ * [-ZeroThreshold, ZeroThreshold].
+ * When ZeroThreshold is 0, zero count bucket stores values that cannot be
+ * expressed using the standard exponential formula as well as values that
+ * have been rounded to zero.
+ *
+ * Generated from protobuf field double zero_threshold = 14;
+ * @param float $var
+ * @return $this
+ */
+ public function setZeroThreshold(float $var)
+ {
+ $this->zero_threshold = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/ExponentialHistogramDataPoint/Buckets.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/ExponentialHistogramDataPoint/Buckets.php
new file mode 100644
index 000000000..55c02a13c
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/ExponentialHistogramDataPoint/Buckets.php
@@ -0,0 +1,135 @@
+opentelemetry.proto.metrics.v1.ExponentialHistogramDataPoint.Buckets
+ */
+class Buckets extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * The bucket index of the first entry in the bucket_counts array.
+ * Note: This uses a varint encoding as a simple form of compression.
+ *
+ * Generated from protobuf field sint32 offset = 1;
+ */
+ protected $offset = 0;
+ /**
+ * An array of count values, where bucket_counts[i] carries
+ * the count of the bucket at index (offset+i). bucket_counts[i] is the count
+ * of values greater than base^(offset+i) and less than or equal to
+ * base^(offset+i+1).
+ * Note: By contrast, the explicit HistogramDataPoint uses
+ * fixed64. This field is expected to have many buckets,
+ * especially zeros, so uint64 has been selected to ensure
+ * varint encoding.
+ *
+ * Generated from protobuf field repeated uint64 bucket_counts = 2;
+ */
+ private $bucket_counts;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type int $offset
+ * The bucket index of the first entry in the bucket_counts array.
+ * Note: This uses a varint encoding as a simple form of compression.
+ * @type int[]|string[] $bucket_counts
+ * An array of count values, where bucket_counts[i] carries
+ * the count of the bucket at index (offset+i). bucket_counts[i] is the count
+ * of values greater than base^(offset+i) and less than or equal to
+ * base^(offset+i+1).
+ * Note: By contrast, the explicit HistogramDataPoint uses
+ * fixed64. This field is expected to have many buckets,
+ * especially zeros, so uint64 has been selected to ensure
+ * varint encoding.
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Metrics\V1\Metrics::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * The bucket index of the first entry in the bucket_counts array.
+ * Note: This uses a varint encoding as a simple form of compression.
+ *
+ * Generated from protobuf field sint32 offset = 1;
+ * @return int
+ */
+ public function getOffset()
+ {
+ return $this->offset;
+ }
+
+ /**
+ * The bucket index of the first entry in the bucket_counts array.
+ * Note: This uses a varint encoding as a simple form of compression.
+ *
+ * Generated from protobuf field sint32 offset = 1;
+ * @param int $var
+ * @return $this
+ */
+ public function setOffset(int $var)
+ {
+ GPBUtil::checkInt32($var);
+ $this->offset = $var;
+
+ return $this;
+ }
+
+ /**
+ * An array of count values, where bucket_counts[i] carries
+ * the count of the bucket at index (offset+i). bucket_counts[i] is the count
+ * of values greater than base^(offset+i) and less than or equal to
+ * base^(offset+i+1).
+ * Note: By contrast, the explicit HistogramDataPoint uses
+ * fixed64. This field is expected to have many buckets,
+ * especially zeros, so uint64 has been selected to ensure
+ * varint encoding.
+ *
+ * Generated from protobuf field repeated uint64 bucket_counts = 2;
+ * @return RepeatedField|RepeatedField
+ */
+ public function getBucketCounts()
+ {
+ return $this->bucket_counts;
+ }
+
+ /**
+ * An array of count values, where bucket_counts[i] carries
+ * the count of the bucket at index (offset+i). bucket_counts[i] is the count
+ * of values greater than base^(offset+i) and less than or equal to
+ * base^(offset+i+1).
+ * Note: By contrast, the explicit HistogramDataPoint uses
+ * fixed64. This field is expected to have many buckets,
+ * especially zeros, so uint64 has been selected to ensure
+ * varint encoding.
+ *
+ * Generated from protobuf field repeated uint64 bucket_counts = 2;
+ * @param int[]|string[] $var
+ * @return $this
+ */
+ public function setBucketCounts(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::UINT64);
+ $this->bucket_counts = $arr;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/Gauge.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/Gauge.php
new file mode 100644
index 000000000..097fea489
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/Gauge.php
@@ -0,0 +1,79 @@
+opentelemetry.proto.metrics.v1.Gauge
+ */
+class Gauge extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * The time series data points.
+ * Note: Multiple time series may be included (same timestamp, different attributes).
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.metrics.v1.NumberDataPoint data_points = 1;
+ */
+ private $data_points;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type \Opentelemetry\Proto\Metrics\V1\NumberDataPoint[] $data_points
+ * The time series data points.
+ * Note: Multiple time series may be included (same timestamp, different attributes).
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Metrics\V1\Metrics::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * The time series data points.
+ * Note: Multiple time series may be included (same timestamp, different attributes).
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.metrics.v1.NumberDataPoint data_points = 1;
+ * @return RepeatedField<\Opentelemetry\Proto\Metrics\V1\NumberDataPoint>
+ */
+ public function getDataPoints()
+ {
+ return $this->data_points;
+ }
+
+ /**
+ * The time series data points.
+ * Note: Multiple time series may be included (same timestamp, different attributes).
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.metrics.v1.NumberDataPoint data_points = 1;
+ * @param \Opentelemetry\Proto\Metrics\V1\NumberDataPoint[] $var
+ * @return $this
+ */
+ public function setDataPoints(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Opentelemetry\Proto\Metrics\V1\NumberDataPoint::class);
+ $this->data_points = $arr;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/Histogram.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/Histogram.php
new file mode 100644
index 000000000..dfcd5b608
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/Histogram.php
@@ -0,0 +1,111 @@
+opentelemetry.proto.metrics.v1.Histogram
+ */
+class Histogram extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * The time series data points.
+ * Note: Multiple time series may be included (same timestamp, different attributes).
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.metrics.v1.HistogramDataPoint data_points = 1;
+ */
+ private $data_points;
+ /**
+ * aggregation_temporality describes if the aggregator reports delta changes
+ * since last report time, or cumulative changes since a fixed start time.
+ *
+ * Generated from protobuf field .opentelemetry.proto.metrics.v1.AggregationTemporality aggregation_temporality = 2;
+ */
+ protected $aggregation_temporality = 0;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type \Opentelemetry\Proto\Metrics\V1\HistogramDataPoint[] $data_points
+ * The time series data points.
+ * Note: Multiple time series may be included (same timestamp, different attributes).
+ * @type int $aggregation_temporality
+ * aggregation_temporality describes if the aggregator reports delta changes
+ * since last report time, or cumulative changes since a fixed start time.
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Metrics\V1\Metrics::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * The time series data points.
+ * Note: Multiple time series may be included (same timestamp, different attributes).
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.metrics.v1.HistogramDataPoint data_points = 1;
+ * @return RepeatedField<\Opentelemetry\Proto\Metrics\V1\HistogramDataPoint>
+ */
+ public function getDataPoints()
+ {
+ return $this->data_points;
+ }
+
+ /**
+ * The time series data points.
+ * Note: Multiple time series may be included (same timestamp, different attributes).
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.metrics.v1.HistogramDataPoint data_points = 1;
+ * @param \Opentelemetry\Proto\Metrics\V1\HistogramDataPoint[] $var
+ * @return $this
+ */
+ public function setDataPoints(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Opentelemetry\Proto\Metrics\V1\HistogramDataPoint::class);
+ $this->data_points = $arr;
+
+ return $this;
+ }
+
+ /**
+ * aggregation_temporality describes if the aggregator reports delta changes
+ * since last report time, or cumulative changes since a fixed start time.
+ *
+ * Generated from protobuf field .opentelemetry.proto.metrics.v1.AggregationTemporality aggregation_temporality = 2;
+ * @return int one of the values in {@see \Opentelemetry\Proto\Metrics\V1\AggregationTemporality}
+ */
+ public function getAggregationTemporality()
+ {
+ return $this->aggregation_temporality;
+ }
+
+ /**
+ * aggregation_temporality describes if the aggregator reports delta changes
+ * since last report time, or cumulative changes since a fixed start time.
+ *
+ * Generated from protobuf field .opentelemetry.proto.metrics.v1.AggregationTemporality aggregation_temporality = 2;
+ * @param int $var one of the values in {@see \Opentelemetry\Proto\Metrics\V1\AggregationTemporality}
+ * @return $this
+ */
+ public function setAggregationTemporality(int $var)
+ {
+ GPBUtil::checkEnum($var, \Opentelemetry\Proto\Metrics\V1\AggregationTemporality::class);
+ $this->aggregation_temporality = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/HistogramDataPoint.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/HistogramDataPoint.php
new file mode 100644
index 000000000..9dffebfa7
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/HistogramDataPoint.php
@@ -0,0 +1,583 @@
+opentelemetry.proto.metrics.v1.HistogramDataPoint
+ */
+class HistogramDataPoint extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * The set of key/value pairs that uniquely identify the timeseries from
+ * where this point belongs. The list may be empty (may contain 0 elements).
+ * Attribute keys MUST be unique (it is not allowed to have more than one
+ * attribute with the same key).
+ * The behavior of software that receives duplicated keys can be unpredictable.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.common.v1.KeyValue attributes = 9;
+ */
+ private $attributes;
+ /**
+ * StartTimeUnixNano is optional but strongly encouraged, see the
+ * the detailed comments above Metric.
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January
+ * 1970.
+ *
+ * Generated from protobuf field fixed64 start_time_unix_nano = 2;
+ */
+ protected $start_time_unix_nano = 0;
+ /**
+ * TimeUnixNano is required, see the detailed comments above Metric.
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January
+ * 1970.
+ *
+ * Generated from protobuf field fixed64 time_unix_nano = 3;
+ */
+ protected $time_unix_nano = 0;
+ /**
+ * count is the number of values in the population. Must be non-negative. This
+ * value must be equal to the sum of the "count" fields in buckets if a
+ * histogram is provided.
+ *
+ * Generated from protobuf field fixed64 count = 4;
+ */
+ protected $count = 0;
+ /**
+ * sum of the values in the population. If count is zero then this field
+ * must be zero.
+ * Note: Sum should only be filled out when measuring non-negative discrete
+ * events, and is assumed to be monotonic over the values of these events.
+ * Negative events *can* be recorded, but sum should not be filled out when
+ * doing so. This is specifically to enforce compatibility w/ OpenMetrics,
+ * see: https://github.com/prometheus/OpenMetrics/blob/v1.0.0/specification/OpenMetrics.md#histogram
+ *
+ * Generated from protobuf field optional double sum = 5;
+ */
+ protected $sum = null;
+ /**
+ * bucket_counts is an optional field contains the count values of histogram
+ * for each bucket.
+ * The sum of the bucket_counts must equal the value in the count field.
+ * The number of elements in bucket_counts array must be by one greater than
+ * the number of elements in explicit_bounds array. The exception to this rule
+ * is when the length of bucket_counts is 0, then the length of explicit_bounds
+ * must also be 0.
+ *
+ * Generated from protobuf field repeated fixed64 bucket_counts = 6;
+ */
+ private $bucket_counts;
+ /**
+ * explicit_bounds specifies buckets with explicitly defined bounds for values.
+ * The boundaries for bucket at index i are:
+ * (-infinity, explicit_bounds[i]] for i == 0
+ * (explicit_bounds[i-1], explicit_bounds[i]] for 0 < i < size(explicit_bounds)
+ * (explicit_bounds[i-1], +infinity) for i == size(explicit_bounds)
+ * The values in the explicit_bounds array must be strictly increasing.
+ * Histogram buckets are inclusive of their upper boundary, except the last
+ * bucket where the boundary is at infinity. This format is intentionally
+ * compatible with the OpenMetrics histogram definition.
+ * If bucket_counts length is 0 then explicit_bounds length must also be 0,
+ * otherwise the data point is invalid.
+ *
+ * Generated from protobuf field repeated double explicit_bounds = 7;
+ */
+ private $explicit_bounds;
+ /**
+ * (Optional) List of exemplars collected from
+ * measurements that were used to form the data point
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.metrics.v1.Exemplar exemplars = 8;
+ */
+ private $exemplars;
+ /**
+ * Flags that apply to this specific data point. See DataPointFlags
+ * for the available flags and their meaning.
+ *
+ * Generated from protobuf field uint32 flags = 10;
+ */
+ protected $flags = 0;
+ /**
+ * min is the minimum value over (start_time, end_time].
+ *
+ * Generated from protobuf field optional double min = 11;
+ */
+ protected $min = null;
+ /**
+ * max is the maximum value over (start_time, end_time].
+ *
+ * Generated from protobuf field optional double max = 12;
+ */
+ protected $max = null;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type \Opentelemetry\Proto\Common\V1\KeyValue[] $attributes
+ * The set of key/value pairs that uniquely identify the timeseries from
+ * where this point belongs. The list may be empty (may contain 0 elements).
+ * Attribute keys MUST be unique (it is not allowed to have more than one
+ * attribute with the same key).
+ * The behavior of software that receives duplicated keys can be unpredictable.
+ * @type int|string $start_time_unix_nano
+ * StartTimeUnixNano is optional but strongly encouraged, see the
+ * the detailed comments above Metric.
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January
+ * 1970.
+ * @type int|string $time_unix_nano
+ * TimeUnixNano is required, see the detailed comments above Metric.
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January
+ * 1970.
+ * @type int|string $count
+ * count is the number of values in the population. Must be non-negative. This
+ * value must be equal to the sum of the "count" fields in buckets if a
+ * histogram is provided.
+ * @type float $sum
+ * sum of the values in the population. If count is zero then this field
+ * must be zero.
+ * Note: Sum should only be filled out when measuring non-negative discrete
+ * events, and is assumed to be monotonic over the values of these events.
+ * Negative events *can* be recorded, but sum should not be filled out when
+ * doing so. This is specifically to enforce compatibility w/ OpenMetrics,
+ * see: https://github.com/prometheus/OpenMetrics/blob/v1.0.0/specification/OpenMetrics.md#histogram
+ * @type int[]|string[] $bucket_counts
+ * bucket_counts is an optional field contains the count values of histogram
+ * for each bucket.
+ * The sum of the bucket_counts must equal the value in the count field.
+ * The number of elements in bucket_counts array must be by one greater than
+ * the number of elements in explicit_bounds array. The exception to this rule
+ * is when the length of bucket_counts is 0, then the length of explicit_bounds
+ * must also be 0.
+ * @type float[] $explicit_bounds
+ * explicit_bounds specifies buckets with explicitly defined bounds for values.
+ * The boundaries for bucket at index i are:
+ * (-infinity, explicit_bounds[i]] for i == 0
+ * (explicit_bounds[i-1], explicit_bounds[i]] for 0 < i < size(explicit_bounds)
+ * (explicit_bounds[i-1], +infinity) for i == size(explicit_bounds)
+ * The values in the explicit_bounds array must be strictly increasing.
+ * Histogram buckets are inclusive of their upper boundary, except the last
+ * bucket where the boundary is at infinity. This format is intentionally
+ * compatible with the OpenMetrics histogram definition.
+ * If bucket_counts length is 0 then explicit_bounds length must also be 0,
+ * otherwise the data point is invalid.
+ * @type \Opentelemetry\Proto\Metrics\V1\Exemplar[] $exemplars
+ * (Optional) List of exemplars collected from
+ * measurements that were used to form the data point
+ * @type int $flags
+ * Flags that apply to this specific data point. See DataPointFlags
+ * for the available flags and their meaning.
+ * @type float $min
+ * min is the minimum value over (start_time, end_time].
+ * @type float $max
+ * max is the maximum value over (start_time, end_time].
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Metrics\V1\Metrics::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * The set of key/value pairs that uniquely identify the timeseries from
+ * where this point belongs. The list may be empty (may contain 0 elements).
+ * Attribute keys MUST be unique (it is not allowed to have more than one
+ * attribute with the same key).
+ * The behavior of software that receives duplicated keys can be unpredictable.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.common.v1.KeyValue attributes = 9;
+ * @return RepeatedField<\Opentelemetry\Proto\Common\V1\KeyValue>
+ */
+ public function getAttributes()
+ {
+ return $this->attributes;
+ }
+
+ /**
+ * The set of key/value pairs that uniquely identify the timeseries from
+ * where this point belongs. The list may be empty (may contain 0 elements).
+ * Attribute keys MUST be unique (it is not allowed to have more than one
+ * attribute with the same key).
+ * The behavior of software that receives duplicated keys can be unpredictable.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.common.v1.KeyValue attributes = 9;
+ * @param \Opentelemetry\Proto\Common\V1\KeyValue[] $var
+ * @return $this
+ */
+ public function setAttributes(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Opentelemetry\Proto\Common\V1\KeyValue::class);
+ $this->attributes = $arr;
+
+ return $this;
+ }
+
+ /**
+ * StartTimeUnixNano is optional but strongly encouraged, see the
+ * the detailed comments above Metric.
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January
+ * 1970.
+ *
+ * Generated from protobuf field fixed64 start_time_unix_nano = 2;
+ * @return int|string
+ */
+ public function getStartTimeUnixNano()
+ {
+ return $this->start_time_unix_nano;
+ }
+
+ /**
+ * StartTimeUnixNano is optional but strongly encouraged, see the
+ * the detailed comments above Metric.
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January
+ * 1970.
+ *
+ * Generated from protobuf field fixed64 start_time_unix_nano = 2;
+ * @param int|string $var
+ * @return $this
+ */
+ public function setStartTimeUnixNano(int|string $var)
+ {
+ GPBUtil::checkUint64($var);
+ $this->start_time_unix_nano = $var;
+
+ return $this;
+ }
+
+ /**
+ * TimeUnixNano is required, see the detailed comments above Metric.
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January
+ * 1970.
+ *
+ * Generated from protobuf field fixed64 time_unix_nano = 3;
+ * @return int|string
+ */
+ public function getTimeUnixNano()
+ {
+ return $this->time_unix_nano;
+ }
+
+ /**
+ * TimeUnixNano is required, see the detailed comments above Metric.
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January
+ * 1970.
+ *
+ * Generated from protobuf field fixed64 time_unix_nano = 3;
+ * @param int|string $var
+ * @return $this
+ */
+ public function setTimeUnixNano(int|string $var)
+ {
+ GPBUtil::checkUint64($var);
+ $this->time_unix_nano = $var;
+
+ return $this;
+ }
+
+ /**
+ * count is the number of values in the population. Must be non-negative. This
+ * value must be equal to the sum of the "count" fields in buckets if a
+ * histogram is provided.
+ *
+ * Generated from protobuf field fixed64 count = 4;
+ * @return int|string
+ */
+ public function getCount()
+ {
+ return $this->count;
+ }
+
+ /**
+ * count is the number of values in the population. Must be non-negative. This
+ * value must be equal to the sum of the "count" fields in buckets if a
+ * histogram is provided.
+ *
+ * Generated from protobuf field fixed64 count = 4;
+ * @param int|string $var
+ * @return $this
+ */
+ public function setCount(int|string $var)
+ {
+ GPBUtil::checkUint64($var);
+ $this->count = $var;
+
+ return $this;
+ }
+
+ /**
+ * sum of the values in the population. If count is zero then this field
+ * must be zero.
+ * Note: Sum should only be filled out when measuring non-negative discrete
+ * events, and is assumed to be monotonic over the values of these events.
+ * Negative events *can* be recorded, but sum should not be filled out when
+ * doing so. This is specifically to enforce compatibility w/ OpenMetrics,
+ * see: https://github.com/prometheus/OpenMetrics/blob/v1.0.0/specification/OpenMetrics.md#histogram
+ *
+ * Generated from protobuf field optional double sum = 5;
+ * @return float
+ */
+ public function getSum()
+ {
+ return isset($this->sum) ? $this->sum : 0.0;
+ }
+
+ public function hasSum()
+ {
+ return isset($this->sum);
+ }
+
+ public function clearSum()
+ {
+ unset($this->sum);
+ }
+
+ /**
+ * sum of the values in the population. If count is zero then this field
+ * must be zero.
+ * Note: Sum should only be filled out when measuring non-negative discrete
+ * events, and is assumed to be monotonic over the values of these events.
+ * Negative events *can* be recorded, but sum should not be filled out when
+ * doing so. This is specifically to enforce compatibility w/ OpenMetrics,
+ * see: https://github.com/prometheus/OpenMetrics/blob/v1.0.0/specification/OpenMetrics.md#histogram
+ *
+ * Generated from protobuf field optional double sum = 5;
+ * @param float $var
+ * @return $this
+ */
+ public function setSum(float $var)
+ {
+ $this->sum = $var;
+
+ return $this;
+ }
+
+ /**
+ * bucket_counts is an optional field contains the count values of histogram
+ * for each bucket.
+ * The sum of the bucket_counts must equal the value in the count field.
+ * The number of elements in bucket_counts array must be by one greater than
+ * the number of elements in explicit_bounds array. The exception to this rule
+ * is when the length of bucket_counts is 0, then the length of explicit_bounds
+ * must also be 0.
+ *
+ * Generated from protobuf field repeated fixed64 bucket_counts = 6;
+ * @return RepeatedField|RepeatedField
+ */
+ public function getBucketCounts()
+ {
+ return $this->bucket_counts;
+ }
+
+ /**
+ * bucket_counts is an optional field contains the count values of histogram
+ * for each bucket.
+ * The sum of the bucket_counts must equal the value in the count field.
+ * The number of elements in bucket_counts array must be by one greater than
+ * the number of elements in explicit_bounds array. The exception to this rule
+ * is when the length of bucket_counts is 0, then the length of explicit_bounds
+ * must also be 0.
+ *
+ * Generated from protobuf field repeated fixed64 bucket_counts = 6;
+ * @param int[]|string[] $var
+ * @return $this
+ */
+ public function setBucketCounts(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::FIXED64);
+ $this->bucket_counts = $arr;
+
+ return $this;
+ }
+
+ /**
+ * explicit_bounds specifies buckets with explicitly defined bounds for values.
+ * The boundaries for bucket at index i are:
+ * (-infinity, explicit_bounds[i]] for i == 0
+ * (explicit_bounds[i-1], explicit_bounds[i]] for 0 < i < size(explicit_bounds)
+ * (explicit_bounds[i-1], +infinity) for i == size(explicit_bounds)
+ * The values in the explicit_bounds array must be strictly increasing.
+ * Histogram buckets are inclusive of their upper boundary, except the last
+ * bucket where the boundary is at infinity. This format is intentionally
+ * compatible with the OpenMetrics histogram definition.
+ * If bucket_counts length is 0 then explicit_bounds length must also be 0,
+ * otherwise the data point is invalid.
+ *
+ * Generated from protobuf field repeated double explicit_bounds = 7;
+ * @return RepeatedField
+ */
+ public function getExplicitBounds()
+ {
+ return $this->explicit_bounds;
+ }
+
+ /**
+ * explicit_bounds specifies buckets with explicitly defined bounds for values.
+ * The boundaries for bucket at index i are:
+ * (-infinity, explicit_bounds[i]] for i == 0
+ * (explicit_bounds[i-1], explicit_bounds[i]] for 0 < i < size(explicit_bounds)
+ * (explicit_bounds[i-1], +infinity) for i == size(explicit_bounds)
+ * The values in the explicit_bounds array must be strictly increasing.
+ * Histogram buckets are inclusive of their upper boundary, except the last
+ * bucket where the boundary is at infinity. This format is intentionally
+ * compatible with the OpenMetrics histogram definition.
+ * If bucket_counts length is 0 then explicit_bounds length must also be 0,
+ * otherwise the data point is invalid.
+ *
+ * Generated from protobuf field repeated double explicit_bounds = 7;
+ * @param float[] $var
+ * @return $this
+ */
+ public function setExplicitBounds(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::DOUBLE);
+ $this->explicit_bounds = $arr;
+
+ return $this;
+ }
+
+ /**
+ * (Optional) List of exemplars collected from
+ * measurements that were used to form the data point
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.metrics.v1.Exemplar exemplars = 8;
+ * @return RepeatedField<\Opentelemetry\Proto\Metrics\V1\Exemplar>
+ */
+ public function getExemplars()
+ {
+ return $this->exemplars;
+ }
+
+ /**
+ * (Optional) List of exemplars collected from
+ * measurements that were used to form the data point
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.metrics.v1.Exemplar exemplars = 8;
+ * @param \Opentelemetry\Proto\Metrics\V1\Exemplar[] $var
+ * @return $this
+ */
+ public function setExemplars(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Opentelemetry\Proto\Metrics\V1\Exemplar::class);
+ $this->exemplars = $arr;
+
+ return $this;
+ }
+
+ /**
+ * Flags that apply to this specific data point. See DataPointFlags
+ * for the available flags and their meaning.
+ *
+ * Generated from protobuf field uint32 flags = 10;
+ * @return int
+ */
+ public function getFlags()
+ {
+ return $this->flags;
+ }
+
+ /**
+ * Flags that apply to this specific data point. See DataPointFlags
+ * for the available flags and their meaning.
+ *
+ * Generated from protobuf field uint32 flags = 10;
+ * @param int $var
+ * @return $this
+ */
+ public function setFlags(int $var)
+ {
+ GPBUtil::checkUint32($var);
+ $this->flags = $var;
+
+ return $this;
+ }
+
+ /**
+ * min is the minimum value over (start_time, end_time].
+ *
+ * Generated from protobuf field optional double min = 11;
+ * @return float
+ */
+ public function getMin()
+ {
+ return isset($this->min) ? $this->min : 0.0;
+ }
+
+ public function hasMin()
+ {
+ return isset($this->min);
+ }
+
+ public function clearMin()
+ {
+ unset($this->min);
+ }
+
+ /**
+ * min is the minimum value over (start_time, end_time].
+ *
+ * Generated from protobuf field optional double min = 11;
+ * @param float $var
+ * @return $this
+ */
+ public function setMin(float $var)
+ {
+ $this->min = $var;
+
+ return $this;
+ }
+
+ /**
+ * max is the maximum value over (start_time, end_time].
+ *
+ * Generated from protobuf field optional double max = 12;
+ * @return float
+ */
+ public function getMax()
+ {
+ return isset($this->max) ? $this->max : 0.0;
+ }
+
+ public function hasMax()
+ {
+ return isset($this->max);
+ }
+
+ public function clearMax()
+ {
+ unset($this->max);
+ }
+
+ /**
+ * max is the maximum value over (start_time, end_time].
+ *
+ * Generated from protobuf field optional double max = 12;
+ * @param float $var
+ * @return $this
+ */
+ public function setMax(float $var)
+ {
+ $this->max = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/Metric.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/Metric.php
new file mode 100644
index 000000000..12a7b1761
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/Metric.php
@@ -0,0 +1,416 @@
+ |Gauge, Sum, Histogram, Summary, ... |
+ * +------------+ +------------------------------------+
+ * Data [One of Gauge, Sum, Histogram, Summary, ...]
+ * +-----------+
+ * |... | // Metadata about the Data.
+ * |points |--+
+ * +-----------+ |
+ * | +---------------------------+
+ * | |DataPoint 1 |
+ * v |+------+------+ +------+ |
+ * +-----+ ||label |label |...|label | |
+ * | 1 |-->||value1|value2|...|valueN| |
+ * +-----+ |+------+------+ +------+ |
+ * | . | |+-----+ |
+ * | . | ||value| |
+ * | . | |+-----+ |
+ * | . | +---------------------------+
+ * | . | .
+ * | . | .
+ * | . | .
+ * | . | +---------------------------+
+ * | . | |DataPoint M |
+ * +-----+ |+------+------+ +------+ |
+ * | M |-->||label |label |...|label | |
+ * +-----+ ||value1|value2|...|valueN| |
+ * |+------+------+ +------+ |
+ * |+-----+ |
+ * ||value| |
+ * |+-----+ |
+ * +---------------------------+
+ * Each distinct type of DataPoint represents the output of a specific
+ * aggregation function, the result of applying the DataPoint's
+ * associated function of to one or more measurements.
+ * All DataPoint types have three common fields:
+ * - Attributes includes key-value pairs associated with the data point
+ * - TimeUnixNano is required, set to the end time of the aggregation
+ * - StartTimeUnixNano is optional, but strongly encouraged for DataPoints
+ * having an AggregationTemporality field, as discussed below.
+ * Both TimeUnixNano and StartTimeUnixNano values are expressed as
+ * UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970.
+ * # TimeUnixNano
+ * This field is required, having consistent interpretation across
+ * DataPoint types. TimeUnixNano is the moment corresponding to when
+ * the data point's aggregate value was captured.
+ * Data points with the 0 value for TimeUnixNano SHOULD be rejected
+ * by consumers.
+ * # StartTimeUnixNano
+ * StartTimeUnixNano in general allows detecting when a sequence of
+ * observations is unbroken. This field indicates to consumers the
+ * start time for points with cumulative and delta
+ * AggregationTemporality, and it should be included whenever possible
+ * to support correct rate calculation. Although it may be omitted
+ * when the start time is truly unknown, setting StartTimeUnixNano is
+ * strongly encouraged.
+ *
+ * Generated from protobuf message opentelemetry.proto.metrics.v1.Metric
+ */
+class Metric extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * The name of the metric.
+ *
+ * Generated from protobuf field string name = 1;
+ */
+ protected $name = '';
+ /**
+ * A description of the metric, which can be used in documentation.
+ *
+ * Generated from protobuf field string description = 2;
+ */
+ protected $description = '';
+ /**
+ * The unit in which the metric value is reported. Follows the format
+ * described by https://unitsofmeasure.org/ucum.html.
+ *
+ * Generated from protobuf field string unit = 3;
+ */
+ protected $unit = '';
+ /**
+ * Additional metadata attributes that describe the metric. [Optional].
+ * Attributes are non-identifying.
+ * Consumers SHOULD NOT need to be aware of these attributes.
+ * These attributes MAY be used to encode information allowing
+ * for lossless roundtrip translation to / from another data model.
+ * Attribute keys MUST be unique (it is not allowed to have more than one
+ * attribute with the same key).
+ * The behavior of software that receives duplicated keys can be unpredictable.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.common.v1.KeyValue metadata = 12;
+ */
+ private $metadata;
+ protected $data;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type string $name
+ * The name of the metric.
+ * @type string $description
+ * A description of the metric, which can be used in documentation.
+ * @type string $unit
+ * The unit in which the metric value is reported. Follows the format
+ * described by https://unitsofmeasure.org/ucum.html.
+ * @type \Opentelemetry\Proto\Metrics\V1\Gauge $gauge
+ * @type \Opentelemetry\Proto\Metrics\V1\Sum $sum
+ * @type \Opentelemetry\Proto\Metrics\V1\Histogram $histogram
+ * @type \Opentelemetry\Proto\Metrics\V1\ExponentialHistogram $exponential_histogram
+ * @type \Opentelemetry\Proto\Metrics\V1\Summary $summary
+ * @type \Opentelemetry\Proto\Common\V1\KeyValue[] $metadata
+ * Additional metadata attributes that describe the metric. [Optional].
+ * Attributes are non-identifying.
+ * Consumers SHOULD NOT need to be aware of these attributes.
+ * These attributes MAY be used to encode information allowing
+ * for lossless roundtrip translation to / from another data model.
+ * Attribute keys MUST be unique (it is not allowed to have more than one
+ * attribute with the same key).
+ * The behavior of software that receives duplicated keys can be unpredictable.
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Metrics\V1\Metrics::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * The name of the metric.
+ *
+ * Generated from protobuf field string name = 1;
+ * @return string
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * The name of the metric.
+ *
+ * Generated from protobuf field string name = 1;
+ * @param string $var
+ * @return $this
+ */
+ public function setName(string $var)
+ {
+ GPBUtil::checkString($var, true);
+ $this->name = $var;
+
+ return $this;
+ }
+
+ /**
+ * A description of the metric, which can be used in documentation.
+ *
+ * Generated from protobuf field string description = 2;
+ * @return string
+ */
+ public function getDescription()
+ {
+ return $this->description;
+ }
+
+ /**
+ * A description of the metric, which can be used in documentation.
+ *
+ * Generated from protobuf field string description = 2;
+ * @param string $var
+ * @return $this
+ */
+ public function setDescription(string $var)
+ {
+ GPBUtil::checkString($var, true);
+ $this->description = $var;
+
+ return $this;
+ }
+
+ /**
+ * The unit in which the metric value is reported. Follows the format
+ * described by https://unitsofmeasure.org/ucum.html.
+ *
+ * Generated from protobuf field string unit = 3;
+ * @return string
+ */
+ public function getUnit()
+ {
+ return $this->unit;
+ }
+
+ /**
+ * The unit in which the metric value is reported. Follows the format
+ * described by https://unitsofmeasure.org/ucum.html.
+ *
+ * Generated from protobuf field string unit = 3;
+ * @param string $var
+ * @return $this
+ */
+ public function setUnit(string $var)
+ {
+ GPBUtil::checkString($var, true);
+ $this->unit = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field .opentelemetry.proto.metrics.v1.Gauge gauge = 5;
+ * @return \Opentelemetry\Proto\Metrics\V1\Gauge|null
+ */
+ public function getGauge()
+ {
+ return $this->readOneof(5);
+ }
+
+ public function hasGauge()
+ {
+ return $this->hasOneof(5);
+ }
+
+ /**
+ * Generated from protobuf field .opentelemetry.proto.metrics.v1.Gauge gauge = 5;
+ * @param \Opentelemetry\Proto\Metrics\V1\Gauge $var
+ * @return $this
+ */
+ public function setGauge(\Opentelemetry\Proto\Metrics\V1\Gauge|null $var)
+ {
+ $this->writeOneof(5, $var);
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field .opentelemetry.proto.metrics.v1.Sum sum = 7;
+ * @return \Opentelemetry\Proto\Metrics\V1\Sum|null
+ */
+ public function getSum()
+ {
+ return $this->readOneof(7);
+ }
+
+ public function hasSum()
+ {
+ return $this->hasOneof(7);
+ }
+
+ /**
+ * Generated from protobuf field .opentelemetry.proto.metrics.v1.Sum sum = 7;
+ * @param \Opentelemetry\Proto\Metrics\V1\Sum $var
+ * @return $this
+ */
+ public function setSum(\Opentelemetry\Proto\Metrics\V1\Sum|null $var)
+ {
+ $this->writeOneof(7, $var);
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field .opentelemetry.proto.metrics.v1.Histogram histogram = 9;
+ * @return \Opentelemetry\Proto\Metrics\V1\Histogram|null
+ */
+ public function getHistogram()
+ {
+ return $this->readOneof(9);
+ }
+
+ public function hasHistogram()
+ {
+ return $this->hasOneof(9);
+ }
+
+ /**
+ * Generated from protobuf field .opentelemetry.proto.metrics.v1.Histogram histogram = 9;
+ * @param \Opentelemetry\Proto\Metrics\V1\Histogram $var
+ * @return $this
+ */
+ public function setHistogram(\Opentelemetry\Proto\Metrics\V1\Histogram|null $var)
+ {
+ $this->writeOneof(9, $var);
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field .opentelemetry.proto.metrics.v1.ExponentialHistogram exponential_histogram = 10;
+ * @return \Opentelemetry\Proto\Metrics\V1\ExponentialHistogram|null
+ */
+ public function getExponentialHistogram()
+ {
+ return $this->readOneof(10);
+ }
+
+ public function hasExponentialHistogram()
+ {
+ return $this->hasOneof(10);
+ }
+
+ /**
+ * Generated from protobuf field .opentelemetry.proto.metrics.v1.ExponentialHistogram exponential_histogram = 10;
+ * @param \Opentelemetry\Proto\Metrics\V1\ExponentialHistogram $var
+ * @return $this
+ */
+ public function setExponentialHistogram(\Opentelemetry\Proto\Metrics\V1\ExponentialHistogram|null $var)
+ {
+ $this->writeOneof(10, $var);
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field .opentelemetry.proto.metrics.v1.Summary summary = 11;
+ * @return \Opentelemetry\Proto\Metrics\V1\Summary|null
+ */
+ public function getSummary()
+ {
+ return $this->readOneof(11);
+ }
+
+ public function hasSummary()
+ {
+ return $this->hasOneof(11);
+ }
+
+ /**
+ * Generated from protobuf field .opentelemetry.proto.metrics.v1.Summary summary = 11;
+ * @param \Opentelemetry\Proto\Metrics\V1\Summary $var
+ * @return $this
+ */
+ public function setSummary(\Opentelemetry\Proto\Metrics\V1\Summary|null $var)
+ {
+ $this->writeOneof(11, $var);
+
+ return $this;
+ }
+
+ /**
+ * Additional metadata attributes that describe the metric. [Optional].
+ * Attributes are non-identifying.
+ * Consumers SHOULD NOT need to be aware of these attributes.
+ * These attributes MAY be used to encode information allowing
+ * for lossless roundtrip translation to / from another data model.
+ * Attribute keys MUST be unique (it is not allowed to have more than one
+ * attribute with the same key).
+ * The behavior of software that receives duplicated keys can be unpredictable.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.common.v1.KeyValue metadata = 12;
+ * @return RepeatedField<\Opentelemetry\Proto\Common\V1\KeyValue>
+ */
+ public function getMetadata()
+ {
+ return $this->metadata;
+ }
+
+ /**
+ * Additional metadata attributes that describe the metric. [Optional].
+ * Attributes are non-identifying.
+ * Consumers SHOULD NOT need to be aware of these attributes.
+ * These attributes MAY be used to encode information allowing
+ * for lossless roundtrip translation to / from another data model.
+ * Attribute keys MUST be unique (it is not allowed to have more than one
+ * attribute with the same key).
+ * The behavior of software that receives duplicated keys can be unpredictable.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.common.v1.KeyValue metadata = 12;
+ * @param \Opentelemetry\Proto\Common\V1\KeyValue[] $var
+ * @return $this
+ */
+ public function setMetadata(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Opentelemetry\Proto\Common\V1\KeyValue::class);
+ $this->metadata = $arr;
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getData()
+ {
+ return $this->whichOneof("data");
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/MetricsData.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/MetricsData.php
new file mode 100644
index 000000000..8cee58063
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/MetricsData.php
@@ -0,0 +1,108 @@
+opentelemetry.proto.metrics.v1.MetricsData
+ */
+class MetricsData extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * An array of ResourceMetrics.
+ * For data coming from a single resource this array will typically contain
+ * one element. Intermediary nodes that receive data from multiple origins
+ * typically batch the data before forwarding further and in that case this
+ * array will contain multiple elements.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.metrics.v1.ResourceMetrics resource_metrics = 1;
+ */
+ private $resource_metrics;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type \Opentelemetry\Proto\Metrics\V1\ResourceMetrics[] $resource_metrics
+ * An array of ResourceMetrics.
+ * For data coming from a single resource this array will typically contain
+ * one element. Intermediary nodes that receive data from multiple origins
+ * typically batch the data before forwarding further and in that case this
+ * array will contain multiple elements.
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Metrics\V1\Metrics::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * An array of ResourceMetrics.
+ * For data coming from a single resource this array will typically contain
+ * one element. Intermediary nodes that receive data from multiple origins
+ * typically batch the data before forwarding further and in that case this
+ * array will contain multiple elements.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.metrics.v1.ResourceMetrics resource_metrics = 1;
+ * @return RepeatedField<\Opentelemetry\Proto\Metrics\V1\ResourceMetrics>
+ */
+ public function getResourceMetrics()
+ {
+ return $this->resource_metrics;
+ }
+
+ /**
+ * An array of ResourceMetrics.
+ * For data coming from a single resource this array will typically contain
+ * one element. Intermediary nodes that receive data from multiple origins
+ * typically batch the data before forwarding further and in that case this
+ * array will contain multiple elements.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.metrics.v1.ResourceMetrics resource_metrics = 1;
+ * @param \Opentelemetry\Proto\Metrics\V1\ResourceMetrics[] $var
+ * @return $this
+ */
+ public function setResourceMetrics(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Opentelemetry\Proto\Metrics\V1\ResourceMetrics::class);
+ $this->resource_metrics = $arr;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/NumberDataPoint.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/NumberDataPoint.php
new file mode 100644
index 000000000..89fe670d2
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/NumberDataPoint.php
@@ -0,0 +1,313 @@
+opentelemetry.proto.metrics.v1.NumberDataPoint
+ */
+class NumberDataPoint extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * The set of key/value pairs that uniquely identify the timeseries from
+ * where this point belongs. The list may be empty (may contain 0 elements).
+ * Attribute keys MUST be unique (it is not allowed to have more than one
+ * attribute with the same key).
+ * The behavior of software that receives duplicated keys can be unpredictable.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.common.v1.KeyValue attributes = 7;
+ */
+ private $attributes;
+ /**
+ * StartTimeUnixNano is optional but strongly encouraged, see the
+ * the detailed comments above Metric.
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January
+ * 1970.
+ *
+ * Generated from protobuf field fixed64 start_time_unix_nano = 2;
+ */
+ protected $start_time_unix_nano = 0;
+ /**
+ * TimeUnixNano is required, see the detailed comments above Metric.
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January
+ * 1970.
+ *
+ * Generated from protobuf field fixed64 time_unix_nano = 3;
+ */
+ protected $time_unix_nano = 0;
+ /**
+ * (Optional) List of exemplars collected from
+ * measurements that were used to form the data point
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.metrics.v1.Exemplar exemplars = 5;
+ */
+ private $exemplars;
+ /**
+ * Flags that apply to this specific data point. See DataPointFlags
+ * for the available flags and their meaning.
+ *
+ * Generated from protobuf field uint32 flags = 8;
+ */
+ protected $flags = 0;
+ protected $value;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type \Opentelemetry\Proto\Common\V1\KeyValue[] $attributes
+ * The set of key/value pairs that uniquely identify the timeseries from
+ * where this point belongs. The list may be empty (may contain 0 elements).
+ * Attribute keys MUST be unique (it is not allowed to have more than one
+ * attribute with the same key).
+ * The behavior of software that receives duplicated keys can be unpredictable.
+ * @type int|string $start_time_unix_nano
+ * StartTimeUnixNano is optional but strongly encouraged, see the
+ * the detailed comments above Metric.
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January
+ * 1970.
+ * @type int|string $time_unix_nano
+ * TimeUnixNano is required, see the detailed comments above Metric.
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January
+ * 1970.
+ * @type float $as_double
+ * @type int|string $as_int
+ * @type \Opentelemetry\Proto\Metrics\V1\Exemplar[] $exemplars
+ * (Optional) List of exemplars collected from
+ * measurements that were used to form the data point
+ * @type int $flags
+ * Flags that apply to this specific data point. See DataPointFlags
+ * for the available flags and their meaning.
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Metrics\V1\Metrics::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * The set of key/value pairs that uniquely identify the timeseries from
+ * where this point belongs. The list may be empty (may contain 0 elements).
+ * Attribute keys MUST be unique (it is not allowed to have more than one
+ * attribute with the same key).
+ * The behavior of software that receives duplicated keys can be unpredictable.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.common.v1.KeyValue attributes = 7;
+ * @return RepeatedField<\Opentelemetry\Proto\Common\V1\KeyValue>
+ */
+ public function getAttributes()
+ {
+ return $this->attributes;
+ }
+
+ /**
+ * The set of key/value pairs that uniquely identify the timeseries from
+ * where this point belongs. The list may be empty (may contain 0 elements).
+ * Attribute keys MUST be unique (it is not allowed to have more than one
+ * attribute with the same key).
+ * The behavior of software that receives duplicated keys can be unpredictable.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.common.v1.KeyValue attributes = 7;
+ * @param \Opentelemetry\Proto\Common\V1\KeyValue[] $var
+ * @return $this
+ */
+ public function setAttributes(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Opentelemetry\Proto\Common\V1\KeyValue::class);
+ $this->attributes = $arr;
+
+ return $this;
+ }
+
+ /**
+ * StartTimeUnixNano is optional but strongly encouraged, see the
+ * the detailed comments above Metric.
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January
+ * 1970.
+ *
+ * Generated from protobuf field fixed64 start_time_unix_nano = 2;
+ * @return int|string
+ */
+ public function getStartTimeUnixNano()
+ {
+ return $this->start_time_unix_nano;
+ }
+
+ /**
+ * StartTimeUnixNano is optional but strongly encouraged, see the
+ * the detailed comments above Metric.
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January
+ * 1970.
+ *
+ * Generated from protobuf field fixed64 start_time_unix_nano = 2;
+ * @param int|string $var
+ * @return $this
+ */
+ public function setStartTimeUnixNano(int|string $var)
+ {
+ GPBUtil::checkUint64($var);
+ $this->start_time_unix_nano = $var;
+
+ return $this;
+ }
+
+ /**
+ * TimeUnixNano is required, see the detailed comments above Metric.
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January
+ * 1970.
+ *
+ * Generated from protobuf field fixed64 time_unix_nano = 3;
+ * @return int|string
+ */
+ public function getTimeUnixNano()
+ {
+ return $this->time_unix_nano;
+ }
+
+ /**
+ * TimeUnixNano is required, see the detailed comments above Metric.
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January
+ * 1970.
+ *
+ * Generated from protobuf field fixed64 time_unix_nano = 3;
+ * @param int|string $var
+ * @return $this
+ */
+ public function setTimeUnixNano(int|string $var)
+ {
+ GPBUtil::checkUint64($var);
+ $this->time_unix_nano = $var;
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field double as_double = 4;
+ * @return float
+ */
+ public function getAsDouble()
+ {
+ return $this->readOneof(4);
+ }
+
+ public function hasAsDouble()
+ {
+ return $this->hasOneof(4);
+ }
+
+ /**
+ * Generated from protobuf field double as_double = 4;
+ * @param float $var
+ * @return $this
+ */
+ public function setAsDouble(float $var)
+ {
+ $this->writeOneof(4, $var);
+
+ return $this;
+ }
+
+ /**
+ * Generated from protobuf field sfixed64 as_int = 6;
+ * @return int|string
+ */
+ public function getAsInt()
+ {
+ return $this->readOneof(6);
+ }
+
+ public function hasAsInt()
+ {
+ return $this->hasOneof(6);
+ }
+
+ /**
+ * Generated from protobuf field sfixed64 as_int = 6;
+ * @param int|string $var
+ * @return $this
+ */
+ public function setAsInt(int|string $var)
+ {
+ GPBUtil::checkInt64($var);
+ $this->writeOneof(6, $var);
+
+ return $this;
+ }
+
+ /**
+ * (Optional) List of exemplars collected from
+ * measurements that were used to form the data point
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.metrics.v1.Exemplar exemplars = 5;
+ * @return RepeatedField<\Opentelemetry\Proto\Metrics\V1\Exemplar>
+ */
+ public function getExemplars()
+ {
+ return $this->exemplars;
+ }
+
+ /**
+ * (Optional) List of exemplars collected from
+ * measurements that were used to form the data point
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.metrics.v1.Exemplar exemplars = 5;
+ * @param \Opentelemetry\Proto\Metrics\V1\Exemplar[] $var
+ * @return $this
+ */
+ public function setExemplars(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Opentelemetry\Proto\Metrics\V1\Exemplar::class);
+ $this->exemplars = $arr;
+
+ return $this;
+ }
+
+ /**
+ * Flags that apply to this specific data point. See DataPointFlags
+ * for the available flags and their meaning.
+ *
+ * Generated from protobuf field uint32 flags = 8;
+ * @return int
+ */
+ public function getFlags()
+ {
+ return $this->flags;
+ }
+
+ /**
+ * Flags that apply to this specific data point. See DataPointFlags
+ * for the available flags and their meaning.
+ *
+ * Generated from protobuf field uint32 flags = 8;
+ * @param int $var
+ * @return $this
+ */
+ public function setFlags(int $var)
+ {
+ GPBUtil::checkUint32($var);
+ $this->flags = $var;
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getValue()
+ {
+ return $this->whichOneof("value");
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/ResourceMetrics.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/ResourceMetrics.php
new file mode 100644
index 000000000..e280a3eb6
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/ResourceMetrics.php
@@ -0,0 +1,169 @@
+opentelemetry.proto.metrics.v1.ResourceMetrics
+ */
+class ResourceMetrics extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * The resource for the metrics in this message.
+ * If this field is not set then no resource info is known.
+ *
+ * Generated from protobuf field .opentelemetry.proto.resource.v1.Resource resource = 1;
+ */
+ protected $resource = null;
+ /**
+ * A list of metrics that originate from a resource.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.metrics.v1.ScopeMetrics scope_metrics = 2;
+ */
+ private $scope_metrics;
+ /**
+ * The Schema URL, if known. This is the identifier of the Schema that the resource data
+ * is recorded in. Notably, the last part of the URL path is the version number of the
+ * schema: http[s]://server[:port]/path/. To learn more about Schema URL see
+ * https://opentelemetry.io/docs/specs/otel/schemas/#schema-url
+ * This schema_url applies to the data in the "resource" field. It does not apply
+ * to the data in the "scope_metrics" field which have their own schema_url field.
+ *
+ * Generated from protobuf field string schema_url = 3;
+ */
+ protected $schema_url = '';
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type \Opentelemetry\Proto\Resource\V1\Resource $resource
+ * The resource for the metrics in this message.
+ * If this field is not set then no resource info is known.
+ * @type \Opentelemetry\Proto\Metrics\V1\ScopeMetrics[] $scope_metrics
+ * A list of metrics that originate from a resource.
+ * @type string $schema_url
+ * The Schema URL, if known. This is the identifier of the Schema that the resource data
+ * is recorded in. Notably, the last part of the URL path is the version number of the
+ * schema: http[s]://server[:port]/path/. To learn more about Schema URL see
+ * https://opentelemetry.io/docs/specs/otel/schemas/#schema-url
+ * This schema_url applies to the data in the "resource" field. It does not apply
+ * to the data in the "scope_metrics" field which have their own schema_url field.
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Metrics\V1\Metrics::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * The resource for the metrics in this message.
+ * If this field is not set then no resource info is known.
+ *
+ * Generated from protobuf field .opentelemetry.proto.resource.v1.Resource resource = 1;
+ * @return \Opentelemetry\Proto\Resource\V1\Resource|null
+ */
+ public function getResource()
+ {
+ return $this->resource;
+ }
+
+ public function hasResource()
+ {
+ return isset($this->resource);
+ }
+
+ public function clearResource()
+ {
+ unset($this->resource);
+ }
+
+ /**
+ * The resource for the metrics in this message.
+ * If this field is not set then no resource info is known.
+ *
+ * Generated from protobuf field .opentelemetry.proto.resource.v1.Resource resource = 1;
+ * @param \Opentelemetry\Proto\Resource\V1\Resource $var
+ * @return $this
+ */
+ public function setResource(\Opentelemetry\Proto\Resource\V1\Resource|null $var)
+ {
+ $this->resource = $var;
+
+ return $this;
+ }
+
+ /**
+ * A list of metrics that originate from a resource.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.metrics.v1.ScopeMetrics scope_metrics = 2;
+ * @return RepeatedField<\Opentelemetry\Proto\Metrics\V1\ScopeMetrics>
+ */
+ public function getScopeMetrics()
+ {
+ return $this->scope_metrics;
+ }
+
+ /**
+ * A list of metrics that originate from a resource.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.metrics.v1.ScopeMetrics scope_metrics = 2;
+ * @param \Opentelemetry\Proto\Metrics\V1\ScopeMetrics[] $var
+ * @return $this
+ */
+ public function setScopeMetrics(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Opentelemetry\Proto\Metrics\V1\ScopeMetrics::class);
+ $this->scope_metrics = $arr;
+
+ return $this;
+ }
+
+ /**
+ * The Schema URL, if known. This is the identifier of the Schema that the resource data
+ * is recorded in. Notably, the last part of the URL path is the version number of the
+ * schema: http[s]://server[:port]/path/. To learn more about Schema URL see
+ * https://opentelemetry.io/docs/specs/otel/schemas/#schema-url
+ * This schema_url applies to the data in the "resource" field. It does not apply
+ * to the data in the "scope_metrics" field which have their own schema_url field.
+ *
+ * Generated from protobuf field string schema_url = 3;
+ * @return string
+ */
+ public function getSchemaUrl()
+ {
+ return $this->schema_url;
+ }
+
+ /**
+ * The Schema URL, if known. This is the identifier of the Schema that the resource data
+ * is recorded in. Notably, the last part of the URL path is the version number of the
+ * schema: http[s]://server[:port]/path/. To learn more about Schema URL see
+ * https://opentelemetry.io/docs/specs/otel/schemas/#schema-url
+ * This schema_url applies to the data in the "resource" field. It does not apply
+ * to the data in the "scope_metrics" field which have their own schema_url field.
+ *
+ * Generated from protobuf field string schema_url = 3;
+ * @param string $var
+ * @return $this
+ */
+ public function setSchemaUrl(string $var)
+ {
+ GPBUtil::checkString($var, true);
+ $this->schema_url = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/ScopeMetrics.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/ScopeMetrics.php
new file mode 100644
index 000000000..07fc78241
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/ScopeMetrics.php
@@ -0,0 +1,173 @@
+opentelemetry.proto.metrics.v1.ScopeMetrics
+ */
+class ScopeMetrics extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * The instrumentation scope information for the metrics in this message.
+ * Semantically when InstrumentationScope isn't set, it is equivalent with
+ * an empty instrumentation scope name (unknown).
+ *
+ * Generated from protobuf field .opentelemetry.proto.common.v1.InstrumentationScope scope = 1;
+ */
+ protected $scope = null;
+ /**
+ * A list of metrics that originate from an instrumentation library.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.metrics.v1.Metric metrics = 2;
+ */
+ private $metrics;
+ /**
+ * The Schema URL, if known. This is the identifier of the Schema that the metric data
+ * is recorded in. Notably, the last part of the URL path is the version number of the
+ * schema: http[s]://server[:port]/path/. To learn more about Schema URL see
+ * https://opentelemetry.io/docs/specs/otel/schemas/#schema-url
+ * This schema_url applies to the data in the "scope" field and all metrics in the
+ * "metrics" field.
+ *
+ * Generated from protobuf field string schema_url = 3;
+ */
+ protected $schema_url = '';
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type \Opentelemetry\Proto\Common\V1\InstrumentationScope $scope
+ * The instrumentation scope information for the metrics in this message.
+ * Semantically when InstrumentationScope isn't set, it is equivalent with
+ * an empty instrumentation scope name (unknown).
+ * @type \Opentelemetry\Proto\Metrics\V1\Metric[] $metrics
+ * A list of metrics that originate from an instrumentation library.
+ * @type string $schema_url
+ * The Schema URL, if known. This is the identifier of the Schema that the metric data
+ * is recorded in. Notably, the last part of the URL path is the version number of the
+ * schema: http[s]://server[:port]/path/. To learn more about Schema URL see
+ * https://opentelemetry.io/docs/specs/otel/schemas/#schema-url
+ * This schema_url applies to the data in the "scope" field and all metrics in the
+ * "metrics" field.
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Metrics\V1\Metrics::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * The instrumentation scope information for the metrics in this message.
+ * Semantically when InstrumentationScope isn't set, it is equivalent with
+ * an empty instrumentation scope name (unknown).
+ *
+ * Generated from protobuf field .opentelemetry.proto.common.v1.InstrumentationScope scope = 1;
+ * @return \Opentelemetry\Proto\Common\V1\InstrumentationScope|null
+ */
+ public function getScope()
+ {
+ return $this->scope;
+ }
+
+ public function hasScope()
+ {
+ return isset($this->scope);
+ }
+
+ public function clearScope()
+ {
+ unset($this->scope);
+ }
+
+ /**
+ * The instrumentation scope information for the metrics in this message.
+ * Semantically when InstrumentationScope isn't set, it is equivalent with
+ * an empty instrumentation scope name (unknown).
+ *
+ * Generated from protobuf field .opentelemetry.proto.common.v1.InstrumentationScope scope = 1;
+ * @param \Opentelemetry\Proto\Common\V1\InstrumentationScope $var
+ * @return $this
+ */
+ public function setScope(\Opentelemetry\Proto\Common\V1\InstrumentationScope|null $var)
+ {
+ $this->scope = $var;
+
+ return $this;
+ }
+
+ /**
+ * A list of metrics that originate from an instrumentation library.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.metrics.v1.Metric metrics = 2;
+ * @return RepeatedField<\Opentelemetry\Proto\Metrics\V1\Metric>
+ */
+ public function getMetrics()
+ {
+ return $this->metrics;
+ }
+
+ /**
+ * A list of metrics that originate from an instrumentation library.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.metrics.v1.Metric metrics = 2;
+ * @param \Opentelemetry\Proto\Metrics\V1\Metric[] $var
+ * @return $this
+ */
+ public function setMetrics(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Opentelemetry\Proto\Metrics\V1\Metric::class);
+ $this->metrics = $arr;
+
+ return $this;
+ }
+
+ /**
+ * The Schema URL, if known. This is the identifier of the Schema that the metric data
+ * is recorded in. Notably, the last part of the URL path is the version number of the
+ * schema: http[s]://server[:port]/path/. To learn more about Schema URL see
+ * https://opentelemetry.io/docs/specs/otel/schemas/#schema-url
+ * This schema_url applies to the data in the "scope" field and all metrics in the
+ * "metrics" field.
+ *
+ * Generated from protobuf field string schema_url = 3;
+ * @return string
+ */
+ public function getSchemaUrl()
+ {
+ return $this->schema_url;
+ }
+
+ /**
+ * The Schema URL, if known. This is the identifier of the Schema that the metric data
+ * is recorded in. Notably, the last part of the URL path is the version number of the
+ * schema: http[s]://server[:port]/path/. To learn more about Schema URL see
+ * https://opentelemetry.io/docs/specs/otel/schemas/#schema-url
+ * This schema_url applies to the data in the "scope" field and all metrics in the
+ * "metrics" field.
+ *
+ * Generated from protobuf field string schema_url = 3;
+ * @param string $var
+ * @return $this
+ */
+ public function setSchemaUrl(string $var)
+ {
+ GPBUtil::checkString($var, true);
+ $this->schema_url = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/Sum.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/Sum.php
new file mode 100644
index 000000000..d720843f6
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/Sum.php
@@ -0,0 +1,144 @@
+opentelemetry.proto.metrics.v1.Sum
+ */
+class Sum extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * The time series data points.
+ * Note: Multiple time series may be included (same timestamp, different attributes).
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.metrics.v1.NumberDataPoint data_points = 1;
+ */
+ private $data_points;
+ /**
+ * aggregation_temporality describes if the aggregator reports delta changes
+ * since last report time, or cumulative changes since a fixed start time.
+ *
+ * Generated from protobuf field .opentelemetry.proto.metrics.v1.AggregationTemporality aggregation_temporality = 2;
+ */
+ protected $aggregation_temporality = 0;
+ /**
+ * Represents whether the sum is monotonic.
+ *
+ * Generated from protobuf field bool is_monotonic = 3;
+ */
+ protected $is_monotonic = false;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type \Opentelemetry\Proto\Metrics\V1\NumberDataPoint[] $data_points
+ * The time series data points.
+ * Note: Multiple time series may be included (same timestamp, different attributes).
+ * @type int $aggregation_temporality
+ * aggregation_temporality describes if the aggregator reports delta changes
+ * since last report time, or cumulative changes since a fixed start time.
+ * @type bool $is_monotonic
+ * Represents whether the sum is monotonic.
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Metrics\V1\Metrics::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * The time series data points.
+ * Note: Multiple time series may be included (same timestamp, different attributes).
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.metrics.v1.NumberDataPoint data_points = 1;
+ * @return RepeatedField<\Opentelemetry\Proto\Metrics\V1\NumberDataPoint>
+ */
+ public function getDataPoints()
+ {
+ return $this->data_points;
+ }
+
+ /**
+ * The time series data points.
+ * Note: Multiple time series may be included (same timestamp, different attributes).
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.metrics.v1.NumberDataPoint data_points = 1;
+ * @param \Opentelemetry\Proto\Metrics\V1\NumberDataPoint[] $var
+ * @return $this
+ */
+ public function setDataPoints(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Opentelemetry\Proto\Metrics\V1\NumberDataPoint::class);
+ $this->data_points = $arr;
+
+ return $this;
+ }
+
+ /**
+ * aggregation_temporality describes if the aggregator reports delta changes
+ * since last report time, or cumulative changes since a fixed start time.
+ *
+ * Generated from protobuf field .opentelemetry.proto.metrics.v1.AggregationTemporality aggregation_temporality = 2;
+ * @return int one of the values in {@see \Opentelemetry\Proto\Metrics\V1\AggregationTemporality}
+ */
+ public function getAggregationTemporality()
+ {
+ return $this->aggregation_temporality;
+ }
+
+ /**
+ * aggregation_temporality describes if the aggregator reports delta changes
+ * since last report time, or cumulative changes since a fixed start time.
+ *
+ * Generated from protobuf field .opentelemetry.proto.metrics.v1.AggregationTemporality aggregation_temporality = 2;
+ * @param int $var one of the values in {@see \Opentelemetry\Proto\Metrics\V1\AggregationTemporality}
+ * @return $this
+ */
+ public function setAggregationTemporality(int $var)
+ {
+ GPBUtil::checkEnum($var, \Opentelemetry\Proto\Metrics\V1\AggregationTemporality::class);
+ $this->aggregation_temporality = $var;
+
+ return $this;
+ }
+
+ /**
+ * Represents whether the sum is monotonic.
+ *
+ * Generated from protobuf field bool is_monotonic = 3;
+ * @return bool
+ */
+ public function getIsMonotonic()
+ {
+ return $this->is_monotonic;
+ }
+
+ /**
+ * Represents whether the sum is monotonic.
+ *
+ * Generated from protobuf field bool is_monotonic = 3;
+ * @param bool $var
+ * @return $this
+ */
+ public function setIsMonotonic(bool $var)
+ {
+ $this->is_monotonic = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/Summary.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/Summary.php
new file mode 100644
index 000000000..bd1f9fc06
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/Summary.php
@@ -0,0 +1,80 @@
+opentelemetry.proto.metrics.v1.Summary
+ */
+class Summary extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * The time series data points.
+ * Note: Multiple time series may be included (same timestamp, different attributes).
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.metrics.v1.SummaryDataPoint data_points = 1;
+ */
+ private $data_points;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type \Opentelemetry\Proto\Metrics\V1\SummaryDataPoint[] $data_points
+ * The time series data points.
+ * Note: Multiple time series may be included (same timestamp, different attributes).
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Metrics\V1\Metrics::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * The time series data points.
+ * Note: Multiple time series may be included (same timestamp, different attributes).
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.metrics.v1.SummaryDataPoint data_points = 1;
+ * @return RepeatedField<\Opentelemetry\Proto\Metrics\V1\SummaryDataPoint>
+ */
+ public function getDataPoints()
+ {
+ return $this->data_points;
+ }
+
+ /**
+ * The time series data points.
+ * Note: Multiple time series may be included (same timestamp, different attributes).
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.metrics.v1.SummaryDataPoint data_points = 1;
+ * @param \Opentelemetry\Proto\Metrics\V1\SummaryDataPoint[] $var
+ * @return $this
+ */
+ public function setDataPoints(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Opentelemetry\Proto\Metrics\V1\SummaryDataPoint::class);
+ $this->data_points = $arr;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/SummaryDataPoint.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/SummaryDataPoint.php
new file mode 100644
index 000000000..f08e4230f
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/SummaryDataPoint.php
@@ -0,0 +1,341 @@
+opentelemetry.proto.metrics.v1.SummaryDataPoint
+ */
+class SummaryDataPoint extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * The set of key/value pairs that uniquely identify the timeseries from
+ * where this point belongs. The list may be empty (may contain 0 elements).
+ * Attribute keys MUST be unique (it is not allowed to have more than one
+ * attribute with the same key).
+ * The behavior of software that receives duplicated keys can be unpredictable.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.common.v1.KeyValue attributes = 7;
+ */
+ private $attributes;
+ /**
+ * StartTimeUnixNano is optional but strongly encouraged, see the
+ * the detailed comments above Metric.
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January
+ * 1970.
+ *
+ * Generated from protobuf field fixed64 start_time_unix_nano = 2;
+ */
+ protected $start_time_unix_nano = 0;
+ /**
+ * TimeUnixNano is required, see the detailed comments above Metric.
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January
+ * 1970.
+ *
+ * Generated from protobuf field fixed64 time_unix_nano = 3;
+ */
+ protected $time_unix_nano = 0;
+ /**
+ * count is the number of values in the population. Must be non-negative.
+ *
+ * Generated from protobuf field fixed64 count = 4;
+ */
+ protected $count = 0;
+ /**
+ * sum of the values in the population. If count is zero then this field
+ * must be zero.
+ * Note: Sum should only be filled out when measuring non-negative discrete
+ * events, and is assumed to be monotonic over the values of these events.
+ * Negative events *can* be recorded, but sum should not be filled out when
+ * doing so. This is specifically to enforce compatibility w/ OpenMetrics,
+ * see: https://github.com/prometheus/OpenMetrics/blob/v1.0.0/specification/OpenMetrics.md#summary
+ *
+ * Generated from protobuf field double sum = 5;
+ */
+ protected $sum = 0.0;
+ /**
+ * (Optional) list of values at different quantiles of the distribution calculated
+ * from the current snapshot. The quantiles must be strictly increasing.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.metrics.v1.SummaryDataPoint.ValueAtQuantile quantile_values = 6;
+ */
+ private $quantile_values;
+ /**
+ * Flags that apply to this specific data point. See DataPointFlags
+ * for the available flags and their meaning.
+ *
+ * Generated from protobuf field uint32 flags = 8;
+ */
+ protected $flags = 0;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type \Opentelemetry\Proto\Common\V1\KeyValue[] $attributes
+ * The set of key/value pairs that uniquely identify the timeseries from
+ * where this point belongs. The list may be empty (may contain 0 elements).
+ * Attribute keys MUST be unique (it is not allowed to have more than one
+ * attribute with the same key).
+ * The behavior of software that receives duplicated keys can be unpredictable.
+ * @type int|string $start_time_unix_nano
+ * StartTimeUnixNano is optional but strongly encouraged, see the
+ * the detailed comments above Metric.
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January
+ * 1970.
+ * @type int|string $time_unix_nano
+ * TimeUnixNano is required, see the detailed comments above Metric.
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January
+ * 1970.
+ * @type int|string $count
+ * count is the number of values in the population. Must be non-negative.
+ * @type float $sum
+ * sum of the values in the population. If count is zero then this field
+ * must be zero.
+ * Note: Sum should only be filled out when measuring non-negative discrete
+ * events, and is assumed to be monotonic over the values of these events.
+ * Negative events *can* be recorded, but sum should not be filled out when
+ * doing so. This is specifically to enforce compatibility w/ OpenMetrics,
+ * see: https://github.com/prometheus/OpenMetrics/blob/v1.0.0/specification/OpenMetrics.md#summary
+ * @type \Opentelemetry\Proto\Metrics\V1\SummaryDataPoint\ValueAtQuantile[] $quantile_values
+ * (Optional) list of values at different quantiles of the distribution calculated
+ * from the current snapshot. The quantiles must be strictly increasing.
+ * @type int $flags
+ * Flags that apply to this specific data point. See DataPointFlags
+ * for the available flags and their meaning.
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Metrics\V1\Metrics::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * The set of key/value pairs that uniquely identify the timeseries from
+ * where this point belongs. The list may be empty (may contain 0 elements).
+ * Attribute keys MUST be unique (it is not allowed to have more than one
+ * attribute with the same key).
+ * The behavior of software that receives duplicated keys can be unpredictable.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.common.v1.KeyValue attributes = 7;
+ * @return RepeatedField<\Opentelemetry\Proto\Common\V1\KeyValue>
+ */
+ public function getAttributes()
+ {
+ return $this->attributes;
+ }
+
+ /**
+ * The set of key/value pairs that uniquely identify the timeseries from
+ * where this point belongs. The list may be empty (may contain 0 elements).
+ * Attribute keys MUST be unique (it is not allowed to have more than one
+ * attribute with the same key).
+ * The behavior of software that receives duplicated keys can be unpredictable.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.common.v1.KeyValue attributes = 7;
+ * @param \Opentelemetry\Proto\Common\V1\KeyValue[] $var
+ * @return $this
+ */
+ public function setAttributes(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Opentelemetry\Proto\Common\V1\KeyValue::class);
+ $this->attributes = $arr;
+
+ return $this;
+ }
+
+ /**
+ * StartTimeUnixNano is optional but strongly encouraged, see the
+ * the detailed comments above Metric.
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January
+ * 1970.
+ *
+ * Generated from protobuf field fixed64 start_time_unix_nano = 2;
+ * @return int|string
+ */
+ public function getStartTimeUnixNano()
+ {
+ return $this->start_time_unix_nano;
+ }
+
+ /**
+ * StartTimeUnixNano is optional but strongly encouraged, see the
+ * the detailed comments above Metric.
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January
+ * 1970.
+ *
+ * Generated from protobuf field fixed64 start_time_unix_nano = 2;
+ * @param int|string $var
+ * @return $this
+ */
+ public function setStartTimeUnixNano(int|string $var)
+ {
+ GPBUtil::checkUint64($var);
+ $this->start_time_unix_nano = $var;
+
+ return $this;
+ }
+
+ /**
+ * TimeUnixNano is required, see the detailed comments above Metric.
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January
+ * 1970.
+ *
+ * Generated from protobuf field fixed64 time_unix_nano = 3;
+ * @return int|string
+ */
+ public function getTimeUnixNano()
+ {
+ return $this->time_unix_nano;
+ }
+
+ /**
+ * TimeUnixNano is required, see the detailed comments above Metric.
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January
+ * 1970.
+ *
+ * Generated from protobuf field fixed64 time_unix_nano = 3;
+ * @param int|string $var
+ * @return $this
+ */
+ public function setTimeUnixNano(int|string $var)
+ {
+ GPBUtil::checkUint64($var);
+ $this->time_unix_nano = $var;
+
+ return $this;
+ }
+
+ /**
+ * count is the number of values in the population. Must be non-negative.
+ *
+ * Generated from protobuf field fixed64 count = 4;
+ * @return int|string
+ */
+ public function getCount()
+ {
+ return $this->count;
+ }
+
+ /**
+ * count is the number of values in the population. Must be non-negative.
+ *
+ * Generated from protobuf field fixed64 count = 4;
+ * @param int|string $var
+ * @return $this
+ */
+ public function setCount(int|string $var)
+ {
+ GPBUtil::checkUint64($var);
+ $this->count = $var;
+
+ return $this;
+ }
+
+ /**
+ * sum of the values in the population. If count is zero then this field
+ * must be zero.
+ * Note: Sum should only be filled out when measuring non-negative discrete
+ * events, and is assumed to be monotonic over the values of these events.
+ * Negative events *can* be recorded, but sum should not be filled out when
+ * doing so. This is specifically to enforce compatibility w/ OpenMetrics,
+ * see: https://github.com/prometheus/OpenMetrics/blob/v1.0.0/specification/OpenMetrics.md#summary
+ *
+ * Generated from protobuf field double sum = 5;
+ * @return float
+ */
+ public function getSum()
+ {
+ return $this->sum;
+ }
+
+ /**
+ * sum of the values in the population. If count is zero then this field
+ * must be zero.
+ * Note: Sum should only be filled out when measuring non-negative discrete
+ * events, and is assumed to be monotonic over the values of these events.
+ * Negative events *can* be recorded, but sum should not be filled out when
+ * doing so. This is specifically to enforce compatibility w/ OpenMetrics,
+ * see: https://github.com/prometheus/OpenMetrics/blob/v1.0.0/specification/OpenMetrics.md#summary
+ *
+ * Generated from protobuf field double sum = 5;
+ * @param float $var
+ * @return $this
+ */
+ public function setSum(float $var)
+ {
+ $this->sum = $var;
+
+ return $this;
+ }
+
+ /**
+ * (Optional) list of values at different quantiles of the distribution calculated
+ * from the current snapshot. The quantiles must be strictly increasing.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.metrics.v1.SummaryDataPoint.ValueAtQuantile quantile_values = 6;
+ * @return RepeatedField<\Opentelemetry\Proto\Metrics\V1\SummaryDataPoint\ValueAtQuantile>
+ */
+ public function getQuantileValues()
+ {
+ return $this->quantile_values;
+ }
+
+ /**
+ * (Optional) list of values at different quantiles of the distribution calculated
+ * from the current snapshot. The quantiles must be strictly increasing.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.metrics.v1.SummaryDataPoint.ValueAtQuantile quantile_values = 6;
+ * @param \Opentelemetry\Proto\Metrics\V1\SummaryDataPoint\ValueAtQuantile[] $var
+ * @return $this
+ */
+ public function setQuantileValues(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Opentelemetry\Proto\Metrics\V1\SummaryDataPoint\ValueAtQuantile::class);
+ $this->quantile_values = $arr;
+
+ return $this;
+ }
+
+ /**
+ * Flags that apply to this specific data point. See DataPointFlags
+ * for the available flags and their meaning.
+ *
+ * Generated from protobuf field uint32 flags = 8;
+ * @return int
+ */
+ public function getFlags()
+ {
+ return $this->flags;
+ }
+
+ /**
+ * Flags that apply to this specific data point. See DataPointFlags
+ * for the available flags and their meaning.
+ *
+ * Generated from protobuf field uint32 flags = 8;
+ * @param int $var
+ * @return $this
+ */
+ public function setFlags(int $var)
+ {
+ GPBUtil::checkUint32($var);
+ $this->flags = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/SummaryDataPoint/ValueAtQuantile.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/SummaryDataPoint/ValueAtQuantile.php
new file mode 100644
index 000000000..820c1d1db
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Metrics/V1/SummaryDataPoint/ValueAtQuantile.php
@@ -0,0 +1,113 @@
+opentelemetry.proto.metrics.v1.SummaryDataPoint.ValueAtQuantile
+ */
+class ValueAtQuantile extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * The quantile of a distribution. Must be in the interval
+ * [0.0, 1.0].
+ *
+ * Generated from protobuf field double quantile = 1;
+ */
+ protected $quantile = 0.0;
+ /**
+ * The value at the given quantile of a distribution.
+ * Quantile values must NOT be negative.
+ *
+ * Generated from protobuf field double value = 2;
+ */
+ protected $value = 0.0;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type float $quantile
+ * The quantile of a distribution. Must be in the interval
+ * [0.0, 1.0].
+ * @type float $value
+ * The value at the given quantile of a distribution.
+ * Quantile values must NOT be negative.
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Metrics\V1\Metrics::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * The quantile of a distribution. Must be in the interval
+ * [0.0, 1.0].
+ *
+ * Generated from protobuf field double quantile = 1;
+ * @return float
+ */
+ public function getQuantile()
+ {
+ return $this->quantile;
+ }
+
+ /**
+ * The quantile of a distribution. Must be in the interval
+ * [0.0, 1.0].
+ *
+ * Generated from protobuf field double quantile = 1;
+ * @param float $var
+ * @return $this
+ */
+ public function setQuantile(float $var)
+ {
+ $this->quantile = $var;
+
+ return $this;
+ }
+
+ /**
+ * The value at the given quantile of a distribution.
+ * Quantile values must NOT be negative.
+ *
+ * Generated from protobuf field double value = 2;
+ * @return float
+ */
+ public function getValue()
+ {
+ return $this->value;
+ }
+
+ /**
+ * The value at the given quantile of a distribution.
+ * Quantile values must NOT be negative.
+ *
+ * Generated from protobuf field double value = 2;
+ * @param float $var
+ * @return $this
+ */
+ public function setValue(float $var)
+ {
+ $this->value = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Profiles/V1development/KeyValueAndUnit.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Profiles/V1development/KeyValueAndUnit.php
new file mode 100644
index 000000000..46e42e0d3
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Profiles/V1development/KeyValueAndUnit.php
@@ -0,0 +1,151 @@
+opentelemetry.proto.profiles.v1development.KeyValueAndUnit
+ */
+class KeyValueAndUnit extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * The index into the string table for the attribute's key.
+ *
+ * Generated from protobuf field int32 key_strindex = 1;
+ */
+ protected $key_strindex = 0;
+ /**
+ * The value of the attribute.
+ *
+ * Generated from protobuf field .opentelemetry.proto.common.v1.AnyValue value = 2;
+ */
+ protected $value = null;
+ /**
+ * The index into the string table for the attribute's unit.
+ * zero indicates implicit (by semconv) or non-defined unit.
+ *
+ * Generated from protobuf field int32 unit_strindex = 3;
+ */
+ protected $unit_strindex = 0;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type int $key_strindex
+ * The index into the string table for the attribute's key.
+ * @type \Opentelemetry\Proto\Common\V1\AnyValue $value
+ * The value of the attribute.
+ * @type int $unit_strindex
+ * The index into the string table for the attribute's unit.
+ * zero indicates implicit (by semconv) or non-defined unit.
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Profiles\V1Development\Profiles::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * The index into the string table for the attribute's key.
+ *
+ * Generated from protobuf field int32 key_strindex = 1;
+ * @return int
+ */
+ public function getKeyStrindex()
+ {
+ return $this->key_strindex;
+ }
+
+ /**
+ * The index into the string table for the attribute's key.
+ *
+ * Generated from protobuf field int32 key_strindex = 1;
+ * @param int $var
+ * @return $this
+ */
+ public function setKeyStrindex(int $var)
+ {
+ GPBUtil::checkInt32($var);
+ $this->key_strindex = $var;
+
+ return $this;
+ }
+
+ /**
+ * The value of the attribute.
+ *
+ * Generated from protobuf field .opentelemetry.proto.common.v1.AnyValue value = 2;
+ * @return \Opentelemetry\Proto\Common\V1\AnyValue|null
+ */
+ public function getValue()
+ {
+ return $this->value;
+ }
+
+ public function hasValue()
+ {
+ return isset($this->value);
+ }
+
+ public function clearValue()
+ {
+ unset($this->value);
+ }
+
+ /**
+ * The value of the attribute.
+ *
+ * Generated from protobuf field .opentelemetry.proto.common.v1.AnyValue value = 2;
+ * @param \Opentelemetry\Proto\Common\V1\AnyValue $var
+ * @return $this
+ */
+ public function setValue(\Opentelemetry\Proto\Common\V1\AnyValue|null $var)
+ {
+ $this->value = $var;
+
+ return $this;
+ }
+
+ /**
+ * The index into the string table for the attribute's unit.
+ * zero indicates implicit (by semconv) or non-defined unit.
+ *
+ * Generated from protobuf field int32 unit_strindex = 3;
+ * @return int
+ */
+ public function getUnitStrindex()
+ {
+ return $this->unit_strindex;
+ }
+
+ /**
+ * The index into the string table for the attribute's unit.
+ * zero indicates implicit (by semconv) or non-defined unit.
+ *
+ * Generated from protobuf field int32 unit_strindex = 3;
+ * @param int $var
+ * @return $this
+ */
+ public function setUnitStrindex(int $var)
+ {
+ GPBUtil::checkInt32($var);
+ $this->unit_strindex = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Profiles/V1development/Line.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Profiles/V1development/Line.php
new file mode 100644
index 000000000..b6e15a0f4
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Profiles/V1development/Line.php
@@ -0,0 +1,136 @@
+opentelemetry.proto.profiles.v1development.Line
+ */
+class Line extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * Reference to function in ProfilesDictionary.function_table.
+ *
+ * Generated from protobuf field int32 function_index = 1;
+ */
+ protected $function_index = 0;
+ /**
+ * Line number in source code. 0 means unset.
+ *
+ * Generated from protobuf field int64 line = 2;
+ */
+ protected $line = 0;
+ /**
+ * Column number in source code. 0 means unset.
+ *
+ * Generated from protobuf field int64 column = 3;
+ */
+ protected $column = 0;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type int $function_index
+ * Reference to function in ProfilesDictionary.function_table.
+ * @type int|string $line
+ * Line number in source code. 0 means unset.
+ * @type int|string $column
+ * Column number in source code. 0 means unset.
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Profiles\V1Development\Profiles::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * Reference to function in ProfilesDictionary.function_table.
+ *
+ * Generated from protobuf field int32 function_index = 1;
+ * @return int
+ */
+ public function getFunctionIndex()
+ {
+ return $this->function_index;
+ }
+
+ /**
+ * Reference to function in ProfilesDictionary.function_table.
+ *
+ * Generated from protobuf field int32 function_index = 1;
+ * @param int $var
+ * @return $this
+ */
+ public function setFunctionIndex(int $var)
+ {
+ GPBUtil::checkInt32($var);
+ $this->function_index = $var;
+
+ return $this;
+ }
+
+ /**
+ * Line number in source code. 0 means unset.
+ *
+ * Generated from protobuf field int64 line = 2;
+ * @return int|string
+ */
+ public function getLine()
+ {
+ return $this->line;
+ }
+
+ /**
+ * Line number in source code. 0 means unset.
+ *
+ * Generated from protobuf field int64 line = 2;
+ * @param int|string $var
+ * @return $this
+ */
+ public function setLine(int|string $var)
+ {
+ GPBUtil::checkInt64($var);
+ $this->line = $var;
+
+ return $this;
+ }
+
+ /**
+ * Column number in source code. 0 means unset.
+ *
+ * Generated from protobuf field int64 column = 3;
+ * @return int|string
+ */
+ public function getColumn()
+ {
+ return $this->column;
+ }
+
+ /**
+ * Column number in source code. 0 means unset.
+ *
+ * Generated from protobuf field int64 column = 3;
+ * @param int|string $var
+ * @return $this
+ */
+ public function setColumn(int|string $var)
+ {
+ GPBUtil::checkInt64($var);
+ $this->column = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Profiles/V1development/Link.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Profiles/V1development/Link.php
new file mode 100644
index 000000000..0a95ffab2
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Profiles/V1development/Link.php
@@ -0,0 +1,107 @@
+opentelemetry.proto.profiles.v1development.Link
+ */
+class Link extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * A unique identifier of a trace that this linked span is part of. The ID is a
+ * 16-byte array.
+ *
+ * Generated from protobuf field bytes trace_id = 1;
+ */
+ protected $trace_id = '';
+ /**
+ * A unique identifier for the linked span. The ID is an 8-byte array.
+ *
+ * Generated from protobuf field bytes span_id = 2;
+ */
+ protected $span_id = '';
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type string $trace_id
+ * A unique identifier of a trace that this linked span is part of. The ID is a
+ * 16-byte array.
+ * @type string $span_id
+ * A unique identifier for the linked span. The ID is an 8-byte array.
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Profiles\V1Development\Profiles::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * A unique identifier of a trace that this linked span is part of. The ID is a
+ * 16-byte array.
+ *
+ * Generated from protobuf field bytes trace_id = 1;
+ * @return string
+ */
+ public function getTraceId()
+ {
+ return $this->trace_id;
+ }
+
+ /**
+ * A unique identifier of a trace that this linked span is part of. The ID is a
+ * 16-byte array.
+ *
+ * Generated from protobuf field bytes trace_id = 1;
+ * @param string $var
+ * @return $this
+ */
+ public function setTraceId(string $var)
+ {
+ GPBUtil::checkString($var, false);
+ $this->trace_id = $var;
+
+ return $this;
+ }
+
+ /**
+ * A unique identifier for the linked span. The ID is an 8-byte array.
+ *
+ * Generated from protobuf field bytes span_id = 2;
+ * @return string
+ */
+ public function getSpanId()
+ {
+ return $this->span_id;
+ }
+
+ /**
+ * A unique identifier for the linked span. The ID is an 8-byte array.
+ *
+ * Generated from protobuf field bytes span_id = 2;
+ * @param string $var
+ * @return $this
+ */
+ public function setSpanId(string $var)
+ {
+ GPBUtil::checkString($var, false);
+ $this->span_id = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Profiles/V1development/Location.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Profiles/V1development/Location.php
new file mode 100644
index 000000000..abef251b5
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Profiles/V1development/Location.php
@@ -0,0 +1,214 @@
+opentelemetry.proto.profiles.v1development.Location
+ */
+class Location extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * Reference to mapping in ProfilesDictionary.mapping_table.
+ * It can be unset / set to 0 if the mapping is unknown or not applicable for
+ * this profile type, as mapping_table[0] is always a 'null' default mapping.
+ *
+ * Generated from protobuf field int32 mapping_index = 1;
+ */
+ protected $mapping_index = 0;
+ /**
+ * The instruction address for this location, if available. It
+ * should be within [Mapping.memory_start...Mapping.memory_limit]
+ * for the corresponding mapping. A non-leaf address may be in the
+ * middle of a call instruction. It is up to display tools to find
+ * the beginning of the instruction if necessary.
+ *
+ * Generated from protobuf field uint64 address = 2;
+ */
+ protected $address = 0;
+ /**
+ * Multiple line indicates this location has inlined functions,
+ * where the last entry represents the caller into which the
+ * preceding entries were inlined.
+ * E.g., if memcpy() is inlined into printf:
+ * lines[0].function_name == "memcpy"
+ * lines[1].function_name == "printf"
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.profiles.v1development.Line lines = 3;
+ */
+ private $lines;
+ /**
+ * References to attributes in ProfilesDictionary.attribute_table. [optional]
+ *
+ * Generated from protobuf field repeated int32 attribute_indices = 4;
+ */
+ private $attribute_indices;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type int $mapping_index
+ * Reference to mapping in ProfilesDictionary.mapping_table.
+ * It can be unset / set to 0 if the mapping is unknown or not applicable for
+ * this profile type, as mapping_table[0] is always a 'null' default mapping.
+ * @type int|string $address
+ * The instruction address for this location, if available. It
+ * should be within [Mapping.memory_start...Mapping.memory_limit]
+ * for the corresponding mapping. A non-leaf address may be in the
+ * middle of a call instruction. It is up to display tools to find
+ * the beginning of the instruction if necessary.
+ * @type \Opentelemetry\Proto\Profiles\V1development\Line[] $lines
+ * Multiple line indicates this location has inlined functions,
+ * where the last entry represents the caller into which the
+ * preceding entries were inlined.
+ * E.g., if memcpy() is inlined into printf:
+ * lines[0].function_name == "memcpy"
+ * lines[1].function_name == "printf"
+ * @type int[] $attribute_indices
+ * References to attributes in ProfilesDictionary.attribute_table. [optional]
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Profiles\V1Development\Profiles::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * Reference to mapping in ProfilesDictionary.mapping_table.
+ * It can be unset / set to 0 if the mapping is unknown or not applicable for
+ * this profile type, as mapping_table[0] is always a 'null' default mapping.
+ *
+ * Generated from protobuf field int32 mapping_index = 1;
+ * @return int
+ */
+ public function getMappingIndex()
+ {
+ return $this->mapping_index;
+ }
+
+ /**
+ * Reference to mapping in ProfilesDictionary.mapping_table.
+ * It can be unset / set to 0 if the mapping is unknown or not applicable for
+ * this profile type, as mapping_table[0] is always a 'null' default mapping.
+ *
+ * Generated from protobuf field int32 mapping_index = 1;
+ * @param int $var
+ * @return $this
+ */
+ public function setMappingIndex(int $var)
+ {
+ GPBUtil::checkInt32($var);
+ $this->mapping_index = $var;
+
+ return $this;
+ }
+
+ /**
+ * The instruction address for this location, if available. It
+ * should be within [Mapping.memory_start...Mapping.memory_limit]
+ * for the corresponding mapping. A non-leaf address may be in the
+ * middle of a call instruction. It is up to display tools to find
+ * the beginning of the instruction if necessary.
+ *
+ * Generated from protobuf field uint64 address = 2;
+ * @return int|string
+ */
+ public function getAddress()
+ {
+ return $this->address;
+ }
+
+ /**
+ * The instruction address for this location, if available. It
+ * should be within [Mapping.memory_start...Mapping.memory_limit]
+ * for the corresponding mapping. A non-leaf address may be in the
+ * middle of a call instruction. It is up to display tools to find
+ * the beginning of the instruction if necessary.
+ *
+ * Generated from protobuf field uint64 address = 2;
+ * @param int|string $var
+ * @return $this
+ */
+ public function setAddress(int|string $var)
+ {
+ GPBUtil::checkUint64($var);
+ $this->address = $var;
+
+ return $this;
+ }
+
+ /**
+ * Multiple line indicates this location has inlined functions,
+ * where the last entry represents the caller into which the
+ * preceding entries were inlined.
+ * E.g., if memcpy() is inlined into printf:
+ * lines[0].function_name == "memcpy"
+ * lines[1].function_name == "printf"
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.profiles.v1development.Line lines = 3;
+ * @return RepeatedField<\Opentelemetry\Proto\Profiles\V1development\Line>
+ */
+ public function getLines()
+ {
+ return $this->lines;
+ }
+
+ /**
+ * Multiple line indicates this location has inlined functions,
+ * where the last entry represents the caller into which the
+ * preceding entries were inlined.
+ * E.g., if memcpy() is inlined into printf:
+ * lines[0].function_name == "memcpy"
+ * lines[1].function_name == "printf"
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.profiles.v1development.Line lines = 3;
+ * @param \Opentelemetry\Proto\Profiles\V1development\Line[] $var
+ * @return $this
+ */
+ public function setLines(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Opentelemetry\Proto\Profiles\V1development\Line::class);
+ $this->lines = $arr;
+
+ return $this;
+ }
+
+ /**
+ * References to attributes in ProfilesDictionary.attribute_table. [optional]
+ *
+ * Generated from protobuf field repeated int32 attribute_indices = 4;
+ * @return RepeatedField
+ */
+ public function getAttributeIndices()
+ {
+ return $this->attribute_indices;
+ }
+
+ /**
+ * References to attributes in ProfilesDictionary.attribute_table. [optional]
+ *
+ * Generated from protobuf field repeated int32 attribute_indices = 4;
+ * @param int[] $var
+ * @return $this
+ */
+ public function setAttributeIndices(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::INT32);
+ $this->attribute_indices = $arr;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Profiles/V1development/Mapping.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Profiles/V1development/Mapping.php
new file mode 100644
index 000000000..c1a9d3f34
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Profiles/V1development/Mapping.php
@@ -0,0 +1,213 @@
+opentelemetry.proto.profiles.v1development.Mapping
+ */
+class Mapping extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * Address at which the binary (or DLL) is loaded into memory.
+ *
+ * Generated from protobuf field uint64 memory_start = 1;
+ */
+ protected $memory_start = 0;
+ /**
+ * The limit of the address range occupied by this mapping.
+ *
+ * Generated from protobuf field uint64 memory_limit = 2;
+ */
+ protected $memory_limit = 0;
+ /**
+ * Offset in the binary that corresponds to the first mapped address.
+ *
+ * Generated from protobuf field uint64 file_offset = 3;
+ */
+ protected $file_offset = 0;
+ /**
+ * The object this entry is loaded from. This can be a filename on
+ * disk for the main binary and shared libraries, or virtual
+ * abstractions like "[vdso]".
+ *
+ * Generated from protobuf field int32 filename_strindex = 4;
+ */
+ protected $filename_strindex = 0;
+ /**
+ * References to attributes in ProfilesDictionary.attribute_table. [optional]
+ *
+ * Generated from protobuf field repeated int32 attribute_indices = 5;
+ */
+ private $attribute_indices;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type int|string $memory_start
+ * Address at which the binary (or DLL) is loaded into memory.
+ * @type int|string $memory_limit
+ * The limit of the address range occupied by this mapping.
+ * @type int|string $file_offset
+ * Offset in the binary that corresponds to the first mapped address.
+ * @type int $filename_strindex
+ * The object this entry is loaded from. This can be a filename on
+ * disk for the main binary and shared libraries, or virtual
+ * abstractions like "[vdso]".
+ * @type int[] $attribute_indices
+ * References to attributes in ProfilesDictionary.attribute_table. [optional]
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Profiles\V1Development\Profiles::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * Address at which the binary (or DLL) is loaded into memory.
+ *
+ * Generated from protobuf field uint64 memory_start = 1;
+ * @return int|string
+ */
+ public function getMemoryStart()
+ {
+ return $this->memory_start;
+ }
+
+ /**
+ * Address at which the binary (or DLL) is loaded into memory.
+ *
+ * Generated from protobuf field uint64 memory_start = 1;
+ * @param int|string $var
+ * @return $this
+ */
+ public function setMemoryStart(int|string $var)
+ {
+ GPBUtil::checkUint64($var);
+ $this->memory_start = $var;
+
+ return $this;
+ }
+
+ /**
+ * The limit of the address range occupied by this mapping.
+ *
+ * Generated from protobuf field uint64 memory_limit = 2;
+ * @return int|string
+ */
+ public function getMemoryLimit()
+ {
+ return $this->memory_limit;
+ }
+
+ /**
+ * The limit of the address range occupied by this mapping.
+ *
+ * Generated from protobuf field uint64 memory_limit = 2;
+ * @param int|string $var
+ * @return $this
+ */
+ public function setMemoryLimit(int|string $var)
+ {
+ GPBUtil::checkUint64($var);
+ $this->memory_limit = $var;
+
+ return $this;
+ }
+
+ /**
+ * Offset in the binary that corresponds to the first mapped address.
+ *
+ * Generated from protobuf field uint64 file_offset = 3;
+ * @return int|string
+ */
+ public function getFileOffset()
+ {
+ return $this->file_offset;
+ }
+
+ /**
+ * Offset in the binary that corresponds to the first mapped address.
+ *
+ * Generated from protobuf field uint64 file_offset = 3;
+ * @param int|string $var
+ * @return $this
+ */
+ public function setFileOffset(int|string $var)
+ {
+ GPBUtil::checkUint64($var);
+ $this->file_offset = $var;
+
+ return $this;
+ }
+
+ /**
+ * The object this entry is loaded from. This can be a filename on
+ * disk for the main binary and shared libraries, or virtual
+ * abstractions like "[vdso]".
+ *
+ * Generated from protobuf field int32 filename_strindex = 4;
+ * @return int
+ */
+ public function getFilenameStrindex()
+ {
+ return $this->filename_strindex;
+ }
+
+ /**
+ * The object this entry is loaded from. This can be a filename on
+ * disk for the main binary and shared libraries, or virtual
+ * abstractions like "[vdso]".
+ *
+ * Generated from protobuf field int32 filename_strindex = 4;
+ * @param int $var
+ * @return $this
+ */
+ public function setFilenameStrindex(int $var)
+ {
+ GPBUtil::checkInt32($var);
+ $this->filename_strindex = $var;
+
+ return $this;
+ }
+
+ /**
+ * References to attributes in ProfilesDictionary.attribute_table. [optional]
+ *
+ * Generated from protobuf field repeated int32 attribute_indices = 5;
+ * @return RepeatedField
+ */
+ public function getAttributeIndices()
+ {
+ return $this->attribute_indices;
+ }
+
+ /**
+ * References to attributes in ProfilesDictionary.attribute_table. [optional]
+ *
+ * Generated from protobuf field repeated int32 attribute_indices = 5;
+ * @param int[] $var
+ * @return $this
+ */
+ public function setAttributeIndices(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::INT32);
+ $this->attribute_indices = $arr;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Profiles/V1development/PBFunction.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Profiles/V1development/PBFunction.php
new file mode 100644
index 000000000..2747c1dc8
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Profiles/V1development/PBFunction.php
@@ -0,0 +1,175 @@
+opentelemetry.proto.profiles.v1development.Function
+ */
+class PBFunction extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * The function name. Empty string if not available.
+ *
+ * Generated from protobuf field int32 name_strindex = 1;
+ */
+ protected $name_strindex = 0;
+ /**
+ * Function name, as identified by the system. For instance,
+ * it can be a C++ mangled name. Empty string if not available.
+ *
+ * Generated from protobuf field int32 system_name_strindex = 2;
+ */
+ protected $system_name_strindex = 0;
+ /**
+ * Source file containing the function. Empty string if not available.
+ *
+ * Generated from protobuf field int32 filename_strindex = 3;
+ */
+ protected $filename_strindex = 0;
+ /**
+ * Line number in source file. 0 means unset.
+ *
+ * Generated from protobuf field int64 start_line = 4;
+ */
+ protected $start_line = 0;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type int $name_strindex
+ * The function name. Empty string if not available.
+ * @type int $system_name_strindex
+ * Function name, as identified by the system. For instance,
+ * it can be a C++ mangled name. Empty string if not available.
+ * @type int $filename_strindex
+ * Source file containing the function. Empty string if not available.
+ * @type int|string $start_line
+ * Line number in source file. 0 means unset.
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Profiles\V1Development\Profiles::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * The function name. Empty string if not available.
+ *
+ * Generated from protobuf field int32 name_strindex = 1;
+ * @return int
+ */
+ public function getNameStrindex()
+ {
+ return $this->name_strindex;
+ }
+
+ /**
+ * The function name. Empty string if not available.
+ *
+ * Generated from protobuf field int32 name_strindex = 1;
+ * @param int $var
+ * @return $this
+ */
+ public function setNameStrindex(int $var)
+ {
+ GPBUtil::checkInt32($var);
+ $this->name_strindex = $var;
+
+ return $this;
+ }
+
+ /**
+ * Function name, as identified by the system. For instance,
+ * it can be a C++ mangled name. Empty string if not available.
+ *
+ * Generated from protobuf field int32 system_name_strindex = 2;
+ * @return int
+ */
+ public function getSystemNameStrindex()
+ {
+ return $this->system_name_strindex;
+ }
+
+ /**
+ * Function name, as identified by the system. For instance,
+ * it can be a C++ mangled name. Empty string if not available.
+ *
+ * Generated from protobuf field int32 system_name_strindex = 2;
+ * @param int $var
+ * @return $this
+ */
+ public function setSystemNameStrindex(int $var)
+ {
+ GPBUtil::checkInt32($var);
+ $this->system_name_strindex = $var;
+
+ return $this;
+ }
+
+ /**
+ * Source file containing the function. Empty string if not available.
+ *
+ * Generated from protobuf field int32 filename_strindex = 3;
+ * @return int
+ */
+ public function getFilenameStrindex()
+ {
+ return $this->filename_strindex;
+ }
+
+ /**
+ * Source file containing the function. Empty string if not available.
+ *
+ * Generated from protobuf field int32 filename_strindex = 3;
+ * @param int $var
+ * @return $this
+ */
+ public function setFilenameStrindex(int $var)
+ {
+ GPBUtil::checkInt32($var);
+ $this->filename_strindex = $var;
+
+ return $this;
+ }
+
+ /**
+ * Line number in source file. 0 means unset.
+ *
+ * Generated from protobuf field int64 start_line = 4;
+ * @return int|string
+ */
+ public function getStartLine()
+ {
+ return $this->start_line;
+ }
+
+ /**
+ * Line number in source file. 0 means unset.
+ *
+ * Generated from protobuf field int64 start_line = 4;
+ * @param int|string $var
+ * @return $this
+ */
+ public function setStartLine(int|string $var)
+ {
+ GPBUtil::checkInt64($var);
+ $this->start_line = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Profiles/V1development/Profile.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Profiles/V1development/Profile.php
new file mode 100644
index 000000000..2a43117e3
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Profiles/V1development/Profile.php
@@ -0,0 +1,560 @@
+opentelemetry.proto.profiles.v1development.Profile
+ */
+class Profile extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * The type and unit of all Sample.values in this profile.
+ * For a cpu or off-cpu profile this might be:
+ * ["cpu","nanoseconds"] or ["off_cpu","nanoseconds"]
+ * For a heap profile, this might be:
+ * ["allocated_objects","count"] or ["allocated_space","bytes"],
+ *
+ * Generated from protobuf field .opentelemetry.proto.profiles.v1development.ValueType sample_type = 1;
+ */
+ protected $sample_type = null;
+ /**
+ * The set of samples recorded in this profile.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.profiles.v1development.Sample samples = 2;
+ */
+ private $samples;
+ /**
+ * Time of collection. Value is UNIX Epoch time in nanoseconds since 00:00:00
+ * UTC on 1 January 1970.
+ *
+ * Generated from protobuf field fixed64 time_unix_nano = 3;
+ */
+ protected $time_unix_nano = 0;
+ /**
+ * Duration of the profile. For instant profiles like live heap snapshot, the
+ * duration can be zero but it may be preferable to set time_unix_nano to the
+ * process start time and duration_nano to the relative time when the profile
+ * was gathered. This ensures Sample.timestamps_unix_nano values such as
+ * allocation timestamp fall into the profile time range.
+ *
+ * Generated from protobuf field uint64 duration_nano = 4;
+ */
+ protected $duration_nano = 0;
+ /**
+ * The kind of events between sampled occurrences.
+ * e.g [ "cpu","cycles" ] or [ "heap","bytes" ]
+ *
+ * Generated from protobuf field .opentelemetry.proto.profiles.v1development.ValueType period_type = 5;
+ */
+ protected $period_type = null;
+ /**
+ * The number of events between sampled occurrences.
+ *
+ * Generated from protobuf field int64 period = 6;
+ */
+ protected $period = 0;
+ /**
+ * A globally unique identifier for a profile. The ID is a 16-byte array. An ID with
+ * all zeroes is considered invalid. It may be used for deduplication and signal
+ * correlation purposes. It is acceptable to treat two profiles with different values
+ * in this field as not equal, even if they represented the same object at an earlier
+ * time.
+ * This field is optional; an ID may be assigned to an ID-less profile in a later step.
+ *
+ * Generated from protobuf field bytes profile_id = 7;
+ */
+ protected $profile_id = '';
+ /**
+ * The number of attributes that were discarded. Attributes
+ * can be discarded because their keys are too long or because there are too many
+ * attributes. If this value is 0, then no attributes were dropped.
+ *
+ * Generated from protobuf field uint32 dropped_attributes_count = 8;
+ */
+ protected $dropped_attributes_count = 0;
+ /**
+ * The original payload format. See also original_payload. Optional, but the
+ * format and the bytes must be set or unset together.
+ * The allowed values for the format string are defined by the OpenTelemetry
+ * specification. Some examples are "jfr", "pprof", "linux_perf".
+ * The original payload may be optionally provided when the conversion to the
+ * OLTP format was done from a different format with some loss of the fidelity
+ * and the receiver may want to store the original payload to allow future
+ * lossless export or reinterpretation. Some examples of the original format
+ * are JFR (Java Flight Recorder), pprof, Linux perf.
+ * Even when the original payload is in a format that is semantically close to
+ * OTLP, such as pprof, a conversion may still be lossy in some cases (e.g. if
+ * the pprof file contains custom extensions or conventions).
+ * The original payload can be large in size, so including the original
+ * payload should be configurable by the profiler or collector options. The
+ * default behavior should be to not include the original payload.
+ *
+ * Generated from protobuf field string original_payload_format = 9;
+ */
+ protected $original_payload_format = '';
+ /**
+ * The original payload bytes. See also original_payload_format. Optional, but
+ * format and the bytes must be set or unset together.
+ *
+ * Generated from protobuf field bytes original_payload = 10;
+ */
+ protected $original_payload = '';
+ /**
+ * References to attributes in attribute_table. [optional]
+ *
+ * Generated from protobuf field repeated int32 attribute_indices = 11;
+ */
+ private $attribute_indices;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type \Opentelemetry\Proto\Profiles\V1development\ValueType $sample_type
+ * The type and unit of all Sample.values in this profile.
+ * For a cpu or off-cpu profile this might be:
+ * ["cpu","nanoseconds"] or ["off_cpu","nanoseconds"]
+ * For a heap profile, this might be:
+ * ["allocated_objects","count"] or ["allocated_space","bytes"],
+ * @type \Opentelemetry\Proto\Profiles\V1development\Sample[] $samples
+ * The set of samples recorded in this profile.
+ * @type int|string $time_unix_nano
+ * Time of collection. Value is UNIX Epoch time in nanoseconds since 00:00:00
+ * UTC on 1 January 1970.
+ * @type int|string $duration_nano
+ * Duration of the profile. For instant profiles like live heap snapshot, the
+ * duration can be zero but it may be preferable to set time_unix_nano to the
+ * process start time and duration_nano to the relative time when the profile
+ * was gathered. This ensures Sample.timestamps_unix_nano values such as
+ * allocation timestamp fall into the profile time range.
+ * @type \Opentelemetry\Proto\Profiles\V1development\ValueType $period_type
+ * The kind of events between sampled occurrences.
+ * e.g [ "cpu","cycles" ] or [ "heap","bytes" ]
+ * @type int|string $period
+ * The number of events between sampled occurrences.
+ * @type string $profile_id
+ * A globally unique identifier for a profile. The ID is a 16-byte array. An ID with
+ * all zeroes is considered invalid. It may be used for deduplication and signal
+ * correlation purposes. It is acceptable to treat two profiles with different values
+ * in this field as not equal, even if they represented the same object at an earlier
+ * time.
+ * This field is optional; an ID may be assigned to an ID-less profile in a later step.
+ * @type int $dropped_attributes_count
+ * The number of attributes that were discarded. Attributes
+ * can be discarded because their keys are too long or because there are too many
+ * attributes. If this value is 0, then no attributes were dropped.
+ * @type string $original_payload_format
+ * The original payload format. See also original_payload. Optional, but the
+ * format and the bytes must be set or unset together.
+ * The allowed values for the format string are defined by the OpenTelemetry
+ * specification. Some examples are "jfr", "pprof", "linux_perf".
+ * The original payload may be optionally provided when the conversion to the
+ * OLTP format was done from a different format with some loss of the fidelity
+ * and the receiver may want to store the original payload to allow future
+ * lossless export or reinterpretation. Some examples of the original format
+ * are JFR (Java Flight Recorder), pprof, Linux perf.
+ * Even when the original payload is in a format that is semantically close to
+ * OTLP, such as pprof, a conversion may still be lossy in some cases (e.g. if
+ * the pprof file contains custom extensions or conventions).
+ * The original payload can be large in size, so including the original
+ * payload should be configurable by the profiler or collector options. The
+ * default behavior should be to not include the original payload.
+ * @type string $original_payload
+ * The original payload bytes. See also original_payload_format. Optional, but
+ * format and the bytes must be set or unset together.
+ * @type int[] $attribute_indices
+ * References to attributes in attribute_table. [optional]
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Profiles\V1Development\Profiles::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * The type and unit of all Sample.values in this profile.
+ * For a cpu or off-cpu profile this might be:
+ * ["cpu","nanoseconds"] or ["off_cpu","nanoseconds"]
+ * For a heap profile, this might be:
+ * ["allocated_objects","count"] or ["allocated_space","bytes"],
+ *
+ * Generated from protobuf field .opentelemetry.proto.profiles.v1development.ValueType sample_type = 1;
+ * @return \Opentelemetry\Proto\Profiles\V1development\ValueType|null
+ */
+ public function getSampleType()
+ {
+ return $this->sample_type;
+ }
+
+ public function hasSampleType()
+ {
+ return isset($this->sample_type);
+ }
+
+ public function clearSampleType()
+ {
+ unset($this->sample_type);
+ }
+
+ /**
+ * The type and unit of all Sample.values in this profile.
+ * For a cpu or off-cpu profile this might be:
+ * ["cpu","nanoseconds"] or ["off_cpu","nanoseconds"]
+ * For a heap profile, this might be:
+ * ["allocated_objects","count"] or ["allocated_space","bytes"],
+ *
+ * Generated from protobuf field .opentelemetry.proto.profiles.v1development.ValueType sample_type = 1;
+ * @param \Opentelemetry\Proto\Profiles\V1development\ValueType $var
+ * @return $this
+ */
+ public function setSampleType(\Opentelemetry\Proto\Profiles\V1development\ValueType|null $var)
+ {
+ $this->sample_type = $var;
+
+ return $this;
+ }
+
+ /**
+ * The set of samples recorded in this profile.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.profiles.v1development.Sample samples = 2;
+ * @return RepeatedField<\Opentelemetry\Proto\Profiles\V1development\Sample>
+ */
+ public function getSamples()
+ {
+ return $this->samples;
+ }
+
+ /**
+ * The set of samples recorded in this profile.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.profiles.v1development.Sample samples = 2;
+ * @param \Opentelemetry\Proto\Profiles\V1development\Sample[] $var
+ * @return $this
+ */
+ public function setSamples(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Opentelemetry\Proto\Profiles\V1development\Sample::class);
+ $this->samples = $arr;
+
+ return $this;
+ }
+
+ /**
+ * Time of collection. Value is UNIX Epoch time in nanoseconds since 00:00:00
+ * UTC on 1 January 1970.
+ *
+ * Generated from protobuf field fixed64 time_unix_nano = 3;
+ * @return int|string
+ */
+ public function getTimeUnixNano()
+ {
+ return $this->time_unix_nano;
+ }
+
+ /**
+ * Time of collection. Value is UNIX Epoch time in nanoseconds since 00:00:00
+ * UTC on 1 January 1970.
+ *
+ * Generated from protobuf field fixed64 time_unix_nano = 3;
+ * @param int|string $var
+ * @return $this
+ */
+ public function setTimeUnixNano(int|string $var)
+ {
+ GPBUtil::checkUint64($var);
+ $this->time_unix_nano = $var;
+
+ return $this;
+ }
+
+ /**
+ * Duration of the profile. For instant profiles like live heap snapshot, the
+ * duration can be zero but it may be preferable to set time_unix_nano to the
+ * process start time and duration_nano to the relative time when the profile
+ * was gathered. This ensures Sample.timestamps_unix_nano values such as
+ * allocation timestamp fall into the profile time range.
+ *
+ * Generated from protobuf field uint64 duration_nano = 4;
+ * @return int|string
+ */
+ public function getDurationNano()
+ {
+ return $this->duration_nano;
+ }
+
+ /**
+ * Duration of the profile. For instant profiles like live heap snapshot, the
+ * duration can be zero but it may be preferable to set time_unix_nano to the
+ * process start time and duration_nano to the relative time when the profile
+ * was gathered. This ensures Sample.timestamps_unix_nano values such as
+ * allocation timestamp fall into the profile time range.
+ *
+ * Generated from protobuf field uint64 duration_nano = 4;
+ * @param int|string $var
+ * @return $this
+ */
+ public function setDurationNano(int|string $var)
+ {
+ GPBUtil::checkUint64($var);
+ $this->duration_nano = $var;
+
+ return $this;
+ }
+
+ /**
+ * The kind of events between sampled occurrences.
+ * e.g [ "cpu","cycles" ] or [ "heap","bytes" ]
+ *
+ * Generated from protobuf field .opentelemetry.proto.profiles.v1development.ValueType period_type = 5;
+ * @return \Opentelemetry\Proto\Profiles\V1development\ValueType|null
+ */
+ public function getPeriodType()
+ {
+ return $this->period_type;
+ }
+
+ public function hasPeriodType()
+ {
+ return isset($this->period_type);
+ }
+
+ public function clearPeriodType()
+ {
+ unset($this->period_type);
+ }
+
+ /**
+ * The kind of events between sampled occurrences.
+ * e.g [ "cpu","cycles" ] or [ "heap","bytes" ]
+ *
+ * Generated from protobuf field .opentelemetry.proto.profiles.v1development.ValueType period_type = 5;
+ * @param \Opentelemetry\Proto\Profiles\V1development\ValueType $var
+ * @return $this
+ */
+ public function setPeriodType(\Opentelemetry\Proto\Profiles\V1development\ValueType|null $var)
+ {
+ $this->period_type = $var;
+
+ return $this;
+ }
+
+ /**
+ * The number of events between sampled occurrences.
+ *
+ * Generated from protobuf field int64 period = 6;
+ * @return int|string
+ */
+ public function getPeriod()
+ {
+ return $this->period;
+ }
+
+ /**
+ * The number of events between sampled occurrences.
+ *
+ * Generated from protobuf field int64 period = 6;
+ * @param int|string $var
+ * @return $this
+ */
+ public function setPeriod(int|string $var)
+ {
+ GPBUtil::checkInt64($var);
+ $this->period = $var;
+
+ return $this;
+ }
+
+ /**
+ * A globally unique identifier for a profile. The ID is a 16-byte array. An ID with
+ * all zeroes is considered invalid. It may be used for deduplication and signal
+ * correlation purposes. It is acceptable to treat two profiles with different values
+ * in this field as not equal, even if they represented the same object at an earlier
+ * time.
+ * This field is optional; an ID may be assigned to an ID-less profile in a later step.
+ *
+ * Generated from protobuf field bytes profile_id = 7;
+ * @return string
+ */
+ public function getProfileId()
+ {
+ return $this->profile_id;
+ }
+
+ /**
+ * A globally unique identifier for a profile. The ID is a 16-byte array. An ID with
+ * all zeroes is considered invalid. It may be used for deduplication and signal
+ * correlation purposes. It is acceptable to treat two profiles with different values
+ * in this field as not equal, even if they represented the same object at an earlier
+ * time.
+ * This field is optional; an ID may be assigned to an ID-less profile in a later step.
+ *
+ * Generated from protobuf field bytes profile_id = 7;
+ * @param string $var
+ * @return $this
+ */
+ public function setProfileId(string $var)
+ {
+ GPBUtil::checkString($var, false);
+ $this->profile_id = $var;
+
+ return $this;
+ }
+
+ /**
+ * The number of attributes that were discarded. Attributes
+ * can be discarded because their keys are too long or because there are too many
+ * attributes. If this value is 0, then no attributes were dropped.
+ *
+ * Generated from protobuf field uint32 dropped_attributes_count = 8;
+ * @return int
+ */
+ public function getDroppedAttributesCount()
+ {
+ return $this->dropped_attributes_count;
+ }
+
+ /**
+ * The number of attributes that were discarded. Attributes
+ * can be discarded because their keys are too long or because there are too many
+ * attributes. If this value is 0, then no attributes were dropped.
+ *
+ * Generated from protobuf field uint32 dropped_attributes_count = 8;
+ * @param int $var
+ * @return $this
+ */
+ public function setDroppedAttributesCount(int $var)
+ {
+ GPBUtil::checkUint32($var);
+ $this->dropped_attributes_count = $var;
+
+ return $this;
+ }
+
+ /**
+ * The original payload format. See also original_payload. Optional, but the
+ * format and the bytes must be set or unset together.
+ * The allowed values for the format string are defined by the OpenTelemetry
+ * specification. Some examples are "jfr", "pprof", "linux_perf".
+ * The original payload may be optionally provided when the conversion to the
+ * OLTP format was done from a different format with some loss of the fidelity
+ * and the receiver may want to store the original payload to allow future
+ * lossless export or reinterpretation. Some examples of the original format
+ * are JFR (Java Flight Recorder), pprof, Linux perf.
+ * Even when the original payload is in a format that is semantically close to
+ * OTLP, such as pprof, a conversion may still be lossy in some cases (e.g. if
+ * the pprof file contains custom extensions or conventions).
+ * The original payload can be large in size, so including the original
+ * payload should be configurable by the profiler or collector options. The
+ * default behavior should be to not include the original payload.
+ *
+ * Generated from protobuf field string original_payload_format = 9;
+ * @return string
+ */
+ public function getOriginalPayloadFormat()
+ {
+ return $this->original_payload_format;
+ }
+
+ /**
+ * The original payload format. See also original_payload. Optional, but the
+ * format and the bytes must be set or unset together.
+ * The allowed values for the format string are defined by the OpenTelemetry
+ * specification. Some examples are "jfr", "pprof", "linux_perf".
+ * The original payload may be optionally provided when the conversion to the
+ * OLTP format was done from a different format with some loss of the fidelity
+ * and the receiver may want to store the original payload to allow future
+ * lossless export or reinterpretation. Some examples of the original format
+ * are JFR (Java Flight Recorder), pprof, Linux perf.
+ * Even when the original payload is in a format that is semantically close to
+ * OTLP, such as pprof, a conversion may still be lossy in some cases (e.g. if
+ * the pprof file contains custom extensions or conventions).
+ * The original payload can be large in size, so including the original
+ * payload should be configurable by the profiler or collector options. The
+ * default behavior should be to not include the original payload.
+ *
+ * Generated from protobuf field string original_payload_format = 9;
+ * @param string $var
+ * @return $this
+ */
+ public function setOriginalPayloadFormat(string $var)
+ {
+ GPBUtil::checkString($var, true);
+ $this->original_payload_format = $var;
+
+ return $this;
+ }
+
+ /**
+ * The original payload bytes. See also original_payload_format. Optional, but
+ * format and the bytes must be set or unset together.
+ *
+ * Generated from protobuf field bytes original_payload = 10;
+ * @return string
+ */
+ public function getOriginalPayload()
+ {
+ return $this->original_payload;
+ }
+
+ /**
+ * The original payload bytes. See also original_payload_format. Optional, but
+ * format and the bytes must be set or unset together.
+ *
+ * Generated from protobuf field bytes original_payload = 10;
+ * @param string $var
+ * @return $this
+ */
+ public function setOriginalPayload(string $var)
+ {
+ GPBUtil::checkString($var, false);
+ $this->original_payload = $var;
+
+ return $this;
+ }
+
+ /**
+ * References to attributes in attribute_table. [optional]
+ *
+ * Generated from protobuf field repeated int32 attribute_indices = 11;
+ * @return RepeatedField
+ */
+ public function getAttributeIndices()
+ {
+ return $this->attribute_indices;
+ }
+
+ /**
+ * References to attributes in attribute_table. [optional]
+ *
+ * Generated from protobuf field repeated int32 attribute_indices = 11;
+ * @param int[] $var
+ * @return $this
+ */
+ public function setAttributeIndices(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::INT32);
+ $this->attribute_indices = $arr;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Profiles/V1development/ProfilesData.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Profiles/V1development/ProfilesData.php
new file mode 100644
index 000000000..58d0b5d44
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Profiles/V1development/ProfilesData.php
@@ -0,0 +1,150 @@
+opentelemetry.proto.profiles.v1development.ProfilesData
+ */
+class ProfilesData extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * An array of ResourceProfiles.
+ * For data coming from an SDK profiler, this array will typically contain one
+ * element. Host-level profilers will usually create one ResourceProfile per
+ * container, as well as one additional ResourceProfile grouping all samples
+ * from non-containerized processes.
+ * Other resource groupings are possible as well and clarified via
+ * Resource.attributes and semantic conventions.
+ * Tools that visualize profiles should prefer displaying
+ * resources_profiles[0].scope_profiles[0].profiles[0] by default.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.profiles.v1development.ResourceProfiles resource_profiles = 1;
+ */
+ private $resource_profiles;
+ /**
+ * One instance of ProfilesDictionary
+ *
+ * Generated from protobuf field .opentelemetry.proto.profiles.v1development.ProfilesDictionary dictionary = 2;
+ */
+ protected $dictionary = null;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type \Opentelemetry\Proto\Profiles\V1development\ResourceProfiles[] $resource_profiles
+ * An array of ResourceProfiles.
+ * For data coming from an SDK profiler, this array will typically contain one
+ * element. Host-level profilers will usually create one ResourceProfile per
+ * container, as well as one additional ResourceProfile grouping all samples
+ * from non-containerized processes.
+ * Other resource groupings are possible as well and clarified via
+ * Resource.attributes and semantic conventions.
+ * Tools that visualize profiles should prefer displaying
+ * resources_profiles[0].scope_profiles[0].profiles[0] by default.
+ * @type \Opentelemetry\Proto\Profiles\V1development\ProfilesDictionary $dictionary
+ * One instance of ProfilesDictionary
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Profiles\V1Development\Profiles::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * An array of ResourceProfiles.
+ * For data coming from an SDK profiler, this array will typically contain one
+ * element. Host-level profilers will usually create one ResourceProfile per
+ * container, as well as one additional ResourceProfile grouping all samples
+ * from non-containerized processes.
+ * Other resource groupings are possible as well and clarified via
+ * Resource.attributes and semantic conventions.
+ * Tools that visualize profiles should prefer displaying
+ * resources_profiles[0].scope_profiles[0].profiles[0] by default.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.profiles.v1development.ResourceProfiles resource_profiles = 1;
+ * @return RepeatedField<\Opentelemetry\Proto\Profiles\V1development\ResourceProfiles>
+ */
+ public function getResourceProfiles()
+ {
+ return $this->resource_profiles;
+ }
+
+ /**
+ * An array of ResourceProfiles.
+ * For data coming from an SDK profiler, this array will typically contain one
+ * element. Host-level profilers will usually create one ResourceProfile per
+ * container, as well as one additional ResourceProfile grouping all samples
+ * from non-containerized processes.
+ * Other resource groupings are possible as well and clarified via
+ * Resource.attributes and semantic conventions.
+ * Tools that visualize profiles should prefer displaying
+ * resources_profiles[0].scope_profiles[0].profiles[0] by default.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.profiles.v1development.ResourceProfiles resource_profiles = 1;
+ * @param \Opentelemetry\Proto\Profiles\V1development\ResourceProfiles[] $var
+ * @return $this
+ */
+ public function setResourceProfiles(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Opentelemetry\Proto\Profiles\V1development\ResourceProfiles::class);
+ $this->resource_profiles = $arr;
+
+ return $this;
+ }
+
+ /**
+ * One instance of ProfilesDictionary
+ *
+ * Generated from protobuf field .opentelemetry.proto.profiles.v1development.ProfilesDictionary dictionary = 2;
+ * @return \Opentelemetry\Proto\Profiles\V1development\ProfilesDictionary|null
+ */
+ public function getDictionary()
+ {
+ return $this->dictionary;
+ }
+
+ public function hasDictionary()
+ {
+ return isset($this->dictionary);
+ }
+
+ public function clearDictionary()
+ {
+ unset($this->dictionary);
+ }
+
+ /**
+ * One instance of ProfilesDictionary
+ *
+ * Generated from protobuf field .opentelemetry.proto.profiles.v1development.ProfilesDictionary dictionary = 2;
+ * @param \Opentelemetry\Proto\Profiles\V1development\ProfilesDictionary $var
+ * @return $this
+ */
+ public function setDictionary(\Opentelemetry\Proto\Profiles\V1development\ProfilesDictionary|null $var)
+ {
+ $this->dictionary = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Profiles/V1development/ProfilesDictionary.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Profiles/V1development/ProfilesDictionary.php
new file mode 100644
index 000000000..21108414a
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Profiles/V1development/ProfilesDictionary.php
@@ -0,0 +1,382 @@
+opentelemetry.proto.profiles.v1development.ProfilesDictionary
+ */
+class ProfilesDictionary extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * Mappings from address ranges to the image/binary/library mapped
+ * into that address range referenced by locations via Location.mapping_index.
+ * mapping_table[0] must always be zero value (Mapping{}) and present.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.profiles.v1development.Mapping mapping_table = 1;
+ */
+ private $mapping_table;
+ /**
+ * Locations referenced by samples via Stack.location_indices.
+ * location_table[0] must always be zero value (Location{}) and present.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.profiles.v1development.Location location_table = 2;
+ */
+ private $location_table;
+ /**
+ * Functions referenced by locations via Line.function_index.
+ * function_table[0] must always be zero value (Function{}) and present.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.profiles.v1development.Function function_table = 3;
+ */
+ private $function_table;
+ /**
+ * Links referenced by samples via Sample.link_index.
+ * link_table[0] must always be zero value (Link{}) and present.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.profiles.v1development.Link link_table = 4;
+ */
+ private $link_table;
+ /**
+ * A common table for strings referenced by various messages.
+ * string_table[0] must always be "" and present.
+ *
+ * Generated from protobuf field repeated string string_table = 5;
+ */
+ private $string_table;
+ /**
+ * A common table for attributes referenced by the Profile, Sample, Mapping
+ * and Location messages below through attribute_indices field. Each entry is
+ * a key/value pair with an optional unit. Since this is a dictionary table,
+ * multiple entries with the same key may be present, unlike direct attribute
+ * tables like Resource.attributes. The referencing attribute_indices fields,
+ * though, do maintain the key uniqueness requirement.
+ * It's recommended to use attributes for variables with bounded cardinality,
+ * such as categorical variables
+ * (https://en.wikipedia.org/wiki/Categorical_variable). Using an attribute of
+ * a floating point type (e.g., CPU time) in a sample can quickly make every
+ * attribute value unique, defeating the purpose of the dictionary and
+ * impractically increasing the profile size.
+ * Examples of attributes:
+ * "/http/user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36"
+ * "abc.com/myattribute": true
+ * "allocation_size": 128 bytes
+ * attribute_table[0] must always be zero value (KeyValueAndUnit{}) and present.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.profiles.v1development.KeyValueAndUnit attribute_table = 6;
+ */
+ private $attribute_table;
+ /**
+ * Stacks referenced by samples via Sample.stack_index.
+ * stack_table[0] must always be zero value (Stack{}) and present.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.profiles.v1development.Stack stack_table = 7;
+ */
+ private $stack_table;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type \Opentelemetry\Proto\Profiles\V1development\Mapping[] $mapping_table
+ * Mappings from address ranges to the image/binary/library mapped
+ * into that address range referenced by locations via Location.mapping_index.
+ * mapping_table[0] must always be zero value (Mapping{}) and present.
+ * @type \Opentelemetry\Proto\Profiles\V1development\Location[] $location_table
+ * Locations referenced by samples via Stack.location_indices.
+ * location_table[0] must always be zero value (Location{}) and present.
+ * @type \Opentelemetry\Proto\Profiles\V1development\PBFunction[] $function_table
+ * Functions referenced by locations via Line.function_index.
+ * function_table[0] must always be zero value (Function{}) and present.
+ * @type \Opentelemetry\Proto\Profiles\V1development\Link[] $link_table
+ * Links referenced by samples via Sample.link_index.
+ * link_table[0] must always be zero value (Link{}) and present.
+ * @type string[] $string_table
+ * A common table for strings referenced by various messages.
+ * string_table[0] must always be "" and present.
+ * @type \Opentelemetry\Proto\Profiles\V1development\KeyValueAndUnit[] $attribute_table
+ * A common table for attributes referenced by the Profile, Sample, Mapping
+ * and Location messages below through attribute_indices field. Each entry is
+ * a key/value pair with an optional unit. Since this is a dictionary table,
+ * multiple entries with the same key may be present, unlike direct attribute
+ * tables like Resource.attributes. The referencing attribute_indices fields,
+ * though, do maintain the key uniqueness requirement.
+ * It's recommended to use attributes for variables with bounded cardinality,
+ * such as categorical variables
+ * (https://en.wikipedia.org/wiki/Categorical_variable). Using an attribute of
+ * a floating point type (e.g., CPU time) in a sample can quickly make every
+ * attribute value unique, defeating the purpose of the dictionary and
+ * impractically increasing the profile size.
+ * Examples of attributes:
+ * "/http/user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36"
+ * "abc.com/myattribute": true
+ * "allocation_size": 128 bytes
+ * attribute_table[0] must always be zero value (KeyValueAndUnit{}) and present.
+ * @type \Opentelemetry\Proto\Profiles\V1development\Stack[] $stack_table
+ * Stacks referenced by samples via Sample.stack_index.
+ * stack_table[0] must always be zero value (Stack{}) and present.
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Profiles\V1Development\Profiles::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * Mappings from address ranges to the image/binary/library mapped
+ * into that address range referenced by locations via Location.mapping_index.
+ * mapping_table[0] must always be zero value (Mapping{}) and present.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.profiles.v1development.Mapping mapping_table = 1;
+ * @return RepeatedField<\Opentelemetry\Proto\Profiles\V1development\Mapping>
+ */
+ public function getMappingTable()
+ {
+ return $this->mapping_table;
+ }
+
+ /**
+ * Mappings from address ranges to the image/binary/library mapped
+ * into that address range referenced by locations via Location.mapping_index.
+ * mapping_table[0] must always be zero value (Mapping{}) and present.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.profiles.v1development.Mapping mapping_table = 1;
+ * @param \Opentelemetry\Proto\Profiles\V1development\Mapping[] $var
+ * @return $this
+ */
+ public function setMappingTable(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Opentelemetry\Proto\Profiles\V1development\Mapping::class);
+ $this->mapping_table = $arr;
+
+ return $this;
+ }
+
+ /**
+ * Locations referenced by samples via Stack.location_indices.
+ * location_table[0] must always be zero value (Location{}) and present.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.profiles.v1development.Location location_table = 2;
+ * @return RepeatedField<\Opentelemetry\Proto\Profiles\V1development\Location>
+ */
+ public function getLocationTable()
+ {
+ return $this->location_table;
+ }
+
+ /**
+ * Locations referenced by samples via Stack.location_indices.
+ * location_table[0] must always be zero value (Location{}) and present.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.profiles.v1development.Location location_table = 2;
+ * @param \Opentelemetry\Proto\Profiles\V1development\Location[] $var
+ * @return $this
+ */
+ public function setLocationTable(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Opentelemetry\Proto\Profiles\V1development\Location::class);
+ $this->location_table = $arr;
+
+ return $this;
+ }
+
+ /**
+ * Functions referenced by locations via Line.function_index.
+ * function_table[0] must always be zero value (Function{}) and present.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.profiles.v1development.Function function_table = 3;
+ * @return RepeatedField<\Opentelemetry\Proto\Profiles\V1development\PBFunction>
+ */
+ public function getFunctionTable()
+ {
+ return $this->function_table;
+ }
+
+ /**
+ * Functions referenced by locations via Line.function_index.
+ * function_table[0] must always be zero value (Function{}) and present.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.profiles.v1development.Function function_table = 3;
+ * @param \Opentelemetry\Proto\Profiles\V1development\PBFunction[] $var
+ * @return $this
+ */
+ public function setFunctionTable(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Opentelemetry\Proto\Profiles\V1development\PBFunction::class);
+ $this->function_table = $arr;
+
+ return $this;
+ }
+
+ /**
+ * Links referenced by samples via Sample.link_index.
+ * link_table[0] must always be zero value (Link{}) and present.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.profiles.v1development.Link link_table = 4;
+ * @return RepeatedField<\Opentelemetry\Proto\Profiles\V1development\Link>
+ */
+ public function getLinkTable()
+ {
+ return $this->link_table;
+ }
+
+ /**
+ * Links referenced by samples via Sample.link_index.
+ * link_table[0] must always be zero value (Link{}) and present.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.profiles.v1development.Link link_table = 4;
+ * @param \Opentelemetry\Proto\Profiles\V1development\Link[] $var
+ * @return $this
+ */
+ public function setLinkTable(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Opentelemetry\Proto\Profiles\V1development\Link::class);
+ $this->link_table = $arr;
+
+ return $this;
+ }
+
+ /**
+ * A common table for strings referenced by various messages.
+ * string_table[0] must always be "" and present.
+ *
+ * Generated from protobuf field repeated string string_table = 5;
+ * @return RepeatedField
+ */
+ public function getStringTable()
+ {
+ return $this->string_table;
+ }
+
+ /**
+ * A common table for strings referenced by various messages.
+ * string_table[0] must always be "" and present.
+ *
+ * Generated from protobuf field repeated string string_table = 5;
+ * @param string[] $var
+ * @return $this
+ */
+ public function setStringTable(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::STRING);
+ $this->string_table = $arr;
+
+ return $this;
+ }
+
+ /**
+ * A common table for attributes referenced by the Profile, Sample, Mapping
+ * and Location messages below through attribute_indices field. Each entry is
+ * a key/value pair with an optional unit. Since this is a dictionary table,
+ * multiple entries with the same key may be present, unlike direct attribute
+ * tables like Resource.attributes. The referencing attribute_indices fields,
+ * though, do maintain the key uniqueness requirement.
+ * It's recommended to use attributes for variables with bounded cardinality,
+ * such as categorical variables
+ * (https://en.wikipedia.org/wiki/Categorical_variable). Using an attribute of
+ * a floating point type (e.g., CPU time) in a sample can quickly make every
+ * attribute value unique, defeating the purpose of the dictionary and
+ * impractically increasing the profile size.
+ * Examples of attributes:
+ * "/http/user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36"
+ * "abc.com/myattribute": true
+ * "allocation_size": 128 bytes
+ * attribute_table[0] must always be zero value (KeyValueAndUnit{}) and present.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.profiles.v1development.KeyValueAndUnit attribute_table = 6;
+ * @return RepeatedField<\Opentelemetry\Proto\Profiles\V1development\KeyValueAndUnit>
+ */
+ public function getAttributeTable()
+ {
+ return $this->attribute_table;
+ }
+
+ /**
+ * A common table for attributes referenced by the Profile, Sample, Mapping
+ * and Location messages below through attribute_indices field. Each entry is
+ * a key/value pair with an optional unit. Since this is a dictionary table,
+ * multiple entries with the same key may be present, unlike direct attribute
+ * tables like Resource.attributes. The referencing attribute_indices fields,
+ * though, do maintain the key uniqueness requirement.
+ * It's recommended to use attributes for variables with bounded cardinality,
+ * such as categorical variables
+ * (https://en.wikipedia.org/wiki/Categorical_variable). Using an attribute of
+ * a floating point type (e.g., CPU time) in a sample can quickly make every
+ * attribute value unique, defeating the purpose of the dictionary and
+ * impractically increasing the profile size.
+ * Examples of attributes:
+ * "/http/user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36"
+ * "abc.com/myattribute": true
+ * "allocation_size": 128 bytes
+ * attribute_table[0] must always be zero value (KeyValueAndUnit{}) and present.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.profiles.v1development.KeyValueAndUnit attribute_table = 6;
+ * @param \Opentelemetry\Proto\Profiles\V1development\KeyValueAndUnit[] $var
+ * @return $this
+ */
+ public function setAttributeTable(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Opentelemetry\Proto\Profiles\V1development\KeyValueAndUnit::class);
+ $this->attribute_table = $arr;
+
+ return $this;
+ }
+
+ /**
+ * Stacks referenced by samples via Sample.stack_index.
+ * stack_table[0] must always be zero value (Stack{}) and present.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.profiles.v1development.Stack stack_table = 7;
+ * @return RepeatedField<\Opentelemetry\Proto\Profiles\V1development\Stack>
+ */
+ public function getStackTable()
+ {
+ return $this->stack_table;
+ }
+
+ /**
+ * Stacks referenced by samples via Sample.stack_index.
+ * stack_table[0] must always be zero value (Stack{}) and present.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.profiles.v1development.Stack stack_table = 7;
+ * @param \Opentelemetry\Proto\Profiles\V1development\Stack[] $var
+ * @return $this
+ */
+ public function setStackTable(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Opentelemetry\Proto\Profiles\V1development\Stack::class);
+ $this->stack_table = $arr;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Profiles/V1development/ResourceProfiles.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Profiles/V1development/ResourceProfiles.php
new file mode 100644
index 000000000..4a67a9d30
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Profiles/V1development/ResourceProfiles.php
@@ -0,0 +1,169 @@
+opentelemetry.proto.profiles.v1development.ResourceProfiles
+ */
+class ResourceProfiles extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * The resource for the profiles in this message.
+ * If this field is not set then no resource info is known.
+ *
+ * Generated from protobuf field .opentelemetry.proto.resource.v1.Resource resource = 1;
+ */
+ protected $resource = null;
+ /**
+ * A list of ScopeProfiles that originate from a resource.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.profiles.v1development.ScopeProfiles scope_profiles = 2;
+ */
+ private $scope_profiles;
+ /**
+ * The Schema URL, if known. This is the identifier of the Schema that the resource data
+ * is recorded in. Notably, the last part of the URL path is the version number of the
+ * schema: http[s]://server[:port]/path/. To learn more about Schema URL see
+ * https://opentelemetry.io/docs/specs/otel/schemas/#schema-url
+ * This schema_url applies to the data in the "resource" field. It does not apply
+ * to the data in the "scope_profiles" field which have their own schema_url field.
+ *
+ * Generated from protobuf field string schema_url = 3;
+ */
+ protected $schema_url = '';
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type \Opentelemetry\Proto\Resource\V1\Resource $resource
+ * The resource for the profiles in this message.
+ * If this field is not set then no resource info is known.
+ * @type \Opentelemetry\Proto\Profiles\V1development\ScopeProfiles[] $scope_profiles
+ * A list of ScopeProfiles that originate from a resource.
+ * @type string $schema_url
+ * The Schema URL, if known. This is the identifier of the Schema that the resource data
+ * is recorded in. Notably, the last part of the URL path is the version number of the
+ * schema: http[s]://server[:port]/path/. To learn more about Schema URL see
+ * https://opentelemetry.io/docs/specs/otel/schemas/#schema-url
+ * This schema_url applies to the data in the "resource" field. It does not apply
+ * to the data in the "scope_profiles" field which have their own schema_url field.
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Profiles\V1Development\Profiles::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * The resource for the profiles in this message.
+ * If this field is not set then no resource info is known.
+ *
+ * Generated from protobuf field .opentelemetry.proto.resource.v1.Resource resource = 1;
+ * @return \Opentelemetry\Proto\Resource\V1\Resource|null
+ */
+ public function getResource()
+ {
+ return $this->resource;
+ }
+
+ public function hasResource()
+ {
+ return isset($this->resource);
+ }
+
+ public function clearResource()
+ {
+ unset($this->resource);
+ }
+
+ /**
+ * The resource for the profiles in this message.
+ * If this field is not set then no resource info is known.
+ *
+ * Generated from protobuf field .opentelemetry.proto.resource.v1.Resource resource = 1;
+ * @param \Opentelemetry\Proto\Resource\V1\Resource $var
+ * @return $this
+ */
+ public function setResource(\Opentelemetry\Proto\Resource\V1\Resource|null $var)
+ {
+ $this->resource = $var;
+
+ return $this;
+ }
+
+ /**
+ * A list of ScopeProfiles that originate from a resource.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.profiles.v1development.ScopeProfiles scope_profiles = 2;
+ * @return RepeatedField<\Opentelemetry\Proto\Profiles\V1development\ScopeProfiles>
+ */
+ public function getScopeProfiles()
+ {
+ return $this->scope_profiles;
+ }
+
+ /**
+ * A list of ScopeProfiles that originate from a resource.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.profiles.v1development.ScopeProfiles scope_profiles = 2;
+ * @param \Opentelemetry\Proto\Profiles\V1development\ScopeProfiles[] $var
+ * @return $this
+ */
+ public function setScopeProfiles(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Opentelemetry\Proto\Profiles\V1development\ScopeProfiles::class);
+ $this->scope_profiles = $arr;
+
+ return $this;
+ }
+
+ /**
+ * The Schema URL, if known. This is the identifier of the Schema that the resource data
+ * is recorded in. Notably, the last part of the URL path is the version number of the
+ * schema: http[s]://server[:port]/path/. To learn more about Schema URL see
+ * https://opentelemetry.io/docs/specs/otel/schemas/#schema-url
+ * This schema_url applies to the data in the "resource" field. It does not apply
+ * to the data in the "scope_profiles" field which have their own schema_url field.
+ *
+ * Generated from protobuf field string schema_url = 3;
+ * @return string
+ */
+ public function getSchemaUrl()
+ {
+ return $this->schema_url;
+ }
+
+ /**
+ * The Schema URL, if known. This is the identifier of the Schema that the resource data
+ * is recorded in. Notably, the last part of the URL path is the version number of the
+ * schema: http[s]://server[:port]/path/. To learn more about Schema URL see
+ * https://opentelemetry.io/docs/specs/otel/schemas/#schema-url
+ * This schema_url applies to the data in the "resource" field. It does not apply
+ * to the data in the "scope_profiles" field which have their own schema_url field.
+ *
+ * Generated from protobuf field string schema_url = 3;
+ * @param string $var
+ * @return $this
+ */
+ public function setSchemaUrl(string $var)
+ {
+ GPBUtil::checkString($var, true);
+ $this->schema_url = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Profiles/V1development/Sample.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Profiles/V1development/Sample.php
new file mode 100644
index 000000000..d393bee79
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Profiles/V1development/Sample.php
@@ -0,0 +1,241 @@
+opentelemetry.proto.profiles.v1development.Sample
+ */
+class Sample extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * Reference to stack in ProfilesDictionary.stack_table.
+ *
+ * Generated from protobuf field int32 stack_index = 1;
+ */
+ protected $stack_index = 0;
+ /**
+ * References to attributes in ProfilesDictionary.attribute_table. [optional]
+ *
+ * Generated from protobuf field repeated int32 attribute_indices = 2;
+ */
+ private $attribute_indices;
+ /**
+ * Reference to link in ProfilesDictionary.link_table. [optional]
+ * It can be unset / set to 0 if no link exists, as link_table[0] is always a 'null' default value.
+ *
+ * Generated from protobuf field int32 link_index = 3;
+ */
+ protected $link_index = 0;
+ /**
+ * The type and unit of each value is defined by Profile.sample_type.
+ *
+ * Generated from protobuf field repeated int64 values = 4;
+ */
+ private $values;
+ /**
+ * Timestamps associated with Sample. Value is UNIX Epoch time in nanoseconds
+ * since 00:00:00 UTC on 1 January 1970. The timestamps should fall within the
+ * [Profile.time_unix_nano, Profile.time_unix_nano + Profile.duration_nano)
+ * time range.
+ *
+ * Generated from protobuf field repeated fixed64 timestamps_unix_nano = 5;
+ */
+ private $timestamps_unix_nano;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type int $stack_index
+ * Reference to stack in ProfilesDictionary.stack_table.
+ * @type int[] $attribute_indices
+ * References to attributes in ProfilesDictionary.attribute_table. [optional]
+ * @type int $link_index
+ * Reference to link in ProfilesDictionary.link_table. [optional]
+ * It can be unset / set to 0 if no link exists, as link_table[0] is always a 'null' default value.
+ * @type int[]|string[] $values
+ * The type and unit of each value is defined by Profile.sample_type.
+ * @type int[]|string[] $timestamps_unix_nano
+ * Timestamps associated with Sample. Value is UNIX Epoch time in nanoseconds
+ * since 00:00:00 UTC on 1 January 1970. The timestamps should fall within the
+ * [Profile.time_unix_nano, Profile.time_unix_nano + Profile.duration_nano)
+ * time range.
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Profiles\V1Development\Profiles::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * Reference to stack in ProfilesDictionary.stack_table.
+ *
+ * Generated from protobuf field int32 stack_index = 1;
+ * @return int
+ */
+ public function getStackIndex()
+ {
+ return $this->stack_index;
+ }
+
+ /**
+ * Reference to stack in ProfilesDictionary.stack_table.
+ *
+ * Generated from protobuf field int32 stack_index = 1;
+ * @param int $var
+ * @return $this
+ */
+ public function setStackIndex(int $var)
+ {
+ GPBUtil::checkInt32($var);
+ $this->stack_index = $var;
+
+ return $this;
+ }
+
+ /**
+ * References to attributes in ProfilesDictionary.attribute_table. [optional]
+ *
+ * Generated from protobuf field repeated int32 attribute_indices = 2;
+ * @return RepeatedField
+ */
+ public function getAttributeIndices()
+ {
+ return $this->attribute_indices;
+ }
+
+ /**
+ * References to attributes in ProfilesDictionary.attribute_table. [optional]
+ *
+ * Generated from protobuf field repeated int32 attribute_indices = 2;
+ * @param int[] $var
+ * @return $this
+ */
+ public function setAttributeIndices(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::INT32);
+ $this->attribute_indices = $arr;
+
+ return $this;
+ }
+
+ /**
+ * Reference to link in ProfilesDictionary.link_table. [optional]
+ * It can be unset / set to 0 if no link exists, as link_table[0] is always a 'null' default value.
+ *
+ * Generated from protobuf field int32 link_index = 3;
+ * @return int
+ */
+ public function getLinkIndex()
+ {
+ return $this->link_index;
+ }
+
+ /**
+ * Reference to link in ProfilesDictionary.link_table. [optional]
+ * It can be unset / set to 0 if no link exists, as link_table[0] is always a 'null' default value.
+ *
+ * Generated from protobuf field int32 link_index = 3;
+ * @param int $var
+ * @return $this
+ */
+ public function setLinkIndex(int $var)
+ {
+ GPBUtil::checkInt32($var);
+ $this->link_index = $var;
+
+ return $this;
+ }
+
+ /**
+ * The type and unit of each value is defined by Profile.sample_type.
+ *
+ * Generated from protobuf field repeated int64 values = 4;
+ * @return RepeatedField|RepeatedField
+ */
+ public function getValues()
+ {
+ return $this->values;
+ }
+
+ /**
+ * The type and unit of each value is defined by Profile.sample_type.
+ *
+ * Generated from protobuf field repeated int64 values = 4;
+ * @param int[]|string[] $var
+ * @return $this
+ */
+ public function setValues(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::INT64);
+ $this->values = $arr;
+
+ return $this;
+ }
+
+ /**
+ * Timestamps associated with Sample. Value is UNIX Epoch time in nanoseconds
+ * since 00:00:00 UTC on 1 January 1970. The timestamps should fall within the
+ * [Profile.time_unix_nano, Profile.time_unix_nano + Profile.duration_nano)
+ * time range.
+ *
+ * Generated from protobuf field repeated fixed64 timestamps_unix_nano = 5;
+ * @return RepeatedField|RepeatedField
+ */
+ public function getTimestampsUnixNano()
+ {
+ return $this->timestamps_unix_nano;
+ }
+
+ /**
+ * Timestamps associated with Sample. Value is UNIX Epoch time in nanoseconds
+ * since 00:00:00 UTC on 1 January 1970. The timestamps should fall within the
+ * [Profile.time_unix_nano, Profile.time_unix_nano + Profile.duration_nano)
+ * time range.
+ *
+ * Generated from protobuf field repeated fixed64 timestamps_unix_nano = 5;
+ * @param int[]|string[] $var
+ * @return $this
+ */
+ public function setTimestampsUnixNano(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::FIXED64);
+ $this->timestamps_unix_nano = $arr;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Profiles/V1development/ScopeProfiles.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Profiles/V1development/ScopeProfiles.php
new file mode 100644
index 000000000..f20e4bfaa
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Profiles/V1development/ScopeProfiles.php
@@ -0,0 +1,173 @@
+opentelemetry.proto.profiles.v1development.ScopeProfiles
+ */
+class ScopeProfiles extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * The instrumentation scope information for the profiles in this message.
+ * Semantically when InstrumentationScope isn't set, it is equivalent with
+ * an empty instrumentation scope name (unknown).
+ *
+ * Generated from protobuf field .opentelemetry.proto.common.v1.InstrumentationScope scope = 1;
+ */
+ protected $scope = null;
+ /**
+ * A list of Profiles that originate from an instrumentation scope.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.profiles.v1development.Profile profiles = 2;
+ */
+ private $profiles;
+ /**
+ * The Schema URL, if known. This is the identifier of the Schema that the profile data
+ * is recorded in. Notably, the last part of the URL path is the version number of the
+ * schema: http[s]://server[:port]/path/. To learn more about Schema URL see
+ * https://opentelemetry.io/docs/specs/otel/schemas/#schema-url
+ * This schema_url applies to the data in the "scope" field and all profiles in the
+ * "profiles" field.
+ *
+ * Generated from protobuf field string schema_url = 3;
+ */
+ protected $schema_url = '';
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type \Opentelemetry\Proto\Common\V1\InstrumentationScope $scope
+ * The instrumentation scope information for the profiles in this message.
+ * Semantically when InstrumentationScope isn't set, it is equivalent with
+ * an empty instrumentation scope name (unknown).
+ * @type \Opentelemetry\Proto\Profiles\V1development\Profile[] $profiles
+ * A list of Profiles that originate from an instrumentation scope.
+ * @type string $schema_url
+ * The Schema URL, if known. This is the identifier of the Schema that the profile data
+ * is recorded in. Notably, the last part of the URL path is the version number of the
+ * schema: http[s]://server[:port]/path/. To learn more about Schema URL see
+ * https://opentelemetry.io/docs/specs/otel/schemas/#schema-url
+ * This schema_url applies to the data in the "scope" field and all profiles in the
+ * "profiles" field.
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Profiles\V1Development\Profiles::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * The instrumentation scope information for the profiles in this message.
+ * Semantically when InstrumentationScope isn't set, it is equivalent with
+ * an empty instrumentation scope name (unknown).
+ *
+ * Generated from protobuf field .opentelemetry.proto.common.v1.InstrumentationScope scope = 1;
+ * @return \Opentelemetry\Proto\Common\V1\InstrumentationScope|null
+ */
+ public function getScope()
+ {
+ return $this->scope;
+ }
+
+ public function hasScope()
+ {
+ return isset($this->scope);
+ }
+
+ public function clearScope()
+ {
+ unset($this->scope);
+ }
+
+ /**
+ * The instrumentation scope information for the profiles in this message.
+ * Semantically when InstrumentationScope isn't set, it is equivalent with
+ * an empty instrumentation scope name (unknown).
+ *
+ * Generated from protobuf field .opentelemetry.proto.common.v1.InstrumentationScope scope = 1;
+ * @param \Opentelemetry\Proto\Common\V1\InstrumentationScope $var
+ * @return $this
+ */
+ public function setScope(\Opentelemetry\Proto\Common\V1\InstrumentationScope|null $var)
+ {
+ $this->scope = $var;
+
+ return $this;
+ }
+
+ /**
+ * A list of Profiles that originate from an instrumentation scope.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.profiles.v1development.Profile profiles = 2;
+ * @return RepeatedField<\Opentelemetry\Proto\Profiles\V1development\Profile>
+ */
+ public function getProfiles()
+ {
+ return $this->profiles;
+ }
+
+ /**
+ * A list of Profiles that originate from an instrumentation scope.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.profiles.v1development.Profile profiles = 2;
+ * @param \Opentelemetry\Proto\Profiles\V1development\Profile[] $var
+ * @return $this
+ */
+ public function setProfiles(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Opentelemetry\Proto\Profiles\V1development\Profile::class);
+ $this->profiles = $arr;
+
+ return $this;
+ }
+
+ /**
+ * The Schema URL, if known. This is the identifier of the Schema that the profile data
+ * is recorded in. Notably, the last part of the URL path is the version number of the
+ * schema: http[s]://server[:port]/path/. To learn more about Schema URL see
+ * https://opentelemetry.io/docs/specs/otel/schemas/#schema-url
+ * This schema_url applies to the data in the "scope" field and all profiles in the
+ * "profiles" field.
+ *
+ * Generated from protobuf field string schema_url = 3;
+ * @return string
+ */
+ public function getSchemaUrl()
+ {
+ return $this->schema_url;
+ }
+
+ /**
+ * The Schema URL, if known. This is the identifier of the Schema that the profile data
+ * is recorded in. Notably, the last part of the URL path is the version number of the
+ * schema: http[s]://server[:port]/path/. To learn more about Schema URL see
+ * https://opentelemetry.io/docs/specs/otel/schemas/#schema-url
+ * This schema_url applies to the data in the "scope" field and all profiles in the
+ * "profiles" field.
+ *
+ * Generated from protobuf field string schema_url = 3;
+ * @param string $var
+ * @return $this
+ */
+ public function setSchemaUrl(string $var)
+ {
+ GPBUtil::checkString($var, true);
+ $this->schema_url = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Profiles/V1development/Stack.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Profiles/V1development/Stack.php
new file mode 100644
index 000000000..faaa9bd55
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Profiles/V1development/Stack.php
@@ -0,0 +1,72 @@
+opentelemetry.proto.profiles.v1development.Stack
+ */
+class Stack extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * References to locations in ProfilesDictionary.location_table.
+ * The first location is the leaf frame.
+ *
+ * Generated from protobuf field repeated int32 location_indices = 1;
+ */
+ private $location_indices;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type int[] $location_indices
+ * References to locations in ProfilesDictionary.location_table.
+ * The first location is the leaf frame.
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Profiles\V1Development\Profiles::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * References to locations in ProfilesDictionary.location_table.
+ * The first location is the leaf frame.
+ *
+ * Generated from protobuf field repeated int32 location_indices = 1;
+ * @return RepeatedField
+ */
+ public function getLocationIndices()
+ {
+ return $this->location_indices;
+ }
+
+ /**
+ * References to locations in ProfilesDictionary.location_table.
+ * The first location is the leaf frame.
+ *
+ * Generated from protobuf field repeated int32 location_indices = 1;
+ * @param int[] $var
+ * @return $this
+ */
+ public function setLocationIndices(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::INT32);
+ $this->location_indices = $arr;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Profiles/V1development/ValueType.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Profiles/V1development/ValueType.php
new file mode 100644
index 000000000..bf130a905
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Profiles/V1development/ValueType.php
@@ -0,0 +1,102 @@
+opentelemetry.proto.profiles.v1development.ValueType
+ */
+class ValueType extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * Index into ProfilesDictionary.string_table.
+ *
+ * Generated from protobuf field int32 type_strindex = 1;
+ */
+ protected $type_strindex = 0;
+ /**
+ * Index into ProfilesDictionary.string_table.
+ *
+ * Generated from protobuf field int32 unit_strindex = 2;
+ */
+ protected $unit_strindex = 0;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type int $type_strindex
+ * Index into ProfilesDictionary.string_table.
+ * @type int $unit_strindex
+ * Index into ProfilesDictionary.string_table.
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Profiles\V1Development\Profiles::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * Index into ProfilesDictionary.string_table.
+ *
+ * Generated from protobuf field int32 type_strindex = 1;
+ * @return int
+ */
+ public function getTypeStrindex()
+ {
+ return $this->type_strindex;
+ }
+
+ /**
+ * Index into ProfilesDictionary.string_table.
+ *
+ * Generated from protobuf field int32 type_strindex = 1;
+ * @param int $var
+ * @return $this
+ */
+ public function setTypeStrindex(int $var)
+ {
+ GPBUtil::checkInt32($var);
+ $this->type_strindex = $var;
+
+ return $this;
+ }
+
+ /**
+ * Index into ProfilesDictionary.string_table.
+ *
+ * Generated from protobuf field int32 unit_strindex = 2;
+ * @return int
+ */
+ public function getUnitStrindex()
+ {
+ return $this->unit_strindex;
+ }
+
+ /**
+ * Index into ProfilesDictionary.string_table.
+ *
+ * Generated from protobuf field int32 unit_strindex = 2;
+ * @param int $var
+ * @return $this
+ */
+ public function setUnitStrindex(int $var)
+ {
+ GPBUtil::checkInt32($var);
+ $this->unit_strindex = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Resource/V1/Resource.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Resource/V1/Resource.php
new file mode 100644
index 000000000..9a82d525d
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Resource/V1/Resource.php
@@ -0,0 +1,160 @@
+opentelemetry.proto.resource.v1.Resource
+ */
+class Resource extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * Set of attributes that describe the resource.
+ * Attribute keys MUST be unique (it is not allowed to have more than one
+ * attribute with the same key).
+ * The behavior of software that receives duplicated keys can be unpredictable.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.common.v1.KeyValue attributes = 1;
+ */
+ private $attributes;
+ /**
+ * The number of dropped attributes. If the value is 0, then
+ * no attributes were dropped.
+ *
+ * Generated from protobuf field uint32 dropped_attributes_count = 2;
+ */
+ protected $dropped_attributes_count = 0;
+ /**
+ * Set of entities that participate in this Resource.
+ * Note: keys in the references MUST exist in attributes of this message.
+ * Status: [Development]
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.common.v1.EntityRef entity_refs = 3;
+ */
+ private $entity_refs;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type \Opentelemetry\Proto\Common\V1\KeyValue[] $attributes
+ * Set of attributes that describe the resource.
+ * Attribute keys MUST be unique (it is not allowed to have more than one
+ * attribute with the same key).
+ * The behavior of software that receives duplicated keys can be unpredictable.
+ * @type int $dropped_attributes_count
+ * The number of dropped attributes. If the value is 0, then
+ * no attributes were dropped.
+ * @type \Opentelemetry\Proto\Common\V1\EntityRef[] $entity_refs
+ * Set of entities that participate in this Resource.
+ * Note: keys in the references MUST exist in attributes of this message.
+ * Status: [Development]
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Resource\V1\Resource::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * Set of attributes that describe the resource.
+ * Attribute keys MUST be unique (it is not allowed to have more than one
+ * attribute with the same key).
+ * The behavior of software that receives duplicated keys can be unpredictable.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.common.v1.KeyValue attributes = 1;
+ * @return RepeatedField<\Opentelemetry\Proto\Common\V1\KeyValue>
+ */
+ public function getAttributes()
+ {
+ return $this->attributes;
+ }
+
+ /**
+ * Set of attributes that describe the resource.
+ * Attribute keys MUST be unique (it is not allowed to have more than one
+ * attribute with the same key).
+ * The behavior of software that receives duplicated keys can be unpredictable.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.common.v1.KeyValue attributes = 1;
+ * @param \Opentelemetry\Proto\Common\V1\KeyValue[] $var
+ * @return $this
+ */
+ public function setAttributes(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Opentelemetry\Proto\Common\V1\KeyValue::class);
+ $this->attributes = $arr;
+
+ return $this;
+ }
+
+ /**
+ * The number of dropped attributes. If the value is 0, then
+ * no attributes were dropped.
+ *
+ * Generated from protobuf field uint32 dropped_attributes_count = 2;
+ * @return int
+ */
+ public function getDroppedAttributesCount()
+ {
+ return $this->dropped_attributes_count;
+ }
+
+ /**
+ * The number of dropped attributes. If the value is 0, then
+ * no attributes were dropped.
+ *
+ * Generated from protobuf field uint32 dropped_attributes_count = 2;
+ * @param int $var
+ * @return $this
+ */
+ public function setDroppedAttributesCount(int $var)
+ {
+ GPBUtil::checkUint32($var);
+ $this->dropped_attributes_count = $var;
+
+ return $this;
+ }
+
+ /**
+ * Set of entities that participate in this Resource.
+ * Note: keys in the references MUST exist in attributes of this message.
+ * Status: [Development]
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.common.v1.EntityRef entity_refs = 3;
+ * @return RepeatedField<\Opentelemetry\Proto\Common\V1\EntityRef>
+ */
+ public function getEntityRefs()
+ {
+ return $this->entity_refs;
+ }
+
+ /**
+ * Set of entities that participate in this Resource.
+ * Note: keys in the references MUST exist in attributes of this message.
+ * Status: [Development]
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.common.v1.EntityRef entity_refs = 3;
+ * @param \Opentelemetry\Proto\Common\V1\EntityRef[] $var
+ * @return $this
+ */
+ public function setEntityRefs(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Opentelemetry\Proto\Common\V1\EntityRef::class);
+ $this->entity_refs = $arr;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Trace/V1/ResourceSpans.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Trace/V1/ResourceSpans.php
new file mode 100644
index 000000000..147d9fdba
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Trace/V1/ResourceSpans.php
@@ -0,0 +1,169 @@
+opentelemetry.proto.trace.v1.ResourceSpans
+ */
+class ResourceSpans extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * The resource for the spans in this message.
+ * If this field is not set then no resource info is known.
+ *
+ * Generated from protobuf field .opentelemetry.proto.resource.v1.Resource resource = 1;
+ */
+ protected $resource = null;
+ /**
+ * A list of ScopeSpans that originate from a resource.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.trace.v1.ScopeSpans scope_spans = 2;
+ */
+ private $scope_spans;
+ /**
+ * The Schema URL, if known. This is the identifier of the Schema that the resource data
+ * is recorded in. Notably, the last part of the URL path is the version number of the
+ * schema: http[s]://server[:port]/path/. To learn more about Schema URL see
+ * https://opentelemetry.io/docs/specs/otel/schemas/#schema-url
+ * This schema_url applies to the data in the "resource" field. It does not apply
+ * to the data in the "scope_spans" field which have their own schema_url field.
+ *
+ * Generated from protobuf field string schema_url = 3;
+ */
+ protected $schema_url = '';
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type \Opentelemetry\Proto\Resource\V1\Resource $resource
+ * The resource for the spans in this message.
+ * If this field is not set then no resource info is known.
+ * @type \Opentelemetry\Proto\Trace\V1\ScopeSpans[] $scope_spans
+ * A list of ScopeSpans that originate from a resource.
+ * @type string $schema_url
+ * The Schema URL, if known. This is the identifier of the Schema that the resource data
+ * is recorded in. Notably, the last part of the URL path is the version number of the
+ * schema: http[s]://server[:port]/path/. To learn more about Schema URL see
+ * https://opentelemetry.io/docs/specs/otel/schemas/#schema-url
+ * This schema_url applies to the data in the "resource" field. It does not apply
+ * to the data in the "scope_spans" field which have their own schema_url field.
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Trace\V1\Trace::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * The resource for the spans in this message.
+ * If this field is not set then no resource info is known.
+ *
+ * Generated from protobuf field .opentelemetry.proto.resource.v1.Resource resource = 1;
+ * @return \Opentelemetry\Proto\Resource\V1\Resource|null
+ */
+ public function getResource()
+ {
+ return $this->resource;
+ }
+
+ public function hasResource()
+ {
+ return isset($this->resource);
+ }
+
+ public function clearResource()
+ {
+ unset($this->resource);
+ }
+
+ /**
+ * The resource for the spans in this message.
+ * If this field is not set then no resource info is known.
+ *
+ * Generated from protobuf field .opentelemetry.proto.resource.v1.Resource resource = 1;
+ * @param \Opentelemetry\Proto\Resource\V1\Resource $var
+ * @return $this
+ */
+ public function setResource(\Opentelemetry\Proto\Resource\V1\Resource|null $var)
+ {
+ $this->resource = $var;
+
+ return $this;
+ }
+
+ /**
+ * A list of ScopeSpans that originate from a resource.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.trace.v1.ScopeSpans scope_spans = 2;
+ * @return RepeatedField<\Opentelemetry\Proto\Trace\V1\ScopeSpans>
+ */
+ public function getScopeSpans()
+ {
+ return $this->scope_spans;
+ }
+
+ /**
+ * A list of ScopeSpans that originate from a resource.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.trace.v1.ScopeSpans scope_spans = 2;
+ * @param \Opentelemetry\Proto\Trace\V1\ScopeSpans[] $var
+ * @return $this
+ */
+ public function setScopeSpans(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Opentelemetry\Proto\Trace\V1\ScopeSpans::class);
+ $this->scope_spans = $arr;
+
+ return $this;
+ }
+
+ /**
+ * The Schema URL, if known. This is the identifier of the Schema that the resource data
+ * is recorded in. Notably, the last part of the URL path is the version number of the
+ * schema: http[s]://server[:port]/path/. To learn more about Schema URL see
+ * https://opentelemetry.io/docs/specs/otel/schemas/#schema-url
+ * This schema_url applies to the data in the "resource" field. It does not apply
+ * to the data in the "scope_spans" field which have their own schema_url field.
+ *
+ * Generated from protobuf field string schema_url = 3;
+ * @return string
+ */
+ public function getSchemaUrl()
+ {
+ return $this->schema_url;
+ }
+
+ /**
+ * The Schema URL, if known. This is the identifier of the Schema that the resource data
+ * is recorded in. Notably, the last part of the URL path is the version number of the
+ * schema: http[s]://server[:port]/path/. To learn more about Schema URL see
+ * https://opentelemetry.io/docs/specs/otel/schemas/#schema-url
+ * This schema_url applies to the data in the "resource" field. It does not apply
+ * to the data in the "scope_spans" field which have their own schema_url field.
+ *
+ * Generated from protobuf field string schema_url = 3;
+ * @param string $var
+ * @return $this
+ */
+ public function setSchemaUrl(string $var)
+ {
+ GPBUtil::checkString($var, true);
+ $this->schema_url = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Trace/V1/ScopeSpans.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Trace/V1/ScopeSpans.php
new file mode 100644
index 000000000..2da130e88
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Trace/V1/ScopeSpans.php
@@ -0,0 +1,173 @@
+opentelemetry.proto.trace.v1.ScopeSpans
+ */
+class ScopeSpans extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * The instrumentation scope information for the spans in this message.
+ * Semantically when InstrumentationScope isn't set, it is equivalent with
+ * an empty instrumentation scope name (unknown).
+ *
+ * Generated from protobuf field .opentelemetry.proto.common.v1.InstrumentationScope scope = 1;
+ */
+ protected $scope = null;
+ /**
+ * A list of Spans that originate from an instrumentation scope.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.trace.v1.Span spans = 2;
+ */
+ private $spans;
+ /**
+ * The Schema URL, if known. This is the identifier of the Schema that the span data
+ * is recorded in. Notably, the last part of the URL path is the version number of the
+ * schema: http[s]://server[:port]/path/. To learn more about Schema URL see
+ * https://opentelemetry.io/docs/specs/otel/schemas/#schema-url
+ * This schema_url applies to the data in the "scope" field and all spans and span
+ * events in the "spans" field.
+ *
+ * Generated from protobuf field string schema_url = 3;
+ */
+ protected $schema_url = '';
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type \Opentelemetry\Proto\Common\V1\InstrumentationScope $scope
+ * The instrumentation scope information for the spans in this message.
+ * Semantically when InstrumentationScope isn't set, it is equivalent with
+ * an empty instrumentation scope name (unknown).
+ * @type \Opentelemetry\Proto\Trace\V1\Span[] $spans
+ * A list of Spans that originate from an instrumentation scope.
+ * @type string $schema_url
+ * The Schema URL, if known. This is the identifier of the Schema that the span data
+ * is recorded in. Notably, the last part of the URL path is the version number of the
+ * schema: http[s]://server[:port]/path/. To learn more about Schema URL see
+ * https://opentelemetry.io/docs/specs/otel/schemas/#schema-url
+ * This schema_url applies to the data in the "scope" field and all spans and span
+ * events in the "spans" field.
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Trace\V1\Trace::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * The instrumentation scope information for the spans in this message.
+ * Semantically when InstrumentationScope isn't set, it is equivalent with
+ * an empty instrumentation scope name (unknown).
+ *
+ * Generated from protobuf field .opentelemetry.proto.common.v1.InstrumentationScope scope = 1;
+ * @return \Opentelemetry\Proto\Common\V1\InstrumentationScope|null
+ */
+ public function getScope()
+ {
+ return $this->scope;
+ }
+
+ public function hasScope()
+ {
+ return isset($this->scope);
+ }
+
+ public function clearScope()
+ {
+ unset($this->scope);
+ }
+
+ /**
+ * The instrumentation scope information for the spans in this message.
+ * Semantically when InstrumentationScope isn't set, it is equivalent with
+ * an empty instrumentation scope name (unknown).
+ *
+ * Generated from protobuf field .opentelemetry.proto.common.v1.InstrumentationScope scope = 1;
+ * @param \Opentelemetry\Proto\Common\V1\InstrumentationScope $var
+ * @return $this
+ */
+ public function setScope(\Opentelemetry\Proto\Common\V1\InstrumentationScope|null $var)
+ {
+ $this->scope = $var;
+
+ return $this;
+ }
+
+ /**
+ * A list of Spans that originate from an instrumentation scope.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.trace.v1.Span spans = 2;
+ * @return RepeatedField<\Opentelemetry\Proto\Trace\V1\Span>
+ */
+ public function getSpans()
+ {
+ return $this->spans;
+ }
+
+ /**
+ * A list of Spans that originate from an instrumentation scope.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.trace.v1.Span spans = 2;
+ * @param \Opentelemetry\Proto\Trace\V1\Span[] $var
+ * @return $this
+ */
+ public function setSpans(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Opentelemetry\Proto\Trace\V1\Span::class);
+ $this->spans = $arr;
+
+ return $this;
+ }
+
+ /**
+ * The Schema URL, if known. This is the identifier of the Schema that the span data
+ * is recorded in. Notably, the last part of the URL path is the version number of the
+ * schema: http[s]://server[:port]/path/. To learn more about Schema URL see
+ * https://opentelemetry.io/docs/specs/otel/schemas/#schema-url
+ * This schema_url applies to the data in the "scope" field and all spans and span
+ * events in the "spans" field.
+ *
+ * Generated from protobuf field string schema_url = 3;
+ * @return string
+ */
+ public function getSchemaUrl()
+ {
+ return $this->schema_url;
+ }
+
+ /**
+ * The Schema URL, if known. This is the identifier of the Schema that the span data
+ * is recorded in. Notably, the last part of the URL path is the version number of the
+ * schema: http[s]://server[:port]/path/. To learn more about Schema URL see
+ * https://opentelemetry.io/docs/specs/otel/schemas/#schema-url
+ * This schema_url applies to the data in the "scope" field and all spans and span
+ * events in the "spans" field.
+ *
+ * Generated from protobuf field string schema_url = 3;
+ * @param string $var
+ * @return $this
+ */
+ public function setSchemaUrl(string $var)
+ {
+ GPBUtil::checkString($var, true);
+ $this->schema_url = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Trace/V1/Span.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Trace/V1/Span.php
new file mode 100644
index 000000000..a429db070
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Trace/V1/Span.php
@@ -0,0 +1,816 @@
+opentelemetry.proto.trace.v1.Span
+ */
+class Span extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * A unique identifier for a trace. All spans from the same trace share
+ * the same `trace_id`. The ID is a 16-byte array. An ID with all zeroes OR
+ * of length other than 16 bytes is considered invalid (empty string in OTLP/JSON
+ * is zero-length and thus is also invalid).
+ * This field is required.
+ *
+ * Generated from protobuf field bytes trace_id = 1;
+ */
+ protected $trace_id = '';
+ /**
+ * A unique identifier for a span within a trace, assigned when the span
+ * is created. The ID is an 8-byte array. An ID with all zeroes OR of length
+ * other than 8 bytes is considered invalid (empty string in OTLP/JSON
+ * is zero-length and thus is also invalid).
+ * This field is required.
+ *
+ * Generated from protobuf field bytes span_id = 2;
+ */
+ protected $span_id = '';
+ /**
+ * trace_state conveys information about request position in multiple distributed tracing graphs.
+ * It is a trace_state in w3c-trace-context format: https://www.w3.org/TR/trace-context/#tracestate-header
+ * See also https://github.com/w3c/distributed-tracing for more details about this field.
+ *
+ * Generated from protobuf field string trace_state = 3;
+ */
+ protected $trace_state = '';
+ /**
+ * The `span_id` of this span's parent span. If this is a root span, then this
+ * field must be empty. The ID is an 8-byte array.
+ *
+ * Generated from protobuf field bytes parent_span_id = 4;
+ */
+ protected $parent_span_id = '';
+ /**
+ * Flags, a bit field.
+ * Bits 0-7 (8 least significant bits) are the trace flags as defined in W3C Trace
+ * Context specification. To read the 8-bit W3C trace flag, use
+ * `flags & SPAN_FLAGS_TRACE_FLAGS_MASK`.
+ * See https://www.w3.org/TR/trace-context-2/#trace-flags for the flag definitions.
+ * Bits 8 and 9 represent the 3 states of whether a span's parent
+ * is remote. The states are (unknown, is not remote, is remote).
+ * To read whether the value is known, use `(flags & SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK) != 0`.
+ * To read whether the span is remote, use `(flags & SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) != 0`.
+ * When creating span messages, if the message is logically forwarded from another source
+ * with an equivalent flags fields (i.e., usually another OTLP span message), the field SHOULD
+ * be copied as-is. If creating from a source that does not have an equivalent flags field
+ * (such as a runtime representation of an OpenTelemetry span), the high 22 bits MUST
+ * be set to zero.
+ * Readers MUST NOT assume that bits 10-31 (22 most significant bits) will be zero.
+ * [Optional].
+ *
+ * Generated from protobuf field fixed32 flags = 16;
+ */
+ protected $flags = 0;
+ /**
+ * A description of the span's operation.
+ * For example, the name can be a qualified method name or a file name
+ * and a line number where the operation is called. A best practice is to use
+ * the same display name at the same call point in an application.
+ * This makes it easier to correlate spans in different traces.
+ * This field is semantically required to be set to non-empty string.
+ * Empty value is equivalent to an unknown span name.
+ * This field is required.
+ *
+ * Generated from protobuf field string name = 5;
+ */
+ protected $name = '';
+ /**
+ * Distinguishes between spans generated in a particular context. For example,
+ * two spans with the same name may be distinguished using `CLIENT` (caller)
+ * and `SERVER` (callee) to identify queueing latency associated with the span.
+ *
+ * Generated from protobuf field .opentelemetry.proto.trace.v1.Span.SpanKind kind = 6;
+ */
+ protected $kind = 0;
+ /**
+ * The start time of the span. On the client side, this is the time
+ * kept by the local machine where the span execution starts. On the server side, this
+ * is the time when the server's application handler starts running.
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970.
+ * This field is semantically required and it is expected that end_time >= start_time.
+ *
+ * Generated from protobuf field fixed64 start_time_unix_nano = 7;
+ */
+ protected $start_time_unix_nano = 0;
+ /**
+ * The end time of the span. On the client side, this is the time
+ * kept by the local machine where the span execution ends. On the server side, this
+ * is the time when the server application handler stops running.
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970.
+ * This field is semantically required and it is expected that end_time >= start_time.
+ *
+ * Generated from protobuf field fixed64 end_time_unix_nano = 8;
+ */
+ protected $end_time_unix_nano = 0;
+ /**
+ * A collection of key/value pairs. Note, global attributes
+ * like server name can be set using the resource API. Examples of attributes:
+ * "/http/user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36"
+ * "/http/server_latency": 300
+ * "example.com/myattribute": true
+ * "example.com/score": 10.239
+ * Attribute keys MUST be unique (it is not allowed to have more than one
+ * attribute with the same key).
+ * The behavior of software that receives duplicated keys can be unpredictable.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.common.v1.KeyValue attributes = 9;
+ */
+ private $attributes;
+ /**
+ * The number of attributes that were discarded. Attributes
+ * can be discarded because their keys are too long or because there are too many
+ * attributes. If this value is 0, then no attributes were dropped.
+ *
+ * Generated from protobuf field uint32 dropped_attributes_count = 10;
+ */
+ protected $dropped_attributes_count = 0;
+ /**
+ * A collection of Event items.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.trace.v1.Span.Event events = 11;
+ */
+ private $events;
+ /**
+ * The number of dropped events. If the value is 0, then no
+ * events were dropped.
+ *
+ * Generated from protobuf field uint32 dropped_events_count = 12;
+ */
+ protected $dropped_events_count = 0;
+ /**
+ * A collection of Links, which are references from this span to a span
+ * in the same or different trace.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.trace.v1.Span.Link links = 13;
+ */
+ private $links;
+ /**
+ * The number of dropped links after the maximum size was
+ * enforced. If this value is 0, then no links were dropped.
+ *
+ * Generated from protobuf field uint32 dropped_links_count = 14;
+ */
+ protected $dropped_links_count = 0;
+ /**
+ * An optional final status for this span. Semantically when Status isn't set, it means
+ * span's status code is unset, i.e. assume STATUS_CODE_UNSET (code = 0).
+ *
+ * Generated from protobuf field .opentelemetry.proto.trace.v1.Status status = 15;
+ */
+ protected $status = null;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type string $trace_id
+ * A unique identifier for a trace. All spans from the same trace share
+ * the same `trace_id`. The ID is a 16-byte array. An ID with all zeroes OR
+ * of length other than 16 bytes is considered invalid (empty string in OTLP/JSON
+ * is zero-length and thus is also invalid).
+ * This field is required.
+ * @type string $span_id
+ * A unique identifier for a span within a trace, assigned when the span
+ * is created. The ID is an 8-byte array. An ID with all zeroes OR of length
+ * other than 8 bytes is considered invalid (empty string in OTLP/JSON
+ * is zero-length and thus is also invalid).
+ * This field is required.
+ * @type string $trace_state
+ * trace_state conveys information about request position in multiple distributed tracing graphs.
+ * It is a trace_state in w3c-trace-context format: https://www.w3.org/TR/trace-context/#tracestate-header
+ * See also https://github.com/w3c/distributed-tracing for more details about this field.
+ * @type string $parent_span_id
+ * The `span_id` of this span's parent span. If this is a root span, then this
+ * field must be empty. The ID is an 8-byte array.
+ * @type int $flags
+ * Flags, a bit field.
+ * Bits 0-7 (8 least significant bits) are the trace flags as defined in W3C Trace
+ * Context specification. To read the 8-bit W3C trace flag, use
+ * `flags & SPAN_FLAGS_TRACE_FLAGS_MASK`.
+ * See https://www.w3.org/TR/trace-context-2/#trace-flags for the flag definitions.
+ * Bits 8 and 9 represent the 3 states of whether a span's parent
+ * is remote. The states are (unknown, is not remote, is remote).
+ * To read whether the value is known, use `(flags & SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK) != 0`.
+ * To read whether the span is remote, use `(flags & SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) != 0`.
+ * When creating span messages, if the message is logically forwarded from another source
+ * with an equivalent flags fields (i.e., usually another OTLP span message), the field SHOULD
+ * be copied as-is. If creating from a source that does not have an equivalent flags field
+ * (such as a runtime representation of an OpenTelemetry span), the high 22 bits MUST
+ * be set to zero.
+ * Readers MUST NOT assume that bits 10-31 (22 most significant bits) will be zero.
+ * [Optional].
+ * @type string $name
+ * A description of the span's operation.
+ * For example, the name can be a qualified method name or a file name
+ * and a line number where the operation is called. A best practice is to use
+ * the same display name at the same call point in an application.
+ * This makes it easier to correlate spans in different traces.
+ * This field is semantically required to be set to non-empty string.
+ * Empty value is equivalent to an unknown span name.
+ * This field is required.
+ * @type int $kind
+ * Distinguishes between spans generated in a particular context. For example,
+ * two spans with the same name may be distinguished using `CLIENT` (caller)
+ * and `SERVER` (callee) to identify queueing latency associated with the span.
+ * @type int|string $start_time_unix_nano
+ * The start time of the span. On the client side, this is the time
+ * kept by the local machine where the span execution starts. On the server side, this
+ * is the time when the server's application handler starts running.
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970.
+ * This field is semantically required and it is expected that end_time >= start_time.
+ * @type int|string $end_time_unix_nano
+ * The end time of the span. On the client side, this is the time
+ * kept by the local machine where the span execution ends. On the server side, this
+ * is the time when the server application handler stops running.
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970.
+ * This field is semantically required and it is expected that end_time >= start_time.
+ * @type \Opentelemetry\Proto\Common\V1\KeyValue[] $attributes
+ * A collection of key/value pairs. Note, global attributes
+ * like server name can be set using the resource API. Examples of attributes:
+ * "/http/user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36"
+ * "/http/server_latency": 300
+ * "example.com/myattribute": true
+ * "example.com/score": 10.239
+ * Attribute keys MUST be unique (it is not allowed to have more than one
+ * attribute with the same key).
+ * The behavior of software that receives duplicated keys can be unpredictable.
+ * @type int $dropped_attributes_count
+ * The number of attributes that were discarded. Attributes
+ * can be discarded because their keys are too long or because there are too many
+ * attributes. If this value is 0, then no attributes were dropped.
+ * @type \Opentelemetry\Proto\Trace\V1\Span\Event[] $events
+ * A collection of Event items.
+ * @type int $dropped_events_count
+ * The number of dropped events. If the value is 0, then no
+ * events were dropped.
+ * @type \Opentelemetry\Proto\Trace\V1\Span\Link[] $links
+ * A collection of Links, which are references from this span to a span
+ * in the same or different trace.
+ * @type int $dropped_links_count
+ * The number of dropped links after the maximum size was
+ * enforced. If this value is 0, then no links were dropped.
+ * @type \Opentelemetry\Proto\Trace\V1\Status $status
+ * An optional final status for this span. Semantically when Status isn't set, it means
+ * span's status code is unset, i.e. assume STATUS_CODE_UNSET (code = 0).
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Trace\V1\Trace::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * A unique identifier for a trace. All spans from the same trace share
+ * the same `trace_id`. The ID is a 16-byte array. An ID with all zeroes OR
+ * of length other than 16 bytes is considered invalid (empty string in OTLP/JSON
+ * is zero-length and thus is also invalid).
+ * This field is required.
+ *
+ * Generated from protobuf field bytes trace_id = 1;
+ * @return string
+ */
+ public function getTraceId()
+ {
+ return $this->trace_id;
+ }
+
+ /**
+ * A unique identifier for a trace. All spans from the same trace share
+ * the same `trace_id`. The ID is a 16-byte array. An ID with all zeroes OR
+ * of length other than 16 bytes is considered invalid (empty string in OTLP/JSON
+ * is zero-length and thus is also invalid).
+ * This field is required.
+ *
+ * Generated from protobuf field bytes trace_id = 1;
+ * @param string $var
+ * @return $this
+ */
+ public function setTraceId(string $var)
+ {
+ GPBUtil::checkString($var, false);
+ $this->trace_id = $var;
+
+ return $this;
+ }
+
+ /**
+ * A unique identifier for a span within a trace, assigned when the span
+ * is created. The ID is an 8-byte array. An ID with all zeroes OR of length
+ * other than 8 bytes is considered invalid (empty string in OTLP/JSON
+ * is zero-length and thus is also invalid).
+ * This field is required.
+ *
+ * Generated from protobuf field bytes span_id = 2;
+ * @return string
+ */
+ public function getSpanId()
+ {
+ return $this->span_id;
+ }
+
+ /**
+ * A unique identifier for a span within a trace, assigned when the span
+ * is created. The ID is an 8-byte array. An ID with all zeroes OR of length
+ * other than 8 bytes is considered invalid (empty string in OTLP/JSON
+ * is zero-length and thus is also invalid).
+ * This field is required.
+ *
+ * Generated from protobuf field bytes span_id = 2;
+ * @param string $var
+ * @return $this
+ */
+ public function setSpanId(string $var)
+ {
+ GPBUtil::checkString($var, false);
+ $this->span_id = $var;
+
+ return $this;
+ }
+
+ /**
+ * trace_state conveys information about request position in multiple distributed tracing graphs.
+ * It is a trace_state in w3c-trace-context format: https://www.w3.org/TR/trace-context/#tracestate-header
+ * See also https://github.com/w3c/distributed-tracing for more details about this field.
+ *
+ * Generated from protobuf field string trace_state = 3;
+ * @return string
+ */
+ public function getTraceState()
+ {
+ return $this->trace_state;
+ }
+
+ /**
+ * trace_state conveys information about request position in multiple distributed tracing graphs.
+ * It is a trace_state in w3c-trace-context format: https://www.w3.org/TR/trace-context/#tracestate-header
+ * See also https://github.com/w3c/distributed-tracing for more details about this field.
+ *
+ * Generated from protobuf field string trace_state = 3;
+ * @param string $var
+ * @return $this
+ */
+ public function setTraceState(string $var)
+ {
+ GPBUtil::checkString($var, true);
+ $this->trace_state = $var;
+
+ return $this;
+ }
+
+ /**
+ * The `span_id` of this span's parent span. If this is a root span, then this
+ * field must be empty. The ID is an 8-byte array.
+ *
+ * Generated from protobuf field bytes parent_span_id = 4;
+ * @return string
+ */
+ public function getParentSpanId()
+ {
+ return $this->parent_span_id;
+ }
+
+ /**
+ * The `span_id` of this span's parent span. If this is a root span, then this
+ * field must be empty. The ID is an 8-byte array.
+ *
+ * Generated from protobuf field bytes parent_span_id = 4;
+ * @param string $var
+ * @return $this
+ */
+ public function setParentSpanId(string $var)
+ {
+ GPBUtil::checkString($var, false);
+ $this->parent_span_id = $var;
+
+ return $this;
+ }
+
+ /**
+ * Flags, a bit field.
+ * Bits 0-7 (8 least significant bits) are the trace flags as defined in W3C Trace
+ * Context specification. To read the 8-bit W3C trace flag, use
+ * `flags & SPAN_FLAGS_TRACE_FLAGS_MASK`.
+ * See https://www.w3.org/TR/trace-context-2/#trace-flags for the flag definitions.
+ * Bits 8 and 9 represent the 3 states of whether a span's parent
+ * is remote. The states are (unknown, is not remote, is remote).
+ * To read whether the value is known, use `(flags & SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK) != 0`.
+ * To read whether the span is remote, use `(flags & SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) != 0`.
+ * When creating span messages, if the message is logically forwarded from another source
+ * with an equivalent flags fields (i.e., usually another OTLP span message), the field SHOULD
+ * be copied as-is. If creating from a source that does not have an equivalent flags field
+ * (such as a runtime representation of an OpenTelemetry span), the high 22 bits MUST
+ * be set to zero.
+ * Readers MUST NOT assume that bits 10-31 (22 most significant bits) will be zero.
+ * [Optional].
+ *
+ * Generated from protobuf field fixed32 flags = 16;
+ * @return int
+ */
+ public function getFlags()
+ {
+ return $this->flags;
+ }
+
+ /**
+ * Flags, a bit field.
+ * Bits 0-7 (8 least significant bits) are the trace flags as defined in W3C Trace
+ * Context specification. To read the 8-bit W3C trace flag, use
+ * `flags & SPAN_FLAGS_TRACE_FLAGS_MASK`.
+ * See https://www.w3.org/TR/trace-context-2/#trace-flags for the flag definitions.
+ * Bits 8 and 9 represent the 3 states of whether a span's parent
+ * is remote. The states are (unknown, is not remote, is remote).
+ * To read whether the value is known, use `(flags & SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK) != 0`.
+ * To read whether the span is remote, use `(flags & SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) != 0`.
+ * When creating span messages, if the message is logically forwarded from another source
+ * with an equivalent flags fields (i.e., usually another OTLP span message), the field SHOULD
+ * be copied as-is. If creating from a source that does not have an equivalent flags field
+ * (such as a runtime representation of an OpenTelemetry span), the high 22 bits MUST
+ * be set to zero.
+ * Readers MUST NOT assume that bits 10-31 (22 most significant bits) will be zero.
+ * [Optional].
+ *
+ * Generated from protobuf field fixed32 flags = 16;
+ * @param int $var
+ * @return $this
+ */
+ public function setFlags(int $var)
+ {
+ GPBUtil::checkUint32($var);
+ $this->flags = $var;
+
+ return $this;
+ }
+
+ /**
+ * A description of the span's operation.
+ * For example, the name can be a qualified method name or a file name
+ * and a line number where the operation is called. A best practice is to use
+ * the same display name at the same call point in an application.
+ * This makes it easier to correlate spans in different traces.
+ * This field is semantically required to be set to non-empty string.
+ * Empty value is equivalent to an unknown span name.
+ * This field is required.
+ *
+ * Generated from protobuf field string name = 5;
+ * @return string
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * A description of the span's operation.
+ * For example, the name can be a qualified method name or a file name
+ * and a line number where the operation is called. A best practice is to use
+ * the same display name at the same call point in an application.
+ * This makes it easier to correlate spans in different traces.
+ * This field is semantically required to be set to non-empty string.
+ * Empty value is equivalent to an unknown span name.
+ * This field is required.
+ *
+ * Generated from protobuf field string name = 5;
+ * @param string $var
+ * @return $this
+ */
+ public function setName(string $var)
+ {
+ GPBUtil::checkString($var, true);
+ $this->name = $var;
+
+ return $this;
+ }
+
+ /**
+ * Distinguishes between spans generated in a particular context. For example,
+ * two spans with the same name may be distinguished using `CLIENT` (caller)
+ * and `SERVER` (callee) to identify queueing latency associated with the span.
+ *
+ * Generated from protobuf field .opentelemetry.proto.trace.v1.Span.SpanKind kind = 6;
+ * @return int one of the values in {@see \Opentelemetry\Proto\Trace\V1\Span\SpanKind}
+ */
+ public function getKind()
+ {
+ return $this->kind;
+ }
+
+ /**
+ * Distinguishes between spans generated in a particular context. For example,
+ * two spans with the same name may be distinguished using `CLIENT` (caller)
+ * and `SERVER` (callee) to identify queueing latency associated with the span.
+ *
+ * Generated from protobuf field .opentelemetry.proto.trace.v1.Span.SpanKind kind = 6;
+ * @param int $var one of the values in {@see \Opentelemetry\Proto\Trace\V1\Span\SpanKind}
+ * @return $this
+ */
+ public function setKind(int $var)
+ {
+ GPBUtil::checkEnum($var, \Opentelemetry\Proto\Trace\V1\Span\SpanKind::class);
+ $this->kind = $var;
+
+ return $this;
+ }
+
+ /**
+ * The start time of the span. On the client side, this is the time
+ * kept by the local machine where the span execution starts. On the server side, this
+ * is the time when the server's application handler starts running.
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970.
+ * This field is semantically required and it is expected that end_time >= start_time.
+ *
+ * Generated from protobuf field fixed64 start_time_unix_nano = 7;
+ * @return int|string
+ */
+ public function getStartTimeUnixNano()
+ {
+ return $this->start_time_unix_nano;
+ }
+
+ /**
+ * The start time of the span. On the client side, this is the time
+ * kept by the local machine where the span execution starts. On the server side, this
+ * is the time when the server's application handler starts running.
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970.
+ * This field is semantically required and it is expected that end_time >= start_time.
+ *
+ * Generated from protobuf field fixed64 start_time_unix_nano = 7;
+ * @param int|string $var
+ * @return $this
+ */
+ public function setStartTimeUnixNano(int|string $var)
+ {
+ GPBUtil::checkUint64($var);
+ $this->start_time_unix_nano = $var;
+
+ return $this;
+ }
+
+ /**
+ * The end time of the span. On the client side, this is the time
+ * kept by the local machine where the span execution ends. On the server side, this
+ * is the time when the server application handler stops running.
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970.
+ * This field is semantically required and it is expected that end_time >= start_time.
+ *
+ * Generated from protobuf field fixed64 end_time_unix_nano = 8;
+ * @return int|string
+ */
+ public function getEndTimeUnixNano()
+ {
+ return $this->end_time_unix_nano;
+ }
+
+ /**
+ * The end time of the span. On the client side, this is the time
+ * kept by the local machine where the span execution ends. On the server side, this
+ * is the time when the server application handler stops running.
+ * Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970.
+ * This field is semantically required and it is expected that end_time >= start_time.
+ *
+ * Generated from protobuf field fixed64 end_time_unix_nano = 8;
+ * @param int|string $var
+ * @return $this
+ */
+ public function setEndTimeUnixNano(int|string $var)
+ {
+ GPBUtil::checkUint64($var);
+ $this->end_time_unix_nano = $var;
+
+ return $this;
+ }
+
+ /**
+ * A collection of key/value pairs. Note, global attributes
+ * like server name can be set using the resource API. Examples of attributes:
+ * "/http/user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36"
+ * "/http/server_latency": 300
+ * "example.com/myattribute": true
+ * "example.com/score": 10.239
+ * Attribute keys MUST be unique (it is not allowed to have more than one
+ * attribute with the same key).
+ * The behavior of software that receives duplicated keys can be unpredictable.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.common.v1.KeyValue attributes = 9;
+ * @return RepeatedField<\Opentelemetry\Proto\Common\V1\KeyValue>
+ */
+ public function getAttributes()
+ {
+ return $this->attributes;
+ }
+
+ /**
+ * A collection of key/value pairs. Note, global attributes
+ * like server name can be set using the resource API. Examples of attributes:
+ * "/http/user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36"
+ * "/http/server_latency": 300
+ * "example.com/myattribute": true
+ * "example.com/score": 10.239
+ * Attribute keys MUST be unique (it is not allowed to have more than one
+ * attribute with the same key).
+ * The behavior of software that receives duplicated keys can be unpredictable.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.common.v1.KeyValue attributes = 9;
+ * @param \Opentelemetry\Proto\Common\V1\KeyValue[] $var
+ * @return $this
+ */
+ public function setAttributes(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Opentelemetry\Proto\Common\V1\KeyValue::class);
+ $this->attributes = $arr;
+
+ return $this;
+ }
+
+ /**
+ * The number of attributes that were discarded. Attributes
+ * can be discarded because their keys are too long or because there are too many
+ * attributes. If this value is 0, then no attributes were dropped.
+ *
+ * Generated from protobuf field uint32 dropped_attributes_count = 10;
+ * @return int
+ */
+ public function getDroppedAttributesCount()
+ {
+ return $this->dropped_attributes_count;
+ }
+
+ /**
+ * The number of attributes that were discarded. Attributes
+ * can be discarded because their keys are too long or because there are too many
+ * attributes. If this value is 0, then no attributes were dropped.
+ *
+ * Generated from protobuf field uint32 dropped_attributes_count = 10;
+ * @param int $var
+ * @return $this
+ */
+ public function setDroppedAttributesCount(int $var)
+ {
+ GPBUtil::checkUint32($var);
+ $this->dropped_attributes_count = $var;
+
+ return $this;
+ }
+
+ /**
+ * A collection of Event items.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.trace.v1.Span.Event events = 11;
+ * @return RepeatedField<\Opentelemetry\Proto\Trace\V1\Span\Event>
+ */
+ public function getEvents()
+ {
+ return $this->events;
+ }
+
+ /**
+ * A collection of Event items.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.trace.v1.Span.Event events = 11;
+ * @param \Opentelemetry\Proto\Trace\V1\Span\Event[] $var
+ * @return $this
+ */
+ public function setEvents(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Opentelemetry\Proto\Trace\V1\Span\Event::class);
+ $this->events = $arr;
+
+ return $this;
+ }
+
+ /**
+ * The number of dropped events. If the value is 0, then no
+ * events were dropped.
+ *
+ * Generated from protobuf field uint32 dropped_events_count = 12;
+ * @return int
+ */
+ public function getDroppedEventsCount()
+ {
+ return $this->dropped_events_count;
+ }
+
+ /**
+ * The number of dropped events. If the value is 0, then no
+ * events were dropped.
+ *
+ * Generated from protobuf field uint32 dropped_events_count = 12;
+ * @param int $var
+ * @return $this
+ */
+ public function setDroppedEventsCount(int $var)
+ {
+ GPBUtil::checkUint32($var);
+ $this->dropped_events_count = $var;
+
+ return $this;
+ }
+
+ /**
+ * A collection of Links, which are references from this span to a span
+ * in the same or different trace.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.trace.v1.Span.Link links = 13;
+ * @return RepeatedField<\Opentelemetry\Proto\Trace\V1\Span\Link>
+ */
+ public function getLinks()
+ {
+ return $this->links;
+ }
+
+ /**
+ * A collection of Links, which are references from this span to a span
+ * in the same or different trace.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.trace.v1.Span.Link links = 13;
+ * @param \Opentelemetry\Proto\Trace\V1\Span\Link[] $var
+ * @return $this
+ */
+ public function setLinks(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Opentelemetry\Proto\Trace\V1\Span\Link::class);
+ $this->links = $arr;
+
+ return $this;
+ }
+
+ /**
+ * The number of dropped links after the maximum size was
+ * enforced. If this value is 0, then no links were dropped.
+ *
+ * Generated from protobuf field uint32 dropped_links_count = 14;
+ * @return int
+ */
+ public function getDroppedLinksCount()
+ {
+ return $this->dropped_links_count;
+ }
+
+ /**
+ * The number of dropped links after the maximum size was
+ * enforced. If this value is 0, then no links were dropped.
+ *
+ * Generated from protobuf field uint32 dropped_links_count = 14;
+ * @param int $var
+ * @return $this
+ */
+ public function setDroppedLinksCount(int $var)
+ {
+ GPBUtil::checkUint32($var);
+ $this->dropped_links_count = $var;
+
+ return $this;
+ }
+
+ /**
+ * An optional final status for this span. Semantically when Status isn't set, it means
+ * span's status code is unset, i.e. assume STATUS_CODE_UNSET (code = 0).
+ *
+ * Generated from protobuf field .opentelemetry.proto.trace.v1.Status status = 15;
+ * @return \Opentelemetry\Proto\Trace\V1\Status|null
+ */
+ public function getStatus()
+ {
+ return $this->status;
+ }
+
+ public function hasStatus()
+ {
+ return isset($this->status);
+ }
+
+ public function clearStatus()
+ {
+ unset($this->status);
+ }
+
+ /**
+ * An optional final status for this span. Semantically when Status isn't set, it means
+ * span's status code is unset, i.e. assume STATUS_CODE_UNSET (code = 0).
+ *
+ * Generated from protobuf field .opentelemetry.proto.trace.v1.Status status = 15;
+ * @param \Opentelemetry\Proto\Trace\V1\Status $var
+ * @return $this
+ */
+ public function setStatus(\Opentelemetry\Proto\Trace\V1\Status|null $var)
+ {
+ $this->status = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Trace/V1/Span/Event.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Trace/V1/Span/Event.php
new file mode 100644
index 000000000..f84d48016
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Trace/V1/Span/Event.php
@@ -0,0 +1,191 @@
+opentelemetry.proto.trace.v1.Span.Event
+ */
+class Event extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * The time the event occurred.
+ *
+ * Generated from protobuf field fixed64 time_unix_nano = 1;
+ */
+ protected $time_unix_nano = 0;
+ /**
+ * The name of the event.
+ * This field is semantically required to be set to non-empty string.
+ *
+ * Generated from protobuf field string name = 2;
+ */
+ protected $name = '';
+ /**
+ * A collection of attribute key/value pairs on the event.
+ * Attribute keys MUST be unique (it is not allowed to have more than one
+ * attribute with the same key).
+ * The behavior of software that receives duplicated keys can be unpredictable.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.common.v1.KeyValue attributes = 3;
+ */
+ private $attributes;
+ /**
+ * The number of dropped attributes. If the value is 0,
+ * then no attributes were dropped.
+ *
+ * Generated from protobuf field uint32 dropped_attributes_count = 4;
+ */
+ protected $dropped_attributes_count = 0;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type int|string $time_unix_nano
+ * The time the event occurred.
+ * @type string $name
+ * The name of the event.
+ * This field is semantically required to be set to non-empty string.
+ * @type \Opentelemetry\Proto\Common\V1\KeyValue[] $attributes
+ * A collection of attribute key/value pairs on the event.
+ * Attribute keys MUST be unique (it is not allowed to have more than one
+ * attribute with the same key).
+ * The behavior of software that receives duplicated keys can be unpredictable.
+ * @type int $dropped_attributes_count
+ * The number of dropped attributes. If the value is 0,
+ * then no attributes were dropped.
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Trace\V1\Trace::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * The time the event occurred.
+ *
+ * Generated from protobuf field fixed64 time_unix_nano = 1;
+ * @return int|string
+ */
+ public function getTimeUnixNano()
+ {
+ return $this->time_unix_nano;
+ }
+
+ /**
+ * The time the event occurred.
+ *
+ * Generated from protobuf field fixed64 time_unix_nano = 1;
+ * @param int|string $var
+ * @return $this
+ */
+ public function setTimeUnixNano(int|string $var)
+ {
+ GPBUtil::checkUint64($var);
+ $this->time_unix_nano = $var;
+
+ return $this;
+ }
+
+ /**
+ * The name of the event.
+ * This field is semantically required to be set to non-empty string.
+ *
+ * Generated from protobuf field string name = 2;
+ * @return string
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * The name of the event.
+ * This field is semantically required to be set to non-empty string.
+ *
+ * Generated from protobuf field string name = 2;
+ * @param string $var
+ * @return $this
+ */
+ public function setName(string $var)
+ {
+ GPBUtil::checkString($var, true);
+ $this->name = $var;
+
+ return $this;
+ }
+
+ /**
+ * A collection of attribute key/value pairs on the event.
+ * Attribute keys MUST be unique (it is not allowed to have more than one
+ * attribute with the same key).
+ * The behavior of software that receives duplicated keys can be unpredictable.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.common.v1.KeyValue attributes = 3;
+ * @return RepeatedField<\Opentelemetry\Proto\Common\V1\KeyValue>
+ */
+ public function getAttributes()
+ {
+ return $this->attributes;
+ }
+
+ /**
+ * A collection of attribute key/value pairs on the event.
+ * Attribute keys MUST be unique (it is not allowed to have more than one
+ * attribute with the same key).
+ * The behavior of software that receives duplicated keys can be unpredictable.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.common.v1.KeyValue attributes = 3;
+ * @param \Opentelemetry\Proto\Common\V1\KeyValue[] $var
+ * @return $this
+ */
+ public function setAttributes(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Opentelemetry\Proto\Common\V1\KeyValue::class);
+ $this->attributes = $arr;
+
+ return $this;
+ }
+
+ /**
+ * The number of dropped attributes. If the value is 0,
+ * then no attributes were dropped.
+ *
+ * Generated from protobuf field uint32 dropped_attributes_count = 4;
+ * @return int
+ */
+ public function getDroppedAttributesCount()
+ {
+ return $this->dropped_attributes_count;
+ }
+
+ /**
+ * The number of dropped attributes. If the value is 0,
+ * then no attributes were dropped.
+ *
+ * Generated from protobuf field uint32 dropped_attributes_count = 4;
+ * @param int $var
+ * @return $this
+ */
+ public function setDroppedAttributesCount(int $var)
+ {
+ GPBUtil::checkUint32($var);
+ $this->dropped_attributes_count = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Trace/V1/Span/Link.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Trace/V1/Span/Link.php
new file mode 100644
index 000000000..4e099f844
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Trace/V1/Span/Link.php
@@ -0,0 +1,305 @@
+opentelemetry.proto.trace.v1.Span.Link
+ */
+class Link extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * A unique identifier of a trace that this linked span is part of. The ID is a
+ * 16-byte array.
+ *
+ * Generated from protobuf field bytes trace_id = 1;
+ */
+ protected $trace_id = '';
+ /**
+ * A unique identifier for the linked span. The ID is an 8-byte array.
+ *
+ * Generated from protobuf field bytes span_id = 2;
+ */
+ protected $span_id = '';
+ /**
+ * The trace_state associated with the link.
+ *
+ * Generated from protobuf field string trace_state = 3;
+ */
+ protected $trace_state = '';
+ /**
+ * A collection of attribute key/value pairs on the link.
+ * Attribute keys MUST be unique (it is not allowed to have more than one
+ * attribute with the same key).
+ * The behavior of software that receives duplicated keys can be unpredictable.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.common.v1.KeyValue attributes = 4;
+ */
+ private $attributes;
+ /**
+ * The number of dropped attributes. If the value is 0,
+ * then no attributes were dropped.
+ *
+ * Generated from protobuf field uint32 dropped_attributes_count = 5;
+ */
+ protected $dropped_attributes_count = 0;
+ /**
+ * Flags, a bit field.
+ * Bits 0-7 (8 least significant bits) are the trace flags as defined in W3C Trace
+ * Context specification. To read the 8-bit W3C trace flag, use
+ * `flags & SPAN_FLAGS_TRACE_FLAGS_MASK`.
+ * See https://www.w3.org/TR/trace-context-2/#trace-flags for the flag definitions.
+ * Bits 8 and 9 represent the 3 states of whether the link is remote.
+ * The states are (unknown, is not remote, is remote).
+ * To read whether the value is known, use `(flags & SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK) != 0`.
+ * To read whether the link is remote, use `(flags & SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) != 0`.
+ * Readers MUST NOT assume that bits 10-31 (22 most significant bits) will be zero.
+ * When creating new spans, bits 10-31 (most-significant 22-bits) MUST be zero.
+ * [Optional].
+ *
+ * Generated from protobuf field fixed32 flags = 6;
+ */
+ protected $flags = 0;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type string $trace_id
+ * A unique identifier of a trace that this linked span is part of. The ID is a
+ * 16-byte array.
+ * @type string $span_id
+ * A unique identifier for the linked span. The ID is an 8-byte array.
+ * @type string $trace_state
+ * The trace_state associated with the link.
+ * @type \Opentelemetry\Proto\Common\V1\KeyValue[] $attributes
+ * A collection of attribute key/value pairs on the link.
+ * Attribute keys MUST be unique (it is not allowed to have more than one
+ * attribute with the same key).
+ * The behavior of software that receives duplicated keys can be unpredictable.
+ * @type int $dropped_attributes_count
+ * The number of dropped attributes. If the value is 0,
+ * then no attributes were dropped.
+ * @type int $flags
+ * Flags, a bit field.
+ * Bits 0-7 (8 least significant bits) are the trace flags as defined in W3C Trace
+ * Context specification. To read the 8-bit W3C trace flag, use
+ * `flags & SPAN_FLAGS_TRACE_FLAGS_MASK`.
+ * See https://www.w3.org/TR/trace-context-2/#trace-flags for the flag definitions.
+ * Bits 8 and 9 represent the 3 states of whether the link is remote.
+ * The states are (unknown, is not remote, is remote).
+ * To read whether the value is known, use `(flags & SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK) != 0`.
+ * To read whether the link is remote, use `(flags & SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) != 0`.
+ * Readers MUST NOT assume that bits 10-31 (22 most significant bits) will be zero.
+ * When creating new spans, bits 10-31 (most-significant 22-bits) MUST be zero.
+ * [Optional].
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Trace\V1\Trace::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * A unique identifier of a trace that this linked span is part of. The ID is a
+ * 16-byte array.
+ *
+ * Generated from protobuf field bytes trace_id = 1;
+ * @return string
+ */
+ public function getTraceId()
+ {
+ return $this->trace_id;
+ }
+
+ /**
+ * A unique identifier of a trace that this linked span is part of. The ID is a
+ * 16-byte array.
+ *
+ * Generated from protobuf field bytes trace_id = 1;
+ * @param string $var
+ * @return $this
+ */
+ public function setTraceId(string $var)
+ {
+ GPBUtil::checkString($var, false);
+ $this->trace_id = $var;
+
+ return $this;
+ }
+
+ /**
+ * A unique identifier for the linked span. The ID is an 8-byte array.
+ *
+ * Generated from protobuf field bytes span_id = 2;
+ * @return string
+ */
+ public function getSpanId()
+ {
+ return $this->span_id;
+ }
+
+ /**
+ * A unique identifier for the linked span. The ID is an 8-byte array.
+ *
+ * Generated from protobuf field bytes span_id = 2;
+ * @param string $var
+ * @return $this
+ */
+ public function setSpanId(string $var)
+ {
+ GPBUtil::checkString($var, false);
+ $this->span_id = $var;
+
+ return $this;
+ }
+
+ /**
+ * The trace_state associated with the link.
+ *
+ * Generated from protobuf field string trace_state = 3;
+ * @return string
+ */
+ public function getTraceState()
+ {
+ return $this->trace_state;
+ }
+
+ /**
+ * The trace_state associated with the link.
+ *
+ * Generated from protobuf field string trace_state = 3;
+ * @param string $var
+ * @return $this
+ */
+ public function setTraceState(string $var)
+ {
+ GPBUtil::checkString($var, true);
+ $this->trace_state = $var;
+
+ return $this;
+ }
+
+ /**
+ * A collection of attribute key/value pairs on the link.
+ * Attribute keys MUST be unique (it is not allowed to have more than one
+ * attribute with the same key).
+ * The behavior of software that receives duplicated keys can be unpredictable.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.common.v1.KeyValue attributes = 4;
+ * @return RepeatedField<\Opentelemetry\Proto\Common\V1\KeyValue>
+ */
+ public function getAttributes()
+ {
+ return $this->attributes;
+ }
+
+ /**
+ * A collection of attribute key/value pairs on the link.
+ * Attribute keys MUST be unique (it is not allowed to have more than one
+ * attribute with the same key).
+ * The behavior of software that receives duplicated keys can be unpredictable.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.common.v1.KeyValue attributes = 4;
+ * @param \Opentelemetry\Proto\Common\V1\KeyValue[] $var
+ * @return $this
+ */
+ public function setAttributes(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Opentelemetry\Proto\Common\V1\KeyValue::class);
+ $this->attributes = $arr;
+
+ return $this;
+ }
+
+ /**
+ * The number of dropped attributes. If the value is 0,
+ * then no attributes were dropped.
+ *
+ * Generated from protobuf field uint32 dropped_attributes_count = 5;
+ * @return int
+ */
+ public function getDroppedAttributesCount()
+ {
+ return $this->dropped_attributes_count;
+ }
+
+ /**
+ * The number of dropped attributes. If the value is 0,
+ * then no attributes were dropped.
+ *
+ * Generated from protobuf field uint32 dropped_attributes_count = 5;
+ * @param int $var
+ * @return $this
+ */
+ public function setDroppedAttributesCount(int $var)
+ {
+ GPBUtil::checkUint32($var);
+ $this->dropped_attributes_count = $var;
+
+ return $this;
+ }
+
+ /**
+ * Flags, a bit field.
+ * Bits 0-7 (8 least significant bits) are the trace flags as defined in W3C Trace
+ * Context specification. To read the 8-bit W3C trace flag, use
+ * `flags & SPAN_FLAGS_TRACE_FLAGS_MASK`.
+ * See https://www.w3.org/TR/trace-context-2/#trace-flags for the flag definitions.
+ * Bits 8 and 9 represent the 3 states of whether the link is remote.
+ * The states are (unknown, is not remote, is remote).
+ * To read whether the value is known, use `(flags & SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK) != 0`.
+ * To read whether the link is remote, use `(flags & SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) != 0`.
+ * Readers MUST NOT assume that bits 10-31 (22 most significant bits) will be zero.
+ * When creating new spans, bits 10-31 (most-significant 22-bits) MUST be zero.
+ * [Optional].
+ *
+ * Generated from protobuf field fixed32 flags = 6;
+ * @return int
+ */
+ public function getFlags()
+ {
+ return $this->flags;
+ }
+
+ /**
+ * Flags, a bit field.
+ * Bits 0-7 (8 least significant bits) are the trace flags as defined in W3C Trace
+ * Context specification. To read the 8-bit W3C trace flag, use
+ * `flags & SPAN_FLAGS_TRACE_FLAGS_MASK`.
+ * See https://www.w3.org/TR/trace-context-2/#trace-flags for the flag definitions.
+ * Bits 8 and 9 represent the 3 states of whether the link is remote.
+ * The states are (unknown, is not remote, is remote).
+ * To read whether the value is known, use `(flags & SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK) != 0`.
+ * To read whether the link is remote, use `(flags & SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) != 0`.
+ * Readers MUST NOT assume that bits 10-31 (22 most significant bits) will be zero.
+ * When creating new spans, bits 10-31 (most-significant 22-bits) MUST be zero.
+ * [Optional].
+ *
+ * Generated from protobuf field fixed32 flags = 6;
+ * @param int $var
+ * @return $this
+ */
+ public function setFlags(int $var)
+ {
+ GPBUtil::checkUint32($var);
+ $this->flags = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Trace/V1/Span/SpanKind.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Trace/V1/Span/SpanKind.php
new file mode 100644
index 000000000..504e13e9a
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Trace/V1/Span/SpanKind.php
@@ -0,0 +1,92 @@
+opentelemetry.proto.trace.v1.Span.SpanKind
+ */
+class SpanKind
+{
+ /**
+ * Unspecified. Do NOT use as default.
+ * Implementations MAY assume SpanKind to be INTERNAL when receiving UNSPECIFIED.
+ *
+ * Generated from protobuf enum SPAN_KIND_UNSPECIFIED = 0;
+ */
+ const SPAN_KIND_UNSPECIFIED = 0;
+ /**
+ * Indicates that the span represents an internal operation within an application,
+ * as opposed to an operation happening at the boundaries. Default value.
+ *
+ * Generated from protobuf enum SPAN_KIND_INTERNAL = 1;
+ */
+ const SPAN_KIND_INTERNAL = 1;
+ /**
+ * Indicates that the span covers server-side handling of an RPC or other
+ * remote network request.
+ *
+ * Generated from protobuf enum SPAN_KIND_SERVER = 2;
+ */
+ const SPAN_KIND_SERVER = 2;
+ /**
+ * Indicates that the span describes a request to some remote service.
+ *
+ * Generated from protobuf enum SPAN_KIND_CLIENT = 3;
+ */
+ const SPAN_KIND_CLIENT = 3;
+ /**
+ * Indicates that the span describes a producer sending a message to a broker.
+ * Unlike CLIENT and SERVER, there is often no direct critical path latency relationship
+ * between producer and consumer spans. A PRODUCER span ends when the message was accepted
+ * by the broker while the logical processing of the message might span a much longer time.
+ *
+ * Generated from protobuf enum SPAN_KIND_PRODUCER = 4;
+ */
+ const SPAN_KIND_PRODUCER = 4;
+ /**
+ * Indicates that the span describes consumer receiving a message from a broker.
+ * Like the PRODUCER kind, there is often no direct critical path latency relationship
+ * between producer and consumer spans.
+ *
+ * Generated from protobuf enum SPAN_KIND_CONSUMER = 5;
+ */
+ const SPAN_KIND_CONSUMER = 5;
+
+ private static $valueToName = [
+ self::SPAN_KIND_UNSPECIFIED => 'SPAN_KIND_UNSPECIFIED',
+ self::SPAN_KIND_INTERNAL => 'SPAN_KIND_INTERNAL',
+ self::SPAN_KIND_SERVER => 'SPAN_KIND_SERVER',
+ self::SPAN_KIND_CLIENT => 'SPAN_KIND_CLIENT',
+ self::SPAN_KIND_PRODUCER => 'SPAN_KIND_PRODUCER',
+ self::SPAN_KIND_CONSUMER => 'SPAN_KIND_CONSUMER',
+ ];
+
+ public static function name($value)
+ {
+ if (!isset(self::$valueToName[$value])) {
+ throw new UnexpectedValueException(sprintf(
+ 'Enum %s has no name defined for value %s', __CLASS__, $value));
+ }
+ return self::$valueToName[$value];
+ }
+
+
+ public static function value($name)
+ {
+ $const = __CLASS__ . '::' . strtoupper($name);
+ if (!defined($const)) {
+ throw new UnexpectedValueException(sprintf(
+ 'Enum %s has no value defined for name %s', __CLASS__, $name));
+ }
+ return constant($const);
+ }
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Trace/V1/SpanFlags.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Trace/V1/SpanFlags.php
new file mode 100644
index 000000000..49c86b03b
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Trace/V1/SpanFlags.php
@@ -0,0 +1,80 @@
+opentelemetry.proto.trace.v1.SpanFlags
+ */
+class SpanFlags
+{
+ /**
+ * The zero value for the enum. Should not be used for comparisons.
+ * Instead use bitwise "and" with the appropriate mask as shown above.
+ *
+ * Generated from protobuf enum SPAN_FLAGS_DO_NOT_USE = 0;
+ */
+ const SPAN_FLAGS_DO_NOT_USE = 0;
+ /**
+ * Bits 0-7 are used for trace flags.
+ *
+ * Generated from protobuf enum SPAN_FLAGS_TRACE_FLAGS_MASK = 255;
+ */
+ const SPAN_FLAGS_TRACE_FLAGS_MASK = 255;
+ /**
+ * Bits 8 and 9 are used to indicate that the parent span or link span is remote.
+ * Bit 8 (`HAS_IS_REMOTE`) indicates whether the value is known.
+ * Bit 9 (`IS_REMOTE`) indicates whether the span or link is remote.
+ *
+ * Generated from protobuf enum SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK = 256;
+ */
+ const SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK = 256;
+ /**
+ * Generated from protobuf enum SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK = 512;
+ */
+ const SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK = 512;
+
+ private static $valueToName = [
+ self::SPAN_FLAGS_DO_NOT_USE => 'SPAN_FLAGS_DO_NOT_USE',
+ self::SPAN_FLAGS_TRACE_FLAGS_MASK => 'SPAN_FLAGS_TRACE_FLAGS_MASK',
+ self::SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK => 'SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK',
+ self::SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK => 'SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK',
+ ];
+
+ public static function name($value)
+ {
+ if (!isset(self::$valueToName[$value])) {
+ throw new UnexpectedValueException(sprintf(
+ 'Enum %s has no name defined for value %s', __CLASS__, $value));
+ }
+ return self::$valueToName[$value];
+ }
+
+
+ public static function value($name)
+ {
+ $const = __CLASS__ . '::' . strtoupper($name);
+ if (!defined($const)) {
+ throw new UnexpectedValueException(sprintf(
+ 'Enum %s has no value defined for name %s', __CLASS__, $name));
+ }
+ return constant($const);
+ }
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Trace/V1/Status.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Trace/V1/Status.php
new file mode 100644
index 000000000..d00623875
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Trace/V1/Status.php
@@ -0,0 +1,103 @@
+opentelemetry.proto.trace.v1.Status
+ */
+class Status extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * A developer-facing human readable error message.
+ *
+ * Generated from protobuf field string message = 2;
+ */
+ protected $message = '';
+ /**
+ * The status code.
+ *
+ * Generated from protobuf field .opentelemetry.proto.trace.v1.Status.StatusCode code = 3;
+ */
+ protected $code = 0;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type string $message
+ * A developer-facing human readable error message.
+ * @type int $code
+ * The status code.
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Trace\V1\Trace::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * A developer-facing human readable error message.
+ *
+ * Generated from protobuf field string message = 2;
+ * @return string
+ */
+ public function getMessage()
+ {
+ return $this->message;
+ }
+
+ /**
+ * A developer-facing human readable error message.
+ *
+ * Generated from protobuf field string message = 2;
+ * @param string $var
+ * @return $this
+ */
+ public function setMessage(string $var)
+ {
+ GPBUtil::checkString($var, true);
+ $this->message = $var;
+
+ return $this;
+ }
+
+ /**
+ * The status code.
+ *
+ * Generated from protobuf field .opentelemetry.proto.trace.v1.Status.StatusCode code = 3;
+ * @return int one of the values in {@see \Opentelemetry\Proto\Trace\V1\Status\StatusCode}
+ */
+ public function getCode()
+ {
+ return $this->code;
+ }
+
+ /**
+ * The status code.
+ *
+ * Generated from protobuf field .opentelemetry.proto.trace.v1.Status.StatusCode code = 3;
+ * @param int $var one of the values in {@see \Opentelemetry\Proto\Trace\V1\Status\StatusCode}
+ * @return $this
+ */
+ public function setCode(int $var)
+ {
+ GPBUtil::checkEnum($var, \Opentelemetry\Proto\Trace\V1\Status\StatusCode::class);
+ $this->code = $var;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Trace/V1/Status/StatusCode.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Trace/V1/Status/StatusCode.php
new file mode 100644
index 000000000..c4f66d9df
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Trace/V1/Status/StatusCode.php
@@ -0,0 +1,64 @@
+opentelemetry.proto.trace.v1.Status.StatusCode
+ */
+class StatusCode
+{
+ /**
+ * The default status.
+ *
+ * Generated from protobuf enum STATUS_CODE_UNSET = 0;
+ */
+ const STATUS_CODE_UNSET = 0;
+ /**
+ * The Span has been validated by an Application developer or Operator to
+ * have completed successfully.
+ *
+ * Generated from protobuf enum STATUS_CODE_OK = 1;
+ */
+ const STATUS_CODE_OK = 1;
+ /**
+ * The Span contains an error.
+ *
+ * Generated from protobuf enum STATUS_CODE_ERROR = 2;
+ */
+ const STATUS_CODE_ERROR = 2;
+
+ private static $valueToName = [
+ self::STATUS_CODE_UNSET => 'STATUS_CODE_UNSET',
+ self::STATUS_CODE_OK => 'STATUS_CODE_OK',
+ self::STATUS_CODE_ERROR => 'STATUS_CODE_ERROR',
+ ];
+
+ public static function name($value)
+ {
+ if (!isset(self::$valueToName[$value])) {
+ throw new UnexpectedValueException(sprintf(
+ 'Enum %s has no name defined for value %s', __CLASS__, $value));
+ }
+ return self::$valueToName[$value];
+ }
+
+
+ public static function value($name)
+ {
+ $const = __CLASS__ . '::' . strtoupper($name);
+ if (!defined($const)) {
+ throw new UnexpectedValueException(sprintf(
+ 'Enum %s has no value defined for name %s', __CLASS__, $name));
+ }
+ return constant($const);
+ }
+}
+
diff --git a/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Trace/V1/TracesData.php b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Trace/V1/TracesData.php
new file mode 100644
index 000000000..ab58d4964
--- /dev/null
+++ b/src/bridge/telemetry/otlp/src/Opentelemetry/Proto/Trace/V1/TracesData.php
@@ -0,0 +1,91 @@
+opentelemetry.proto.trace.v1.TracesData
+ */
+class TracesData extends \Google\Protobuf\Internal\Message
+{
+ /**
+ * An array of ResourceSpans.
+ * For data coming from a single resource this array will typically contain
+ * one element. Intermediary nodes that receive data from multiple origins
+ * typically batch the data before forwarding further and in that case this
+ * array will contain multiple elements.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.trace.v1.ResourceSpans resource_spans = 1;
+ */
+ private $resource_spans;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data {
+ * Optional. Data for populating the Message object.
+ *
+ * @type \Opentelemetry\Proto\Trace\V1\ResourceSpans[] $resource_spans
+ * An array of ResourceSpans.
+ * For data coming from a single resource this array will typically contain
+ * one element. Intermediary nodes that receive data from multiple origins
+ * typically batch the data before forwarding further and in that case this
+ * array will contain multiple elements.
+ * }
+ */
+ public function __construct($data = NULL) {
+ \GPBMetadata\Opentelemetry\Proto\Trace\V1\Trace::initOnce();
+ parent::__construct($data);
+ }
+
+ /**
+ * An array of ResourceSpans.
+ * For data coming from a single resource this array will typically contain
+ * one element. Intermediary nodes that receive data from multiple origins
+ * typically batch the data before forwarding further and in that case this
+ * array will contain multiple elements.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.trace.v1.ResourceSpans resource_spans = 1;
+ * @return RepeatedField<\Opentelemetry\Proto\Trace\V1\ResourceSpans>
+ */
+ public function getResourceSpans()
+ {
+ return $this->resource_spans;
+ }
+
+ /**
+ * An array of ResourceSpans.
+ * For data coming from a single resource this array will typically contain
+ * one element. Intermediary nodes that receive data from multiple origins
+ * typically batch the data before forwarding further and in that case this
+ * array will contain multiple elements.
+ *
+ * Generated from protobuf field repeated .opentelemetry.proto.trace.v1.ResourceSpans resource_spans = 1;
+ * @param \Opentelemetry\Proto\Trace\V1\ResourceSpans[] $var
+ * @return $this
+ */
+ public function setResourceSpans(array|RepeatedField $var)
+ {
+ $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Opentelemetry\Proto\Trace\V1\ResourceSpans::class);
+ $this->resource_spans = $arr;
+
+ return $this;
+ }
+
+}
+
diff --git a/src/bridge/telemetry/otlp/tests/Flow/Bridge/Telemetry/OTLP/Tests/Context/OtelContext.php b/src/bridge/telemetry/otlp/tests/Flow/Bridge/Telemetry/OTLP/Tests/Context/OtelContext.php
index aafdee795..ca775d29c 100644
--- a/src/bridge/telemetry/otlp/tests/Flow/Bridge/Telemetry/OTLP/Tests/Context/OtelContext.php
+++ b/src/bridge/telemetry/otlp/tests/Flow/Bridge/Telemetry/OTLP/Tests/Context/OtelContext.php
@@ -4,7 +4,7 @@
namespace Flow\Bridge\Telemetry\OTLP\Tests\Context;
-use function Flow\Bridge\Telemetry\OTLP\DSL\{otlp_log_exporter, otlp_metric_exporter, otlp_span_exporter};
+use function Flow\Bridge\Telemetry\OTLP\DSL\otlp_exporter;
use function Flow\Telemetry\DSL\{batching_log_processor, batching_metric_processor, batching_span_processor, logger_provider, meter_provider, resource, telemetry, tracer_provider};
use Flow\Bridge\Telemetry\OTLP\Tests\Integration\CollectorMetrics;
use Flow\Telemetry\Context\MemoryContextStorage;
@@ -106,9 +106,9 @@ public function createTelemetry(TransportConfiguration $config, ?Resource $resou
$contextStorage = new MemoryContextStorage();
$batchSize = 1;
- $spanProcessor = batching_span_processor(otlp_span_exporter($config->createTransport($this)), $batchSize);
- $metricProcessor = batching_metric_processor(otlp_metric_exporter($config->createTransport($this)), $batchSize);
- $logProcessor = batching_log_processor(otlp_log_exporter($config->createTransport($this)), $batchSize);
+ $spanProcessor = batching_span_processor(otlp_exporter($config->createTransport($this)), $batchSize);
+ $metricProcessor = batching_metric_processor(otlp_exporter($config->createTransport($this)), $batchSize);
+ $logProcessor = batching_log_processor(otlp_exporter($config->createTransport($this)), $batchSize);
$tracerProvider = tracer_provider($spanProcessor, $clock, $contextStorage);
$meterProvider = meter_provider($metricProcessor, $clock);
diff --git a/src/bridge/telemetry/otlp/tests/Flow/Bridge/Telemetry/OTLP/Tests/Context/Requirements.php b/src/bridge/telemetry/otlp/tests/Flow/Bridge/Telemetry/OTLP/Tests/Context/Requirements.php
new file mode 100644
index 000000000..ba3e67fcb
--- /dev/null
+++ b/src/bridge/telemetry/otlp/tests/Flow/Bridge/Telemetry/OTLP/Tests/Context/Requirements.php
@@ -0,0 +1,32 @@
+serializer) {
- 'json' => otlp_json_serializer(),
- 'protobuf' => otlp_protobuf_serializer(),
- default => throw new \InvalidArgumentException(\sprintf('Unknown serializer: %s', $this->serializer)),
- };
-
return match ($this->transport) {
- 'http' => otlp_http_transport(
- $ctx->httpClient(),
- $ctx->requestFactory(),
- $ctx->streamFactory(),
- $ctx->httpEndpoint(),
- $serializer,
- ),
'curl' => otlp_curl_transport(
$ctx->httpEndpoint(),
- $serializer,
- ),
- 'grpc' => otlp_grpc_transport(
- $ctx->grpcEndpoint(),
- $serializer instanceof ProtobufSerializer ? $serializer : otlp_protobuf_serializer(),
+ match ($this->serializer) {
+ 'json' => otlp_json_serializer(),
+ 'protobuf' => otlp_protobuf_serializer(),
+ default => throw new \InvalidArgumentException(\sprintf('Unknown serializer: %s', $this->serializer)),
+ },
),
+ 'grpc' => otlp_grpc_transport($ctx->grpcEndpoint()),
default => throw new \InvalidArgumentException(\sprintf('Unknown transport: %s', $this->transport)),
};
}
public function isAvailable() : bool
{
- if ($this->transport === 'grpc' && !\extension_loaded('grpc')) {
- return false;
- }
-
- if ($this->serializer === 'protobuf' && !\class_exists(Message::class)) {
- return false;
+ if ($this->transport === 'grpc') {
+ return \extension_loaded('grpc') && \class_exists(Message::class);
}
- return true;
+ return $this->serializer !== 'protobuf' || \class_exists(Message::class);
}
}
diff --git a/src/bridge/telemetry/otlp/tests/Flow/Bridge/Telemetry/OTLP/Tests/Double/RecordingTransport.php b/src/bridge/telemetry/otlp/tests/Flow/Bridge/Telemetry/OTLP/Tests/Double/RecordingTransport.php
new file mode 100644
index 000000000..c0983c47d
--- /dev/null
+++ b/src/bridge/telemetry/otlp/tests/Flow/Bridge/Telemetry/OTLP/Tests/Double/RecordingTransport.php
@@ -0,0 +1,38 @@
+ */
+ public array $sent = [];
+
+ public int $shutdownCalls = 0;
+
+ public ?\Throwable $shutdownException = null;
+
+ public function send(Signals $signal) : void
+ {
+ $this->sent[] = $signal;
+
+ if ($this->sendException !== null) {
+ throw $this->sendException;
+ }
+ }
+
+ public function shutdown() : void
+ {
+ $this->shutdownCalls++;
+
+ if ($this->shutdownException !== null) {
+ throw $this->shutdownException;
+ }
+ }
+}
diff --git a/src/bridge/telemetry/otlp/tests/Flow/Bridge/Telemetry/OTLP/Tests/Integration/StreamExportIntegrationTest.php b/src/bridge/telemetry/otlp/tests/Flow/Bridge/Telemetry/OTLP/Tests/Integration/StreamExportIntegrationTest.php
new file mode 100644
index 000000000..68a344055
--- /dev/null
+++ b/src/bridge/telemetry/otlp/tests/Flow/Bridge/Telemetry/OTLP/Tests/Integration/StreamExportIntegrationTest.php
@@ -0,0 +1,288 @@
+tempDir = \sys_get_temp_dir() . '/flow-otlp-stream-integration-' . \bin2hex(\random_bytes(6));
+ \mkdir($this->tempDir, 0755, true);
+ }
+
+ protected function tearDown() : void
+ {
+ if (!\is_dir($this->tempDir)) {
+ return;
+ }
+
+ foreach (new \RecursiveIteratorIterator(
+ new \RecursiveDirectoryIterator($this->tempDir, \FilesystemIterator::SKIP_DOTS),
+ \RecursiveIteratorIterator::CHILD_FIRST,
+ ) as $entry) {
+ /** @var \SplFileInfo $entry */
+ if ($entry->isDir()) {
+ \rmdir($entry->getPathname());
+ } else {
+ \unlink($entry->getPathname());
+ }
+ }
+
+ \rmdir($this->tempDir);
+ }
+
+ public function test_appends_multiple_batches_to_file_as_separate_lines() : void
+ {
+ $path = $this->tempDir . '/logs.jsonl';
+ $exporter = otlp_exporter(otlp_stream_transport($path));
+
+ self::assertTrue($exporter->export(Signals::logs([LogEntryMother::deterministic('first', Severity::INFO)])));
+ self::assertTrue($exporter->export(Signals::logs([LogEntryMother::deterministic('second', Severity::WARN)])));
+
+ $lines = \array_values(\array_filter(
+ \explode("\n", (string) \file_get_contents($path)),
+ static fn (string $l) : bool => $l !== '',
+ ));
+
+ self::assertCount(2, $lines);
+
+ foreach ($lines as $line) {
+ self::assertJson($line);
+ /** @var array $decoded */
+ $decoded = \json_decode($line, true, flags: \JSON_THROW_ON_ERROR);
+ self::assertArrayHasKey('resourceLogs', $decoded);
+ }
+ }
+
+ public function test_applies_file_permissions_to_newly_created_file() : void
+ {
+ $path = $this->tempDir . '/perms.jsonl';
+ new StreamTransport($path, filePermissions: 0600);
+
+ self::assertSame('0600', \substr(\sprintf('%o', \fileperms($path)), -4));
+ }
+
+ public function test_concurrent_writes_to_shared_stream_interleave_at_line_boundaries() : void
+ {
+ $transportA = otlp_stream_transport('php://temp');
+ $transportB = otlp_stream_transport('php://temp');
+
+ $exporterA = otlp_exporter($transportA);
+ $exporterB = otlp_exporter($transportB);
+
+ for ($i = 0; $i < 10; $i++) {
+ self::assertTrue($exporterA->export(Signals::logs([LogEntryMother::deterministic("a-{$i}", Severity::INFO)])));
+ self::assertTrue($exporterB->export(Signals::logs([LogEntryMother::deterministic("b-{$i}", Severity::INFO)])));
+ }
+
+ self::assertInstanceOf(StreamTransport::class, $transportA);
+ self::assertInstanceOf(StreamTransport::class, $transportB);
+
+ foreach ([$transportA, $transportB] as $transport) {
+ \rewind($transport->stream());
+ $lines = \array_values(\array_filter(
+ \explode("\n", (string) \stream_get_contents($transport->stream())),
+ static fn (string $l) : bool => $l !== '',
+ ));
+
+ self::assertCount(10, $lines);
+
+ foreach ($lines as $line) {
+ self::assertJson($line);
+ }
+ }
+ }
+
+ public function test_creates_parent_directory_when_enabled() : void
+ {
+ $path = $this->tempDir . '/nested/deeply/logs.jsonl';
+ new StreamTransport($path);
+
+ self::assertDirectoryExists(\dirname($path));
+ }
+
+ public function test_does_not_chmod_existing_file() : void
+ {
+ $path = $this->tempDir . '/preexisting.jsonl';
+ \touch($path);
+ \chmod($path, 0640);
+
+ new StreamTransport($path, filePermissions: 0600);
+
+ self::assertSame('0640', \substr(\sprintf('%o', \fileperms($path)), -4));
+ }
+
+ public function test_does_not_create_parent_directory_when_disabled() : void
+ {
+ $this->expectException(TransportException::class);
+
+ new StreamTransport($this->tempDir . '/missing-dir/logs.jsonl', createDirectories: false);
+ }
+
+ public function test_large_batch_above_default_chunk_size_is_written_intact() : void
+ {
+ $transport = otlp_stream_transport('php://temp');
+ $exporter = otlp_exporter($transport);
+
+ $entries = [];
+
+ for ($i = 0; $i < 200; $i++) {
+ $entries[] = LogEntryMother::deterministic(\str_repeat('x', 256) . ' #' . $i, Severity::INFO);
+ }
+
+ self::assertTrue($exporter->export(Signals::logs($entries)));
+
+ self::assertInstanceOf(StreamTransport::class, $transport);
+ \rewind($transport->stream());
+
+ $contents = (string) \stream_get_contents($transport->stream());
+
+ self::assertGreaterThan(50_000, \strlen($contents));
+ self::assertStringEndsWith("\n", $contents);
+ self::assertSame(1, \substr_count($contents, "\n"));
+
+ /** @var array{resourceLogs: list}>}>} $decoded */
+ $decoded = \json_decode(\rtrim($contents, "\n"), true, flags: \JSON_THROW_ON_ERROR);
+ self::assertArrayHasKey('resourceLogs', $decoded);
+ self::assertCount(200, $decoded['resourceLogs'][0]['scopeLogs'][0]['logRecords']);
+ }
+
+ #[TestWith(['file'])]
+ #[TestWith(['stream'])]
+ public function test_logs_metrics_and_spans_export_to_separate_destinations(string $kind) : void
+ {
+ $readLines = static function (StreamTransport $transport, string $destination) use ($kind) : array {
+ if ($kind === 'file') {
+ $contents = (string) \file_get_contents($destination);
+ } else {
+ \rewind($transport->stream());
+ $contents = (string) \stream_get_contents($transport->stream());
+ }
+
+ return \array_values(\array_filter(\explode("\n", $contents), static fn (string $l) : bool => $l !== ''));
+ };
+
+ $logsDest = $kind === 'file' ? $this->tempDir . '/logs.jsonl' : 'php://temp';
+ $metricsDest = $kind === 'file' ? $this->tempDir . '/metrics.jsonl' : 'php://temp';
+ $tracesDest = $kind === 'file' ? $this->tempDir . '/traces.jsonl' : 'php://temp';
+
+ $logsTransport = otlp_stream_transport($logsDest);
+ $metricsTransport = otlp_stream_transport($metricsDest);
+ $tracesTransport = otlp_stream_transport($tracesDest);
+
+ $logsExporter = otlp_exporter($logsTransport);
+ $metricsExporter = otlp_exporter($metricsTransport);
+ $tracesExporter = otlp_exporter($tracesTransport);
+
+ self::assertTrue($logsExporter->export(Signals::logs([
+ LogEntryMother::deterministic('hello', Severity::INFO),
+ LogEntryMother::deterministic('world', Severity::WARN),
+ ])));
+ self::assertTrue($logsExporter->export(Signals::logs([
+ LogEntryMother::deterministic('again', Severity::ERROR),
+ ])));
+
+ self::assertTrue($metricsExporter->export(Signals::metrics([
+ MetricMother::deterministicCounter('requests.count', 7),
+ ])));
+ self::assertTrue($tracesExporter->export(Signals::traces([
+ SpanMother::withName('checkout'),
+ ])));
+
+ self::assertInstanceOf(StreamTransport::class, $logsTransport);
+ self::assertInstanceOf(StreamTransport::class, $metricsTransport);
+ self::assertInstanceOf(StreamTransport::class, $tracesTransport);
+
+ foreach ([
+ [$logsTransport, $logsDest, 'resourceLogs', 2],
+ [$metricsTransport, $metricsDest, 'resourceMetrics', 1],
+ [$tracesTransport, $tracesDest, 'resourceSpans', 1],
+ ] as [$transport, $destination, $key, $expectedCount]) {
+ $lines = $readLines($transport, $destination);
+
+ self::assertCount($expectedCount, $lines);
+
+ foreach ($lines as $line) {
+ self::assertJson($line);
+ /** @var array $decoded */
+ $decoded = \json_decode($line, true, flags: \JSON_THROW_ON_ERROR);
+ self::assertArrayHasKey($key, $decoded);
+ }
+ }
+ }
+
+ #[TestWith(['file'])]
+ #[TestWith(['stream'])]
+ public function test_mixed_signal_destination_appends_one_line_per_signal_type(string $kind) : void
+ {
+ $destination = $kind === 'file' ? $this->tempDir . '/all-signals.jsonl' : 'php://temp';
+
+ $transport = otlp_stream_transport($destination);
+ $exporter = otlp_exporter($transport);
+
+ self::assertTrue($exporter->export(Signals::logs([LogEntryMother::deterministic('hello', Severity::INFO)])));
+ self::assertTrue($exporter->export(Signals::metrics([MetricMother::deterministicCounter('m', 1)])));
+ self::assertTrue($exporter->export(Signals::traces([SpanMother::withName('s')])));
+
+ self::assertInstanceOf(StreamTransport::class, $transport);
+
+ if ($kind === 'file') {
+ $contents = (string) \file_get_contents($destination);
+ } else {
+ \rewind($transport->stream());
+ $contents = (string) \stream_get_contents($transport->stream());
+ }
+
+ $lines = \array_values(\array_filter(\explode("\n", $contents), static fn (string $l) : bool => $l !== ''));
+
+ self::assertCount(3, $lines);
+
+ foreach ([0 => 'resourceLogs', 1 => 'resourceMetrics', 2 => 'resourceSpans'] as $index => $expectedKey) {
+ /** @var array $decoded */
+ $decoded = \json_decode($lines[$index], true, flags: \JSON_THROW_ON_ERROR);
+ self::assertArrayHasKey($expectedKey, $decoded);
+ }
+ }
+
+ #[TestWith([SignalType::LOGS, 'resourceLogs'])]
+ #[TestWith([SignalType::METRICS, 'resourceMetrics'])]
+ #[TestWith([SignalType::TRACES, 'resourceSpans'])]
+ public function test_writes_each_signal_type_to_file_as_single_line_with_trailing_newline(SignalType $type, string $expectedKey) : void
+ {
+ $path = $this->tempDir . '/' . \strtolower($type->name) . '.jsonl';
+ $exporter = otlp_exporter(otlp_stream_transport($path));
+
+ self::assertTrue($exporter->export(match ($type) {
+ SignalType::LOGS => Signals::logs([LogEntryMother::deterministic('hello', Severity::INFO)]),
+ SignalType::METRICS => Signals::metrics([MetricMother::deterministicCounter('test.counter', 42)]),
+ SignalType::TRACES => Signals::traces([SpanMother::withName('span')]),
+ }));
+
+ $contents = (string) \file_get_contents($path);
+
+ self::assertStringEndsWith("\n", $contents);
+ self::assertSame(1, \substr_count($contents, "\n"));
+
+ /** @var array $decoded */
+ $decoded = \json_decode(\rtrim($contents, "\n"), true, flags: \JSON_THROW_ON_ERROR);
+ self::assertArrayHasKey($expectedKey, $decoded);
+ }
+}
diff --git a/src/bridge/telemetry/otlp/tests/Flow/Bridge/Telemetry/OTLP/Tests/Unit/DSL/FunctionsTest.php b/src/bridge/telemetry/otlp/tests/Flow/Bridge/Telemetry/OTLP/Tests/Unit/DSL/FunctionsTest.php
index 854d7871b..e8a85dba5 100644
--- a/src/bridge/telemetry/otlp/tests/Flow/Bridge/Telemetry/OTLP/Tests/Unit/DSL/FunctionsTest.php
+++ b/src/bridge/telemetry/otlp/tests/Flow/Bridge/Telemetry/OTLP/Tests/Unit/DSL/FunctionsTest.php
@@ -4,34 +4,49 @@
namespace Flow\Bridge\Telemetry\OTLP\Tests\Unit\DSL;
-use function Flow\Bridge\Telemetry\OTLP\DSL\{otlp_grpc_transport, otlp_http_transport, otlp_json_serializer, otlp_protobuf_serializer};
+use function Flow\Bridge\Telemetry\OTLP\DSL\{otlp_curl_transport, otlp_exporter, otlp_grpc_transport, otlp_json_serializer, otlp_protobuf_serializer, otlp_stream_transport};
+use Flow\Bridge\Telemetry\OTLP\Exporter\OTLPExporter;
use Flow\Bridge\Telemetry\OTLP\Serializer\{JsonSerializer, ProtobufSerializer};
-use Flow\Bridge\Telemetry\OTLP\Transport\{GrpcTransport, HttpTransport};
-use Google\Protobuf\Internal\Message;
-use Grpc\BaseStub;
-use Opentelemetry\Proto\Collector\Trace\V1\TraceServiceClient;
+use Flow\Bridge\Telemetry\OTLP\Tests\Context\Requirements;
+use Flow\Bridge\Telemetry\OTLP\Transport\{CurlTransport, GrpcTransport, StreamTransport};
use PHPUnit\Framework\TestCase;
-use Psr\Http\Client\ClientInterface;
-use Psr\Http\Message\{RequestFactoryInterface, StreamFactoryInterface};
final class FunctionsTest extends TestCase
{
+ public function test_otlp_curl_transport_defaults_to_json_serializer() : void
+ {
+ self::assertInstanceOf(CurlTransport::class, otlp_curl_transport('http://localhost:4318'));
+ }
+
+ public function test_otlp_curl_transport_returns_curl_transport() : void
+ {
+ $transport = otlp_curl_transport('http://localhost:4318', otlp_json_serializer());
+
+ self::assertInstanceOf(CurlTransport::class, $transport);
+ }
+
+ public function test_otlp_exporter_returns_otlp_exporter() : void
+ {
+ $transport = otlp_curl_transport('http://localhost:4318', otlp_json_serializer());
+
+ self::assertInstanceOf(OTLPExporter::class, otlp_exporter($transport));
+ }
+
public function test_otlp_grpc_transport_returns_grpc_transport() : void
{
- $this->skipIfGrpcNotAvailable();
+ Requirements::requireGrpc();
- $transport = otlp_grpc_transport('localhost:4317', otlp_protobuf_serializer());
+ $transport = otlp_grpc_transport('localhost:4317');
self::assertInstanceOf(GrpcTransport::class, $transport);
}
public function test_otlp_grpc_transport_with_headers() : void
{
- $this->skipIfGrpcNotAvailable();
+ Requirements::requireGrpc();
$transport = otlp_grpc_transport(
endpoint: 'localhost:4317',
- serializer: otlp_protobuf_serializer(),
headers: ['Authorization' => 'Bearer token'],
);
@@ -40,71 +55,44 @@ public function test_otlp_grpc_transport_with_headers() : void
public function test_otlp_grpc_transport_with_secure_option() : void
{
- $this->skipIfGrpcNotAvailable();
+ Requirements::requireGrpc();
$transport = otlp_grpc_transport(
endpoint: 'localhost:4317',
- serializer: otlp_protobuf_serializer(),
insecure: false,
);
self::assertInstanceOf(GrpcTransport::class, $transport);
}
- public function test_otlp_http_transport_returns_http_transport() : void
- {
- $client = $this->createMock(ClientInterface::class);
- $requestFactory = $this->createMock(RequestFactoryInterface::class);
- $streamFactory = $this->createMock(StreamFactoryInterface::class);
-
- $transport = otlp_http_transport($client, $requestFactory, $streamFactory, 'http://localhost:4318', otlp_json_serializer());
-
- self::assertInstanceOf(HttpTransport::class, $transport);
- }
-
public function test_otlp_json_serializer_returns_json_serializer() : void
{
- $serializer = otlp_json_serializer();
-
- self::assertInstanceOf(JsonSerializer::class, $serializer);
+ self::assertInstanceOf(JsonSerializer::class, otlp_json_serializer());
}
public function test_otlp_protobuf_serializer_returns_protobuf_serializer() : void
{
- $this->skipIfProtobufNotAvailable();
-
- $serializer = otlp_protobuf_serializer();
+ Requirements::requireProtobuf();
- self::assertInstanceOf(ProtobufSerializer::class, $serializer);
+ self::assertInstanceOf(ProtobufSerializer::class, otlp_protobuf_serializer());
}
- private function skipIfGrpcNotAvailable() : void
+ public function test_otlp_stream_transport_returns_stream_transport_for_file_path() : void
{
- if (!\extension_loaded('grpc')) {
- self::markTestSkipped('The grpc extension is not available');
- }
-
- if (!\class_exists(BaseStub::class)) {
- self::markTestSkipped('The grpc/grpc package is not installed');
- }
-
- if (!\class_exists(Message::class)) {
- self::markTestSkipped('The google/protobuf package is not installed');
- }
-
- if (!\class_exists(TraceServiceClient::class)) {
- self::markTestSkipped('The open-telemetry/gen-otlp-protobuf package is not installed');
+ $path = \sys_get_temp_dir() . '/flow-otlp-dsl-test-' . \bin2hex(\random_bytes(4)) . '.jsonl';
+
+ try {
+ self::assertInstanceOf(StreamTransport::class, otlp_stream_transport($path));
+ } finally {
+ if (\is_file($path)) {
+ \unlink($path);
+ }
}
}
- private function skipIfProtobufNotAvailable() : void
+ public function test_otlp_stream_transport_returns_stream_transport_for_php_uri() : void
{
- if (!\class_exists(Message::class)) {
- self::markTestSkipped('The google/protobuf package is not installed');
- }
-
- if (!\class_exists(TraceServiceClient::class)) {
- self::markTestSkipped('The open-telemetry/gen-otlp-protobuf package is not installed');
- }
+ self::assertInstanceOf(StreamTransport::class, otlp_stream_transport('php://stdout'));
+ self::assertInstanceOf(StreamTransport::class, otlp_stream_transport('php://stderr'));
}
}
diff --git a/src/bridge/telemetry/otlp/tests/Flow/Bridge/Telemetry/OTLP/Tests/Unit/Exporter/OTLPExporterTest.php b/src/bridge/telemetry/otlp/tests/Flow/Bridge/Telemetry/OTLP/Tests/Unit/Exporter/OTLPExporterTest.php
new file mode 100644
index 000000000..e2f421359
--- /dev/null
+++ b/src/bridge/telemetry/otlp/tests/Flow/Bridge/Telemetry/OTLP/Tests/Unit/Exporter/OTLPExporterTest.php
@@ -0,0 +1,193 @@
+export(Signals::logs([])));
+ self::assertSame(0, $spy->count());
+ }
+
+ public function test_export_empty_logs_returns_true_without_calling_transport() : void
+ {
+ $transport = new RecordingTransport();
+ $exporter = new OTLPExporter($transport);
+
+ self::assertTrue($exporter->export(Signals::logs([])));
+ self::assertSame(0, $transport->callCount());
+ }
+
+ public function test_export_empty_metrics_returns_true_without_calling_transport() : void
+ {
+ $transport = new RecordingTransport();
+ $exporter = new OTLPExporter($transport);
+
+ self::assertTrue($exporter->export(Signals::metrics([])));
+ self::assertSame(0, $transport->callCount());
+ }
+
+ public function test_export_empty_traces_returns_true_without_calling_transport() : void
+ {
+ $transport = new RecordingTransport();
+ $exporter = new OTLPExporter($transport);
+
+ self::assertTrue($exporter->export(Signals::traces([])));
+ self::assertSame(0, $transport->callCount());
+ }
+
+ public function test_export_logs_calls_transport() : void
+ {
+ $transport = new RecordingTransport();
+ $exporter = new OTLPExporter($transport);
+ $entry = new LogEntry(
+ (new LogRecord())->setSeverity(Severity::INFO)->setBody('hello'),
+ ResourceMother::default(),
+ InstrumentationScopeMother::default(),
+ new \DateTimeImmutable(),
+ );
+
+ self::assertTrue($exporter->export(Signals::logs([$entry])));
+ self::assertSame(1, $transport->callCount());
+ }
+
+ public function test_export_metrics_calls_transport() : void
+ {
+ $transport = new RecordingTransport();
+ $exporter = new OTLPExporter($transport);
+ $metric = new Metric(
+ name: 'test.metric',
+ type: MetricType::COUNTER,
+ value: 1,
+ attributes: Attributes::empty(),
+ timestamp: new \DateTimeImmutable(),
+ resource: ResourceMother::default(),
+ scope: InstrumentationScopeMother::default(),
+ );
+
+ self::assertTrue($exporter->export(Signals::metrics([$metric])));
+ self::assertSame(1, $transport->callCount());
+ }
+
+ public function test_export_returns_false_when_transport_throws() : void
+ {
+ $transport = new ThrowingTransport();
+ $exporter = new OTLPExporter($transport, new NullErrorHandler());
+
+ self::assertFalse($exporter->export(Signals::traces([SpanMother::withName('span')])));
+ }
+
+ public function test_export_routes_transport_throwable_to_error_handler() : void
+ {
+ $transport = new ThrowingTransport();
+ $spy = new ErrorHandlerSpy();
+ $exporter = new OTLPExporter($transport, $spy);
+
+ self::assertFalse($exporter->export(Signals::traces([SpanMother::withName('span')])));
+ self::assertSame(1, $spy->count());
+ $last = $spy->last();
+ self::assertInstanceOf(TransportException::class, $last);
+ self::assertSame('boom', $last->getMessage());
+ }
+
+ public function test_export_traces_calls_transport() : void
+ {
+ $transport = new RecordingTransport();
+ $exporter = new OTLPExporter($transport);
+
+ self::assertTrue($exporter->export(Signals::traces([SpanMother::withName('span')])));
+ self::assertSame(1, $transport->callCount());
+ }
+
+ public function test_shutdown_delegates_to_transport() : void
+ {
+ $transport = new RecordingTransport();
+ $exporter = new OTLPExporter($transport);
+
+ $exporter->shutdown();
+
+ self::assertTrue($transport->isShutdown());
+ }
+
+ public function test_shutdown_routes_transport_throwable_to_error_handler() : void
+ {
+ $transport = new ShutdownThrowingTransport();
+ $spy = new ErrorHandlerSpy();
+ $exporter = new OTLPExporter($transport, $spy);
+
+ $exporter->shutdown();
+
+ self::assertSame(1, $spy->count());
+ $last = $spy->last();
+ self::assertInstanceOf(TransportException::class, $last);
+ self::assertSame('shutdown boom', $last->getMessage());
+ }
+}
+
+final class RecordingTransport implements Transport
+{
+ private int $callCount = 0;
+
+ private bool $isShutdown = false;
+
+ public function callCount() : int
+ {
+ return $this->callCount;
+ }
+
+ public function isShutdown() : bool
+ {
+ return $this->isShutdown;
+ }
+
+ public function send(Signals $signal) : void
+ {
+ $this->callCount++;
+ }
+
+ public function shutdown() : void
+ {
+ $this->isShutdown = true;
+ }
+}
+
+final class ThrowingTransport implements Transport
+{
+ public function send(Signals $signal) : void
+ {
+ throw new TransportException('boom');
+ }
+
+ public function shutdown() : void
+ {
+ }
+}
+
+final class ShutdownThrowingTransport implements Transport
+{
+ public function send(Signals $signal) : void
+ {
+ }
+
+ public function shutdown() : void
+ {
+ throw new TransportException('shutdown boom');
+ }
+}
diff --git a/src/bridge/telemetry/otlp/tests/Flow/Bridge/Telemetry/OTLP/Tests/Unit/Transport/CurlTransportOptionsTest.php b/src/bridge/telemetry/otlp/tests/Flow/Bridge/Telemetry/OTLP/Tests/Unit/Transport/CurlTransportOptionsTest.php
index 47ca0e617..6415d9737 100644
--- a/src/bridge/telemetry/otlp/tests/Flow/Bridge/Telemetry/OTLP/Tests/Unit/Transport/CurlTransportOptionsTest.php
+++ b/src/bridge/telemetry/otlp/tests/Flow/Bridge/Telemetry/OTLP/Tests/Unit/Transport/CurlTransportOptionsTest.php
@@ -14,8 +14,8 @@ public function test_default_options() : void
{
$options = new CurlTransportOptions();
- self::assertSame(30, $options->timeout());
- self::assertSame(10, $options->connectTimeout());
+ self::assertSame(CurlTransportOptions::DEFAULT_TIMEOUT_MS, $options->timeoutMs());
+ self::assertSame(CurlTransportOptions::DEFAULT_CONNECT_TIMEOUT_MS, $options->connectTimeoutMs());
self::assertSame([], $options->headers());
self::assertTrue($options->followRedirects());
self::assertSame(3, $options->maxRedirects());
@@ -28,27 +28,34 @@ public function test_default_options() : void
self::assertFalse($options->compression());
}
+ public function test_default_shutdown_timeout() : void
+ {
+ $options = new CurlTransportOptions();
+
+ self::assertSame(CurlTransportOptions::DEFAULT_SHUTDOWN_TIMEOUT_MS, $options->shutdownTimeoutMs());
+ }
+
public function test_dsl_function_creates_options() : void
{
$options = otlp_curl_options();
self::assertInstanceOf(CurlTransportOptions::class, $options);
- self::assertSame(30, $options->timeout());
+ self::assertSame(CurlTransportOptions::DEFAULT_TIMEOUT_MS, $options->timeoutMs());
}
public function test_fluent_chaining() : void
{
$options = otlp_curl_options()
- ->withTimeout(60)
- ->withConnectTimeout(15)
+ ->withTimeout(2000)
+ ->withConnectTimeout(500)
->withHeader('Authorization', 'Bearer token')
->withFollowRedirects(true, 5)
->withSslVerification(true)
->withProxy('http://proxy:8080')
->withCompression();
- self::assertSame(60, $options->timeout());
- self::assertSame(15, $options->connectTimeout());
+ self::assertSame(2000, $options->timeoutMs());
+ self::assertSame(500, $options->connectTimeoutMs());
self::assertSame(['Authorization' => 'Bearer token'], $options->headers());
self::assertTrue($options->followRedirects());
self::assertSame(5, $options->maxRedirects());
@@ -71,8 +78,8 @@ public function test_to_curl_options_basic() : void
self::assertTrue($curlOptions[\CURLOPT_POST]);
self::assertSame('{"data": "test"}', $curlOptions[\CURLOPT_POSTFIELDS]);
self::assertTrue($curlOptions[\CURLOPT_RETURNTRANSFER]);
- self::assertSame(30, $curlOptions[\CURLOPT_TIMEOUT]);
- self::assertSame(10, $curlOptions[\CURLOPT_CONNECTTIMEOUT]);
+ self::assertSame(CurlTransportOptions::DEFAULT_TIMEOUT_MS, $curlOptions[\CURLOPT_TIMEOUT_MS]);
+ self::assertSame(CurlTransportOptions::DEFAULT_CONNECT_TIMEOUT_MS, $curlOptions[\CURLOPT_CONNECTTIMEOUT_MS]);
self::assertSame(['Content-Type: application/json'], $curlOptions[\CURLOPT_HTTPHEADER]);
self::assertTrue($curlOptions[\CURLOPT_FOLLOWLOCATION]);
self::assertSame(3, $curlOptions[\CURLOPT_MAXREDIRS]);
@@ -83,8 +90,8 @@ public function test_to_curl_options_basic() : void
public function test_to_curl_options_with_all_settings() : void
{
$options = (new CurlTransportOptions())
- ->withTimeout(60)
- ->withConnectTimeout(15)
+ ->withTimeout(2000)
+ ->withConnectTimeout(500)
->withSslVerification(false, false)
->withSslCertificate('/path/to/cert.pem', '/path/to/key.pem')
->withCaInfo('/path/to/ca.crt')
@@ -93,8 +100,8 @@ public function test_to_curl_options_with_all_settings() : void
$curlOptions = $options->toCurlOptions('http://example.com', 'body', ['Header: value']);
- self::assertSame(60, $curlOptions[\CURLOPT_TIMEOUT]);
- self::assertSame(15, $curlOptions[\CURLOPT_CONNECTTIMEOUT]);
+ self::assertSame(2000, $curlOptions[\CURLOPT_TIMEOUT_MS]);
+ self::assertSame(500, $curlOptions[\CURLOPT_CONNECTTIMEOUT_MS]);
self::assertFalse($curlOptions[\CURLOPT_SSL_VERIFYPEER]);
self::assertSame(0, $curlOptions[\CURLOPT_SSL_VERIFYHOST]);
self::assertSame('/path/to/cert.pem', $curlOptions[\CURLOPT_SSLCERT]);
@@ -129,9 +136,9 @@ public function test_with_compression_enabled() : void
public function test_with_connect_timeout() : void
{
- $options = (new CurlTransportOptions())->withConnectTimeout(15);
+ $options = (new CurlTransportOptions())->withConnectTimeout(750);
- self::assertSame(15, $options->connectTimeout());
+ self::assertSame(750, $options->connectTimeoutMs());
}
public function test_with_connect_timeout_rejects_negative_value() : void
@@ -206,6 +213,21 @@ public function test_with_proxy() : void
self::assertSame('http://proxy:8080', $options->proxy());
}
+ public function test_with_shutdown_timeout() : void
+ {
+ $options = (new CurlTransportOptions())->withShutdownTimeout(7500);
+
+ self::assertSame(7500, $options->shutdownTimeoutMs());
+ }
+
+ public function test_with_shutdown_timeout_rejects_negative_value() : void
+ {
+ $this->expectException(\InvalidArgumentException::class);
+ $this->expectExceptionMessage('Shutdown timeout must be non-negative');
+
+ (new CurlTransportOptions())->withShutdownTimeout(-1);
+ }
+
public function test_with_ssl_certificate() : void
{
$options = (new CurlTransportOptions())->withSslCertificate('/path/to/cert.pem');
@@ -240,9 +262,9 @@ public function test_with_ssl_verification_enabled() : void
public function test_with_timeout() : void
{
- $options = (new CurlTransportOptions())->withTimeout(60);
+ $options = (new CurlTransportOptions())->withTimeout(2500);
- self::assertSame(60, $options->timeout());
+ self::assertSame(2500, $options->timeoutMs());
}
public function test_with_timeout_rejects_negative_value() : void
diff --git a/src/bridge/telemetry/otlp/tests/Flow/Bridge/Telemetry/OTLP/Tests/Unit/Transport/CurlTransportTest.php b/src/bridge/telemetry/otlp/tests/Flow/Bridge/Telemetry/OTLP/Tests/Unit/Transport/CurlTransportTest.php
index cd4dd71db..68cf68b78 100644
--- a/src/bridge/telemetry/otlp/tests/Flow/Bridge/Telemetry/OTLP/Tests/Unit/Transport/CurlTransportTest.php
+++ b/src/bridge/telemetry/otlp/tests/Flow/Bridge/Telemetry/OTLP/Tests/Unit/Transport/CurlTransportTest.php
@@ -6,12 +6,13 @@
use function Flow\Bridge\Telemetry\OTLP\DSL\{otlp_curl_options, otlp_curl_transport, otlp_json_serializer};
use Flow\Bridge\Telemetry\OTLP\Serializer\JsonSerializer;
-use Flow\Bridge\Telemetry\OTLP\Transport\{CurlTransport, CurlTransportOptions};
+use Flow\Bridge\Telemetry\OTLP\Tests\Double\RecordingTransport;
+use Flow\Bridge\Telemetry\OTLP\Transport\{CurlTransport, CurlTransportOptions, FailoverTransportException, TransportException};
use Flow\Telemetry\Context\{SpanId, TraceId};
use Flow\Telemetry\InstrumentationScope;
+use Flow\Telemetry\Signal\Signals;
use Flow\Telemetry\Tests\Mother\ResourceMother;
use Flow\Telemetry\Tracer\{Span, SpanContext, SpanKind};
-use Flow\Telemetry\Transport\TransportException;
use PHPUnit\Framework\TestCase;
final class CurlTransportTest extends TestCase
@@ -74,8 +75,8 @@ public function test_creates_transport_with_custom_timeouts() : void
'http://localhost:4318',
otlp_json_serializer(),
otlp_curl_options()
- ->withTimeout(60)
- ->withConnectTimeout(30),
+ ->withTimeout(2000)
+ ->withConnectTimeout(500),
);
self::assertInstanceOf(CurlTransport::class, $transport);
@@ -90,7 +91,7 @@ public function test_creates_transport_with_options_object() : void
$options = new CurlTransportOptions();
$options = $options
- ->withTimeout(60)
+ ->withTimeout(2000)
->withCompression()
->withSslVerification(false);
@@ -104,6 +105,75 @@ public function test_creates_transport_with_options_object() : void
$transport->shutdown();
}
+ public function test_failover_receives_failed_batches_and_shutdown_throws_composite() : void
+ {
+ if (!\extension_loaded('curl')) {
+ self::markTestSkipped('ext-curl is required');
+ }
+
+ $failover = new RecordingTransport();
+ $transport = new CurlTransport(
+ 'http://127.0.0.1:1',
+ new JsonSerializer(),
+ (new CurlTransportOptions())->withConnectTimeout(1)->withTimeout(1),
+ $failover,
+ );
+
+ $batchA = Signals::traces($this->createSpans());
+ $batchB = Signals::traces($this->createSpans());
+
+ $transport->send($batchA);
+ $transport->send($batchB);
+
+ try {
+ $transport->shutdown();
+ self::fail('Expected FailoverTransportException');
+ } catch (FailoverTransportException $e) {
+ self::assertCount(2, $e->failures);
+
+ foreach ($e->failures as $failure) {
+ self::assertInstanceOf(TransportException::class, $failure['primary']);
+ self::assertNull($failure['failover']);
+ }
+ }
+
+ self::assertCount(2, $failover->sent);
+ self::assertContains($batchA, $failover->sent);
+ self::assertContains($batchB, $failover->sent);
+ self::assertSame(1, $failover->shutdownCalls);
+ }
+
+ public function test_failover_records_double_failure_when_failover_send_also_throws() : void
+ {
+ if (!\extension_loaded('curl')) {
+ self::markTestSkipped('ext-curl is required');
+ }
+
+ $failover = new RecordingTransport();
+ $failover->sendException = new TransportException('failover down');
+
+ $transport = new CurlTransport(
+ 'http://127.0.0.1:1',
+ new JsonSerializer(),
+ (new CurlTransportOptions())->withConnectTimeout(1)->withTimeout(1),
+ $failover,
+ );
+
+ $transport->send(Signals::traces($this->createSpans()));
+
+ try {
+ $transport->shutdown();
+ self::fail('Expected FailoverTransportException');
+ } catch (FailoverTransportException $e) {
+ self::assertCount(1, $e->failures);
+ self::assertInstanceOf(TransportException::class, $e->failures[0]['primary']);
+ self::assertInstanceOf(TransportException::class, $e->failures[0]['failover']);
+ self::assertStringContainsString('failover down', $e->failures[0]['failover']->getMessage());
+ }
+
+ self::assertCount(1, $failover->sent);
+ }
+
public function test_send_after_shutdown_throws_exception() : void
{
if (!\extension_loaded('curl')) {
@@ -120,7 +190,56 @@ public function test_send_after_shutdown_throws_exception() : void
$this->expectException(TransportException::class);
$this->expectExceptionMessage('Cannot send after shutdown');
- $transport->sendSpans($this->createSpans());
+ $transport->send(Signals::traces($this->createSpans()));
+ }
+
+ public function test_shutdown_aggregates_curl_connection_failures() : void
+ {
+ if (!\extension_loaded('curl')) {
+ self::markTestSkipped('ext-curl is required');
+ }
+
+ $transport = otlp_curl_transport(
+ 'http://127.0.0.1:1',
+ otlp_json_serializer(),
+ otlp_curl_options()
+ ->withConnectTimeout(1)
+ ->withTimeout(1),
+ );
+
+ $transport->send(Signals::traces($this->createSpans()));
+ $transport->send(Signals::traces($this->createSpans()));
+
+ $this->expectException(TransportException::class);
+ $this->expectExceptionMessageMatches('/OTLP curl shutdown: 2 exports failed; first error: curl error \\d+/');
+
+ $transport->shutdown();
+ }
+
+ public function test_shutdown_cascades_to_failover_shutdown() : void
+ {
+ if (!\extension_loaded('curl')) {
+ self::markTestSkipped('ext-curl is required');
+ }
+
+ $failover = new RecordingTransport();
+ $transport = new CurlTransport(
+ 'http://127.0.0.1:1',
+ new JsonSerializer(),
+ (new CurlTransportOptions())->withConnectTimeout(1)->withTimeout(1),
+ $failover,
+ );
+
+ $transport->send(Signals::traces($this->createSpans()));
+
+ try {
+ $transport->shutdown();
+ self::fail('Expected FailoverTransportException');
+ } catch (FailoverTransportException $e) {
+ self::assertCount(1, $e->failures);
+ }
+
+ self::assertSame(1, $failover->shutdownCalls);
}
public function test_shutdown_is_idempotent() : void
@@ -140,6 +259,92 @@ public function test_shutdown_is_idempotent() : void
$this->addToAssertionCount(1);
}
+ public function test_shutdown_surfaces_failover_shutdown_exception_when_no_deferred_failures() : void
+ {
+ if (!\extension_loaded('curl')) {
+ self::markTestSkipped('ext-curl is required');
+ }
+
+ $failover = new RecordingTransport();
+ $failover->shutdownException = new \RuntimeException('boom');
+
+ $transport = new CurlTransport(
+ 'http://localhost:4318',
+ new JsonSerializer(),
+ new CurlTransportOptions(),
+ $failover,
+ );
+
+ $this->expectException(TransportException::class);
+ $this->expectExceptionMessage('failover shutdown failed: boom');
+
+ $transport->shutdown();
+ }
+
+ public function test_shutdown_with_zero_timeout_forwards_pending_to_failover() : void
+ {
+ if (!\extension_loaded('curl')) {
+ self::markTestSkipped('ext-curl is required');
+ }
+
+ $failover = new RecordingTransport();
+ $batch = Signals::traces($this->createSpans());
+
+ $transport = new CurlTransport(
+ 'http://127.0.0.1:1',
+ new JsonSerializer(),
+ (new CurlTransportOptions())
+ ->withConnectTimeout(60_000)
+ ->withTimeout(60_000)
+ ->withShutdownTimeout(0),
+ $failover,
+ );
+
+ $transport->send($batch);
+
+ try {
+ $transport->shutdown();
+ self::addToAssertionCount(1);
+ } catch (FailoverTransportException $e) {
+ self::assertGreaterThanOrEqual(1, \count($e->failures));
+ // Either the still-pending path forwarded the batch, or the normal failover drain did.
+ self::assertContains($batch, $failover->sent);
+ }
+
+ self::assertSame(1, $failover->shutdownCalls);
+ }
+
+ public function test_shutdown_with_zero_timeout_marks_pending_as_failed_in_legacy_mode() : void
+ {
+ if (!\extension_loaded('curl')) {
+ self::markTestSkipped('ext-curl is required');
+ }
+
+ $transport = new CurlTransport(
+ 'http://127.0.0.1:1',
+ new JsonSerializer(),
+ (new CurlTransportOptions())
+ ->withConnectTimeout(60_000)
+ ->withTimeout(60_000)
+ ->withShutdownTimeout(0),
+ );
+
+ $transport->send(Signals::traces($this->createSpans()));
+
+ try {
+ $transport->shutdown();
+
+ // If everything completed before shutdown_timeout=0 fired, that's also valid (race condition).
+ self::addToAssertionCount(1);
+ } catch (TransportException $e) {
+ // Either the shutdown-deadline-reached path OR the normal connection-refused path.
+ self::assertTrue(
+ \str_contains($e->getMessage(), 'shutdown_timeout=0ms expired')
+ || \str_contains($e->getMessage(), 'curl error'),
+ );
+ }
+ }
+
/**
* @return array
*/
diff --git a/src/bridge/telemetry/otlp/tests/Flow/Bridge/Telemetry/OTLP/Tests/Unit/Transport/GrpcTransportTest.php b/src/bridge/telemetry/otlp/tests/Flow/Bridge/Telemetry/OTLP/Tests/Unit/Transport/GrpcTransportTest.php
index 04e80175d..57b8ca216 100644
--- a/src/bridge/telemetry/otlp/tests/Flow/Bridge/Telemetry/OTLP/Tests/Unit/Transport/GrpcTransportTest.php
+++ b/src/bridge/telemetry/otlp/tests/Flow/Bridge/Telemetry/OTLP/Tests/Unit/Transport/GrpcTransportTest.php
@@ -4,16 +4,39 @@
namespace Flow\Bridge\Telemetry\OTLP\Tests\Unit\Transport;
-use Flow\Bridge\Telemetry\OTLP\Serializer\{GrpcSerializer, ProtobufSerializer};
-use Flow\Bridge\Telemetry\OTLP\Transport\GrpcTransport;
+use Flow\Bridge\Telemetry\OTLP\Tests\Double\RecordingTransport;
+use Flow\Bridge\Telemetry\OTLP\Transport\{FailoverTransportException, GrpcTransport, TransportException};
+use Flow\Telemetry\Signal\Signals;
+use Flow\Telemetry\Tests\Mother\SpanMother;
use Google\Protobuf\Internal\Message;
use Grpc\BaseStub;
-use Opentelemetry\Proto\Collector\Trace\V1\TraceServiceClient;
use PHPUnit\Framework\Attributes\RequiresPhpExtension;
use PHPUnit\Framework\TestCase;
final class GrpcTransportTest extends TestCase
{
+ #[RequiresPhpExtension('grpc')]
+ public function test_constructor_rejects_negative_shutdown_timeout() : void
+ {
+ $this->skipIfGrpcDependenciesNotAvailable();
+
+ $this->expectException(\InvalidArgumentException::class);
+ $this->expectExceptionMessage('Shutdown timeout must be non-negative');
+
+ new GrpcTransport('localhost:4317', shutdownTimeoutMs: -1);
+ }
+
+ #[RequiresPhpExtension('grpc')]
+ public function test_constructor_rejects_negative_timeout() : void
+ {
+ $this->skipIfGrpcDependenciesNotAvailable();
+
+ $this->expectException(\InvalidArgumentException::class);
+ $this->expectExceptionMessage('Timeout must be non-negative');
+
+ new GrpcTransport('localhost:4317', timeoutMs: -1);
+ }
+
public function test_constructor_throws_when_grpc_extension_not_loaded() : void
{
if (\extension_loaded('grpc')) {
@@ -23,7 +46,7 @@ public function test_constructor_throws_when_grpc_extension_not_loaded() : void
$this->expectException(\RuntimeException::class);
$this->expectExceptionMessage('grpc PHP extension is required');
- new GrpcTransport('localhost:4317', $this->createMock(GrpcSerializer::class));
+ new GrpcTransport('localhost:4317');
}
#[RequiresPhpExtension('grpc')]
@@ -33,7 +56,6 @@ public function test_creates_transport_with_custom_headers() : void
$transport = new GrpcTransport(
endpoint: 'localhost:4317',
- serializer: new ProtobufSerializer(),
headers: ['Authorization' => 'Bearer token'],
);
@@ -45,7 +67,7 @@ public function test_creates_transport_with_default_options() : void
{
$this->skipIfGrpcDependenciesNotAvailable();
- $transport = new GrpcTransport('localhost:4317', new ProtobufSerializer());
+ $transport = new GrpcTransport('localhost:4317');
self::assertInstanceOf(GrpcTransport::class, $transport);
}
@@ -57,19 +79,108 @@ public function test_creates_transport_with_secure_mode() : void
$transport = new GrpcTransport(
endpoint: 'localhost:4317',
- serializer: new ProtobufSerializer(),
insecure: false,
);
self::assertInstanceOf(GrpcTransport::class, $transport);
}
+ #[RequiresPhpExtension('grpc')]
+ public function test_failover_receives_prior_batch_on_subsequent_send_and_throws_composite() : void
+ {
+ $this->skipIfGrpcDependenciesNotAvailable();
+
+ $failover = new RecordingTransport();
+ $transport = new GrpcTransport(endpoint: '127.0.0.1:1', failover: $failover);
+
+ $batchA = Signals::traces([SpanMother::withName('span-a')]);
+ $batchB = Signals::traces([SpanMother::withName('span-b')]);
+
+ $transport->send($batchA);
+
+ try {
+ $transport->send($batchB);
+ self::fail('Expected FailoverTransportException for batch A');
+ } catch (FailoverTransportException $e) {
+ self::assertCount(1, $e->failures);
+ self::assertNull($e->failures[0]['failover']);
+ }
+
+ try {
+ $transport->shutdown();
+ self::fail('Expected FailoverTransportException for batch B');
+ } catch (FailoverTransportException $e) {
+ self::assertCount(1, $e->failures);
+ self::assertNull($e->failures[0]['failover']);
+ }
+
+ self::assertCount(2, $failover->sent);
+ self::assertContains($batchA, $failover->sent);
+ self::assertContains($batchB, $failover->sent);
+ self::assertSame(1, $failover->shutdownCalls);
+ }
+
+ #[RequiresPhpExtension('grpc')]
+ public function test_failover_records_double_failure_when_failover_send_also_throws() : void
+ {
+ $this->skipIfGrpcDependenciesNotAvailable();
+
+ $failover = new RecordingTransport();
+ $failover->sendException = new TransportException('failover down');
+
+ $transport = new GrpcTransport(endpoint: '127.0.0.1:1', failover: $failover);
+
+ $transport->send(Signals::traces([SpanMother::withName('span-a')]));
+
+ try {
+ $transport->shutdown();
+ self::fail('Expected FailoverTransportException');
+ } catch (FailoverTransportException $e) {
+ self::assertCount(1, $e->failures);
+ self::assertInstanceOf(\Throwable::class, $e->failures[0]['primary']);
+ self::assertInstanceOf(TransportException::class, $e->failures[0]['failover']);
+ self::assertStringContainsString('failover down', $e->failures[0]['failover']->getMessage());
+ }
+
+ self::assertCount(1, $failover->sent);
+ }
+
+ #[RequiresPhpExtension('grpc')]
+ public function test_send_after_shutdown_throws() : void
+ {
+ $this->skipIfGrpcDependenciesNotAvailable();
+
+ $transport = new GrpcTransport('localhost:4317');
+ $transport->shutdown();
+
+ $this->expectException(TransportException::class);
+ $this->expectExceptionMessage('Cannot send after shutdown');
+
+ $transport->send(Signals::traces([]));
+ }
+
+ #[RequiresPhpExtension('grpc')]
+ public function test_shutdown_aggregates_grpc_failures() : void
+ {
+ $this->skipIfGrpcDependenciesNotAvailable();
+
+ $transport = new GrpcTransport(endpoint: '127.0.0.1:1');
+
+ $transport->send(Signals::traces([SpanMother::withName('span-a')]));
+ $transport->send(Signals::traces([SpanMother::withName('span-b')]));
+
+ $this->expectException(TransportException::class);
+ $this->expectExceptionMessageMatches('/OTLP gRPC shutdown: 2 exports failed; first error: gRPC status \\d+/');
+
+ $transport->shutdown();
+ }
+
#[RequiresPhpExtension('grpc')]
public function test_shutdown_can_be_called_multiple_times() : void
{
$this->skipIfGrpcDependenciesNotAvailable();
- $transport = new GrpcTransport('localhost:4317', new ProtobufSerializer());
+ $transport = new GrpcTransport('localhost:4317');
$transport->shutdown();
$transport->shutdown();
@@ -77,6 +188,93 @@ public function test_shutdown_can_be_called_multiple_times() : void
self::addToAssertionCount(1);
}
+ #[RequiresPhpExtension('grpc')]
+ public function test_shutdown_cascades_to_failover_shutdown() : void
+ {
+ $this->skipIfGrpcDependenciesNotAvailable();
+
+ $failover = new RecordingTransport();
+ $transport = new GrpcTransport(endpoint: '127.0.0.1:1', failover: $failover);
+
+ $transport->send(Signals::traces([SpanMother::withName('span-a')]));
+
+ try {
+ $transport->shutdown();
+ self::fail('Expected FailoverTransportException');
+ } catch (FailoverTransportException $e) {
+ self::assertCount(1, $e->failures);
+ }
+
+ self::assertSame(1, $failover->shutdownCalls);
+ }
+
+ #[RequiresPhpExtension('grpc')]
+ public function test_shutdown_surfaces_failover_shutdown_exception_when_no_deferred_failures() : void
+ {
+ $this->skipIfGrpcDependenciesNotAvailable();
+
+ $failover = new RecordingTransport();
+ $failover->shutdownException = new \RuntimeException('boom');
+
+ $transport = new GrpcTransport(endpoint: 'localhost:4317', failover: $failover);
+
+ $this->expectException(TransportException::class);
+ $this->expectExceptionMessage('failover shutdown failed: boom');
+
+ $transport->shutdown();
+ }
+
+ #[RequiresPhpExtension('grpc')]
+ public function test_shutdown_with_zero_timeout_cancels_pending_calls_legacy_mode() : void
+ {
+ $this->skipIfGrpcDependenciesNotAvailable();
+
+ $transport = new GrpcTransport(
+ endpoint: '127.0.0.1:1',
+ timeoutMs: 60_000,
+ shutdownTimeoutMs: 0,
+ );
+
+ $transport->send(Signals::traces([SpanMother::withName('span-a')]));
+
+ try {
+ $transport->shutdown();
+ self::addToAssertionCount(1);
+ } catch (TransportException $e) {
+ self::assertTrue(
+ \str_contains($e->getMessage(), 'shutdown_timeout=0ms expired')
+ || \str_contains($e->getMessage(), 'gRPC status'),
+ );
+ }
+ }
+
+ #[RequiresPhpExtension('grpc')]
+ public function test_shutdown_with_zero_timeout_forwards_pending_to_failover() : void
+ {
+ $this->skipIfGrpcDependenciesNotAvailable();
+
+ $failover = new RecordingTransport();
+ $batch = Signals::traces([SpanMother::withName('span-a')]);
+
+ $transport = new GrpcTransport(
+ endpoint: '127.0.0.1:1',
+ timeoutMs: 60_000,
+ shutdownTimeoutMs: 0,
+ failover: $failover,
+ );
+
+ $transport->send($batch);
+
+ try {
+ $transport->shutdown();
+ self::addToAssertionCount(1);
+ } catch (FailoverTransportException $e) {
+ self::assertGreaterThanOrEqual(1, \count($e->failures));
+ }
+
+ self::assertSame(1, $failover->shutdownCalls);
+ }
+
private function skipIfGrpcDependenciesNotAvailable() : void
{
if (!\class_exists(BaseStub::class)) {
@@ -86,9 +284,5 @@ private function skipIfGrpcDependenciesNotAvailable() : void
if (!\class_exists(Message::class)) {
self::markTestSkipped('The google/protobuf package is not installed');
}
-
- if (!\class_exists(TraceServiceClient::class)) {
- self::markTestSkipped('The open-telemetry/gen-otlp-protobuf package is not installed');
- }
}
}
diff --git a/src/bridge/telemetry/otlp/tests/Flow/Bridge/Telemetry/OTLP/Tests/Unit/Transport/HttpTransportTest.php b/src/bridge/telemetry/otlp/tests/Flow/Bridge/Telemetry/OTLP/Tests/Unit/Transport/HttpTransportTest.php
deleted file mode 100644
index 135f5ee0a..000000000
--- a/src/bridge/telemetry/otlp/tests/Flow/Bridge/Telemetry/OTLP/Tests/Unit/Transport/HttpTransportTest.php
+++ /dev/null
@@ -1,156 +0,0 @@
-createMock(StreamInterface::class);
- $streamFactory = $this->createMock(StreamFactoryInterface::class);
- $streamFactory->method('createStream')
- ->willReturn($stream);
-
- $request = $this->createMock(RequestInterface::class);
- $request->method('withHeader')
- ->willReturnSelf();
- $request->method('withBody')
- ->willReturnSelf();
-
- $requestFactory = $this->createMock(RequestFactoryInterface::class);
- $requestFactory->expects(self::once())
- ->method('createRequest')
- ->with('POST', 'http://localhost:4318/v1/traces')
- ->willReturn($request);
-
- $response = $this->createMock(ResponseInterface::class);
- $response->method('getStatusCode')
- ->willReturn(200);
-
- $client = $this->createMock(ClientInterface::class);
- $client->expects(self::once())
- ->method('sendRequest')
- ->willReturn($response);
-
- $transport = otlp_http_transport($client, $requestFactory, $streamFactory, 'http://localhost:4318', otlp_json_serializer());
- $transport->sendSpans($this->createSpans());
- }
-
- public function test_send_includes_custom_headers() : void
- {
- $stream = $this->createMock(StreamInterface::class);
- $streamFactory = $this->createMock(StreamFactoryInterface::class);
- $streamFactory->method('createStream')
- ->willReturn($stream);
-
- $request = $this->createMock(RequestInterface::class);
- $request->expects(self::exactly(2))
- ->method('withHeader')
- ->willReturnSelf();
- $request->method('withBody')
- ->willReturnSelf();
-
- $requestFactory = $this->createMock(RequestFactoryInterface::class);
- $requestFactory->method('createRequest')
- ->willReturn($request);
-
- $response = $this->createMock(ResponseInterface::class);
- $response->method('getStatusCode')
- ->willReturn(200);
-
- $client = $this->createMock(ClientInterface::class);
- $client->method('sendRequest')
- ->willReturn($response);
-
- $transport = new HttpTransport(
- $client,
- $requestFactory,
- $streamFactory,
- 'http://localhost:4318',
- new JsonSerializer(),
- ['Authorization' => 'Bearer token'],
- );
- $transport->sendSpans($this->createSpans());
- }
-
- public function test_send_throws_on_http_error() : void
- {
- $stream = $this->createMock(StreamInterface::class);
- $streamFactory = $this->createMock(StreamFactoryInterface::class);
- $streamFactory->method('createStream')
- ->willReturn($stream);
-
- $request = $this->createMock(RequestInterface::class);
- $request->method('withHeader')
- ->willReturnSelf();
- $request->method('withBody')
- ->willReturnSelf();
-
- $requestFactory = $this->createMock(RequestFactoryInterface::class);
- $requestFactory->method('createRequest')
- ->willReturn($request);
-
- $responseBody = $this->createMock(StreamInterface::class);
- $responseBody->method('__toString')
- ->willReturn('Internal Server Error');
-
- $response = $this->createMock(ResponseInterface::class);
- $response->method('getStatusCode')
- ->willReturn(500);
- $response->method('getBody')
- ->willReturn($responseBody);
-
- $client = $this->createMock(ClientInterface::class);
- $client->method('sendRequest')
- ->willReturn($response);
-
- $transport = otlp_http_transport($client, $requestFactory, $streamFactory, 'http://localhost:4318', otlp_json_serializer());
-
- $this->expectException(TransportException::class);
- $this->expectExceptionMessage('HTTP 500');
-
- $transport->sendSpans($this->createSpans());
- }
-
- public function test_shutdown_does_nothing() : void
- {
- $client = $this->createMock(ClientInterface::class);
- $requestFactory = $this->createMock(RequestFactoryInterface::class);
- $streamFactory = $this->createMock(StreamFactoryInterface::class);
-
- $transport = otlp_http_transport($client, $requestFactory, $streamFactory, 'http://localhost:4318', otlp_json_serializer());
- $transport->shutdown();
-
- $this->addToAssertionCount(1);
- }
-
- /**
- * @return array
- */
- private function createSpans() : array
- {
- return [new Span(
- 'test-span',
- SpanContext::create(TraceId::generate(), SpanId::generate()),
- SpanKind::INTERNAL,
- new \DateTimeImmutable(),
- ResourceMother::default(),
- new InstrumentationScope('test', '1.0.0'),
- )];
- }
-}
diff --git a/src/bridge/telemetry/otlp/tests/Flow/Bridge/Telemetry/OTLP/Tests/Unit/Transport/StreamTransportTest.php b/src/bridge/telemetry/otlp/tests/Flow/Bridge/Telemetry/OTLP/Tests/Unit/Transport/StreamTransportTest.php
new file mode 100644
index 000000000..9c06bfb20
--- /dev/null
+++ b/src/bridge/telemetry/otlp/tests/Flow/Bridge/Telemetry/OTLP/Tests/Unit/Transport/StreamTransportTest.php
@@ -0,0 +1,265 @@
+stream());
+ }
+
+ public function test_appends_multiple_batches_as_separate_lines() : void
+ {
+ $transport = new StreamTransport('php://memory');
+
+ $transport->send(Signals::logs([LogEntryMother::deterministic('first', Severity::INFO)]));
+ $transport->send(Signals::logs([LogEntryMother::deterministic('second', Severity::WARN)]));
+
+ \rewind($transport->stream());
+ $contents = (string) \stream_get_contents($transport->stream());
+ $lines = \array_values(\array_filter(\explode("\n", $contents), static fn (string $l) : bool => $l !== ''));
+
+ self::assertCount(2, $lines);
+ }
+
+ public function test_constructor_sets_stream_chunk_size() : void
+ {
+ $transport = new StreamTransport('php://memory');
+
+ $previous = \stream_set_chunk_size($transport->stream(), 8192);
+
+ self::assertSame(StreamTransport::STREAM_CHUNK_SIZE, $previous);
+ }
+
+ public function test_empty_destination_throws() : void
+ {
+ $this->expectException(\InvalidArgumentException::class);
+ $this->expectExceptionMessage('non-empty');
+
+ new StreamTransport('');
+ }
+
+ public function test_empty_signal_does_not_write() : void
+ {
+ $transport = new StreamTransport('php://memory');
+
+ $transport->send(Signals::logs([]));
+ $transport->send(Signals::metrics([]));
+ $transport->send(Signals::traces([]));
+
+ \rewind($transport->stream());
+ self::assertSame('', (string) \stream_get_contents($transport->stream()));
+ }
+
+ public function test_failed_write_throws() : void
+ {
+ \stream_wrapper_register('flow-failwrite', FailingWriteStreamWrapper::class);
+
+ try {
+ $transport = new StreamTransport('flow-failwrite://buf');
+
+ $this->expectException(TransportException::class);
+ $this->expectExceptionMessage('Failed to write OTLP payload to "flow-failwrite://buf"');
+
+ $transport->send(Signals::logs([LogEntryMother::deterministic('hello', Severity::INFO)]));
+ } finally {
+ \stream_wrapper_unregister('flow-failwrite');
+ }
+ }
+
+ #[TestWith([-1])]
+ #[TestWith([01000])]
+ public function test_out_of_range_file_permissions_throw(int $perm) : void
+ {
+ $this->expectException(\InvalidArgumentException::class);
+ $this->expectExceptionMessage('between 0 and 0777');
+
+ new StreamTransport('php://memory', filePermissions: $perm);
+ }
+
+ public function test_partial_write_throws() : void
+ {
+ \stream_wrapper_register('flow-partial', PartialWriteStreamWrapper::class);
+
+ try {
+ $transport = new StreamTransport('flow-partial://buf');
+
+ $this->expectException(TransportException::class);
+ $this->expectExceptionMessageMatches('/Partial write to OTLP stream "flow-partial:\\/\\/buf": wrote \\d+ of \\d+ bytes/');
+
+ $transport->send(Signals::logs([LogEntryMother::deterministic('hello', Severity::INFO)]));
+ } finally {
+ \stream_wrapper_unregister('flow-partial');
+ }
+ }
+
+ public function test_send_after_shutdown_throws() : void
+ {
+ $transport = new StreamTransport('php://memory');
+
+ $transport->shutdown();
+
+ $this->expectException(TransportException::class);
+ $this->expectExceptionMessage('Cannot send after shutdown');
+
+ $transport->send(Signals::logs([LogEntryMother::deterministic('hello', Severity::INFO)]));
+ }
+
+ public function test_shutdown_is_idempotent() : void
+ {
+ $transport = new StreamTransport('php://memory');
+
+ $transport->shutdown();
+ $transport->shutdown();
+
+ $this->expectException(TransportException::class);
+ $transport->send(Signals::logs([LogEntryMother::deterministic('hello', Severity::INFO)]));
+ }
+
+ #[TestWith([SignalType::LOGS, 'resourceLogs'])]
+ #[TestWith([SignalType::METRICS, 'resourceMetrics'])]
+ #[TestWith([SignalType::TRACES, 'resourceSpans'])]
+ public function test_writes_each_signal_type_as_single_line_with_trailing_newline(SignalType $type, string $expectedKey) : void
+ {
+ $transport = new StreamTransport('php://memory');
+
+ $transport->send(match ($type) {
+ SignalType::LOGS => Signals::logs([LogEntryMother::deterministic('hello', Severity::INFO)]),
+ SignalType::METRICS => Signals::metrics([MetricMother::deterministicCounter('test.counter', 42)]),
+ SignalType::TRACES => Signals::traces([SpanMother::withName('span')]),
+ });
+
+ \rewind($transport->stream());
+ $contents = (string) \stream_get_contents($transport->stream());
+
+ self::assertStringEndsWith("\n", $contents);
+ self::assertSame(1, \substr_count($contents, "\n"));
+
+ /** @var array $decoded */
+ $decoded = \json_decode(\rtrim($contents, "\n"), true, flags: \JSON_THROW_ON_ERROR);
+ self::assertArrayHasKey($expectedKey, $decoded);
+ }
+}
+
+final class PartialWriteStreamWrapper
+{
+ /** @var resource */
+ public $context;
+
+ public function stream_close() : void
+ {
+ }
+
+ public function stream_eof() : bool
+ {
+ return true;
+ }
+
+ public function stream_flush() : bool
+ {
+ return true;
+ }
+
+ public function stream_lock(int $operation) : bool
+ {
+ return true;
+ }
+
+ public function stream_open(string $path, string $mode, int $options, ?string &$openedPath) : bool
+ {
+ return true;
+ }
+
+ public function stream_set_option(int $option, int $arg1, ?int $arg2) : bool
+ {
+ return true;
+ }
+
+ /**
+ * @return array
+ */
+ public function stream_stat() : array
+ {
+ return [];
+ }
+
+ public function stream_write(string $data) : int
+ {
+ return (int) \floor(\strlen($data) / 2);
+ }
+
+ public function url_stat(string $path, int $flags) : false
+ {
+ return false;
+ }
+}
+
+final class FailingWriteStreamWrapper
+{
+ /** @var resource */
+ public $context;
+
+ public function stream_close() : void
+ {
+ }
+
+ public function stream_eof() : bool
+ {
+ return true;
+ }
+
+ public function stream_flush() : bool
+ {
+ return true;
+ }
+
+ public function stream_lock(int $operation) : bool
+ {
+ return true;
+ }
+
+ public function stream_open(string $path, string $mode, int $options, ?string &$openedPath) : bool
+ {
+ return true;
+ }
+
+ public function stream_set_option(int $option, int $arg1, ?int $arg2) : bool
+ {
+ return true;
+ }
+
+ /**
+ * @return array
+ */
+ public function stream_stat() : array
+ {
+ return [];
+ }
+
+ public function stream_write(string $data) : false
+ {
+ return false;
+ }
+
+ public function url_stat(string $path, int $flags) : false
+ {
+ return false;
+ }
+}
diff --git a/src/core/etl/tests/Flow/ETL/Tests/Integration/Cache/TraceableCacheTestSuite.php b/src/core/etl/tests/Flow/ETL/Tests/Integration/Cache/TraceableCacheTestSuite.php
index aacb2a281..03daa40a6 100644
--- a/src/core/etl/tests/Flow/ETL/Tests/Integration/Cache/TraceableCacheTestSuite.php
+++ b/src/core/etl/tests/Flow/ETL/Tests/Integration/Cache/TraceableCacheTestSuite.php
@@ -11,7 +11,7 @@
use Flow\Telemetry\Meter\MeterProvider;
use Flow\Telemetry\Provider\Clock\SystemClock;
use Flow\Telemetry\Provider\Memory\{MemoryLogProcessor, MemoryMetricProcessor, MemorySpanProcessor};
-use Flow\Telemetry\Provider\Void\{VoidLogExporter, VoidMetricExporter, VoidSpanExporter};
+use Flow\Telemetry\Provider\Void\VoidExporter;
use Flow\Telemetry\{Resource, Telemetry};
use Flow\Telemetry\Tracer\TracerProvider;
@@ -27,9 +27,9 @@ protected function setUp() : void
$clock = new SystemClock();
$contextStorage = new MemoryContextStorage();
- $spanProcessor = new MemorySpanProcessor(new VoidSpanExporter());
- $metricProcessor = new MemoryMetricProcessor(new VoidMetricExporter());
- $logProcessor = new MemoryLogProcessor(new VoidLogExporter());
+ $spanProcessor = new MemorySpanProcessor(new VoidExporter());
+ $metricProcessor = new MemoryMetricProcessor(new VoidExporter());
+ $logProcessor = new MemoryLogProcessor(new VoidExporter());
$this->telemetry = new Telemetry(
Resource::create(['service.name' => 'test-service']),
diff --git a/src/core/etl/tests/Flow/ETL/Tests/Integration/DataFrame/CacheTest.php b/src/core/etl/tests/Flow/ETL/Tests/Integration/DataFrame/CacheTest.php
index 66d0ce8d2..785effa49 100644
--- a/src/core/etl/tests/Flow/ETL/Tests/Integration/DataFrame/CacheTest.php
+++ b/src/core/etl/tests/Flow/ETL/Tests/Integration/DataFrame/CacheTest.php
@@ -15,7 +15,7 @@
use Flow\Telemetry\Meter\MeterProvider;
use Flow\Telemetry\Provider\Clock\SystemClock;
use Flow\Telemetry\Provider\Memory\{MemoryLogProcessor, MemoryMetricProcessor, MemorySpanProcessor};
-use Flow\Telemetry\Provider\Void\{VoidLogExporter, VoidMetricExporter, VoidSpanExporter};
+use Flow\Telemetry\Provider\Void\VoidExporter;
use Flow\Telemetry\{Resource, Telemetry};
use Flow\Telemetry\Tracer\TracerProvider;
@@ -98,9 +98,9 @@ public function test_cache_with_telemetry_collects_spans_and_metrics() : void
$clock = new SystemClock();
$contextStorage = new MemoryContextStorage();
- $spanProcessor = new MemorySpanProcessor(new VoidSpanExporter());
- $metricProcessor = new MemoryMetricProcessor(new VoidMetricExporter());
- $logProcessor = new MemoryLogProcessor(new VoidLogExporter());
+ $spanProcessor = new MemorySpanProcessor(new VoidExporter());
+ $metricProcessor = new MemoryMetricProcessor(new VoidExporter());
+ $logProcessor = new MemoryLogProcessor(new VoidExporter());
$telemetry = new Telemetry(
Resource::create(['service.name' => 'test-service']),
diff --git a/src/core/etl/tests/Flow/ETL/Tests/Integration/DataFrame/ConfigBuilderTest.php b/src/core/etl/tests/Flow/ETL/Tests/Integration/DataFrame/ConfigBuilderTest.php
index 826061e09..e60adc40b 100644
--- a/src/core/etl/tests/Flow/ETL/Tests/Integration/DataFrame/ConfigBuilderTest.php
+++ b/src/core/etl/tests/Flow/ETL/Tests/Integration/DataFrame/ConfigBuilderTest.php
@@ -6,7 +6,7 @@
use function Flow\ETL\DSL\{analyze, config_builder, telemetry_options};
use function Flow\Filesystem\DSL\{filesystem_telemetry_options, native_local_filesystem};
-use function Flow\Telemetry\DSL\{logger_provider, memory_context_storage, memory_log_processor, memory_metric_processor, memory_span_processor, meter_provider, resource, telemetry, tracer_provider, void_log_exporter, void_metric_exporter, void_span_exporter};
+use function Flow\Telemetry\DSL\{logger_provider, memory_context_storage, memory_log_processor, memory_metric_processor, memory_span_processor, meter_provider, resource, telemetry, tracer_provider, void_exporter};
use Flow\ETL\Config\Cache\CacheConfig;
use Flow\ETL\Sort\SortAlgorithms;
use Flow\ETL\Tests\FlowIntegrationTestCase;
@@ -164,9 +164,9 @@ private function createTelemetry() : Telemetry
return telemetry(
resource(),
- tracer_provider(memory_span_processor(void_span_exporter()), $clock, $contextStorage),
- meter_provider(memory_metric_processor(void_metric_exporter()), $clock),
- logger_provider(memory_log_processor(void_log_exporter()), $clock, $contextStorage),
+ tracer_provider(memory_span_processor(void_exporter()), $clock, $contextStorage),
+ meter_provider(memory_metric_processor(void_exporter()), $clock),
+ logger_provider(memory_log_processor(void_exporter()), $clock, $contextStorage),
);
}
}
diff --git a/src/core/etl/tests/Flow/ETL/Tests/Integration/DataFrame/TelemetryTest.php b/src/core/etl/tests/Flow/ETL/Tests/Integration/DataFrame/TelemetryTest.php
index cda5056dc..b72ba6c87 100644
--- a/src/core/etl/tests/Flow/ETL/Tests/Integration/DataFrame/TelemetryTest.php
+++ b/src/core/etl/tests/Flow/ETL/Tests/Integration/DataFrame/TelemetryTest.php
@@ -10,7 +10,7 @@
use Flow\Telemetry\Logger\{LoggerProvider, Severity};
use Flow\Telemetry\Meter\MeterProvider;
use Flow\Telemetry\Provider\Memory\{MemoryLogProcessor, MemoryMetricProcessor, MemorySpanProcessor};
-use Flow\Telemetry\Provider\Void\{VoidLogExporter, VoidMetricExporter, VoidSpanExporter};
+use Flow\Telemetry\Provider\Void\VoidExporter;
use Flow\Telemetry\{Resource, Telemetry};
use Flow\Telemetry\Tracer\TracerProvider;
use Psr\Clock\ClockInterface;
@@ -19,9 +19,9 @@ final class TelemetryTest extends FlowTestCase
{
public function test_dataframe_collects_metrics_when_enabled() : void
{
- $spanProcessor = new MemorySpanProcessor(new VoidSpanExporter());
- $metricProcessor = new MemoryMetricProcessor(new VoidMetricExporter());
- $logProcessor = new MemoryLogProcessor(new VoidLogExporter());
+ $spanProcessor = new MemorySpanProcessor(new VoidExporter());
+ $metricProcessor = new MemoryMetricProcessor(new VoidExporter());
+ $logProcessor = new MemoryLogProcessor(new VoidExporter());
$clock = $this->createFrozenClock();
$contextStorage = new MemoryContextStorage();
@@ -53,9 +53,9 @@ public function test_dataframe_collects_metrics_when_enabled() : void
public function test_dataframe_loading_traced_when_enabled() : void
{
- $spanProcessor = new MemorySpanProcessor(new VoidSpanExporter());
- $metricProcessor = new MemoryMetricProcessor(new VoidMetricExporter());
- $logProcessor = new MemoryLogProcessor(new VoidLogExporter());
+ $spanProcessor = new MemorySpanProcessor(new VoidExporter());
+ $metricProcessor = new MemoryMetricProcessor(new VoidExporter());
+ $logProcessor = new MemoryLogProcessor(new VoidExporter());
$clock = $this->createFrozenClock();
$contextStorage = new MemoryContextStorage();
@@ -104,9 +104,9 @@ public function test_dataframe_loading_traced_when_enabled() : void
public function test_dataframe_run_creates_telemetry_span() : void
{
- $spanProcessor = new MemorySpanProcessor(new VoidSpanExporter());
- $metricProcessor = new MemoryMetricProcessor(new VoidMetricExporter());
- $logProcessor = new MemoryLogProcessor(new VoidLogExporter());
+ $spanProcessor = new MemorySpanProcessor(new VoidExporter());
+ $metricProcessor = new MemoryMetricProcessor(new VoidExporter());
+ $logProcessor = new MemoryLogProcessor(new VoidExporter());
$clock = $this->createFrozenClock();
$contextStorage = new MemoryContextStorage();
@@ -139,9 +139,9 @@ public function test_dataframe_run_creates_telemetry_span() : void
public function test_dataframe_run_logs_start_and_completion() : void
{
- $spanProcessor = new MemorySpanProcessor(new VoidSpanExporter());
- $metricProcessor = new MemoryMetricProcessor(new VoidMetricExporter());
- $logProcessor = new MemoryLogProcessor(new VoidLogExporter());
+ $spanProcessor = new MemorySpanProcessor(new VoidExporter());
+ $metricProcessor = new MemoryMetricProcessor(new VoidExporter());
+ $logProcessor = new MemoryLogProcessor(new VoidExporter());
$clock = $this->createFrozenClock();
$contextStorage = new MemoryContextStorage();
@@ -185,9 +185,9 @@ public function test_dataframe_run_logs_start_and_completion() : void
public function test_dataframe_span_contains_row_statistics() : void
{
- $spanProcessor = new MemorySpanProcessor(new VoidSpanExporter());
- $metricProcessor = new MemoryMetricProcessor(new VoidMetricExporter());
- $logProcessor = new MemoryLogProcessor(new VoidLogExporter());
+ $spanProcessor = new MemorySpanProcessor(new VoidExporter());
+ $metricProcessor = new MemoryMetricProcessor(new VoidExporter());
+ $logProcessor = new MemoryLogProcessor(new VoidExporter());
$clock = $this->createFrozenClock();
$contextStorage = new MemoryContextStorage();
@@ -226,9 +226,9 @@ public function test_dataframe_span_contains_row_statistics() : void
public function test_dataframe_transformations_traced_when_enabled() : void
{
- $spanProcessor = new MemorySpanProcessor(new VoidSpanExporter());
- $metricProcessor = new MemoryMetricProcessor(new VoidMetricExporter());
- $logProcessor = new MemoryLogProcessor(new VoidLogExporter());
+ $spanProcessor = new MemorySpanProcessor(new VoidExporter());
+ $metricProcessor = new MemoryMetricProcessor(new VoidExporter());
+ $logProcessor = new MemoryLogProcessor(new VoidExporter());
$clock = $this->createFrozenClock();
$contextStorage = new MemoryContextStorage();
diff --git a/src/core/etl/tests/Flow/ETL/Tests/Unit/Cache/Implementation/TraceableCacheTest.php b/src/core/etl/tests/Flow/ETL/Tests/Unit/Cache/Implementation/TraceableCacheTest.php
index e9f697a35..38a1ba12a 100644
--- a/src/core/etl/tests/Flow/ETL/Tests/Unit/Cache/Implementation/TraceableCacheTest.php
+++ b/src/core/etl/tests/Flow/ETL/Tests/Unit/Cache/Implementation/TraceableCacheTest.php
@@ -15,7 +15,7 @@
use Flow\Telemetry\Meter\MeterProvider;
use Flow\Telemetry\Provider\Clock\SystemClock;
use Flow\Telemetry\Provider\Memory\{MemoryLogProcessor, MemoryMetricProcessor, MemorySpanProcessor};
-use Flow\Telemetry\Provider\Void\{VoidLogExporter, VoidMetricExporter, VoidSpanExporter};
+use Flow\Telemetry\Provider\Void\VoidExporter;
use Flow\Telemetry\{Resource, Telemetry};
use Flow\Telemetry\Tracer\{SpanKind, TracerProvider};
use PHPUnit\Framework\Attributes\CoversClass;
@@ -34,9 +34,9 @@ protected function setUp() : void
$clock = new SystemClock();
$contextStorage = new MemoryContextStorage();
- $this->spanProcessor = new MemorySpanProcessor(new VoidSpanExporter());
- $this->metricProcessor = new MemoryMetricProcessor(new VoidMetricExporter());
- $logProcessor = new MemoryLogProcessor(new VoidLogExporter());
+ $this->spanProcessor = new MemorySpanProcessor(new VoidExporter());
+ $this->metricProcessor = new MemoryMetricProcessor(new VoidExporter());
+ $logProcessor = new MemoryLogProcessor(new VoidExporter());
$this->telemetry = new Telemetry(
Resource::create(['service.name' => 'test-service']),
diff --git a/src/core/etl/tests/Flow/ETL/Tests/Unit/Config/Telemetry/TelemetryContextTest.php b/src/core/etl/tests/Flow/ETL/Tests/Unit/Config/Telemetry/TelemetryContextTest.php
index 6ed57933b..a777e33c0 100644
--- a/src/core/etl/tests/Flow/ETL/Tests/Unit/Config/Telemetry/TelemetryContextTest.php
+++ b/src/core/etl/tests/Flow/ETL/Tests/Unit/Config/Telemetry/TelemetryContextTest.php
@@ -14,7 +14,7 @@
use Flow\Telemetry\Logger\{LoggerProvider, Severity};
use Flow\Telemetry\Meter\MeterProvider;
use Flow\Telemetry\Provider\Memory\{MemoryLogProcessor, MemoryMetricProcessor, MemorySpanProcessor};
-use Flow\Telemetry\Provider\Void\{VoidLogExporter, VoidMetricExporter, VoidSpanExporter};
+use Flow\Telemetry\Provider\Void\VoidExporter;
use Flow\Telemetry\{Resource, Telemetry};
use Flow\Telemetry\Tracer\TracerProvider;
use Psr\Clock\ClockInterface;
@@ -23,9 +23,9 @@ final class TelemetryContextTest extends FlowTestCase
{
public function test_dataframe_batch_processed_tracks_rows_and_memory() : void
{
- $spanProcessor = new MemorySpanProcessor(new VoidSpanExporter());
- $metricProcessor = new MemoryMetricProcessor(new VoidMetricExporter());
- $logProcessor = new MemoryLogProcessor(new VoidLogExporter());
+ $spanProcessor = new MemorySpanProcessor(new VoidExporter());
+ $metricProcessor = new MemoryMetricProcessor(new VoidExporter());
+ $logProcessor = new MemoryLogProcessor(new VoidExporter());
$clock = $this->createFrozenClock();
$contextStorage = new MemoryContextStorage();
@@ -72,9 +72,9 @@ public function test_dataframe_batch_processed_tracks_rows_and_memory() : void
public function test_dataframe_completed_finalizes_span_with_statistics() : void
{
- $spanProcessor = new MemorySpanProcessor(new VoidSpanExporter());
- $metricProcessor = new MemoryMetricProcessor(new VoidMetricExporter());
- $logProcessor = new MemoryLogProcessor(new VoidLogExporter());
+ $spanProcessor = new MemorySpanProcessor(new VoidExporter());
+ $metricProcessor = new MemoryMetricProcessor(new VoidExporter());
+ $logProcessor = new MemoryLogProcessor(new VoidExporter());
$clock = $this->createFrozenClock();
$contextStorage = new MemoryContextStorage();
@@ -128,9 +128,9 @@ public function test_dataframe_completed_finalizes_span_with_statistics() : void
public function test_dataframe_failed_logs_error_and_sets_span_status() : void
{
- $spanProcessor = new MemorySpanProcessor(new VoidSpanExporter());
- $metricProcessor = new MemoryMetricProcessor(new VoidMetricExporter());
- $logProcessor = new MemoryLogProcessor(new VoidLogExporter());
+ $spanProcessor = new MemorySpanProcessor(new VoidExporter());
+ $metricProcessor = new MemoryMetricProcessor(new VoidExporter());
+ $logProcessor = new MemoryLogProcessor(new VoidExporter());
$clock = $this->createFrozenClock();
$contextStorage = new MemoryContextStorage();
@@ -179,9 +179,9 @@ public function test_dataframe_failed_logs_error_and_sets_span_status() : void
public function test_dataframe_started_creates_span_and_logs_debug_message() : void
{
- $spanProcessor = new MemorySpanProcessor(new VoidSpanExporter());
- $metricProcessor = new MemoryMetricProcessor(new VoidMetricExporter());
- $logProcessor = new MemoryLogProcessor(new VoidLogExporter());
+ $spanProcessor = new MemorySpanProcessor(new VoidExporter());
+ $metricProcessor = new MemoryMetricProcessor(new VoidExporter());
+ $logProcessor = new MemoryLogProcessor(new VoidExporter());
$clock = $this->createFrozenClock();
$contextStorage = new MemoryContextStorage();
@@ -218,9 +218,9 @@ public function test_dataframe_started_creates_span_and_logs_debug_message() : v
public function test_loading_completed_finalizes_span_with_ok_status() : void
{
- $spanProcessor = new MemorySpanProcessor(new VoidSpanExporter());
- $metricProcessor = new MemoryMetricProcessor(new VoidMetricExporter());
- $logProcessor = new MemoryLogProcessor(new VoidLogExporter());
+ $spanProcessor = new MemorySpanProcessor(new VoidExporter());
+ $metricProcessor = new MemoryMetricProcessor(new VoidExporter());
+ $logProcessor = new MemoryLogProcessor(new VoidExporter());
$clock = $this->createFrozenClock();
$contextStorage = new MemoryContextStorage();
@@ -262,9 +262,9 @@ public function test_loading_completed_finalizes_span_with_ok_status() : void
public function test_loading_failed_logs_error_and_sets_span_status() : void
{
- $spanProcessor = new MemorySpanProcessor(new VoidSpanExporter());
- $metricProcessor = new MemoryMetricProcessor(new VoidMetricExporter());
- $logProcessor = new MemoryLogProcessor(new VoidLogExporter());
+ $spanProcessor = new MemorySpanProcessor(new VoidExporter());
+ $metricProcessor = new MemoryMetricProcessor(new VoidExporter());
+ $logProcessor = new MemoryLogProcessor(new VoidExporter());
$clock = $this->createFrozenClock();
$contextStorage = new MemoryContextStorage();
@@ -309,9 +309,9 @@ public function test_loading_failed_logs_error_and_sets_span_status() : void
public function test_loading_started_creates_span_when_trace_loading_enabled() : void
{
- $spanProcessor = new MemorySpanProcessor(new VoidSpanExporter());
- $metricProcessor = new MemoryMetricProcessor(new VoidMetricExporter());
- $logProcessor = new MemoryLogProcessor(new VoidLogExporter());
+ $spanProcessor = new MemorySpanProcessor(new VoidExporter());
+ $metricProcessor = new MemoryMetricProcessor(new VoidExporter());
+ $logProcessor = new MemoryLogProcessor(new VoidExporter());
$clock = $this->createFrozenClock();
$contextStorage = new MemoryContextStorage();
@@ -348,9 +348,9 @@ public function test_loading_started_creates_span_when_trace_loading_enabled() :
public function test_loading_started_does_not_create_span_when_trace_loading_disabled() : void
{
- $spanProcessor = new MemorySpanProcessor(new VoidSpanExporter());
- $metricProcessor = new MemoryMetricProcessor(new VoidMetricExporter());
- $logProcessor = new MemoryLogProcessor(new VoidLogExporter());
+ $spanProcessor = new MemorySpanProcessor(new VoidExporter());
+ $metricProcessor = new MemoryMetricProcessor(new VoidExporter());
+ $logProcessor = new MemoryLogProcessor(new VoidExporter());
$clock = $this->createFrozenClock();
$contextStorage = new MemoryContextStorage();
@@ -386,9 +386,9 @@ public function test_loading_started_does_not_create_span_when_trace_loading_dis
public function test_metrics_collected_when_collect_metrics_enabled() : void
{
- $spanProcessor = new MemorySpanProcessor(new VoidSpanExporter());
- $metricProcessor = new MemoryMetricProcessor(new VoidMetricExporter());
- $logProcessor = new MemoryLogProcessor(new VoidLogExporter());
+ $spanProcessor = new MemorySpanProcessor(new VoidExporter());
+ $metricProcessor = new MemoryMetricProcessor(new VoidExporter());
+ $logProcessor = new MemoryLogProcessor(new VoidExporter());
$clock = $this->createFrozenClock();
$contextStorage = new MemoryContextStorage();
@@ -431,9 +431,9 @@ public function test_metrics_collected_when_collect_metrics_enabled() : void
public function test_metrics_include_dataframe_name_attribute() : void
{
- $spanProcessor = new MemorySpanProcessor(new VoidSpanExporter());
- $metricProcessor = new MemoryMetricProcessor(new VoidMetricExporter());
- $logProcessor = new MemoryLogProcessor(new VoidLogExporter());
+ $spanProcessor = new MemorySpanProcessor(new VoidExporter());
+ $metricProcessor = new MemoryMetricProcessor(new VoidExporter());
+ $logProcessor = new MemoryLogProcessor(new VoidExporter());
$clock = $this->createFrozenClock();
$contextStorage = new MemoryContextStorage();
@@ -482,9 +482,9 @@ public function test_metrics_include_dataframe_name_attribute() : void
public function test_metrics_not_collected_when_collect_metrics_disabled() : void
{
- $spanProcessor = new MemorySpanProcessor(new VoidSpanExporter());
- $metricProcessor = new MemoryMetricProcessor(new VoidMetricExporter());
- $logProcessor = new MemoryLogProcessor(new VoidLogExporter());
+ $spanProcessor = new MemorySpanProcessor(new VoidExporter());
+ $metricProcessor = new MemoryMetricProcessor(new VoidExporter());
+ $logProcessor = new MemoryLogProcessor(new VoidExporter());
$clock = $this->createFrozenClock();
$contextStorage = new MemoryContextStorage();
@@ -522,9 +522,9 @@ public function test_metrics_not_collected_when_collect_metrics_disabled() : voi
public function test_transformation_completed_finalizes_span() : void
{
- $spanProcessor = new MemorySpanProcessor(new VoidSpanExporter());
- $metricProcessor = new MemoryMetricProcessor(new VoidMetricExporter());
- $logProcessor = new MemoryLogProcessor(new VoidLogExporter());
+ $spanProcessor = new MemorySpanProcessor(new VoidExporter());
+ $metricProcessor = new MemoryMetricProcessor(new VoidExporter());
+ $logProcessor = new MemoryLogProcessor(new VoidExporter());
$clock = $this->createFrozenClock();
$contextStorage = new MemoryContextStorage();
@@ -566,9 +566,9 @@ public function test_transformation_completed_finalizes_span() : void
public function test_transformation_failed_logs_error_and_sets_span_status() : void
{
- $spanProcessor = new MemorySpanProcessor(new VoidSpanExporter());
- $metricProcessor = new MemoryMetricProcessor(new VoidMetricExporter());
- $logProcessor = new MemoryLogProcessor(new VoidLogExporter());
+ $spanProcessor = new MemorySpanProcessor(new VoidExporter());
+ $metricProcessor = new MemoryMetricProcessor(new VoidExporter());
+ $logProcessor = new MemoryLogProcessor(new VoidExporter());
$clock = $this->createFrozenClock();
$contextStorage = new MemoryContextStorage();
@@ -613,9 +613,9 @@ public function test_transformation_failed_logs_error_and_sets_span_status() : v
public function test_transformation_started_creates_span_when_trace_transformations_enabled() : void
{
- $spanProcessor = new MemorySpanProcessor(new VoidSpanExporter());
- $metricProcessor = new MemoryMetricProcessor(new VoidMetricExporter());
- $logProcessor = new MemoryLogProcessor(new VoidLogExporter());
+ $spanProcessor = new MemorySpanProcessor(new VoidExporter());
+ $metricProcessor = new MemoryMetricProcessor(new VoidExporter());
+ $logProcessor = new MemoryLogProcessor(new VoidExporter());
$clock = $this->createFrozenClock();
$contextStorage = new MemoryContextStorage();
@@ -652,9 +652,9 @@ public function test_transformation_started_creates_span_when_trace_transformati
public function test_transformation_started_does_not_create_span_when_trace_transformations_disabled() : void
{
- $spanProcessor = new MemorySpanProcessor(new VoidSpanExporter());
- $metricProcessor = new MemoryMetricProcessor(new VoidMetricExporter());
- $logProcessor = new MemoryLogProcessor(new VoidLogExporter());
+ $spanProcessor = new MemorySpanProcessor(new VoidExporter());
+ $metricProcessor = new MemoryMetricProcessor(new VoidExporter());
+ $logProcessor = new MemoryLogProcessor(new VoidExporter());
$clock = $this->createFrozenClock();
$contextStorage = new MemoryContextStorage();
diff --git a/src/lib/filesystem/tests/Flow/Filesystem/Tests/Integration/Telemetry/TraceableFilesystemIntegrationTest.php b/src/lib/filesystem/tests/Flow/Filesystem/Tests/Integration/Telemetry/TraceableFilesystemIntegrationTest.php
index 2e2ccc899..e11edb026 100644
--- a/src/lib/filesystem/tests/Flow/Filesystem/Tests/Integration/Telemetry/TraceableFilesystemIntegrationTest.php
+++ b/src/lib/filesystem/tests/Flow/Filesystem/Tests/Integration/Telemetry/TraceableFilesystemIntegrationTest.php
@@ -5,7 +5,7 @@
namespace Flow\Filesystem\Tests\Integration\Telemetry;
use function Flow\Filesystem\DSL\{filesystem_telemetry_options, native_local_filesystem, path};
-use function Flow\Telemetry\DSL\{memory_metric_processor, memory_span_processor, void_metric_exporter, void_span_exporter};
+use function Flow\Telemetry\DSL\{memory_metric_processor, memory_span_processor, void_exporter};
use Flow\Filesystem\Telemetry\FilesystemTelemetryAttributes;
use Flow\Filesystem\Tests\Mother\FilesystemTelemetryConfigMother;
use PHPUnit\Framework\TestCase;
@@ -24,7 +24,7 @@ protected function tearDown() : void
public function test_complete_read_write_workflow_produces_lifecycle_spans() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$fs = FilesystemTelemetryConfigMother::createTraceableFilesystem($spanProcessor);
$testFile = path(__DIR__ . '/var/test_file.txt');
@@ -63,7 +63,7 @@ public function test_complete_read_write_workflow_produces_lifecycle_spans() : v
public function test_filesystem_operations_do_not_create_spans() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$fs = FilesystemTelemetryConfigMother::createTraceableFilesystem($spanProcessor);
$testFile = path(__DIR__ . '/var/no_fs_trace.txt');
@@ -79,7 +79,7 @@ public function test_filesystem_operations_do_not_create_spans() : void
public function test_from_resource_tracks_bytes_in_lifecycle_span() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$fs = FilesystemTelemetryConfigMother::createTraceableFilesystem($spanProcessor);
$sourceFilePath = __DIR__ . '/../Fixtures/orders.csv';
@@ -109,7 +109,7 @@ public function test_from_resource_tracks_bytes_in_lifecycle_span() : void
public function test_iterate_tracks_total_bytes_in_lifecycle_span() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$fs = FilesystemTelemetryConfigMother::createTraceableFilesystem($spanProcessor);
$testFile = path(__DIR__ . '/var/iterate_test.txt');
@@ -140,7 +140,7 @@ public function test_iterate_tracks_total_bytes_in_lifecycle_span() : void
public function test_list_operation_does_not_create_span() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$fs = FilesystemTelemetryConfigMother::createTraceableFilesystem($spanProcessor);
$testDir = __DIR__ . '/var';
@@ -159,8 +159,8 @@ public function test_list_operation_does_not_create_span() : void
public function test_metrics_are_collected_for_stream_operations() : void
{
- $metricProcessor = memory_metric_processor(void_metric_exporter());
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $metricProcessor = memory_metric_processor(void_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
[$fs, $telemetry] = FilesystemTelemetryConfigMother::createTraceableFilesystemWithTelemetry($spanProcessor, null, $metricProcessor);
$testFile = path(__DIR__ . '/var/metrics_test.txt');
@@ -188,7 +188,7 @@ public function test_metrics_are_collected_for_stream_operations() : void
public function test_multiple_appends_create_single_span_with_cumulative_metrics() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$fs = FilesystemTelemetryConfigMother::createTraceableFilesystem($spanProcessor);
$testFile = path(__DIR__ . '/var/multiple_appends.txt');
@@ -212,7 +212,7 @@ public function test_multiple_appends_create_single_span_with_cumulative_metrics
public function test_mv_operation_does_not_create_span() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$fs = FilesystemTelemetryConfigMother::createTraceableFilesystem($spanProcessor);
$testDir = __DIR__ . '/var';
@@ -232,7 +232,7 @@ public function test_mv_operation_does_not_create_span() : void
public function test_read_lines_tracks_bytes_in_lifecycle_span() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$fs = FilesystemTelemetryConfigMother::createTraceableFilesystem($spanProcessor);
$testFile = path(__DIR__ . '/var/lines_test.txt');
@@ -257,7 +257,7 @@ public function test_read_lines_tracks_bytes_in_lifecycle_span() : void
public function test_rm_operation_does_not_create_span() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$fs = FilesystemTelemetryConfigMother::createTraceableFilesystem($spanProcessor);
$testFile = path(__DIR__ . '/var/to_remove.txt');
@@ -274,7 +274,7 @@ public function test_rm_operation_does_not_create_span() : void
public function test_status_operation_does_not_create_span() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$fs = FilesystemTelemetryConfigMother::createTraceableFilesystem($spanProcessor);
$testFile = path(__DIR__ . '/var/status_test.txt');
@@ -292,7 +292,7 @@ public function test_status_operation_does_not_create_span() : void
public function test_stream_lifecycle_tracing_can_be_disabled() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$fs = FilesystemTelemetryConfigMother::createTraceableFilesystem($spanProcessor, filesystem_telemetry_options(
traceStreams: false,
));
diff --git a/src/lib/filesystem/tests/Flow/Filesystem/Tests/Mother/FilesystemTelemetryConfigMother.php b/src/lib/filesystem/tests/Flow/Filesystem/Tests/Mother/FilesystemTelemetryConfigMother.php
index 6ae4b9d46..b2b7d56e6 100644
--- a/src/lib/filesystem/tests/Flow/Filesystem/Tests/Mother/FilesystemTelemetryConfigMother.php
+++ b/src/lib/filesystem/tests/Flow/Filesystem/Tests/Mother/FilesystemTelemetryConfigMother.php
@@ -5,7 +5,7 @@
namespace Flow\Filesystem\Tests\Mother;
use function Flow\Filesystem\DSL\{filesystem_telemetry_config, filesystem_telemetry_options, native_local_filesystem};
-use function Flow\Telemetry\DSL\{logger_provider, memory_context_storage, memory_log_processor, memory_metric_processor, memory_span_processor, meter_provider, resource, telemetry, tracer_provider, void_log_exporter, void_metric_exporter, void_span_exporter};
+use function Flow\Telemetry\DSL\{logger_provider, memory_context_storage, memory_log_processor, memory_metric_processor, memory_span_processor, meter_provider, resource, telemetry, tracer_provider, void_exporter};
use Flow\Filesystem\Telemetry\{FilesystemTelemetryConfig, FilesystemTelemetryOptions, TraceableFilesystem};
use Flow\Telemetry\Provider\Clock\SystemClock;
use Flow\Telemetry\Provider\Memory\{MemoryLogProcessor, MemoryMetricProcessor, MemorySpanProcessor};
@@ -24,8 +24,8 @@ public static function create(
$tel = telemetry(
resource(),
tracer_provider($spanProcessor, $clock, $contextStorage),
- meter_provider(memory_metric_processor(void_metric_exporter()), $clock),
- logger_provider(memory_log_processor(void_log_exporter()), $clock, $contextStorage),
+ meter_provider(memory_metric_processor(void_exporter()), $clock),
+ logger_provider(memory_log_processor(void_exporter()), $clock, $contextStorage),
);
return filesystem_telemetry_config($tel, $clock, $options ?? filesystem_telemetry_options());
@@ -37,9 +37,9 @@ public static function createTelemetry(ClockInterface $clock) : Telemetry
return telemetry(
resource(),
- tracer_provider(memory_span_processor(void_span_exporter()), $clock, $contextStorage),
- meter_provider(memory_metric_processor(void_metric_exporter()), $clock),
- logger_provider(memory_log_processor(void_log_exporter()), $clock, $contextStorage),
+ tracer_provider(memory_span_processor(void_exporter()), $clock, $contextStorage),
+ meter_provider(memory_metric_processor(void_exporter()), $clock),
+ logger_provider(memory_log_processor(void_exporter()), $clock, $contextStorage),
);
}
@@ -83,8 +83,8 @@ public static function createWithTelemetry(
$tel = telemetry(
resource(),
tracer_provider($spanProcessor, $clock, $contextStorage),
- meter_provider($metricProcessor ?? memory_metric_processor(void_metric_exporter()), $clock),
- logger_provider($logProcessor ?? memory_log_processor(void_log_exporter()), $clock, $contextStorage),
+ meter_provider($metricProcessor ?? memory_metric_processor(void_exporter()), $clock),
+ logger_provider($logProcessor ?? memory_log_processor(void_exporter()), $clock, $contextStorage),
);
return [filesystem_telemetry_config($tel, $clock, $options ?? filesystem_telemetry_options()), $tel];
diff --git a/src/lib/filesystem/tests/Flow/Filesystem/Tests/Unit/FilesystemTableTest.php b/src/lib/filesystem/tests/Flow/Filesystem/Tests/Unit/FilesystemTableTest.php
index b2e6c4b89..9c35ebd72 100644
--- a/src/lib/filesystem/tests/Flow/Filesystem/Tests/Unit/FilesystemTableTest.php
+++ b/src/lib/filesystem/tests/Flow/Filesystem/Tests/Unit/FilesystemTableTest.php
@@ -4,7 +4,7 @@
namespace Flow\Filesystem\Tests\Unit;
-use function Flow\Telemetry\DSL\{memory_span_processor, void_span_exporter};
+use function Flow\Telemetry\DSL\{memory_span_processor, void_exporter};
use Flow\Filesystem\Exception\InvalidArgumentException;
use Flow\Filesystem\{Filesystem, FilesystemTable, Mount};
use Flow\Filesystem\Telemetry\TraceableFilesystem;
@@ -47,7 +47,7 @@ public function test_for_throws_when_protocol_not_mounted() : void
public function test_mount_does_not_double_wrap_traceable_filesystem() : void
{
$fstab = new FilesystemTable();
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = FilesystemTelemetryConfigMother::create($spanProcessor);
$fs = $this->filesystem('s3');
@@ -73,7 +73,7 @@ public function test_mount_does_not_wrap_when_telemetry_not_configured() : void
public function test_mount_wraps_new_filesystem_when_telemetry_configured() : void
{
$fstab = new FilesystemTable();
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = FilesystemTelemetryConfigMother::create($spanProcessor);
$fstab->withTelemetry($config);
@@ -107,7 +107,7 @@ public function test_unmount_throws_when_protocol_not_mounted() : void
public function test_with_telemetry_skips_already_traceable_filesystems() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = FilesystemTelemetryConfigMother::create($spanProcessor);
$traceableFs = new TraceableFilesystem($this->filesystem('sftp'), $config);
@@ -121,7 +121,7 @@ public function test_with_telemetry_skips_already_traceable_filesystems() : void
public function test_with_telemetry_wraps_existing_filesystems_in_traceable() : void
{
$fstab = new FilesystemTable($this->filesystem('ftp'));
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = FilesystemTelemetryConfigMother::create($spanProcessor);
$fstab->withTelemetry($config);
diff --git a/src/lib/filesystem/tests/Flow/Filesystem/Tests/Unit/Telemetry/TraceableDestinationStreamTest.php b/src/lib/filesystem/tests/Flow/Filesystem/Tests/Unit/Telemetry/TraceableDestinationStreamTest.php
index 1ff035ebc..994a7c94a 100644
--- a/src/lib/filesystem/tests/Flow/Filesystem/Tests/Unit/Telemetry/TraceableDestinationStreamTest.php
+++ b/src/lib/filesystem/tests/Flow/Filesystem/Tests/Unit/Telemetry/TraceableDestinationStreamTest.php
@@ -5,7 +5,7 @@
namespace Flow\Filesystem\Tests\Unit\Telemetry;
use function Flow\Filesystem\DSL\filesystem_telemetry_options;
-use function Flow\Telemetry\DSL\{memory_span_processor, void_span_exporter};
+use function Flow\Telemetry\DSL\{memory_span_processor, void_exporter};
use Flow\Filesystem\{DestinationStream, Path};
use Flow\Filesystem\Telemetry\{FilesystemTelemetryAttributes, TraceableDestinationStream};
use Flow\Filesystem\Tests\Mother\FilesystemTelemetryConfigMother;
@@ -15,7 +15,7 @@ final class TraceableDestinationStreamTest extends TestCase
{
public function test_append_tracks_bytes_written() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = FilesystemTelemetryConfigMother::create($spanProcessor);
$path = Path::realpath('/tmp/test.txt');
$data = 'Hello, World!';
@@ -43,7 +43,7 @@ public function test_append_tracks_bytes_written() : void
public function test_close_completes_lifecycle_span_with_final_attributes() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = FilesystemTelemetryConfigMother::create($spanProcessor);
$path = Path::realpath('/tmp/test.txt');
$data = 'Hello, World!';
@@ -68,7 +68,7 @@ public function test_close_completes_lifecycle_span_with_final_attributes() : vo
public function test_close_records_exception_and_rethrows() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = FilesystemTelemetryConfigMother::create($spanProcessor);
$path = Path::realpath('/tmp/test.txt');
$exception = new \RuntimeException('Close failed');
@@ -97,7 +97,7 @@ public function test_close_records_exception_and_rethrows() : void
public function test_close_without_operations_still_creates_span() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = FilesystemTelemetryConfigMother::create($spanProcessor);
$path = Path::realpath('/tmp/test.txt');
@@ -115,7 +115,7 @@ public function test_close_without_operations_still_creates_span() : void
public function test_from_resource_tracks_bytes_written() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = FilesystemTelemetryConfigMother::create($spanProcessor);
$path = Path::realpath('/tmp/test.txt');
$resource = \fopen('php://memory', 'rb');
@@ -145,7 +145,7 @@ public function test_from_resource_tracks_bytes_written() : void
public function test_is_open_delegates_without_affecting_span() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = FilesystemTelemetryConfigMother::create($spanProcessor);
$path = Path::realpath('/tmp/test.txt');
@@ -161,7 +161,7 @@ public function test_is_open_delegates_without_affecting_span() : void
public function test_multiple_appends_create_single_span_with_cumulative_bytes() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = FilesystemTelemetryConfigMother::create($spanProcessor);
$path = Path::realpath('/tmp/test.txt');
@@ -183,7 +183,7 @@ public function test_multiple_appends_create_single_span_with_cumulative_bytes()
public function test_path_delegates_without_affecting_span() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = FilesystemTelemetryConfigMother::create($spanProcessor);
$path = Path::realpath('/tmp/test.txt');
@@ -198,7 +198,7 @@ public function test_path_delegates_without_affecting_span() : void
public function test_span_created_in_constructor() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = FilesystemTelemetryConfigMother::create($spanProcessor);
$path = Path::realpath('/tmp/test.txt');
@@ -221,7 +221,7 @@ public function test_span_created_in_constructor() : void
public function test_tracing_disabled_does_not_create_spans() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = FilesystemTelemetryConfigMother::create($spanProcessor, filesystem_telemetry_options(
traceStreams: false,
collectMetrics: false,
diff --git a/src/lib/filesystem/tests/Flow/Filesystem/Tests/Unit/Telemetry/TraceableFilesystemTest.php b/src/lib/filesystem/tests/Flow/Filesystem/Tests/Unit/Telemetry/TraceableFilesystemTest.php
index b1d73ea5d..cdf9bbbb8 100644
--- a/src/lib/filesystem/tests/Flow/Filesystem/Tests/Unit/Telemetry/TraceableFilesystemTest.php
+++ b/src/lib/filesystem/tests/Flow/Filesystem/Tests/Unit/Telemetry/TraceableFilesystemTest.php
@@ -5,7 +5,7 @@
namespace Flow\Filesystem\Tests\Unit\Telemetry;
use function Flow\Filesystem\DSL\{filesystem_telemetry_options, path};
-use function Flow\Telemetry\DSL\{memory_span_processor, void_span_exporter};
+use function Flow\Telemetry\DSL\{memory_span_processor, void_exporter};
use Flow\Filesystem\{DestinationStream, FileStatus, Filesystem, Mount, Path, SourceStream};
use Flow\Filesystem\Telemetry\{TraceableDestinationStream, TraceableFilesystem, TraceableSourceStream};
use Flow\Filesystem\Tests\Mother\FilesystemTelemetryConfigMother;
@@ -15,7 +15,7 @@ final class TraceableFilesystemTest extends TestCase
{
public function test_all_telemetry_disabled_does_not_wrap_streams() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = FilesystemTelemetryConfigMother::create($spanProcessor, filesystem_telemetry_options(
traceStreams: false,
collectMetrics: false,
@@ -38,7 +38,7 @@ public function test_all_telemetry_disabled_does_not_wrap_streams() : void
public function test_append_to_rethrows_exception() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = FilesystemTelemetryConfigMother::create($spanProcessor);
$path = Path::realpath('/tmp/test.txt');
$exception = new \RuntimeException('Append failed');
@@ -57,7 +57,7 @@ public function test_append_to_rethrows_exception() : void
public function test_append_to_returns_traceable_destination_stream() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = FilesystemTelemetryConfigMother::create($spanProcessor);
$path = Path::realpath('/tmp/test.txt');
@@ -76,7 +76,7 @@ public function test_append_to_returns_traceable_destination_stream() : void
public function test_get_system_tmp_dir_delegates_to_underlying_filesystem() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = FilesystemTelemetryConfigMother::create($spanProcessor);
$tmpPath = Path::realpath('/tmp');
@@ -91,7 +91,7 @@ public function test_get_system_tmp_dir_delegates_to_underlying_filesystem() : v
public function test_list_delegates_to_underlying_filesystem() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = FilesystemTelemetryConfigMother::create($spanProcessor);
$path = path('file://tmp/**/*.txt');
@@ -109,7 +109,7 @@ public function test_list_delegates_to_underlying_filesystem() : void
public function test_mount_delegates_to_underlying_filesystem() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = FilesystemTelemetryConfigMother::create($spanProcessor);
$mount = new Mount('s3');
@@ -124,7 +124,7 @@ public function test_mount_delegates_to_underlying_filesystem() : void
public function test_mv_delegates_to_underlying_filesystem() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = FilesystemTelemetryConfigMother::create($spanProcessor);
$from = Path::realpath('/tmp/source.txt');
$to = Path::realpath('/tmp/dest.txt');
@@ -142,7 +142,7 @@ public function test_mv_delegates_to_underlying_filesystem() : void
public function test_read_from_rethrows_exception() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = FilesystemTelemetryConfigMother::create($spanProcessor);
$path = Path::realpath('/tmp/test.txt');
$exception = new \RuntimeException('Read failed');
@@ -161,7 +161,7 @@ public function test_read_from_rethrows_exception() : void
public function test_read_from_returns_traceable_source_stream() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = FilesystemTelemetryConfigMother::create($spanProcessor);
$path = Path::realpath('/tmp/test.txt');
@@ -180,7 +180,7 @@ public function test_read_from_returns_traceable_source_stream() : void
public function test_rm_delegates_to_underlying_filesystem() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = FilesystemTelemetryConfigMother::create($spanProcessor);
$path = Path::realpath('/tmp/test.txt');
@@ -197,7 +197,7 @@ public function test_rm_delegates_to_underlying_filesystem() : void
public function test_status_delegates_to_underlying_filesystem() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = FilesystemTelemetryConfigMother::create($spanProcessor);
$path = Path::realpath('/tmp/test.txt');
$fileStatus = new FileStatus($path, true);
@@ -215,7 +215,7 @@ public function test_status_delegates_to_underlying_filesystem() : void
public function test_stream_tracing_enabled_wraps_streams() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = FilesystemTelemetryConfigMother::create($spanProcessor, filesystem_telemetry_options(
traceStreams: true,
));
@@ -236,7 +236,7 @@ public function test_stream_tracing_enabled_wraps_streams() : void
public function test_write_to_rethrows_exception() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = FilesystemTelemetryConfigMother::create($spanProcessor);
$path = Path::realpath('/tmp/test.txt');
$exception = new \RuntimeException('Write failed');
@@ -255,7 +255,7 @@ public function test_write_to_rethrows_exception() : void
public function test_write_to_returns_traceable_destination_stream() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = FilesystemTelemetryConfigMother::create($spanProcessor);
$path = Path::realpath('/tmp/test.txt');
diff --git a/src/lib/filesystem/tests/Flow/Filesystem/Tests/Unit/Telemetry/TraceableSourceStreamTest.php b/src/lib/filesystem/tests/Flow/Filesystem/Tests/Unit/Telemetry/TraceableSourceStreamTest.php
index a2e8f421d..aaf7f177e 100644
--- a/src/lib/filesystem/tests/Flow/Filesystem/Tests/Unit/Telemetry/TraceableSourceStreamTest.php
+++ b/src/lib/filesystem/tests/Flow/Filesystem/Tests/Unit/Telemetry/TraceableSourceStreamTest.php
@@ -5,7 +5,7 @@
namespace Flow\Filesystem\Tests\Unit\Telemetry;
use function Flow\Filesystem\DSL\filesystem_telemetry_options;
-use function Flow\Telemetry\DSL\{memory_span_processor, void_span_exporter};
+use function Flow\Telemetry\DSL\{memory_span_processor, void_exporter};
use Flow\Filesystem\{Path, SourceStream};
use Flow\Filesystem\Telemetry\{FilesystemTelemetryAttributes, TraceableSourceStream};
use Flow\Filesystem\Tests\Mother\FilesystemTelemetryConfigMother;
@@ -15,7 +15,7 @@ final class TraceableSourceStreamTest extends TestCase
{
public function test_close_completes_lifecycle_span_with_final_attributes() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = FilesystemTelemetryConfigMother::create($spanProcessor);
$path = Path::realpath('/tmp/test.txt');
$content = 'Hello, World!';
@@ -40,7 +40,7 @@ public function test_close_completes_lifecycle_span_with_final_attributes() : vo
public function test_close_records_exception_and_rethrows() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = FilesystemTelemetryConfigMother::create($spanProcessor);
$path = Path::realpath('/tmp/test.txt');
$exception = new \RuntimeException('Close failed');
@@ -69,7 +69,7 @@ public function test_close_records_exception_and_rethrows() : void
public function test_close_without_operations_still_creates_span() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = FilesystemTelemetryConfigMother::create($spanProcessor);
$path = Path::realpath('/tmp/test.txt');
@@ -87,7 +87,7 @@ public function test_close_without_operations_still_creates_span() : void
public function test_content_tracks_bytes_read() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = FilesystemTelemetryConfigMother::create($spanProcessor);
$path = Path::realpath('/tmp/test.txt');
$content = 'Hello, World!';
@@ -111,7 +111,7 @@ public function test_content_tracks_bytes_read() : void
public function test_is_open_delegates_without_affecting_span() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = FilesystemTelemetryConfigMother::create($spanProcessor);
$path = Path::realpath('/tmp/test.txt');
@@ -127,7 +127,7 @@ public function test_is_open_delegates_without_affecting_span() : void
public function test_iterate_tracks_bytes_read_cumulatively() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = FilesystemTelemetryConfigMother::create($spanProcessor);
$path = Path::realpath('/tmp/test.txt');
$chunks = ['Hello', ', ', 'World', '!'];
@@ -153,7 +153,7 @@ public function test_iterate_tracks_bytes_read_cumulatively() : void
public function test_multiple_operations_track_cumulative_bytes() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = FilesystemTelemetryConfigMother::create($spanProcessor);
$path = Path::realpath('/tmp/test.txt');
@@ -175,7 +175,7 @@ public function test_multiple_operations_track_cumulative_bytes() : void
public function test_path_delegates_without_affecting_span() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = FilesystemTelemetryConfigMother::create($spanProcessor);
$path = Path::realpath('/tmp/test.txt');
@@ -190,7 +190,7 @@ public function test_path_delegates_without_affecting_span() : void
public function test_read_lines_tracks_bytes_read() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = FilesystemTelemetryConfigMother::create($spanProcessor);
$path = Path::realpath('/tmp/test.txt');
$lines = ['line1', 'line2', 'line3'];
@@ -215,7 +215,7 @@ public function test_read_lines_tracks_bytes_read() : void
public function test_read_tracks_bytes_read() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = FilesystemTelemetryConfigMother::create($spanProcessor);
$path = Path::realpath('/tmp/test.txt');
$content = 'Hello';
@@ -239,7 +239,7 @@ public function test_read_tracks_bytes_read() : void
public function test_size_delegates_without_affecting_span() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = FilesystemTelemetryConfigMother::create($spanProcessor);
$path = Path::realpath('/tmp/test.txt');
@@ -255,7 +255,7 @@ public function test_size_delegates_without_affecting_span() : void
public function test_span_created_in_constructor() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = FilesystemTelemetryConfigMother::create($spanProcessor);
$path = Path::realpath('/tmp/test.txt');
@@ -278,7 +278,7 @@ public function test_span_created_in_constructor() : void
public function test_tracing_disabled_does_not_create_spans() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = FilesystemTelemetryConfigMother::create($spanProcessor, filesystem_telemetry_options(
traceStreams: false,
collectMetrics: false,
diff --git a/src/lib/postgresql/tests/Flow/PostgreSql/Tests/Integration/Client/Telemetry/TraceableClientTest.php b/src/lib/postgresql/tests/Flow/PostgreSql/Tests/Integration/Client/Telemetry/TraceableClientTest.php
index cbc9e639d..24739ee4e 100644
--- a/src/lib/postgresql/tests/Flow/PostgreSql/Tests/Integration/Client/Telemetry/TraceableClientTest.php
+++ b/src/lib/postgresql/tests/Flow/PostgreSql/Tests/Integration/Client/Telemetry/TraceableClientTest.php
@@ -5,7 +5,7 @@
namespace Flow\PostgreSql\Tests\Integration\Client\Telemetry;
use function Flow\PostgreSql\DSL\{column, column_type_serial, column_type_text, create, insert, literal, param, postgresql_telemetry_config, postgresql_telemetry_options, primary_key, select, star, table, traceable_postgresql_client};
-use function Flow\Telemetry\DSL\{logger_provider, memory_context_storage, memory_log_processor, memory_metric_processor, memory_span_processor, meter_provider, resource, telemetry, tracer_provider, void_log_exporter, void_metric_exporter, void_span_exporter};
+use function Flow\Telemetry\DSL\{logger_provider, memory_context_storage, memory_log_processor, memory_metric_processor, memory_span_processor, meter_provider, resource, telemetry, tracer_provider, void_exporter};
use Flow\PostgreSql\Client\Client;
use Flow\PostgreSql\Client\Telemetry\PostgreSqlTelemetryAttributes;
use Flow\PostgreSql\Tests\Integration\PostgreSqlTestCase;
@@ -18,7 +18,7 @@ final class TraceableClientTest extends PostgreSqlTestCase
{
public function test_cursor_iteration_creates_span() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor, options: postgresql_telemetry_options(
traceQueries: true,
));
@@ -56,7 +56,7 @@ public function test_cursor_iteration_creates_span() : void
public function test_execute_creates_span_with_database_attributes() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor);
$client = traceable_postgresql_client($this->pgsqlContext()->client(), $config);
@@ -85,7 +85,7 @@ public function test_execute_creates_span_with_database_attributes() : void
public function test_failed_query_records_error_in_span() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor);
$client = traceable_postgresql_client($this->pgsqlContext()->client(), $config);
@@ -103,7 +103,7 @@ public function test_failed_query_records_error_in_span() : void
public function test_fetch_creates_span_with_row_count() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor);
$client = traceable_postgresql_client($this->pgsqlContext()->client(), $config);
@@ -133,7 +133,7 @@ public function test_fetch_creates_span_with_row_count() : void
public function test_logging_records_query_execution() : void
{
- $logProcessor = memory_log_processor(void_log_exporter());
+ $logProcessor = memory_log_processor(void_exporter());
$config = $this->createConfig(logProcessor: $logProcessor, options: postgresql_telemetry_options(
logQueries: true,
));
@@ -154,7 +154,7 @@ public function test_logging_records_query_execution() : void
public function test_metrics_record_operation_duration() : void
{
- $metricProcessor = memory_metric_processor(void_metric_exporter());
+ $metricProcessor = memory_metric_processor(void_exporter());
$config = $this->createConfig(metricProcessor: $metricProcessor, options: postgresql_telemetry_options(
collectMetrics: true,
));
@@ -172,7 +172,7 @@ public function test_metrics_record_operation_duration() : void
public function test_nested_transaction_creates_savepoint_span() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor, options: postgresql_telemetry_options(
traceTransactions: true,
traceQueries: false,
@@ -202,7 +202,7 @@ public function test_nested_transaction_creates_savepoint_span() : void
public function test_parameters_are_included_when_enabled() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor, options: postgresql_telemetry_options(
includeParameters: true,
));
@@ -228,7 +228,7 @@ public function test_parameters_are_included_when_enabled() : void
public function test_transaction_creates_span() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor, options: postgresql_telemetry_options(
traceTransactions: true,
traceQueries: false,
@@ -270,9 +270,9 @@ private function createConfig(
$tel = telemetry(
resource(),
- tracer_provider($spanProcessor ?? memory_span_processor(void_span_exporter()), $clock, $contextStorage),
- meter_provider($metricProcessor ?? memory_metric_processor(void_metric_exporter()), $clock),
- logger_provider($logProcessor ?? memory_log_processor(void_log_exporter()), $clock, $contextStorage),
+ tracer_provider($spanProcessor ?? memory_span_processor(void_exporter()), $clock, $contextStorage),
+ meter_provider($metricProcessor ?? memory_metric_processor(void_exporter()), $clock),
+ logger_provider($logProcessor ?? memory_log_processor(void_exporter()), $clock, $contextStorage),
);
return postgresql_telemetry_config($tel, $clock, $options ?? postgresql_telemetry_options());
diff --git a/src/lib/postgresql/tests/Flow/PostgreSql/Tests/Unit/Client/Telemetry/PostgreSqlTelemetryConfigTest.php b/src/lib/postgresql/tests/Flow/PostgreSql/Tests/Unit/Client/Telemetry/PostgreSqlTelemetryConfigTest.php
index 1ee802375..2886bbc58 100644
--- a/src/lib/postgresql/tests/Flow/PostgreSql/Tests/Unit/Client/Telemetry/PostgreSqlTelemetryConfigTest.php
+++ b/src/lib/postgresql/tests/Flow/PostgreSql/Tests/Unit/Client/Telemetry/PostgreSqlTelemetryConfigTest.php
@@ -5,7 +5,7 @@
namespace Flow\PostgreSql\Tests\Unit\Client\Telemetry;
use function Flow\PostgreSql\DSL\{postgresql_telemetry_config, postgresql_telemetry_options};
-use function Flow\Telemetry\DSL\{logger_provider, memory_context_storage, memory_log_processor, memory_metric_processor, memory_span_processor, meter_provider, resource, telemetry, tracer_provider, void_log_exporter, void_metric_exporter, void_span_exporter};
+use function Flow\Telemetry\DSL\{logger_provider, memory_context_storage, memory_log_processor, memory_metric_processor, memory_span_processor, meter_provider, resource, telemetry, tracer_provider, void_exporter};
use Flow\Telemetry\Provider\Clock\SystemClock;
use PHPUnit\Framework\TestCase;
@@ -54,9 +54,9 @@ private function createTelemetry(SystemClock $clock) : \Flow\Telemetry\Telemetry
return telemetry(
resource(),
- tracer_provider(memory_span_processor(void_span_exporter()), $clock, $contextStorage),
- meter_provider(memory_metric_processor(void_metric_exporter()), $clock),
- logger_provider(memory_log_processor(void_log_exporter()), $clock, $contextStorage),
+ tracer_provider(memory_span_processor(void_exporter()), $clock, $contextStorage),
+ meter_provider(memory_metric_processor(void_exporter()), $clock),
+ logger_provider(memory_log_processor(void_exporter()), $clock, $contextStorage),
);
}
}
diff --git a/src/lib/postgresql/tests/Flow/PostgreSql/Tests/Unit/Client/Telemetry/TraceableClientTelemetryTest.php b/src/lib/postgresql/tests/Flow/PostgreSql/Tests/Unit/Client/Telemetry/TraceableClientTelemetryTest.php
index 4db8146d8..d849150b3 100644
--- a/src/lib/postgresql/tests/Flow/PostgreSql/Tests/Unit/Client/Telemetry/TraceableClientTelemetryTest.php
+++ b/src/lib/postgresql/tests/Flow/PostgreSql/Tests/Unit/Client/Telemetry/TraceableClientTelemetryTest.php
@@ -5,7 +5,7 @@
namespace Flow\PostgreSql\Tests\Unit\Client\Telemetry;
use function Flow\PostgreSql\DSL\{pgsql_connection_params, postgresql_telemetry_config, postgresql_telemetry_options, traceable_postgresql_client};
-use function Flow\Telemetry\DSL\{logger_provider, memory_context_storage, memory_log_processor, memory_metric_processor, memory_span_processor, meter_provider, resource, telemetry, tracer_provider, void_log_exporter, void_metric_exporter, void_span_exporter};
+use function Flow\Telemetry\DSL\{logger_provider, memory_context_storage, memory_log_processor, memory_metric_processor, memory_span_processor, meter_provider, resource, telemetry, tracer_provider, void_exporter};
use Flow\PostgreSql\Client\Client;
use Flow\PostgreSql\Client\Telemetry\{PostgreSqlTelemetryAttributes, PostgreSqlTelemetryConfig, PostgreSqlTelemetryOptions};
use Flow\PostgreSql\Explain\Plan\{Cost, Plan, PlanNode, PlanNodeType};
@@ -21,9 +21,9 @@ final class TraceableClientTelemetryTest extends TestCase
{
public function test_all_telemetry_signals_work_together() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
- $metricProcessor = memory_metric_processor(void_metric_exporter());
- $logProcessor = memory_log_processor(void_log_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
+ $metricProcessor = memory_metric_processor(void_exporter());
+ $logProcessor = memory_log_processor(void_exporter());
$config = $this->createConfig($spanProcessor, $metricProcessor, $logProcessor, postgresql_telemetry_options(
traceQueries: true,
traceTransactions: true,
@@ -63,7 +63,7 @@ public function test_all_telemetry_signals_work_together() : void
public function test_begin_transaction_creates_span_when_tracing_enabled() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor, options: postgresql_telemetry_options(
traceTransactions: true,
));
@@ -92,7 +92,7 @@ public function test_begin_transaction_creates_span_when_tracing_enabled() : voi
public function test_commit_completes_transaction_span() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor, options: postgresql_telemetry_options(
traceTransactions: true,
));
@@ -121,7 +121,7 @@ public function test_commit_completes_transaction_span() : void
public function test_duration_metric_is_recorded_when_metrics_enabled() : void
{
- $metricProcessor = memory_metric_processor(void_metric_exporter());
+ $metricProcessor = memory_metric_processor(void_exporter());
$config = $this->createConfig(metricProcessor: $metricProcessor, options: postgresql_telemetry_options(
traceQueries: false,
collectMetrics: true,
@@ -143,7 +143,7 @@ public function test_duration_metric_is_recorded_when_metrics_enabled() : void
public function test_explain_creates_span() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor, options: postgresql_telemetry_options(
traceQueries: true,
));
@@ -169,7 +169,7 @@ public function test_explain_creates_span() : void
public function test_fetch_all_into_creates_span_with_correct_row_count() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor, options: postgresql_telemetry_options(
traceQueries: true,
));
@@ -188,7 +188,7 @@ public function test_fetch_all_into_creates_span_with_correct_row_count() : void
public function test_fetch_into_creates_span_with_correct_row_count() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor, options: postgresql_telemetry_options(
traceQueries: true,
));
@@ -208,7 +208,7 @@ public function test_fetch_into_creates_span_with_correct_row_count() : void
public function test_fetch_one_into_creates_span() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor, options: postgresql_telemetry_options(
traceQueries: true,
));
@@ -228,7 +228,7 @@ public function test_fetch_one_into_creates_span() : void
public function test_fetch_scalar_bool_creates_span() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor, options: postgresql_telemetry_options(
traceQueries: true,
));
@@ -245,7 +245,7 @@ public function test_fetch_scalar_bool_creates_span() : void
public function test_fetch_scalar_float_creates_span() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor, options: postgresql_telemetry_options(
traceQueries: true,
));
@@ -262,7 +262,7 @@ public function test_fetch_scalar_float_creates_span() : void
public function test_fetch_scalar_int_creates_span() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor, options: postgresql_telemetry_options(
traceQueries: true,
));
@@ -279,7 +279,7 @@ public function test_fetch_scalar_int_creates_span() : void
public function test_fetch_scalar_string_creates_span() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor, options: postgresql_telemetry_options(
traceQueries: true,
));
@@ -296,8 +296,8 @@ public function test_fetch_scalar_string_creates_span() : void
public function test_metrics_are_not_recorded_when_disabled() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
- $metricProcessor = memory_metric_processor(void_metric_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
+ $metricProcessor = memory_metric_processor(void_exporter());
$config = $this->createConfig($spanProcessor, $metricProcessor, options: postgresql_telemetry_options(
traceQueries: true,
collectMetrics: false,
@@ -316,7 +316,7 @@ public function test_metrics_are_not_recorded_when_disabled() : void
public function test_nested_transaction_creates_savepoint_span() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor, options: postgresql_telemetry_options(
traceTransactions: true,
));
@@ -348,7 +348,7 @@ public function test_nested_transaction_creates_savepoint_span() : void
public function test_queries_are_logged_when_logging_enabled() : void
{
- $logProcessor = memory_log_processor(void_log_exporter());
+ $logProcessor = memory_log_processor(void_exporter());
$config = $this->createConfig(logProcessor: $logProcessor, options: postgresql_telemetry_options(
logQueries: true,
));
@@ -368,7 +368,7 @@ public function test_queries_are_logged_when_logging_enabled() : void
public function test_queries_are_not_logged_when_disabled() : void
{
- $logProcessor = memory_log_processor(void_log_exporter());
+ $logProcessor = memory_log_processor(void_exporter());
$config = $this->createConfig(logProcessor: $logProcessor, options: postgresql_telemetry_options(
logQueries: false,
));
@@ -384,7 +384,7 @@ public function test_queries_are_not_logged_when_disabled() : void
public function test_rollback_completes_all_nested_transaction_spans() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor, options: postgresql_telemetry_options(
traceTransactions: true,
));
@@ -412,8 +412,8 @@ public function test_rollback_completes_all_nested_transaction_spans() : void
public function test_row_count_metric_is_recorded_for_fetch_operations() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
- $metricProcessor = memory_metric_processor(void_metric_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
+ $metricProcessor = memory_metric_processor(void_exporter());
$config = $this->createConfig($spanProcessor, $metricProcessor, options: postgresql_telemetry_options(
traceQueries: true,
collectMetrics: true,
@@ -439,7 +439,7 @@ public function test_row_count_metric_is_recorded_for_fetch_operations() : void
public function test_transaction_callback_creates_parent_span_with_query_spans() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor, options: postgresql_telemetry_options(
traceQueries: true,
traceTransactions: true,
@@ -491,9 +491,9 @@ private function createConfig(
$tel = telemetry(
resource(),
- tracer_provider($spanProcessor ?? memory_span_processor(void_span_exporter()), $clock, $contextStorage),
- meter_provider($metricProcessor ?? memory_metric_processor(void_metric_exporter()), $clock),
- logger_provider($logProcessor ?? memory_log_processor(void_log_exporter()), $clock, $contextStorage),
+ tracer_provider($spanProcessor ?? memory_span_processor(void_exporter()), $clock, $contextStorage),
+ meter_provider($metricProcessor ?? memory_metric_processor(void_exporter()), $clock),
+ logger_provider($logProcessor ?? memory_log_processor(void_exporter()), $clock, $contextStorage),
);
return postgresql_telemetry_config($tel, $clock, $options ?? postgresql_telemetry_options());
diff --git a/src/lib/postgresql/tests/Flow/PostgreSql/Tests/Unit/Client/Telemetry/TraceableClientTest.php b/src/lib/postgresql/tests/Flow/PostgreSql/Tests/Unit/Client/Telemetry/TraceableClientTest.php
index f0f813065..cc6622ee6 100644
--- a/src/lib/postgresql/tests/Flow/PostgreSql/Tests/Unit/Client/Telemetry/TraceableClientTest.php
+++ b/src/lib/postgresql/tests/Flow/PostgreSql/Tests/Unit/Client/Telemetry/TraceableClientTest.php
@@ -5,7 +5,7 @@
namespace Flow\PostgreSql\Tests\Unit\Client\Telemetry;
use function Flow\PostgreSql\DSL\{pgsql_connection_params, postgresql_telemetry_config, postgresql_telemetry_options, traceable_postgresql_client};
-use function Flow\Telemetry\DSL\{logger_provider, memory_context_storage, memory_log_processor, memory_metric_processor, memory_span_processor, meter_provider, resource, telemetry, tracer_provider, void_log_exporter, void_metric_exporter, void_span_exporter};
+use function Flow\Telemetry\DSL\{logger_provider, memory_context_storage, memory_log_processor, memory_metric_processor, memory_span_processor, meter_provider, resource, telemetry, tracer_provider, void_exporter};
use Flow\PostgreSql\Client\{Client, ConnectionParameters, Cursor};
use Flow\PostgreSql\Client\Telemetry\{PostgreSqlTelemetryAttributes, PostgreSqlTelemetryConfig, PostgreSqlTelemetryOptions, TraceableCursor};
use Flow\PostgreSql\Client\Types\ValueConverters;
@@ -18,7 +18,7 @@ final class TraceableClientTest extends TestCase
{
public function test_all_telemetry_disabled_does_not_wrap_cursor() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor, postgresql_telemetry_options(
traceQueries: false,
traceTransactions: false,
@@ -38,7 +38,7 @@ public function test_all_telemetry_disabled_does_not_wrap_cursor() : void
public function test_close_delegates_to_underlying_client() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor);
$mockClient = $this->createMockClient();
@@ -50,7 +50,7 @@ public function test_close_delegates_to_underlying_client() : void
public function test_converters_delegates_to_underlying_client() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor);
$converters = ValueConverters::create();
@@ -64,7 +64,7 @@ public function test_converters_delegates_to_underlying_client() : void
public function test_cursor_returns_traceable_cursor_when_tracing_enabled() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor, postgresql_telemetry_options(
traceQueries: true,
));
@@ -81,7 +81,7 @@ public function test_cursor_returns_traceable_cursor_when_tracing_enabled() : vo
public function test_default_port_is_not_included_in_attributes() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor);
$mockClient = $this->createMockClient();
@@ -97,7 +97,7 @@ public function test_default_port_is_not_included_in_attributes() : void
public function test_execute_creates_span_with_correct_attributes() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor);
$mockClient = $this->createMockClient();
@@ -123,7 +123,7 @@ public function test_execute_creates_span_with_correct_attributes() : void
public function test_execute_rethrows_exception_and_records_error() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor);
$exception = new \RuntimeException('Query failed');
@@ -147,7 +147,7 @@ public function test_execute_rethrows_exception_and_records_error() : void
public function test_fetch_all_creates_span_with_row_count() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor);
$mockClient = $this->createMockClient();
@@ -170,7 +170,7 @@ public function test_fetch_all_creates_span_with_row_count() : void
public function test_fetch_creates_span_with_row_count() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor);
$mockClient = $this->createMockClient();
@@ -188,7 +188,7 @@ public function test_fetch_creates_span_with_row_count() : void
public function test_fetch_null_result_records_zero_rows() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor);
$mockClient = $this->createMockClient();
@@ -206,7 +206,7 @@ public function test_fetch_null_result_records_zero_rows() : void
public function test_get_transaction_nesting_level_delegates() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor);
$mockClient = $this->createMockClient();
@@ -219,7 +219,7 @@ public function test_get_transaction_nesting_level_delegates() : void
public function test_is_auto_commit_delegates() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor);
$mockClient = $this->createMockClient();
@@ -232,7 +232,7 @@ public function test_is_auto_commit_delegates() : void
public function test_is_connected_delegates() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor);
$mockClient = $this->createMockClient();
@@ -245,7 +245,7 @@ public function test_is_connected_delegates() : void
public function test_last_insert_id_delegates() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor);
$mockClient = $this->createMockClient();
@@ -258,7 +258,7 @@ public function test_last_insert_id_delegates() : void
public function test_non_default_port_is_included_in_attributes() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor);
$connectionParams = pgsql_connection_params('testdb', 'localhost', 5433, 'user');
@@ -275,7 +275,7 @@ public function test_non_default_port_is_included_in_attributes() : void
public function test_parameter_count_is_limited_by_default() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor, postgresql_telemetry_options(
includeParameters: true,
));
@@ -302,7 +302,7 @@ public function test_parameter_count_is_limited_by_default() : void
public function test_parameter_count_unlimited_when_null() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor, postgresql_telemetry_options(
includeParameters: true,
maxParameters: null,
@@ -326,7 +326,7 @@ public function test_parameter_count_unlimited_when_null() : void
public function test_parameter_limits_ignored_when_include_parameters_false() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor, postgresql_telemetry_options(
includeParameters: false,
maxParameters: 5,
@@ -347,7 +347,7 @@ public function test_parameter_limits_ignored_when_include_parameters_false() :
public function test_parameter_values_are_truncated_by_default() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor, postgresql_telemetry_options(
includeParameters: true,
));
@@ -370,7 +370,7 @@ public function test_parameter_values_are_truncated_by_default() : void
public function test_parameter_values_unlimited_when_null() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor, postgresql_telemetry_options(
includeParameters: true,
maxParameterLength: null,
@@ -394,7 +394,7 @@ public function test_parameter_values_unlimited_when_null() : void
public function test_parameters_are_included_when_enabled() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor, postgresql_telemetry_options(
includeParameters: true,
));
@@ -413,7 +413,7 @@ public function test_parameters_are_included_when_enabled() : void
public function test_parameters_are_not_included_by_default() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor);
$mockClient = $this->createMockClient();
@@ -429,7 +429,7 @@ public function test_parameters_are_not_included_by_default() : void
public function test_query_text_is_not_truncated_when_max_length_null() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor, postgresql_telemetry_options(
maxQueryLength: null,
));
@@ -448,7 +448,7 @@ public function test_query_text_is_not_truncated_when_max_length_null() : void
public function test_query_text_is_truncated_when_max_length_set() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor, postgresql_telemetry_options(
maxQueryLength: 20,
));
@@ -466,7 +466,7 @@ public function test_query_text_is_truncated_when_max_length_set() : void
public function test_set_auto_commit_delegates() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor);
$mockClient = $this->createMockClient();
@@ -484,8 +484,8 @@ private function createConfig(MemorySpanProcessor $spanProcessor, ?PostgreSqlTel
$tel = telemetry(
resource(),
tracer_provider($spanProcessor, $clock, $contextStorage),
- meter_provider(memory_metric_processor(void_metric_exporter()), $clock),
- logger_provider(memory_log_processor(void_log_exporter()), $clock, $contextStorage),
+ meter_provider(memory_metric_processor(void_exporter()), $clock),
+ logger_provider(memory_log_processor(void_exporter()), $clock, $contextStorage),
);
return postgresql_telemetry_config($tel, $clock, $options ?? postgresql_telemetry_options());
diff --git a/src/lib/postgresql/tests/Flow/PostgreSql/Tests/Unit/Client/Telemetry/TraceableCursorTest.php b/src/lib/postgresql/tests/Flow/PostgreSql/Tests/Unit/Client/Telemetry/TraceableCursorTest.php
index 8b6b5aa0b..483569f3e 100644
--- a/src/lib/postgresql/tests/Flow/PostgreSql/Tests/Unit/Client/Telemetry/TraceableCursorTest.php
+++ b/src/lib/postgresql/tests/Flow/PostgreSql/Tests/Unit/Client/Telemetry/TraceableCursorTest.php
@@ -5,7 +5,7 @@
namespace Flow\PostgreSql\Tests\Unit\Client\Telemetry;
use function Flow\PostgreSql\DSL\{pgsql_connection_params, postgresql_telemetry_config, postgresql_telemetry_options};
-use function Flow\Telemetry\DSL\{logger_provider, memory_context_storage, memory_log_processor, memory_metric_processor, memory_span_processor, meter_provider, resource, telemetry, tracer_provider, void_log_exporter, void_metric_exporter, void_span_exporter};
+use function Flow\Telemetry\DSL\{logger_provider, memory_context_storage, memory_log_processor, memory_metric_processor, memory_span_processor, meter_provider, resource, telemetry, tracer_provider, void_exporter};
use Flow\PostgreSql\Client\{ConnectionParameters, Cursor};
use Flow\PostgreSql\Client\Telemetry\{PostgreSqlTelemetryAttributes, PostgreSqlTelemetryConfig, PostgreSqlTelemetryOptions, TraceableCursor};
use Flow\PostgreSql\Tests\Unit\Client\RowMapper\Fake\SpyRowMapper;
@@ -17,7 +17,7 @@ final class TraceableCursorTest extends TestCase
{
public function test_count_delegates_to_underlying_cursor() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor);
$mockCursor = $this->createMock(Cursor::class);
@@ -30,7 +30,7 @@ public function test_count_delegates_to_underlying_cursor() : void
public function test_free_completes_span_with_row_count() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor);
$mockCursor = $this->createMock(Cursor::class);
@@ -56,7 +56,7 @@ public function test_free_completes_span_with_row_count() : void
public function test_free_rethrows_exception_and_records_error() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor);
$exception = new \RuntimeException('Free failed');
@@ -80,7 +80,7 @@ public function test_free_rethrows_exception_and_records_error() : void
public function test_iterate_completes_span_after_full_iteration() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor);
$mockCursor = $this->createMock(Cursor::class);
@@ -107,7 +107,7 @@ public function test_iterate_completes_span_after_full_iteration() : void
public function test_iterate_records_error_on_exception() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor);
$mockCursor = $this->createMock(Cursor::class);
@@ -135,7 +135,7 @@ public function test_iterate_records_error_on_exception() : void
public function test_map_completes_span_after_full_iteration() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor);
$mockCursor = $this->createMock(Cursor::class);
@@ -161,7 +161,7 @@ public function test_map_completes_span_after_full_iteration() : void
public function test_next_increments_row_count() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor);
$mockCursor = $this->createMock(Cursor::class);
@@ -187,7 +187,7 @@ public function test_next_increments_row_count() : void
public function test_parameter_count_is_limited_by_default() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor, postgresql_telemetry_options(
includeParameters: true,
));
@@ -212,7 +212,7 @@ public function test_parameter_count_is_limited_by_default() : void
public function test_parameter_count_unlimited_when_null() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor, postgresql_telemetry_options(
includeParameters: true,
maxParameters: null,
@@ -234,7 +234,7 @@ public function test_parameter_count_unlimited_when_null() : void
public function test_parameter_values_are_truncated_by_default() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor, postgresql_telemetry_options(
includeParameters: true,
));
@@ -255,7 +255,7 @@ public function test_parameter_values_are_truncated_by_default() : void
public function test_parameter_values_unlimited_when_null() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor, postgresql_telemetry_options(
includeParameters: true,
maxParameterLength: null,
@@ -277,7 +277,7 @@ public function test_parameter_values_unlimited_when_null() : void
public function test_span_includes_correct_attributes() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor);
$mockCursor = $this->createMock(Cursor::class);
@@ -299,7 +299,7 @@ public function test_span_includes_correct_attributes() : void
public function test_tracing_disabled_does_not_create_span() : void
{
- $spanProcessor = memory_span_processor(void_span_exporter());
+ $spanProcessor = memory_span_processor(void_exporter());
$config = $this->createConfig($spanProcessor, postgresql_telemetry_options(
traceQueries: false,
collectMetrics: false,
@@ -326,8 +326,8 @@ private function createConfig(MemorySpanProcessor $spanProcessor, ?PostgreSqlTel
$tel = telemetry(
resource(),
tracer_provider($spanProcessor, $clock, $contextStorage),
- meter_provider(memory_metric_processor(void_metric_exporter()), $clock),
- logger_provider(memory_log_processor(void_log_exporter()), $clock, $contextStorage),
+ meter_provider(memory_metric_processor(void_exporter()), $clock),
+ logger_provider(memory_log_processor(void_exporter()), $clock, $contextStorage),
);
return postgresql_telemetry_config($tel, $clock, $options ?? postgresql_telemetry_options());
diff --git a/src/lib/telemetry/src/Flow/Telemetry/DSL/functions.php b/src/lib/telemetry/src/Flow/Telemetry/DSL/functions.php
index a0b695b5f..3f30fa36b 100644
--- a/src/lib/telemetry/src/Flow/Telemetry/DSL/functions.php
+++ b/src/lib/telemetry/src/Flow/Telemetry/DSL/functions.php
@@ -7,20 +7,22 @@
use Flow\ETL\Attribute\{DocumentationDSL, Module, Type as DSLType};
use Flow\Telemetry\{Attributes, Resource, Telemetry};
use Flow\Telemetry\Context\{Baggage, Context, ContextStorage, MemoryContextStorage, SpanId, TraceId};
+use Flow\Telemetry\ErrorHandler\{CompositeErrorHandler, ErrorHandler, ErrorLogHandler, ErrorLogMessageType, NullErrorHandler, StreamHandler, SyslogFacility, SyslogHandler, SyslogSeverity, UdpSyslogHandler};
+use Flow\Telemetry\Exporter\Exporter;
use Flow\Telemetry\InstrumentationScope;
-use Flow\Telemetry\Logger\{LogExporter, LogProcessor, LogRecordLimits, LoggerProvider, Severity};
+use Flow\Telemetry\Logger\{LogProcessor, LogRecordLimits, LoggerProvider, Severity};
use Flow\Telemetry\Logger\Processor\{BatchingLogProcessor, PassThroughLogProcessor, SeverityFilteringLogProcessor};
-use Flow\Telemetry\Meter\{AggregationTemporality, MeterProvider, MetricExporter, MetricLimits, MetricProcessor};
+use Flow\Telemetry\Meter\{AggregationTemporality, MeterProvider, MetricLimits, MetricProcessor};
use Flow\Telemetry\Meter\Exemplar\{AlwaysOffExemplarFilter, AlwaysOnExemplarFilter, ExemplarFilter, TraceBasedExemplarFilter};
use Flow\Telemetry\Meter\Processor\{BatchingMetricProcessor, PassThroughMetricProcessor};
use Flow\Telemetry\Propagation\{ArrayCarrier, CompositePropagator, PropagationContext, Propagator, SuperglobalCarrier, W3CBaggage, W3CTraceContext};
use Flow\Telemetry\Provider\Clock\SystemClock;
-use Flow\Telemetry\Provider\Console\{ConsoleLogExporter, ConsoleLogOptions, ConsoleMetricExporter, ConsoleMetricOptions, ConsoleSpanExporter, ConsoleSpanOptions};
-use Flow\Telemetry\Provider\Memory\{MemoryLogExporter, MemoryLogProcessor, MemoryMetricExporter, MemoryMetricProcessor, MemorySpanExporter, MemorySpanProcessor};
-use Flow\Telemetry\Provider\Void\{VoidLogExporter, VoidLogProcessor, VoidMetricExporter, VoidMetricProcessor, VoidSpanExporter, VoidSpanProcessor};
+use Flow\Telemetry\Provider\Console\{ConsoleExporter, ConsoleLogOptions, ConsoleMetricOptions, ConsoleSpanOptions};
+use Flow\Telemetry\Provider\Memory\{MemoryExporter, MemoryLogProcessor, MemoryMetricProcessor, MemorySpanProcessor};
+use Flow\Telemetry\Provider\Void\{VoidExporter, VoidLogProcessor, VoidMetricProcessor, VoidSpanProcessor};
use Flow\Telemetry\Resource\Detector\{CachingDetector, ChainDetector, ComposerDetector, EnvironmentDetector, HostDetector, ManualDetector, OsDetector, ProcessDetector};
use Flow\Telemetry\Resource\ResourceDetector;
-use Flow\Telemetry\Tracer\{GenericEvent, SpanContext, SpanExporter, SpanLimits, SpanLink, SpanProcessor, TracerProvider};
+use Flow\Telemetry\Tracer\{GenericEvent, SpanContext, SpanLimits, SpanLink, SpanProcessor, TracerProvider};
use Flow\Telemetry\Tracer\Processor\{BatchingSpanProcessor, PassThroughSpanProcessor};
use Flow\Telemetry\Tracer\Sampler\{AlwaysOnSampler, Sampler};
use Psr\Clock\ClockInterface;
@@ -160,9 +162,6 @@ function span_link(SpanContext $context, array|Attributes $attributes = []) : Sp
/**
* Create SpanLimits configuration.
*
- * SpanLimits controls the maximum amount of data a span can collect,
- * preventing unbounded memory growth and ensuring reasonable span sizes.
- *
* @param int $attributeCountLimit Maximum number of attributes per span
* @param int $eventCountLimit Maximum number of events per span
* @param int $linkCountLimit Maximum number of links per span
@@ -192,9 +191,6 @@ function span_limits(
/**
* Create LogRecordLimits configuration.
*
- * LogRecordLimits controls the maximum amount of data a log record can collect,
- * preventing unbounded memory growth and ensuring reasonable log record sizes.
- *
* @param int $attributeCountLimit Maximum number of attributes per log record
* @param null|int $attributeValueLengthLimit Maximum length for string attribute values (null = unlimited)
*/
@@ -212,15 +208,6 @@ function log_record_limits(
/**
* Create MetricLimits configuration.
*
- * MetricLimits controls the maximum cardinality (unique attribute combinations)
- * per metric instrument, preventing memory exhaustion from high-cardinality attributes.
- *
- * When the cardinality limit is exceeded, new attribute combinations are aggregated
- * into an overflow data point with `otel.metric.overflow: true` attribute.
- *
- * Note: Unlike spans and logs, metrics are EXEMPT from attribute count and value
- * length limits per the OpenTelemetry specification. Only cardinality is limited.
- *
* @param int $cardinalityLimit Maximum number of unique attribute combinations per instrument
*/
#[DocumentationDSL(module: Module::TELEMETRY, type: DSLType::HELPER)]
@@ -236,7 +223,6 @@ function metric_limits(
* Create a VoidSpanProcessor.
*
* No-op span processor that discards all data.
- * Use this when tracing is disabled to minimize overhead.
*/
#[DocumentationDSL(module: Module::TELEMETRY, type: DSLType::HELPER)]
function void_span_processor() : VoidSpanProcessor
@@ -248,7 +234,6 @@ function void_span_processor() : VoidSpanProcessor
* Create a VoidMetricProcessor.
*
* No-op metric processor that discards all data.
- * Use this when metrics collection is disabled to minimize overhead.
*/
#[DocumentationDSL(module: Module::TELEMETRY, type: DSLType::HELPER)]
function void_metric_processor() : VoidMetricProcessor
@@ -260,7 +245,6 @@ function void_metric_processor() : VoidMetricProcessor
* Create a VoidLogProcessor.
*
* No-op log processor that discards all data.
- * Use this when logging is disabled to minimize overhead.
*/
#[DocumentationDSL(module: Module::TELEMETRY, type: DSLType::HELPER)]
function void_log_processor() : VoidLogProcessor
@@ -269,134 +253,79 @@ function void_log_processor() : VoidLogProcessor
}
/**
- * Create a VoidSpanExporter.
- *
- * No-op span exporter that discards all data.
- * Use this when telemetry export is disabled to minimize overhead.
- */
-#[DocumentationDSL(module: Module::TELEMETRY, type: DSLType::HELPER)]
-function void_span_exporter() : VoidSpanExporter
-{
- return new VoidSpanExporter();
-}
-
-/**
- * Create a VoidMetricExporter.
- *
- * No-op metric exporter that discards all data.
- * Use this when telemetry export is disabled to minimize overhead.
- */
-#[DocumentationDSL(module: Module::TELEMETRY, type: DSLType::HELPER)]
-function void_metric_exporter() : VoidMetricExporter
-{
- return new VoidMetricExporter();
-}
-
-/**
- * Create a VoidLogExporter.
- *
- * No-op log exporter that discards all data.
- * Use this when telemetry export is disabled to minimize overhead.
- */
-#[DocumentationDSL(module: Module::TELEMETRY, type: DSLType::HELPER)]
-function void_log_exporter() : VoidLogExporter
-{
- return new VoidLogExporter();
-}
-
-/**
- * Create a MemorySpanExporter.
- *
- * Span exporter that stores data in memory.
- * Provides direct getter access to exported spans.
- * Useful for testing and inspection without serialization.
- */
-#[DocumentationDSL(module: Module::TELEMETRY, type: DSLType::HELPER)]
-function memory_span_exporter() : MemorySpanExporter
-{
- return new MemorySpanExporter();
-}
-
-/**
- * Create a MemoryMetricExporter.
+ * Create a VoidExporter.
*
- * Metric exporter that stores data in memory.
- * Provides direct getter access to exported metrics.
- * Useful for testing and inspection without serialization.
+ * No-op unified exporter that discards logs, metrics, and spans.
*/
#[DocumentationDSL(module: Module::TELEMETRY, type: DSLType::HELPER)]
-function memory_metric_exporter() : MemoryMetricExporter
+function void_exporter() : VoidExporter
{
- return new MemoryMetricExporter();
+ return new VoidExporter();
}
/**
- * Create a MemoryLogExporter.
+ * Create a MemoryExporter.
*
- * Log exporter that stores data in memory.
- * Provides direct getter access to exported log entries.
+ * Unified exporter that stores logs, metrics, and spans in memory for direct access.
* Useful for testing and inspection without serialization.
*/
#[DocumentationDSL(module: Module::TELEMETRY, type: DSLType::HELPER)]
-function memory_log_exporter() : MemoryLogExporter
+function memory_exporter() : MemoryExporter
{
- return new MemoryLogExporter();
+ return new MemoryExporter();
}
/**
* Create a MemorySpanProcessor.
*
- * Span processor that stores spans in memory and exports via configured exporter.
- * Useful for testing.
- *
- * @param SpanExporter $exporter The exporter to send spans to
+ * @param Exporter $exporter The exporter to send spans to
+ * @param ErrorHandler $errorHandler Handler for Throwables raised by the exporter
*/
#[DocumentationDSL(module: Module::TELEMETRY, type: DSLType::HELPER)]
-function memory_span_processor(SpanExporter $exporter) : MemorySpanProcessor
-{
- return new MemorySpanProcessor($exporter);
+function memory_span_processor(
+ Exporter $exporter,
+ ErrorHandler $errorHandler = new ErrorLogHandler(),
+) : MemorySpanProcessor {
+ return new MemorySpanProcessor($exporter, $errorHandler);
}
/**
* Create a MemoryMetricProcessor.
*
- * Metric processor that stores metrics in memory and exports via configured exporter.
- * Useful for testing.
- *
- * @param MetricExporter $exporter The exporter to send metrics to
+ * @param Exporter $exporter The exporter to send metrics to
+ * @param ErrorHandler $errorHandler Handler for Throwables raised by the exporter
*/
#[DocumentationDSL(module: Module::TELEMETRY, type: DSLType::HELPER)]
-function memory_metric_processor(MetricExporter $exporter) : MemoryMetricProcessor
-{
- return new MemoryMetricProcessor($exporter);
+function memory_metric_processor(
+ Exporter $exporter,
+ ErrorHandler $errorHandler = new ErrorLogHandler(),
+) : MemoryMetricProcessor {
+ return new MemoryMetricProcessor($exporter, $errorHandler);
}
/**
* Create a MemoryLogProcessor.
*
- * Log processor that stores log records in memory and exports via configured exporter.
- * Useful for testing.
- *
- * @param LogExporter $exporter The exporter to send logs to
+ * @param Exporter $exporter The exporter to send logs to
+ * @param ErrorHandler $errorHandler Handler for Throwables raised by the exporter
*/
#[DocumentationDSL(module: Module::TELEMETRY, type: DSLType::HELPER)]
-function memory_log_processor(LogExporter $exporter) : MemoryLogProcessor
-{
- return new MemoryLogProcessor($exporter);
+function memory_log_processor(
+ Exporter $exporter,
+ ErrorHandler $errorHandler = new ErrorLogHandler(),
+) : MemoryLogProcessor {
+ return new MemoryLogProcessor($exporter, $errorHandler);
}
/**
* Create a TracerProvider.
*
- * Creates a provider that uses a SpanProcessor for processing spans.
- * For void/disabled tracing, pass void_processor().
- * For memory-based testing, pass memory_processor() with exporters.
- *
* @param SpanProcessor $processor The processor for spans
* @param ClockInterface $clock The clock for timestamps
* @param ContextStorage $contextStorage Storage for context propagation
* @param Sampler $sampler Sampling strategy for spans
* @param SpanLimits $limits Limits for span attributes, events, and links
+ * @param ErrorHandler $errorHandler Handler for runtime Throwables raised by the processor
*/
#[DocumentationDSL(module: Module::TELEMETRY, type: DSLType::HELPER)]
function tracer_provider(
@@ -405,6 +334,7 @@ function tracer_provider(
ContextStorage $contextStorage,
Sampler $sampler = new AlwaysOnSampler(),
SpanLimits $limits = new SpanLimits(),
+ ErrorHandler $errorHandler = new ErrorLogHandler(),
) : TracerProvider {
return new TracerProvider(
$processor,
@@ -412,20 +342,18 @@ function tracer_provider(
$contextStorage,
$sampler,
$limits,
+ $errorHandler,
);
}
/**
* Create a LoggerProvider.
*
- * Creates a provider that uses a LogProcessor for processing logs.
- * For void/disabled logging, pass void_processor().
- * For memory-based testing, pass memory_processor() with exporters.
- *
* @param LogProcessor $processor The processor for logs
* @param ClockInterface $clock The clock for timestamps
* @param ContextStorage $contextStorage Storage for span correlation
* @param LogRecordLimits $limits Limits for log record attributes
+ * @param ErrorHandler $errorHandler Handler for runtime Throwables raised by the processor
*/
#[DocumentationDSL(module: Module::TELEMETRY, type: DSLType::HELPER)]
function logger_provider(
@@ -433,27 +361,26 @@ function logger_provider(
ClockInterface $clock,
ContextStorage $contextStorage,
LogRecordLimits $limits = new LogRecordLimits(),
+ ErrorHandler $errorHandler = new ErrorLogHandler(),
) : LoggerProvider {
return new LoggerProvider(
$processor,
$clock,
$contextStorage,
$limits,
+ $errorHandler,
);
}
/**
* Create a MeterProvider.
*
- * Creates a provider that uses a MetricProcessor for processing metrics.
- * For void/disabled metrics, pass void_processor().
- * For memory-based testing, pass memory_processor() with exporters.
- *
* @param MetricProcessor $processor The processor for metrics
* @param ClockInterface $clock The clock for timestamps
* @param AggregationTemporality $temporality Aggregation temporality for metrics
* @param ExemplarFilter $exemplarFilter Filter for exemplar sampling (default: TraceBasedExemplarFilter)
* @param MetricLimits $limits Cardinality limits for metric instruments
+ * @param ErrorHandler $errorHandler Handler for runtime Throwables raised by the processor
*/
#[DocumentationDSL(module: Module::TELEMETRY, type: DSLType::HELPER)]
function meter_provider(
@@ -462,6 +389,7 @@ function meter_provider(
AggregationTemporality $temporality = AggregationTemporality::CUMULATIVE,
ExemplarFilter $exemplarFilter = new TraceBasedExemplarFilter(),
MetricLimits $limits = new MetricLimits(),
+ ErrorHandler $errorHandler = new ErrorLogHandler(),
) : MeterProvider {
return new MeterProvider(
$processor,
@@ -469,6 +397,7 @@ function meter_provider(
$temporality,
$exemplarFilter,
$limits,
+ $errorHandler,
);
}
@@ -481,6 +410,7 @@ function meter_provider(
* @param null|TracerProvider $tracerProvider The tracer provider (null for void/disabled)
* @param null|MeterProvider $meterProvider The meter provider (null for void/disabled)
* @param null|LoggerProvider $loggerProvider The logger provider (null for void/disabled)
+ * @param ErrorHandler $errorHandler Handler propagated to default void providers when explicit ones are not supplied
*/
#[DocumentationDSL(module: Module::TELEMETRY, type: DSLType::HELPER)]
function telemetry(
@@ -488,15 +418,16 @@ function telemetry(
?TracerProvider $tracerProvider = null,
?MeterProvider $meterProvider = null,
?LoggerProvider $loggerProvider = null,
+ ErrorHandler $errorHandler = new ErrorLogHandler(),
) : Telemetry {
$clock = new SystemClock();
$contextStorage = new MemoryContextStorage();
return new Telemetry(
$resource,
- $tracerProvider ?? new TracerProvider(new VoidSpanProcessor(), $clock, $contextStorage),
- $meterProvider ?? new MeterProvider(new VoidMetricProcessor(), $clock),
- $loggerProvider ?? new LoggerProvider(new VoidLogProcessor(), $clock, $contextStorage),
+ $tracerProvider ?? new TracerProvider(new VoidSpanProcessor(), $clock, $contextStorage, errorHandler: $errorHandler),
+ $meterProvider ?? new MeterProvider(new VoidMetricProcessor(), $clock, errorHandler: $errorHandler),
+ $loggerProvider ?? new LoggerProvider(new VoidLogProcessor(), $clock, $contextStorage, errorHandler: $errorHandler),
);
}
@@ -520,96 +451,96 @@ function instrumentation_scope(
/**
* Create a BatchingSpanProcessor.
*
- * Collects spans in memory and exports them in batches for efficiency.
- * Spans are exported when batch size is reached, flush() is called, or shutdown().
- *
- * @param SpanExporter $exporter The exporter to send spans to
+ * @param Exporter $exporter The exporter to send spans to
* @param int $batchSize Number of spans to collect before exporting (default 512)
+ * @param ErrorHandler $errorHandler Handler for Throwables raised by the exporter
*/
#[DocumentationDSL(module: Module::TELEMETRY, type: DSLType::HELPER)]
-function batching_span_processor(SpanExporter $exporter, int $batchSize = 512) : BatchingSpanProcessor
-{
- return new BatchingSpanProcessor($exporter, $batchSize);
+function batching_span_processor(
+ Exporter $exporter,
+ int $batchSize = 512,
+ ErrorHandler $errorHandler = new ErrorLogHandler(),
+) : BatchingSpanProcessor {
+ return new BatchingSpanProcessor($exporter, $batchSize, $errorHandler);
}
/**
* Create a PassThroughSpanProcessor.
*
- * Exports each span immediately when it ends.
- * Useful for debugging where immediate visibility is more important than performance.
- *
- * @param SpanExporter $exporter The exporter to send spans to
+ * @param Exporter $exporter The exporter to send spans to
+ * @param ErrorHandler $errorHandler Handler for Throwables raised by the exporter
*/
#[DocumentationDSL(module: Module::TELEMETRY, type: DSLType::HELPER)]
-function pass_through_span_processor(SpanExporter $exporter) : PassThroughSpanProcessor
-{
- return new PassThroughSpanProcessor($exporter);
+function pass_through_span_processor(
+ Exporter $exporter,
+ ErrorHandler $errorHandler = new ErrorLogHandler(),
+) : PassThroughSpanProcessor {
+ return new PassThroughSpanProcessor($exporter, $errorHandler);
}
/**
* Create a BatchingMetricProcessor.
*
- * Collects metrics in memory and exports them in batches for efficiency.
- * Metrics are exported when batch size is reached, flush() is called, or shutdown().
- *
- * @param MetricExporter $exporter The exporter to send metrics to
+ * @param Exporter $exporter The exporter to send metrics to
* @param int $batchSize Number of metrics to collect before exporting (default 512)
+ * @param ErrorHandler $errorHandler Handler for Throwables raised by the exporter
*/
#[DocumentationDSL(module: Module::TELEMETRY, type: DSLType::HELPER)]
-function batching_metric_processor(MetricExporter $exporter, int $batchSize = 512) : BatchingMetricProcessor
-{
- return new BatchingMetricProcessor($exporter, $batchSize);
+function batching_metric_processor(
+ Exporter $exporter,
+ int $batchSize = 512,
+ ErrorHandler $errorHandler = new ErrorLogHandler(),
+) : BatchingMetricProcessor {
+ return new BatchingMetricProcessor($exporter, $batchSize, $errorHandler);
}
/**
* Create a PassThroughMetricProcessor.
*
- * Exports each metric immediately when processed.
- * Useful for debugging where immediate visibility is more important than performance.
- *
- * @param MetricExporter $exporter The exporter to send metrics to
+ * @param Exporter $exporter The exporter to send metrics to
+ * @param ErrorHandler $errorHandler Handler for Throwables raised by the exporter
*/
#[DocumentationDSL(module: Module::TELEMETRY, type: DSLType::HELPER)]
-function pass_through_metric_processor(MetricExporter $exporter) : PassThroughMetricProcessor
-{
- return new PassThroughMetricProcessor($exporter);
+function pass_through_metric_processor(
+ Exporter $exporter,
+ ErrorHandler $errorHandler = new ErrorLogHandler(),
+) : PassThroughMetricProcessor {
+ return new PassThroughMetricProcessor($exporter, $errorHandler);
}
/**
* Create a BatchingLogProcessor.
*
- * Collects log records in memory and exports them in batches for efficiency.
- * Logs are exported when batch size is reached, flush() is called, or shutdown().
- *
- * @param LogExporter $exporter The exporter to send logs to
+ * @param Exporter $exporter The exporter to send logs to
* @param int $batchSize Number of logs to collect before exporting (default 512)
+ * @param ErrorHandler $errorHandler Handler for Throwables raised by the exporter
*/
#[DocumentationDSL(module: Module::TELEMETRY, type: DSLType::HELPER)]
-function batching_log_processor(LogExporter $exporter, int $batchSize = 512) : BatchingLogProcessor
-{
- return new BatchingLogProcessor($exporter, $batchSize);
+function batching_log_processor(
+ Exporter $exporter,
+ int $batchSize = 512,
+ ErrorHandler $errorHandler = new ErrorLogHandler(),
+) : BatchingLogProcessor {
+ return new BatchingLogProcessor($exporter, $batchSize, $errorHandler);
}
/**
* Create a PassThroughLogProcessor.
*
- * Exports each log record immediately when processed.
- * Useful for debugging where immediate visibility is more important than performance.
- *
- * @param LogExporter $exporter The exporter to send logs to
+ * @param Exporter $exporter The exporter to send logs to
+ * @param ErrorHandler $errorHandler Handler for Throwables raised by the exporter
*/
#[DocumentationDSL(module: Module::TELEMETRY, type: DSLType::HELPER)]
-function pass_through_log_processor(LogExporter $exporter) : PassThroughLogProcessor
-{
- return new PassThroughLogProcessor($exporter);
+function pass_through_log_processor(
+ Exporter $exporter,
+ ErrorHandler $errorHandler = new ErrorLogHandler(),
+) : PassThroughLogProcessor {
+ return new PassThroughLogProcessor($exporter, $errorHandler);
}
/**
* Create a SeverityFilteringLogProcessor.
*
- * Filters log entries based on minimum severity level. Only entries at or above
- * the configured threshold are passed to the wrapped processor.
- *
* @param LogProcessor $processor The processor to wrap
* @param Severity $minimumSeverity Minimum severity level (default: INFO)
*/
@@ -622,49 +553,25 @@ function severity_filtering_log_processor(
}
/**
- * Create a ConsoleSpanExporter.
+ * Create a unified ConsoleExporter for logs, metrics, and spans.
*
- * Outputs spans to the console with ASCII table formatting.
- * Useful for debugging and development.
+ * Outputs telemetry to the console with ASCII table formatting and optional ANSI colors.
*
* @param bool $colors Whether to use ANSI colors (default: true)
- * @param ConsoleSpanOptions $options Display options for the exporter
+ * @param null|int $maxLogBodyLength Maximum length for log body+attributes column (null = no limit)
+ * @param ConsoleLogOptions $logOptions Display options for log records
+ * @param ConsoleMetricOptions $metricOptions Display options for metrics
+ * @param ConsoleSpanOptions $spanOptions Display options for spans
*/
#[DocumentationDSL(module: Module::TELEMETRY, type: DSLType::HELPER)]
-function console_span_exporter(bool $colors = true, ConsoleSpanOptions $options = new ConsoleSpanOptions()) : ConsoleSpanExporter
-{
- return new ConsoleSpanExporter($colors, null, $options);
-}
-
-/**
- * Create a ConsoleMetricExporter.
- *
- * Outputs metrics to the console with ASCII table formatting.
- * Useful for debugging and development.
- *
- * @param bool $colors Whether to use ANSI colors (default: true)
- * @param ConsoleMetricOptions $options Display options for the exporter
- */
-#[DocumentationDSL(module: Module::TELEMETRY, type: DSLType::HELPER)]
-function console_metric_exporter(bool $colors = true, ConsoleMetricOptions $options = new ConsoleMetricOptions()) : ConsoleMetricExporter
-{
- return new ConsoleMetricExporter($colors, null, $options);
-}
-
-/**
- * Create a ConsoleLogExporter.
- *
- * Outputs log records to the console with severity-based coloring.
- * Useful for debugging and development.
- *
- * @param bool $colors Whether to use ANSI colors (default: true)
- * @param null|int $maxBodyLength Maximum length for body+attributes column (null = no limit, default: 100)
- * @param ConsoleLogOptions $options Display options for the exporter
- */
-#[DocumentationDSL(module: Module::TELEMETRY, type: DSLType::HELPER)]
-function console_log_exporter(bool $colors = true, ?int $maxBodyLength = 100, ConsoleLogOptions $options = new ConsoleLogOptions()) : ConsoleLogExporter
-{
- return new ConsoleLogExporter($colors, $maxBodyLength, null, $options);
+function console_exporter(
+ bool $colors = true,
+ ?int $maxLogBodyLength = 100,
+ ConsoleLogOptions $logOptions = new ConsoleLogOptions(),
+ ConsoleMetricOptions $metricOptions = new ConsoleMetricOptions(),
+ ConsoleSpanOptions $spanOptions = new ConsoleSpanOptions(),
+) : ConsoleExporter {
+ return new ConsoleExporter($colors, $maxLogBodyLength, null, $logOptions, $metricOptions, $spanOptions);
}
/**
@@ -723,9 +630,6 @@ function console_metric_options_minimal() : ConsoleMetricOptions
/**
* Create an AlwaysOnExemplarFilter.
- *
- * Records exemplars whenever a span context is present.
- * Use this filter for debugging or when complete trace context is important.
*/
#[DocumentationDSL(module: Module::TELEMETRY, type: DSLType::HELPER)]
function always_on_exemplar_filter() : AlwaysOnExemplarFilter
@@ -735,9 +639,6 @@ function always_on_exemplar_filter() : AlwaysOnExemplarFilter
/**
* Create an AlwaysOffExemplarFilter.
- *
- * Never records exemplars. Use this filter to disable exemplar collection
- * entirely for performance optimization.
*/
#[DocumentationDSL(module: Module::TELEMETRY, type: DSLType::HELPER)]
function always_off_exemplar_filter() : AlwaysOffExemplarFilter
@@ -747,9 +648,6 @@ function always_off_exemplar_filter() : AlwaysOffExemplarFilter
/**
* Create a TraceBasedExemplarFilter.
- *
- * Records exemplars only when the span is sampled (has SAMPLED trace flag).
- * This is the default filter, balancing exemplar collection with performance.
*/
#[DocumentationDSL(module: Module::TELEMETRY, type: DSLType::HELPER)]
function trace_based_exemplar_filter() : TraceBasedExemplarFilter
@@ -760,9 +658,6 @@ function trace_based_exemplar_filter() : TraceBasedExemplarFilter
/**
* Create a PropagationContext.
*
- * Value object containing both trace context (SpanContext) and application
- * data (Baggage) that can be propagated across process boundaries.
- *
* @param null|SpanContext $spanContext Optional span context
* @param null|Baggage $baggage Optional baggage
*/
@@ -775,8 +670,6 @@ function propagation_context(?SpanContext $spanContext = null, ?Baggage $baggage
/**
* Create an ArrayCarrier.
*
- * Carrier backed by an associative array with case-insensitive key lookup.
- *
* @param array $data Initial carrier data
*/
#[DocumentationDSL(module: Module::TELEMETRY, type: DSLType::HELPER)]
@@ -787,9 +680,6 @@ function array_carrier(array $data = []) : ArrayCarrier
/**
* Create a SuperglobalCarrier.
- *
- * Read-only carrier that extracts context from PHP superglobals
- * ($_SERVER, $_GET, $_POST, $_COOKIE).
*/
#[DocumentationDSL(module: Module::TELEMETRY, type: DSLType::HELPER)]
function superglobal_carrier() : SuperglobalCarrier
@@ -799,9 +689,6 @@ function superglobal_carrier() : SuperglobalCarrier
/**
* Create a W3CTraceContext propagator.
- *
- * Implements W3C Trace Context specification for propagating trace context
- * using traceparent and tracestate headers.
*/
#[DocumentationDSL(module: Module::TELEMETRY, type: DSLType::HELPER)]
function w3c_trace_context() : W3CTraceContext
@@ -811,9 +698,6 @@ function w3c_trace_context() : W3CTraceContext
/**
* Create a W3CBaggage propagator.
- *
- * Implements W3C Baggage specification for propagating application-specific
- * key-value pairs using the baggage header.
*/
#[DocumentationDSL(module: Module::TELEMETRY, type: DSLType::HELPER)]
function w3c_baggage() : W3CBaggage
@@ -824,9 +708,6 @@ function w3c_baggage() : W3CBaggage
/**
* Create a CompositePropagator.
*
- * Combines multiple propagators into one. On extract, all propagators are
- * invoked and their contexts are merged. On inject, all propagators are invoked.
- *
* @param Propagator ...$propagators The propagators to combine
*/
#[DocumentationDSL(module: Module::TELEMETRY, type: DSLType::HELPER)]
@@ -838,10 +719,6 @@ function composite_propagator(Propagator ...$propagators) : CompositePropagator
/**
* Create a ChainDetector.
*
- * Combines multiple resource detectors into a chain. Detectors are executed
- * in order and their results are merged. Later detectors take precedence
- * over earlier ones when there are conflicting attribute keys.
- *
* @param ResourceDetector ...$detectors The detectors to chain
*/
#[DocumentationDSL(module: Module::TELEMETRY, type: DSLType::HELPER)]
@@ -852,9 +729,6 @@ function chain_detector(ResourceDetector ...$detectors) : ChainDetector
/**
* Create an OsDetector.
- *
- * Detects operating system information including os.type, os.name, os.version,
- * and os.description using PHP's php_uname() function.
*/
#[DocumentationDSL(module: Module::TELEMETRY, type: DSLType::HELPER)]
function os_detector() : OsDetector
@@ -864,9 +738,6 @@ function os_detector() : OsDetector
/**
* Create a HostDetector.
- *
- * Detects host information including host.name, host.arch, and host.id
- * (from /etc/machine-id on Linux or IOPlatformUUID on macOS).
*/
#[DocumentationDSL(module: Module::TELEMETRY, type: DSLType::HELPER)]
function host_detector() : HostDetector
@@ -876,10 +747,6 @@ function host_detector() : HostDetector
/**
* Create a ProcessDetector.
- *
- * Detects process information including process.pid, process.executable.path,
- * process.runtime.name (PHP), process.runtime.version, process.command,
- * and process.owner (on POSIX systems).
*/
#[DocumentationDSL(module: Module::TELEMETRY, type: DSLType::HELPER)]
function process_detector() : ProcessDetector
@@ -889,10 +756,6 @@ function process_detector() : ProcessDetector
/**
* Create an EnvironmentDetector.
- *
- * Detects resource attributes from OpenTelemetry standard environment variables:
- * - OTEL_SERVICE_NAME: Sets service.name attribute
- * - OTEL_RESOURCE_ATTRIBUTES: Sets additional attributes in key=value,key2=value2 format
*/
#[DocumentationDSL(module: Module::TELEMETRY, type: DSLType::HELPER)]
function environment_detector() : EnvironmentDetector
@@ -902,9 +765,6 @@ function environment_detector() : EnvironmentDetector
/**
* Create a ComposerDetector.
- *
- * Detects service.name and service.version from Composer's InstalledVersions
- * using the root package information.
*/
#[DocumentationDSL(module: Module::TELEMETRY, type: DSLType::HELPER)]
function composer_detector() : ComposerDetector
@@ -915,9 +775,6 @@ function composer_detector() : ComposerDetector
/**
* Create a ManualDetector.
*
- * Returns manually specified resource attributes. Use this when you need
- * to set attributes explicitly rather than detecting them automatically.
- *
* @param array|bool|float|int|string> $attributes Resource attributes
*/
#[DocumentationDSL(module: Module::TELEMETRY, type: DSLType::HELPER)]
@@ -929,9 +786,6 @@ function manual_detector(array $attributes) : ManualDetector
/**
* Create a CachingDetector.
*
- * Wraps another detector and caches its results to a file. On subsequent
- * calls, returns the cached resource instead of running detection again.
- *
* @param ResourceDetector $detector The detector to wrap
* @param null|string $cachePath Cache file path (default: sys_get_temp_dir()/flow_telemetry_resource.cache)
*/
@@ -944,15 +798,6 @@ function caching_detector(ResourceDetector $detector, ?string $cachePath = null)
/**
* Create a resource detector chain.
*
- * When no detectors are provided, uses the default detector chain:
- * 1. OsDetector - Operating system information
- * 2. HostDetector - Host information
- * 3. ProcessDetector - Process information
- * 4. ComposerDetector - Service information from Composer
- * 5. EnvironmentDetector - Environment variable overrides (highest precedence)
- *
- * When detectors are provided, uses only those detectors.
- *
* @param array $detectors Optional custom detectors (empty = use defaults)
*/
#[DocumentationDSL(module: Module::TELEMETRY, type: DSLType::HELPER)]
@@ -970,3 +815,74 @@ function resource_detector(array $detectors = []) : ChainDetector
return new ChainDetector(...$detectors);
}
+
+/**
+ * Create the default ErrorLogHandler. Writes via PHP's error_log().
+ */
+#[DocumentationDSL(module: Module::TELEMETRY, type: DSLType::HELPER)]
+function error_log_handler(
+ ErrorLogMessageType $messageType = ErrorLogMessageType::OperatingSystem,
+ bool $expandNewlines = false,
+ string $messagePrefix = '[flow-telemetry]',
+) : ErrorLogHandler {
+ return new ErrorLogHandler($messageType, $expandNewlines, $messagePrefix);
+}
+
+/**
+ * Create a StreamHandler. Appends formatted Throwables (one per line) to a file
+ * path or php:// stream wrapper.
+ */
+#[DocumentationDSL(module: Module::TELEMETRY, type: DSLType::HELPER)]
+function stream_error_handler(
+ string $destination,
+ int $filePermissions = 0644,
+ bool $createDirectories = true,
+ string $messagePrefix = '[flow-telemetry]',
+) : StreamHandler {
+ return new StreamHandler($destination, $filePermissions, $createDirectories, $messagePrefix);
+}
+
+/**
+ * Create a SyslogHandler. Writes via openlog/syslog/closelog.
+ */
+#[DocumentationDSL(module: Module::TELEMETRY, type: DSLType::HELPER)]
+function syslog_error_handler(
+ string $ident = 'flow-telemetry',
+ SyslogFacility $facility = SyslogFacility::User,
+ int $logOpts = \LOG_PID,
+ SyslogSeverity $severity = SyslogSeverity::Error,
+) : SyslogHandler {
+ return new SyslogHandler($ident, $facility, $logOpts, $severity);
+}
+
+/**
+ * Create a UdpSyslogHandler. Sends RFC 5424-style syslog frames over UDP.
+ */
+#[DocumentationDSL(module: Module::TELEMETRY, type: DSLType::HELPER)]
+function udp_syslog_error_handler(
+ string $host,
+ int $port = 514,
+ string $ident = 'flow-telemetry',
+ SyslogFacility $facility = SyslogFacility::User,
+ SyslogSeverity $severity = SyslogSeverity::Error,
+) : UdpSyslogHandler {
+ return new UdpSyslogHandler($host, $port, $ident, $facility, $severity);
+}
+
+/**
+ * Fan errors out to multiple handlers.
+ */
+#[DocumentationDSL(module: Module::TELEMETRY, type: DSLType::HELPER)]
+function composite_error_handler(ErrorHandler ...$handlers) : CompositeErrorHandler
+{
+ return new CompositeErrorHandler(...$handlers);
+}
+
+/**
+ * Discard every error. Use only in tests or for explicit silence.
+ */
+#[DocumentationDSL(module: Module::TELEMETRY, type: DSLType::HELPER)]
+function null_error_handler() : NullErrorHandler
+{
+ return new NullErrorHandler();
+}
diff --git a/src/lib/telemetry/src/Flow/Telemetry/ErrorHandler/CompositeErrorHandler.php b/src/lib/telemetry/src/Flow/Telemetry/ErrorHandler/CompositeErrorHandler.php
new file mode 100644
index 000000000..af5fa60d6
--- /dev/null
+++ b/src/lib/telemetry/src/Flow/Telemetry/ErrorHandler/CompositeErrorHandler.php
@@ -0,0 +1,38 @@
+ */
+ private array $handlers;
+
+ public function __construct(ErrorHandler ...$handlers)
+ {
+ $this->handlers = $handlers;
+ }
+
+ public function handle(\Throwable $error) : void
+ {
+ foreach ($this->handlers as $handler) {
+ try {
+ $handler->handle($error);
+ } catch (\Throwable) {
+ }
+ }
+ }
+
+ /**
+ * @return array
+ */
+ public function handlers() : array
+ {
+ return $this->handlers;
+ }
+}
diff --git a/src/lib/telemetry/src/Flow/Telemetry/ErrorHandler/ErrorHandler.php b/src/lib/telemetry/src/Flow/Telemetry/ErrorHandler/ErrorHandler.php
new file mode 100644
index 000000000..33d49be15
--- /dev/null
+++ b/src/lib/telemetry/src/Flow/Telemetry/ErrorHandler/ErrorHandler.php
@@ -0,0 +1,20 @@
+messagePrefix,
+ $error::class,
+ $error->getMessage(),
+ $error->getFile(),
+ $error->getLine(),
+ );
+
+ if ($this->expandNewlines) {
+ foreach (\explode("\n", $message) as $line) {
+ \error_log($line, $this->messageType->value);
+ }
+
+ return;
+ }
+
+ \error_log(\str_replace("\n", ' ', $message), $this->messageType->value);
+ } catch (\Throwable) {
+ }
+ }
+}
diff --git a/src/lib/telemetry/src/Flow/Telemetry/ErrorHandler/ErrorLogMessageType.php b/src/lib/telemetry/src/Flow/Telemetry/ErrorHandler/ErrorLogMessageType.php
new file mode 100644
index 000000000..f5da78e6b
--- /dev/null
+++ b/src/lib/telemetry/src/Flow/Telemetry/ErrorHandler/ErrorLogMessageType.php
@@ -0,0 +1,16 @@
+ 0777) {
+ throw new \InvalidArgumentException('File permissions must be between 0 and 0777');
+ }
+ }
+
+ public function __destruct()
+ {
+ if (\is_resource($this->stream)) {
+ @\fclose($this->stream);
+ $this->stream = null;
+ }
+ }
+
+ public function handle(\Throwable $error) : void
+ {
+ try {
+ $stream = $this->openStream();
+
+ if ($stream === null) {
+ return;
+ }
+
+ $payload = \sprintf(
+ "%s %s: %s in %s:%d\n",
+ $this->messagePrefix,
+ $error::class,
+ $error->getMessage(),
+ $error->getFile(),
+ $error->getLine(),
+ );
+
+ @\flock($stream, \LOCK_EX);
+
+ try {
+ @\fwrite($stream, $payload);
+ } finally {
+ @\flock($stream, \LOCK_UN);
+ }
+ } catch (\Throwable) {
+ }
+ }
+
+ /**
+ * @return null|resource
+ */
+ private function openStream()
+ {
+ if (\is_resource($this->stream)) {
+ return $this->stream;
+ }
+
+ $isStreamWrapper = \str_starts_with($this->destination, 'php://');
+ $existedBefore = !$isStreamWrapper && \is_file($this->destination);
+
+ if (!$isStreamWrapper && $this->createDirectories) {
+ $directory = \dirname($this->destination);
+
+ if (!\is_dir($directory)) {
+ @\mkdir($directory, 0755, true);
+ }
+ }
+
+ $handle = @\fopen($this->destination, 'a+b');
+
+ if (!\is_resource($handle)) {
+ return null;
+ }
+
+ if (!$isStreamWrapper && !$existedBefore) {
+ @\chmod($this->destination, $this->filePermissions);
+ }
+
+ $this->stream = $handle;
+
+ return $this->stream;
+ }
+}
diff --git a/src/lib/telemetry/src/Flow/Telemetry/ErrorHandler/SyslogFacility.php b/src/lib/telemetry/src/Flow/Telemetry/ErrorHandler/SyslogFacility.php
new file mode 100644
index 000000000..e231199f5
--- /dev/null
+++ b/src/lib/telemetry/src/Flow/Telemetry/ErrorHandler/SyslogFacility.php
@@ -0,0 +1,30 @@
+ident, $this->logOpts, $this->facility->value);
+ \syslog($this->severity->value, \sprintf(
+ '%s: %s in %s:%d',
+ $error::class,
+ $error->getMessage(),
+ $error->getFile(),
+ $error->getLine(),
+ ));
+ \closelog();
+ } catch (\Throwable) {
+ }
+ }
+}
diff --git a/src/lib/telemetry/src/Flow/Telemetry/ErrorHandler/SyslogSeverity.php b/src/lib/telemetry/src/Flow/Telemetry/ErrorHandler/SyslogSeverity.php
new file mode 100644
index 000000000..822d533a5
--- /dev/null
+++ b/src/lib/telemetry/src/Flow/Telemetry/ErrorHandler/SyslogSeverity.php
@@ -0,0 +1,20 @@
+ 65535) {
+ throw new \InvalidArgumentException('UdpSyslogHandler port must be between 1 and 65535');
+ }
+
+ if ($ident === '') {
+ throw new \InvalidArgumentException('UdpSyslogHandler ident must be a non-empty string');
+ }
+ }
+
+ public function __destruct()
+ {
+ if (\is_resource($this->socket)) {
+ @\fclose($this->socket);
+ $this->socket = null;
+ }
+ }
+
+ public function handle(\Throwable $error) : void
+ {
+ try {
+ $socket = $this->openSocket();
+
+ if ($socket === null) {
+ return;
+ }
+
+ $priority = $this->facility->value | $this->severity->value;
+ $timestamp = (new \DateTimeImmutable())->format(\DateTimeInterface::RFC3339);
+ $hostname = \gethostname();
+
+ if ($hostname === false) {
+ $hostname = '-';
+ }
+
+ $message = \sprintf(
+ '<%d>1 %s %s %s - - - %s: %s in %s:%d',
+ $priority,
+ $timestamp,
+ $hostname,
+ $this->ident,
+ $error::class,
+ $error->getMessage(),
+ $error->getFile(),
+ $error->getLine(),
+ );
+
+ @\fwrite($socket, $message);
+ } catch (\Throwable) {
+ }
+ }
+
+ /**
+ * @return null|resource
+ */
+ private function openSocket()
+ {
+ if (\is_resource($this->socket)) {
+ return $this->socket;
+ }
+
+ $handle = @\stream_socket_client(
+ \sprintf('udp://%s:%d', $this->host, $this->port),
+ $errno,
+ $errstr,
+ 1.0,
+ );
+
+ if (!\is_resource($handle)) {
+ return null;
+ }
+
+ $this->socket = $handle;
+
+ return $this->socket;
+ }
+}
diff --git a/src/lib/telemetry/src/Flow/Telemetry/Exporter/Exporter.php b/src/lib/telemetry/src/Flow/Telemetry/Exporter/Exporter.php
new file mode 100644
index 000000000..298f9e79a
--- /dev/null
+++ b/src/lib/telemetry/src/Flow/Telemetry/Exporter/Exporter.php
@@ -0,0 +1,35 @@
+client->sendLogs($entries);
- * }
- * // ...
- * }
- * ```
- */
-interface LogExporter
-{
- /**
- * Export a batch of log entries.
- *
- * Each log entry carries its own Resource and InstrumentationScope.
- *
- * @param array $entries The log entries to export
- *
- * @return bool True on success, false on failure
- */
- public function export(array $entries) : bool;
-
- /**
- * Get the transports used by this exporter.
- *
- * @return array Always returns at least one transport
- */
- public function transports() : array;
-}
diff --git a/src/lib/telemetry/src/Flow/Telemetry/Logger/LogProcessor.php b/src/lib/telemetry/src/Flow/Telemetry/Logger/LogProcessor.php
index d5f893d8a..07712817c 100644
--- a/src/lib/telemetry/src/Flow/Telemetry/Logger/LogProcessor.php
+++ b/src/lib/telemetry/src/Flow/Telemetry/Logger/LogProcessor.php
@@ -9,30 +9,9 @@
*
* Implementations may collect logs for batching, export them immediately,
* filter by severity, or perform other processing like enrichment.
- *
- * Example implementation:
- * ```php
- * final class BatchingLogProcessor implements LogProcessor
- * {
- * private array $buffer = [];
- *
- * public function process(LogEntry $entry): void {
- * $this->buffer[] = $entry;
- * if (count($this->buffer) >= 100) {
- * $this->flush();
- * }
- * }
- * // ...
- * }
- * ```
*/
interface LogProcessor
{
- /**
- * Get the exporter used by this processor.
- */
- public function exporter() : LogExporter;
-
/**
* Export all pending log records.
*
@@ -52,4 +31,12 @@ public function flush() : bool;
* @param LogEntry $entry The complete log entry to process
*/
public function process(LogEntry $entry) : void;
+
+ /**
+ * Shutdown the processor.
+ *
+ * Implementations SHOULD flush() pending data before delegating shutdown
+ * to the underlying exporter. MUST be idempotent and MUST NOT throw.
+ */
+ public function shutdown() : void;
}
diff --git a/src/lib/telemetry/src/Flow/Telemetry/Logger/Logger.php b/src/lib/telemetry/src/Flow/Telemetry/Logger/Logger.php
index 557c25728..7e478c09c 100644
--- a/src/lib/telemetry/src/Flow/Telemetry/Logger/Logger.php
+++ b/src/lib/telemetry/src/Flow/Telemetry/Logger/Logger.php
@@ -6,6 +6,7 @@
use Flow\Telemetry\{AttributeLimitsEnforcer, Attributes};
use Flow\Telemetry\Context\ContextStorage;
+use Flow\Telemetry\ErrorHandler\{ErrorHandler, ErrorLogHandler};
use Flow\Telemetry\{InstrumentationScope, Resource};
use Flow\Telemetry\Tracer\SpanContext;
use Psr\Clock\ClockInterface;
@@ -39,6 +40,7 @@ public function __construct(
private readonly ClockInterface $clock,
private readonly ContextStorage $contextStorage,
private readonly LogRecordLimits $limits = new LogRecordLimits(),
+ private readonly ErrorHandler $errorHandler = new ErrorLogHandler(),
) {
}
@@ -113,7 +115,11 @@ public function emit(LogRecord $record, ?SpanContext $spanContext = null) : void
$droppedAttributeCount,
);
- $this->processor->process($entry);
+ try {
+ $this->processor->process($entry);
+ } catch (\Throwable $e) {
+ $this->errorHandler->handle($e);
+ }
}
/**
@@ -183,7 +189,13 @@ public function fatal(
*/
public function flush() : bool
{
- return $this->processor->flush();
+ try {
+ return $this->processor->flush();
+ } catch (\Throwable $e) {
+ $this->errorHandler->handle($e);
+
+ return false;
+ }
}
/**
diff --git a/src/lib/telemetry/src/Flow/Telemetry/Logger/LoggerProvider.php b/src/lib/telemetry/src/Flow/Telemetry/Logger/LoggerProvider.php
index 2712100ef..da201a675 100644
--- a/src/lib/telemetry/src/Flow/Telemetry/Logger/LoggerProvider.php
+++ b/src/lib/telemetry/src/Flow/Telemetry/Logger/LoggerProvider.php
@@ -6,6 +6,7 @@
use Flow\Telemetry\{Attributes, InstrumentationScope, Resource};
use Flow\Telemetry\Context\ContextStorage;
+use Flow\Telemetry\ErrorHandler\{ErrorHandler, ErrorLogHandler};
use Psr\Clock\ClockInterface;
/**
@@ -17,11 +18,11 @@
* Example usage:
* ```php
* // For testing with memory storage
- * $processor = new MemoryProcessor($exporter, $exporter, $exporter);
+ * $processor = new MemoryLogProcessor(new MemoryExporter());
* $provider = new LoggerProvider($processor, new SystemClock());
*
* // For OTLP export
- * $processor = batching_log_processor(otlp_log_exporter($transport));
+ * $processor = batching_log_processor(otlp_exporter($transport));
* $provider = new LoggerProvider($processor, new SystemClock());
*
* // For void/disabled logging
@@ -38,6 +39,7 @@ public function __construct(
private ClockInterface $clock,
private ContextStorage $contextStorage,
private LogRecordLimits $limits = new LogRecordLimits(),
+ private ErrorHandler $errorHandler = new ErrorLogHandler(),
) {
}
@@ -62,6 +64,7 @@ public function logger(Resource $resource, string $name, string $version = 'unkn
$this->clock,
$this->contextStorage,
$this->limits,
+ $this->errorHandler,
);
}
}
diff --git a/src/lib/telemetry/src/Flow/Telemetry/Logger/Processor/BatchingLogProcessor.php b/src/lib/telemetry/src/Flow/Telemetry/Logger/Processor/BatchingLogProcessor.php
index 4c54add6a..3e7cf391a 100644
--- a/src/lib/telemetry/src/Flow/Telemetry/Logger/Processor/BatchingLogProcessor.php
+++ b/src/lib/telemetry/src/Flow/Telemetry/Logger/Processor/BatchingLogProcessor.php
@@ -4,7 +4,10 @@
namespace Flow\Telemetry\Logger\Processor;
-use Flow\Telemetry\Logger\{LogEntry, LogExporter, LogProcessor};
+use Flow\Telemetry\ErrorHandler\{ErrorHandler, ErrorLogHandler};
+use Flow\Telemetry\Exporter\Exporter;
+use Flow\Telemetry\Logger\{LogEntry, LogProcessor};
+use Flow\Telemetry\Signal\Signals;
/**
* Batches log records for efficient export.
@@ -13,14 +16,6 @@
* - The batch size limit is reached
* - flush() is explicitly called
* - the system is shutting down
- *
- * Example usage:
- * ```php
- * $processor = new BatchingLogProcessor(
- * exporter: $logExporter,
- * batchSize: 100,
- * );
- * ```
*/
final class BatchingLogProcessor implements LogProcessor
{
@@ -29,17 +24,15 @@ final class BatchingLogProcessor implements LogProcessor
*/
private array $buffer = [];
+ private bool $isShutdown = false;
+
public function __construct(
- private readonly LogExporter $exporter,
+ private readonly Exporter $exporter,
private readonly int $batchSize = 512,
+ private readonly ErrorHandler $errorHandler = new ErrorLogHandler(),
) {
}
- public function exporter() : LogExporter
- {
- return $this->exporter;
- }
-
public function flush() : bool
{
if (\count($this->buffer) === 0) {
@@ -49,7 +42,13 @@ public function flush() : bool
$entries = $this->buffer;
$this->buffer = [];
- return $this->exporter->export($entries);
+ try {
+ return $this->exporter->export(Signals::logs($entries));
+ } catch (\Throwable $e) {
+ $this->errorHandler->handle($e);
+
+ return false;
+ }
}
public function process(LogEntry $entry) : void
@@ -60,4 +59,21 @@ public function process(LogEntry $entry) : void
$this->flush();
}
}
+
+ public function shutdown() : void
+ {
+ if ($this->isShutdown) {
+ return;
+ }
+
+ $this->isShutdown = true;
+
+ $this->flush();
+
+ try {
+ $this->exporter->shutdown();
+ } catch (\Throwable $e) {
+ $this->errorHandler->handle($e);
+ }
+ }
}
diff --git a/src/lib/telemetry/src/Flow/Telemetry/Logger/Processor/CompositeLogProcessor.php b/src/lib/telemetry/src/Flow/Telemetry/Logger/Processor/CompositeLogProcessor.php
index 8b7fc73a5..e4d8f2efd 100644
--- a/src/lib/telemetry/src/Flow/Telemetry/Logger/Processor/CompositeLogProcessor.php
+++ b/src/lib/telemetry/src/Flow/Telemetry/Logger/Processor/CompositeLogProcessor.php
@@ -4,7 +4,8 @@
namespace Flow\Telemetry\Logger\Processor;
-use Flow\Telemetry\Logger\{LogEntry, LogExporter, LogProcessor};
+use Flow\Telemetry\ErrorHandler\{ErrorHandler, ErrorLogHandler};
+use Flow\Telemetry\Logger\{LogEntry, LogProcessor};
/**
* Forwards log records to multiple processors.
@@ -13,14 +14,6 @@
* - Send logs to multiple backends (e.g., both console and OTLP)
* - Combine batching with memory storage for testing
* - Add custom processing alongside export
- *
- * Example usage:
- * ```php
- * $processor = new CompositeLogProcessor([
- * new BatchingLogProcessor($otlpExporter),
- * new MemoryLogProcessor(),
- * ]);
- * ```
*/
final readonly class CompositeLogProcessor implements LogProcessor
{
@@ -29,24 +22,21 @@
*/
public function __construct(
private array $processors,
+ private ErrorHandler $errorHandler = new ErrorLogHandler(),
) {
}
- public function exporter() : LogExporter
- {
- if (\count($this->processors) === 0) {
- throw new \RuntimeException('CompositeLogProcessor has no processors');
- }
-
- return $this->processors[0]->exporter();
- }
-
public function flush() : bool
{
$success = true;
foreach ($this->processors as $processor) {
- if (!$processor->flush()) {
+ try {
+ if (!$processor->flush()) {
+ $success = false;
+ }
+ } catch (\Throwable $e) {
+ $this->errorHandler->handle($e);
$success = false;
}
}
@@ -57,7 +47,11 @@ public function flush() : bool
public function process(LogEntry $entry) : void
{
foreach ($this->processors as $processor) {
- $processor->process($entry);
+ try {
+ $processor->process($entry);
+ } catch (\Throwable $e) {
+ $this->errorHandler->handle($e);
+ }
}
}
@@ -70,4 +64,15 @@ public function processors() : array
{
return $this->processors;
}
+
+ public function shutdown() : void
+ {
+ foreach ($this->processors as $processor) {
+ try {
+ $processor->shutdown();
+ } catch (\Throwable $e) {
+ $this->errorHandler->handle($e);
+ }
+ }
+ }
}
diff --git a/src/lib/telemetry/src/Flow/Telemetry/Logger/Processor/PassThroughLogProcessor.php b/src/lib/telemetry/src/Flow/Telemetry/Logger/Processor/PassThroughLogProcessor.php
index a5f2f706e..4527a37a8 100644
--- a/src/lib/telemetry/src/Flow/Telemetry/Logger/Processor/PassThroughLogProcessor.php
+++ b/src/lib/telemetry/src/Flow/Telemetry/Logger/Processor/PassThroughLogProcessor.php
@@ -4,7 +4,10 @@
namespace Flow\Telemetry\Logger\Processor;
-use Flow\Telemetry\Logger\{LogEntry, LogExporter, LogProcessor};
+use Flow\Telemetry\ErrorHandler\{ErrorHandler, ErrorLogHandler};
+use Flow\Telemetry\Exporter\Exporter;
+use Flow\Telemetry\Logger\{LogEntry, LogProcessor};
+use Flow\Telemetry\Signal\Signals;
/**
* Exports each log record immediately when processed.
@@ -12,24 +15,15 @@
* Unlike BatchingLogProcessor, this processor exports log records synchronously
* one at a time. This is useful for debugging and development where
* immediate visibility of logs is more important than performance.
- *
- * Example usage:
- * ```php
- * $processor = new PassThroughLogProcessor($logExporter);
- * ```
*/
final readonly class PassThroughLogProcessor implements LogProcessor
{
public function __construct(
- private LogExporter $exporter,
+ private Exporter $exporter,
+ private ErrorHandler $errorHandler = new ErrorLogHandler(),
) {
}
- public function exporter() : LogExporter
- {
- return $this->exporter;
- }
-
public function flush() : bool
{
return true;
@@ -37,6 +31,19 @@ public function flush() : bool
public function process(LogEntry $entry) : void
{
- $this->exporter->export([$entry]);
+ try {
+ $this->exporter->export(Signals::logs([$entry]));
+ } catch (\Throwable $e) {
+ $this->errorHandler->handle($e);
+ }
+ }
+
+ public function shutdown() : void
+ {
+ try {
+ $this->exporter->shutdown();
+ } catch (\Throwable $e) {
+ $this->errorHandler->handle($e);
+ }
}
}
diff --git a/src/lib/telemetry/src/Flow/Telemetry/Logger/Processor/SeverityFilteringLogProcessor.php b/src/lib/telemetry/src/Flow/Telemetry/Logger/Processor/SeverityFilteringLogProcessor.php
index ba3896c03..a3cd6d4f6 100644
--- a/src/lib/telemetry/src/Flow/Telemetry/Logger/Processor/SeverityFilteringLogProcessor.php
+++ b/src/lib/telemetry/src/Flow/Telemetry/Logger/Processor/SeverityFilteringLogProcessor.php
@@ -4,7 +4,7 @@
namespace Flow\Telemetry\Logger\Processor;
-use Flow\Telemetry\Logger\{LogEntry, LogExporter, LogProcessor, Severity};
+use Flow\Telemetry\Logger\{LogEntry, LogProcessor, Severity};
/**
* Filters log entries based on minimum severity level.
@@ -12,15 +12,6 @@
* This processor wraps another LogProcessor and only passes through
* log entries that are at or above the configured minimum severity level.
* Entries below the threshold are silently discarded.
- *
- * Example usage:
- * ```php
- * // Only export WARN and above
- * $processor = new SeverityFilteringLogProcessor(
- * new BatchingLogProcessor($exporter, 100),
- * Severity::WARN,
- * );
- * ```
*/
final readonly class SeverityFilteringLogProcessor implements LogProcessor
{
@@ -30,11 +21,6 @@ public function __construct(
) {
}
- public function exporter() : LogExporter
- {
- return $this->processor->exporter();
- }
-
public function flush() : bool
{
return $this->processor->flush();
@@ -46,4 +32,9 @@ public function process(LogEntry $entry) : void
$this->processor->process($entry);
}
}
+
+ public function shutdown() : void
+ {
+ $this->processor->shutdown();
+ }
}
diff --git a/src/lib/telemetry/src/Flow/Telemetry/Meter/Meter.php b/src/lib/telemetry/src/Flow/Telemetry/Meter/Meter.php
index 91d2a8810..4a394470f 100644
--- a/src/lib/telemetry/src/Flow/Telemetry/Meter/Meter.php
+++ b/src/lib/telemetry/src/Flow/Telemetry/Meter/Meter.php
@@ -4,6 +4,7 @@
namespace Flow\Telemetry\Meter;
+use Flow\Telemetry\ErrorHandler\{ErrorHandler, ErrorLogHandler};
use Flow\Telemetry\{InstrumentationScope, Resource};
use Flow\Telemetry\Meter\Exemplar\{ExemplarFilter, TraceBasedExemplarFilter};
use Flow\Telemetry\Meter\Instrument\{Counter, Gauge, Histogram, Instrument, Throughput, UpDownCounter};
@@ -55,6 +56,7 @@ public function __construct(
private readonly AggregationTemporality $temporality = AggregationTemporality::CUMULATIVE,
private readonly ExemplarFilter $exemplarFilter = new TraceBasedExemplarFilter(),
private readonly MetricLimits $limits = new MetricLimits(),
+ private readonly ErrorHandler $errorHandler = new ErrorLogHandler(),
) {
}
@@ -89,7 +91,11 @@ public function collect() : array
public function complete(Instrument $instrument) : void
{
foreach ($instrument->collect() as $metric) {
- $this->processor->process($metric);
+ try {
+ $this->processor->process($metric);
+ } catch (\Throwable $e) {
+ $this->errorHandler->handle($e);
+ }
}
$key = $instrument->name() . ':' . $instrument::class;
@@ -286,10 +292,20 @@ public function createUpDownCounter(
public function flush() : bool
{
foreach ($this->collect() as $metric) {
- $this->processor->process($metric);
+ try {
+ $this->processor->process($metric);
+ } catch (\Throwable $e) {
+ $this->errorHandler->handle($e);
+ }
}
- return $this->processor->flush();
+ try {
+ return $this->processor->flush();
+ } catch (\Throwable $e) {
+ $this->errorHandler->handle($e);
+
+ return false;
+ }
}
/**
diff --git a/src/lib/telemetry/src/Flow/Telemetry/Meter/MeterProvider.php b/src/lib/telemetry/src/Flow/Telemetry/Meter/MeterProvider.php
index 0a1e58c1e..a23f122c4 100644
--- a/src/lib/telemetry/src/Flow/Telemetry/Meter/MeterProvider.php
+++ b/src/lib/telemetry/src/Flow/Telemetry/Meter/MeterProvider.php
@@ -5,6 +5,7 @@
namespace Flow\Telemetry\Meter;
use Flow\Telemetry\{Attributes, InstrumentationScope, Resource};
+use Flow\Telemetry\ErrorHandler\{ErrorHandler, ErrorLogHandler};
use Flow\Telemetry\Meter\Exemplar\{ExemplarFilter, TraceBasedExemplarFilter};
use Flow\Telemetry\Provider\Clock\SystemClock;
use Psr\Clock\ClockInterface;
@@ -18,11 +19,11 @@
* Example usage:
* ```php
* // For testing with memory storage
- * $processor = new MemoryProcessor($exporter, $exporter, $exporter);
+ * $processor = new MemoryMetricProcessor(new MemoryExporter());
* $provider = new MeterProvider($processor, new SystemClock());
*
* // For OTLP export
- * $processor = batching_metric_processor(otlp_metric_exporter($transport));
+ * $processor = batching_metric_processor(otlp_exporter($transport));
* $provider = new MeterProvider($processor, new SystemClock());
*
* // For void/disabled metrics
@@ -41,6 +42,7 @@ public function __construct(
private AggregationTemporality $temporality = AggregationTemporality::CUMULATIVE,
private ExemplarFilter $exemplarFilter = new TraceBasedExemplarFilter(),
private MetricLimits $limits = new MetricLimits(),
+ private ErrorHandler $errorHandler = new ErrorLogHandler(),
) {
}
@@ -66,6 +68,7 @@ public function meter(Resource $resource, string $name, string $version = 'unkno
$this->temporality,
$this->exemplarFilter,
$this->limits,
+ $this->errorHandler,
);
}
}
diff --git a/src/lib/telemetry/src/Flow/Telemetry/Meter/MetricExporter.php b/src/lib/telemetry/src/Flow/Telemetry/Meter/MetricExporter.php
deleted file mode 100644
index 6d1c4dd69..000000000
--- a/src/lib/telemetry/src/Flow/Telemetry/Meter/MetricExporter.php
+++ /dev/null
@@ -1,46 +0,0 @@
-client->sendMetrics($metrics);
- * }
- * }
- * ```
- */
-interface MetricExporter
-{
- /**
- * Export a batch of metrics.
- *
- * Each metric carries its own Resource and InstrumentationScope.
- *
- * @param array $metrics The metrics to export
- *
- * @return bool True on success, false on failure
- */
- public function export(array $metrics) : bool;
-
- /**
- * Get the transports used by this exporter.
- *
- * @return array Always returns at least one transport
- */
- public function transports() : array;
-}
diff --git a/src/lib/telemetry/src/Flow/Telemetry/Meter/MetricProcessor.php b/src/lib/telemetry/src/Flow/Telemetry/Meter/MetricProcessor.php
index 3539262ee..b0d640a9b 100644
--- a/src/lib/telemetry/src/Flow/Telemetry/Meter/MetricProcessor.php
+++ b/src/lib/telemetry/src/Flow/Telemetry/Meter/MetricProcessor.php
@@ -9,30 +9,9 @@
*
* Implementations may collect metrics for batching, export them immediately,
* or perform other processing like filtering or aggregation.
- *
- * Example implementation:
- * ```php
- * final class BatchingMetricProcessor implements MetricProcessor
- * {
- * private array $buffer = [];
- *
- * public function process(Metric $metric): void
- * {
- * $this->buffer[] = $metric;
- * if (count($this->buffer) >= 100) {
- * $this->flush();
- * }
- * }
- * }
- * ```
*/
interface MetricProcessor
{
- /**
- * Get the exporter used by this processor.
- */
- public function exporter() : MetricExporter;
-
/**
* Export all pending metrics.
*
@@ -50,4 +29,12 @@ public function flush() : bool;
* on filtering rules.
*/
public function process(Metric $metric) : void;
+
+ /**
+ * Shutdown the processor.
+ *
+ * Implementations SHOULD flush() pending data before delegating shutdown
+ * to the underlying exporter. MUST be idempotent and MUST NOT throw.
+ */
+ public function shutdown() : void;
}
diff --git a/src/lib/telemetry/src/Flow/Telemetry/Meter/Processor/BatchingMetricProcessor.php b/src/lib/telemetry/src/Flow/Telemetry/Meter/Processor/BatchingMetricProcessor.php
index 800d8db84..adc46499d 100644
--- a/src/lib/telemetry/src/Flow/Telemetry/Meter/Processor/BatchingMetricProcessor.php
+++ b/src/lib/telemetry/src/Flow/Telemetry/Meter/Processor/BatchingMetricProcessor.php
@@ -4,7 +4,10 @@
namespace Flow\Telemetry\Meter\Processor;
-use Flow\Telemetry\Meter\{Metric, MetricExporter, MetricProcessor};
+use Flow\Telemetry\ErrorHandler\{ErrorHandler, ErrorLogHandler};
+use Flow\Telemetry\Exporter\Exporter;
+use Flow\Telemetry\Meter\{Metric, MetricProcessor};
+use Flow\Telemetry\Signal\Signals;
/**
* Batches metrics for efficient export.
@@ -13,14 +16,6 @@
* - The batch size limit is reached
* - flush() is explicitly called
* - the system is shutting down
- *
- * Example usage:
- * ```php
- * $processor = new BatchingMetricProcessor(
- * exporter: $metricExporter,
- * batchSize: 100,
- * );
- * ```
*/
final class BatchingMetricProcessor implements MetricProcessor
{
@@ -29,17 +24,15 @@ final class BatchingMetricProcessor implements MetricProcessor
*/
private array $buffer = [];
+ private bool $isShutdown = false;
+
public function __construct(
- private readonly MetricExporter $exporter,
+ private readonly Exporter $exporter,
private readonly int $batchSize = 512,
+ private readonly ErrorHandler $errorHandler = new ErrorLogHandler(),
) {
}
- public function exporter() : MetricExporter
- {
- return $this->exporter;
- }
-
public function flush() : bool
{
if (\count($this->buffer) === 0) {
@@ -49,7 +42,13 @@ public function flush() : bool
$metrics = $this->buffer;
$this->buffer = [];
- return $this->exporter->export($metrics);
+ try {
+ return $this->exporter->export(Signals::metrics($metrics));
+ } catch (\Throwable $e) {
+ $this->errorHandler->handle($e);
+
+ return false;
+ }
}
public function process(Metric $metric) : void
@@ -60,4 +59,21 @@ public function process(Metric $metric) : void
$this->flush();
}
}
+
+ public function shutdown() : void
+ {
+ if ($this->isShutdown) {
+ return;
+ }
+
+ $this->isShutdown = true;
+
+ $this->flush();
+
+ try {
+ $this->exporter->shutdown();
+ } catch (\Throwable $e) {
+ $this->errorHandler->handle($e);
+ }
+ }
}
diff --git a/src/lib/telemetry/src/Flow/Telemetry/Meter/Processor/CompositeMetricProcessor.php b/src/lib/telemetry/src/Flow/Telemetry/Meter/Processor/CompositeMetricProcessor.php
index e5d455c91..cfce2fdc6 100644
--- a/src/lib/telemetry/src/Flow/Telemetry/Meter/Processor/CompositeMetricProcessor.php
+++ b/src/lib/telemetry/src/Flow/Telemetry/Meter/Processor/CompositeMetricProcessor.php
@@ -4,7 +4,8 @@
namespace Flow\Telemetry\Meter\Processor;
-use Flow\Telemetry\Meter\{Metric, MetricExporter, MetricProcessor};
+use Flow\Telemetry\ErrorHandler\{ErrorHandler, ErrorLogHandler};
+use Flow\Telemetry\Meter\{Metric, MetricProcessor};
/**
* Forwards metrics to multiple processors.
@@ -13,14 +14,6 @@
* - Send metrics to multiple backends (e.g., both Prometheus and OTLP)
* - Combine batching with memory storage for testing
* - Add custom processing alongside export
- *
- * Example usage:
- * ```php
- * $processor = new CompositeMetricProcessor([
- * new BatchingMetricProcessor($otlpExporter),
- * new MemoryMetricProcessor(),
- * ]);
- * ```
*/
final readonly class CompositeMetricProcessor implements MetricProcessor
{
@@ -29,24 +22,21 @@
*/
public function __construct(
private array $processors,
+ private ErrorHandler $errorHandler = new ErrorLogHandler(),
) {
}
- public function exporter() : MetricExporter
- {
- if (\count($this->processors) === 0) {
- throw new \RuntimeException('CompositeMetricProcessor has no processors');
- }
-
- return $this->processors[0]->exporter();
- }
-
public function flush() : bool
{
$success = true;
foreach ($this->processors as $processor) {
- if (!$processor->flush()) {
+ try {
+ if (!$processor->flush()) {
+ $success = false;
+ }
+ } catch (\Throwable $e) {
+ $this->errorHandler->handle($e);
$success = false;
}
}
@@ -57,7 +47,11 @@ public function flush() : bool
public function process(Metric $metric) : void
{
foreach ($this->processors as $processor) {
- $processor->process($metric);
+ try {
+ $processor->process($metric);
+ } catch (\Throwable $e) {
+ $this->errorHandler->handle($e);
+ }
}
}
@@ -70,4 +64,15 @@ public function processors() : array
{
return $this->processors;
}
+
+ public function shutdown() : void
+ {
+ foreach ($this->processors as $processor) {
+ try {
+ $processor->shutdown();
+ } catch (\Throwable $e) {
+ $this->errorHandler->handle($e);
+ }
+ }
+ }
}
diff --git a/src/lib/telemetry/src/Flow/Telemetry/Meter/Processor/PassThroughMetricProcessor.php b/src/lib/telemetry/src/Flow/Telemetry/Meter/Processor/PassThroughMetricProcessor.php
index d0f95d0b6..e03d9f051 100644
--- a/src/lib/telemetry/src/Flow/Telemetry/Meter/Processor/PassThroughMetricProcessor.php
+++ b/src/lib/telemetry/src/Flow/Telemetry/Meter/Processor/PassThroughMetricProcessor.php
@@ -4,7 +4,10 @@
namespace Flow\Telemetry\Meter\Processor;
-use Flow\Telemetry\Meter\{Metric, MetricExporter, MetricProcessor};
+use Flow\Telemetry\ErrorHandler\{ErrorHandler, ErrorLogHandler};
+use Flow\Telemetry\Exporter\Exporter;
+use Flow\Telemetry\Meter\{Metric, MetricProcessor};
+use Flow\Telemetry\Signal\Signals;
/**
* Exports each metric immediately when processed.
@@ -12,24 +15,15 @@
* Unlike BatchingMetricProcessor, this processor exports metrics synchronously
* one at a time. This is useful for debugging and development where
* immediate visibility of metrics is more important than performance.
- *
- * Example usage:
- * ```php
- * $processor = new PassThroughMetricProcessor($metricExporter);
- * ```
*/
final readonly class PassThroughMetricProcessor implements MetricProcessor
{
public function __construct(
- private MetricExporter $exporter,
+ private Exporter $exporter,
+ private ErrorHandler $errorHandler = new ErrorLogHandler(),
) {
}
- public function exporter() : MetricExporter
- {
- return $this->exporter;
- }
-
public function flush() : bool
{
return true;
@@ -37,6 +31,19 @@ public function flush() : bool
public function process(Metric $metric) : void
{
- $this->exporter->export([$metric]);
+ try {
+ $this->exporter->export(Signals::metrics([$metric]));
+ } catch (\Throwable $e) {
+ $this->errorHandler->handle($e);
+ }
+ }
+
+ public function shutdown() : void
+ {
+ try {
+ $this->exporter->shutdown();
+ } catch (\Throwable $e) {
+ $this->errorHandler->handle($e);
+ }
}
}
diff --git a/src/lib/telemetry/src/Flow/Telemetry/Provider/Console/ConsoleExporter.php b/src/lib/telemetry/src/Flow/Telemetry/Provider/Console/ConsoleExporter.php
new file mode 100644
index 000000000..f4e12e56d
--- /dev/null
+++ b/src/lib/telemetry/src/Flow/Telemetry/Provider/Console/ConsoleExporter.php
@@ -0,0 +1,865 @@
+output = new ConsoleOutput($colors, $outputStream);
+ }
+
+ public function export(Signals $signal) : bool
+ {
+ return match ($signal->type) {
+ SignalType::LOGS => $this->exportLogs($signal->allLogs()),
+ SignalType::METRICS => $this->exportMetrics($signal->allMetrics()),
+ SignalType::TRACES => $this->exportSpans($signal->allSpans()),
+ };
+ }
+
+ public function shutdown() : void
+ {
+ }
+
+ private function appendMetricExemplarInfo(string &$line, Metric $metric) : void
+ {
+ if (\count($metric->exemplars) === 0) {
+ return;
+ }
+
+ if ($this->metricOptions->showAllExemplars) {
+ foreach ($metric->exemplars as $exemplar) {
+ $traceIdShort = \substr($exemplar->traceId->toHex(), 0, 8);
+ $spanIdShort = \substr($exemplar->spanId->toHex(), 0, 8);
+ $line .= ' ' . $this->output->dim('[trace:' . $traceIdShort . '.. span:' . $spanIdShort . '..]');
+ }
+ } else {
+ $exemplar = $metric->exemplars[0];
+ $traceIdShort = \substr($exemplar->traceId->toHex(), 0, 8);
+ $spanIdShort = \substr($exemplar->spanId->toHex(), 0, 8);
+ $line .= ' ' . $this->output->dim('[trace:' . $traceIdShort . '.. span:' . $spanIdShort . '..]');
+ }
+ }
+
+ /**
+ * @return array
+ */
+ private function buildFullResourceLines(TelemetryResource $resource) : array
+ {
+ $lines = [];
+ $lines[] = $this->output->bold('Resource:');
+
+ $attributes = $resource->all();
+ $maxKeyLength = 0;
+
+ foreach (\array_keys($attributes) as $key) {
+ $maxKeyLength = \max($maxKeyLength, \mb_strlen($key));
+ }
+
+ foreach ($attributes as $key => $value) {
+ $keyStr = $this->output->pad($key, $maxKeyLength);
+ $valueStr = $this->output->formatValue($value);
+ $lines[] = ' ' . $this->output->cyan($keyStr) . ' = ' . $valueStr;
+ }
+
+ return $lines;
+ }
+
+ private function buildLogHeaderLine(int $bodyWidth, bool $hasTrace) : string
+ {
+ $timestamp = $this->output->pad('Timestamp', self::LOG_TIMESTAMP_WIDTH);
+ $level = $this->output->pad('Level', self::LOG_SEVERITY_WIDTH);
+ $message = $this->output->pad('Message', $bodyWidth);
+
+ $line = $this->output->bold($timestamp) . ' | ';
+ $line .= $this->output->bold($level) . ' | ';
+ $line .= $this->output->bold($message);
+
+ if ($hasTrace) {
+ $trace = $this->output->pad('Trace', self::LOG_TRACE_WIDTH);
+ $line .= ' | ' . $this->output->bold($trace);
+ }
+
+ return $line;
+ }
+
+ /**
+ * @param array{timestamp: string, severity: string, severityRaw: Severity, body: string, trace: string, droppedAttributeCount: int} $record
+ */
+ private function buildLogLine(array $record, int $bodyWidth, bool $hasTrace) : string
+ {
+ $timestamp = $this->output->pad($record['timestamp'], self::LOG_TIMESTAMP_WIDTH);
+ $severity = $this->output->pad($record['severity'], self::LOG_SEVERITY_WIDTH);
+ $body = $this->output->truncate($record['body'], $bodyWidth);
+ $body = $this->output->pad($body, $bodyWidth);
+
+ $line = $this->output->gray($timestamp) . ' | ';
+ $line .= $this->colorBySeverity($severity, $record['severityRaw']) . ' | ';
+ $line .= $body;
+
+ if ($this->logOptions->showDroppedAttributeCount && $record['droppedAttributeCount'] > 0) {
+ $line .= ' ' . $this->output->yellow('[dropped:' . $record['droppedAttributeCount'] . ']');
+ }
+
+ if ($hasTrace) {
+ $trace = $this->output->pad($record['trace'], self::LOG_TRACE_WIDTH);
+ $line .= ' | ' . ($record['trace'] !== '' ? $this->output->dim($trace) : $trace);
+ }
+
+ return $line;
+ }
+
+ /**
+ * @return array
+ */
+ private function buildLogResourceLines(TelemetryResource $resource) : array
+ {
+ if ($resource->isEmpty()) {
+ return [];
+ }
+
+ if (!$this->logOptions->showResourceAttributes) {
+ return $this->buildShortResourceLines($resource);
+ }
+
+ return $this->buildFullResourceLines($resource);
+ }
+
+ private function buildLogScopeLine(LogEntry $entry) : ?string
+ {
+ if (!$this->logOptions->showInstrumentationScope) {
+ return null;
+ }
+
+ $scope = $entry->scope;
+
+ return 'Scope: ' . $this->output->dim($scope->name . ' v' . $scope->version);
+ }
+
+ /**
+ * @param array $metrics
+ *
+ * @return array
+ */
+ private function buildMetricLines(array $metrics) : array
+ {
+ $maxNameLength = 0;
+ $maxValueLength = 0;
+
+ foreach ($metrics as $metric) {
+ $maxNameLength = \max($maxNameLength, \mb_strlen($metric->name));
+ $maxValueLength = \max($maxValueLength, \mb_strlen($this->formatMetricValue($metric)));
+ }
+
+ $lines = [];
+
+ foreach ($metrics as $metric) {
+ $icon = $this->metricTypeIcon($metric->type);
+ $name = $this->output->pad($metric->name, $maxNameLength);
+ $value = $this->output->pad($this->formatMetricValue($metric), $maxValueLength);
+ $unit = $metric->unit !== null ? '[' . $metric->unit . ']' : '';
+
+ $line = $this->output->blue($icon) . ' ';
+ $line .= $name . ' ';
+ $line .= $value . ' ';
+ $line .= $this->output->dim($unit);
+
+ if ($this->metricOptions->showDescription && $metric->description !== null) {
+ $line .= ' ' . $this->output->dim('// ' . $metric->description);
+ }
+
+ if (!$metric->attributes->isEmpty()) {
+ $attrParts = [];
+
+ foreach ($metric->attributes->normalize() as $key => $attrValue) {
+ $attrParts[] = $key . '=' . (\is_scalar($attrValue) ? (string) $attrValue : \json_encode($attrValue));
+ }
+ $line .= ' ' . $this->output->dim('{' . \implode(', ', $attrParts) . '}');
+ }
+
+ if ($this->metricOptions->showAggregationTemporality) {
+ $line .= ' ' . $this->output->dim('[' . $metric->temporality->name . ']');
+ }
+
+ if ($this->metricOptions->showStartTimestamp && $metric->startTimestamp !== null) {
+ $line .= ' ' . $this->output->dim('start:' . $metric->startTimestamp->format('H:i:s.u'));
+ }
+
+ $this->appendMetricExemplarInfo($line, $metric);
+ $lines[] = $line;
+ }
+
+ return $lines;
+ }
+
+ /**
+ * @return array
+ */
+ private function buildMetricResourceLines(TelemetryResource $resource) : array
+ {
+ if ($resource->isEmpty()) {
+ return [];
+ }
+
+ if (!$this->metricOptions->showResourceAttributes) {
+ return $this->buildShortResourceLines($resource);
+ }
+
+ return $this->buildFullResourceLines($resource);
+ }
+
+ private function buildMetricScopeLine(Metric $metric) : ?string
+ {
+ if (!$this->metricOptions->showInstrumentationScope) {
+ return null;
+ }
+
+ $scope = $metric->scope;
+
+ return 'Scope: ' . $this->output->dim($scope->name . ' v' . $scope->version);
+ }
+
+ /**
+ * @return array
+ */
+ private function buildShortResourceLines(TelemetryResource $resource) : array
+ {
+ $parts = [];
+ $serviceName = $resource->get('service.name');
+
+ if (\is_string($serviceName)) {
+ $parts[] = $serviceName;
+ }
+
+ $serviceVersion = $resource->get('service.version');
+
+ if (\is_string($serviceVersion)) {
+ $parts[] = 'v' . $serviceVersion;
+ }
+
+ if (\count($parts) === 0) {
+ return [];
+ }
+
+ return ['Resource: ' . $this->output->dim(\implode(' ', $parts))];
+ }
+
+ /**
+ * @return array
+ */
+ private function buildSpanAttributeLines(Span $span) : array
+ {
+ $attributes = $span->attributes();
+
+ if (\count($attributes) === 0) {
+ return [];
+ }
+
+ $lines = [];
+ $lines[] = $this->output->bold('Attributes:');
+
+ $maxKeyLength = 0;
+
+ foreach (\array_keys($attributes) as $key) {
+ $maxKeyLength = \max($maxKeyLength, \mb_strlen($key));
+ }
+
+ foreach ($attributes as $key => $value) {
+ $keyStr = $this->output->pad($key, $maxKeyLength);
+ $valueStr = $this->output->formatValue($value);
+ $lines[] = ' ' . $this->output->cyan($keyStr) . ' = ' . $valueStr;
+ }
+
+ return $lines;
+ }
+
+ /**
+ * @return array
+ */
+ private function buildSpanDroppedCountsLines(Span $span) : array
+ {
+ if (!$this->spanOptions->showDroppedCounts) {
+ return [];
+ }
+
+ $droppedAttrs = $span->droppedAttributeCount();
+ $droppedEvents = $span->droppedEventsCount();
+ $droppedLinks = $span->droppedLinksCount();
+
+ if ($droppedAttrs === 0 && $droppedEvents === 0 && $droppedLinks === 0) {
+ return [];
+ }
+
+ $lines = [];
+ $lines[] = $this->output->bold('Dropped:');
+
+ if ($droppedAttrs > 0) {
+ $lines[] = ' ' . $this->output->yellow('Attributes: ' . $droppedAttrs);
+ }
+
+ if ($droppedEvents > 0) {
+ $lines[] = ' ' . $this->output->yellow('Events: ' . $droppedEvents);
+ }
+
+ if ($droppedLinks > 0) {
+ $lines[] = ' ' . $this->output->yellow('Links: ' . $droppedLinks);
+ }
+
+ return $lines;
+ }
+
+ /**
+ * @return array
+ */
+ private function buildSpanEventLines(Span $span) : array
+ {
+ $events = $span->events();
+
+ if (\count($events) === 0) {
+ return [];
+ }
+
+ $lines = [];
+ $lines[] = $this->output->bold('Events (' . \count($events) . '):');
+
+ foreach ($events as $event) {
+ $timestamp = $event->timestamp()->format('H:i:s.u');
+ $attrs = $event->attributes();
+ $attrStr = \count($attrs) > 0 ? ' ' . $this->output->dim($this->output->formatValue($attrs)) : '';
+ $lines[] = ' ' . $this->output->gray($timestamp) . ' ' . $event->name() . $attrStr;
+ }
+
+ return $lines;
+ }
+
+ /**
+ * @return array
+ */
+ private function buildSpanHeaderLines(Span $span) : array
+ {
+ $context = $span->context();
+ $lines = [];
+
+ $lines[] = 'SPAN: ' . $span->name();
+
+ $traceInfo = 'Trace: ' . $this->output->dim($context->traceId->toHex());
+ $traceInfo .= ' Span: ' . $this->output->dim($context->spanId->toHex());
+
+ if ($context->parentSpanId !== null) {
+ $traceInfo .= ' Parent: ' . $this->output->dim($context->parentSpanId->toHex());
+ }
+
+ $lines[] = $traceInfo;
+
+ $kindStr = $this->output->cyan(\strtoupper($span->kind()->value));
+ $statusStr = $this->formatSpanStatusIcon($span);
+ $durationStr = $this->output->formatDuration($span->duration());
+
+ $details = $this->output->pad('Kind: ' . $kindStr, 22);
+ $details .= 'Status: ' . $statusStr . ' ';
+ $details .= 'Duration: ' . $durationStr;
+ $lines[] = $details;
+
+ $lines[] = 'Start: ' . $this->output->formatTimestamp($span->startTime());
+
+ return $lines;
+ }
+
+ /**
+ * @return array
+ */
+ private function buildSpanLinkLines(Span $span) : array
+ {
+ if (!$this->spanOptions->showLinks) {
+ return [];
+ }
+
+ $links = $span->links();
+
+ if (\count($links) === 0) {
+ return [];
+ }
+
+ $lines = [];
+ $lines[] = $this->output->bold('Links (' . \count($links) . '):');
+
+ foreach ($links as $link) {
+ $traceId = $link->context->traceId->toHex();
+ $spanId = $link->context->spanId->toHex();
+ $lines[] = ' -> ' . $this->output->dim('trace:' . \mb_substr($traceId, 0, 12) . '... span:' . \mb_substr($spanId, 0, 12) . '...');
+ }
+
+ return $lines;
+ }
+
+ /**
+ * @return array
+ */
+ private function buildSpanResourceLines(TelemetryResource $resource) : array
+ {
+ if ($resource->isEmpty()) {
+ return [];
+ }
+
+ if (!$this->spanOptions->showResourceAttributes) {
+ return $this->buildShortResourceLines($resource);
+ }
+
+ return $this->buildFullResourceLines($resource);
+ }
+
+ /**
+ * @return array
+ */
+ private function buildSpanScopeLines(Span $span) : array
+ {
+ if (!$this->spanOptions->showInstrumentationScope) {
+ return [];
+ }
+
+ $scope = $span->scope();
+
+ return ['Scope: ' . $this->output->dim($scope->name . ' v' . $scope->version)];
+ }
+
+ /**
+ * @param array $lines
+ */
+ private function calculateLineWidth(array $lines, int $minWidth) : int
+ {
+ $maxLength = 0;
+
+ foreach ($lines as $line) {
+ $visibleLength = \mb_strlen($this->output->stripColors($line));
+ $maxLength = \max($maxLength, $visibleLength);
+ }
+
+ return \max($minWidth, $maxLength + 4);
+ }
+
+ /**
+ * @param array $records
+ */
+ private function calculateLogBodyWidth(array $records) : int
+ {
+ $maxLength = 0;
+
+ foreach ($records as $record) {
+ $maxLength = \max($maxLength, \mb_strlen($record['body']));
+ }
+
+ if ($this->maxLogBodyLength !== null) {
+ return \min($maxLength, $this->maxLogBodyLength);
+ }
+
+ return $maxLength;
+ }
+
+ private function calculateLogTotalWidth(int $bodyWidth, bool $hasTrace) : int
+ {
+ $width = 4;
+ $width += self::LOG_TIMESTAMP_WIDTH;
+ $width += 3;
+ $width += self::LOG_SEVERITY_WIDTH;
+ $width += 3;
+ $width += $bodyWidth;
+
+ if ($hasTrace) {
+ $width += 3;
+ $width += self::LOG_TRACE_WIDTH;
+ }
+
+ return \max(self::LOG_MIN_WIDTH, $width);
+ }
+
+ private function colorBySeverity(string $text, Severity $severity) : string
+ {
+ return match ($severity) {
+ Severity::TRACE => $this->output->dim($text),
+ Severity::DEBUG => $this->output->gray($text),
+ Severity::INFO => $this->output->green($text),
+ Severity::WARN => $this->output->yellow($text),
+ Severity::ERROR => $this->output->red($text),
+ Severity::FATAL => $this->output->bold($this->output->red($text)),
+ };
+ }
+
+ /**
+ * @param array $entries
+ */
+ private function exportLogs(array $entries) : bool
+ {
+ if (\count($entries) === 0) {
+ return true;
+ }
+
+ $formattedRecords = $this->formatLogEntries($entries);
+ $bodyWidth = $this->calculateLogBodyWidth($formattedRecords);
+ $hasTrace = $this->hasAnySpanContext($entries);
+ $resourceLines = $this->buildLogResourceLines($entries[0]->resource);
+ $scopeLine = $this->buildLogScopeLine($entries[0]);
+ $width = $this->calculateLogTotalWidth($bodyWidth, $hasTrace);
+
+ foreach ($resourceLines as $resourceLine) {
+ $resourceWidth = \mb_strlen($this->output->stripColors($resourceLine)) + 4;
+ $width = \max($width, $resourceWidth);
+ }
+
+ $buffer = $this->output->border($width) . PHP_EOL;
+ $buffer .= $this->output->row($this->output->bold('LOGS'), $width) . PHP_EOL;
+
+ if (\count($resourceLines) > 0) {
+ foreach ($resourceLines as $resourceLine) {
+ $buffer .= $this->output->row($resourceLine, $width) . PHP_EOL;
+ }
+ }
+
+ if ($scopeLine !== null) {
+ $buffer .= $this->output->row($scopeLine, $width) . PHP_EOL;
+ }
+
+ $buffer .= $this->output->border($width) . PHP_EOL;
+ $buffer .= $this->output->row($this->buildLogHeaderLine($bodyWidth, $hasTrace), $width) . PHP_EOL;
+ $buffer .= $this->output->border($width) . PHP_EOL;
+
+ foreach ($formattedRecords as $record) {
+ $buffer .= $this->output->row($this->buildLogLine($record, $bodyWidth, $hasTrace), $width) . PHP_EOL;
+ }
+
+ $buffer .= $this->output->border($width);
+
+ $this->output->write($buffer);
+
+ return true;
+ }
+
+ /**
+ * @param array $metrics
+ */
+ private function exportMetrics(array $metrics) : bool
+ {
+ if (\count($metrics) === 0) {
+ return true;
+ }
+
+ $lines = $this->buildMetricLines($metrics);
+ $resourceLines = $this->buildMetricResourceLines($metrics[0]->resource);
+ $scopeLine = $this->buildMetricScopeLine($metrics[0]);
+
+ $allLines = \array_merge($resourceLines, $scopeLine !== null ? [$scopeLine] : [], $lines);
+ $maxLength = \mb_strlen('METRICS');
+
+ foreach ($allLines as $line) {
+ $maxLength = \max($maxLength, \mb_strlen($this->output->stripColors($line)));
+ }
+
+ $width = \max(self::METRIC_MIN_WIDTH, $maxLength + 4);
+
+ $buffer = $this->output->border($width) . PHP_EOL;
+ $buffer .= $this->output->row($this->output->bold('METRICS'), $width) . PHP_EOL;
+ $buffer .= $this->output->border($width) . PHP_EOL;
+
+ foreach ($resourceLines as $resourceLine) {
+ $buffer .= $this->output->row($resourceLine, $width) . PHP_EOL;
+ }
+
+ if ($scopeLine !== null) {
+ $buffer .= $this->output->row($scopeLine, $width) . PHP_EOL;
+ }
+
+ if (\count($resourceLines) > 0 || $scopeLine !== null) {
+ $buffer .= $this->output->border($width) . PHP_EOL;
+ }
+
+ foreach ($lines as $line) {
+ $buffer .= $this->output->row($line, $width) . PHP_EOL;
+ }
+
+ $buffer .= $this->output->border($width);
+
+ $this->output->write($buffer);
+
+ return true;
+ }
+
+ /**
+ * @param array $spans
+ */
+ private function exportSpans(array $spans) : bool
+ {
+ foreach ($spans as $span) {
+ $this->printSpan($span);
+ }
+
+ return true;
+ }
+
+ /**
+ * @param array|bool|\DateTimeImmutable|float|int|string> $attributes
+ */
+ private function formatLogAttributes(array $attributes) : string
+ {
+ if (\count($attributes) === 0) {
+ return '';
+ }
+
+ $parts = [];
+
+ foreach ($attributes as $key => $value) {
+ $parts[] = $key . ': ' . $this->output->formatValue($this->normalizeLogAttributeValue($value));
+ }
+
+ return '{' . \implode(', ', $parts) . '}';
+ }
+
+ /**
+ * @param array $entries
+ *
+ * @return array
+ */
+ private function formatLogEntries(array $entries) : array
+ {
+ $formatted = [];
+
+ foreach ($entries as $entry) {
+ $body = $entry->record->body;
+ $attrs = $this->formatLogAttributes($entry->record->attributes->normalize());
+
+ if ($attrs !== '') {
+ $body .= ' ' . $attrs;
+ }
+
+ $trace = '';
+
+ if ($entry->spanContext !== null) {
+ $trace = \mb_substr($entry->spanContext->traceId->toHex(), 0, 8) . '/' . \mb_substr($entry->spanContext->spanId->toHex(), 0, 8);
+ }
+
+ $timestamp = $entry->timestamp->format('Y-m-d H:i:s.u');
+
+ if ($this->logOptions->showObservedTimestamp && $entry->record->observedTimestamp !== null) {
+ $timestamp .= ' (obs: ' . $entry->record->observedTimestamp->format('H:i:s.u') . ')';
+ }
+
+ $formatted[] = [
+ 'timestamp' => $timestamp,
+ 'severity' => $entry->record->severity->name,
+ 'severityRaw' => $entry->record->severity,
+ 'body' => $body,
+ 'trace' => $trace,
+ 'droppedAttributeCount' => $entry->droppedAttributeCount,
+ ];
+ }
+
+ return $formatted;
+ }
+
+ private function formatMetricValue(Metric $metric) : string
+ {
+ $value = $metric->value;
+
+ if (\is_float($value)) {
+ if (\abs($value) >= 1000000) {
+ return \sprintf('%.2fM', $value / 1000000);
+ }
+
+ if (\abs($value) >= 1000) {
+ return \sprintf('%.2fK', $value / 1000);
+ }
+
+ return \sprintf('%.2f', $value);
+ }
+
+ if ($value >= 1000000) {
+ return \sprintf('%.2fM', $value / 1000000);
+ }
+
+ if ($value >= 1000) {
+ return \number_format($value);
+ }
+
+ return (string) $value;
+ }
+
+ private function formatSpanStatusIcon(Span $span) : string
+ {
+ $status = $span->status();
+
+ if ($status === null) {
+ return $this->output->yellow('UNSET');
+ }
+
+ $statusText = match ($status->code) {
+ SpanStatusCode::OK => $this->output->green('OK'),
+ SpanStatusCode::ERROR => $this->output->red('ERROR'),
+ SpanStatusCode::UNSET => $this->output->yellow('UNSET'),
+ };
+
+ if ($this->spanOptions->showStatusDescription && $status->code === SpanStatusCode::ERROR && $status->description !== null) {
+ $statusText .= ' ' . $this->output->dim('(' . $status->description . ')');
+ }
+
+ return $statusText;
+ }
+
+ /**
+ * @param array $entries
+ */
+ private function hasAnySpanContext(array $entries) : bool
+ {
+ foreach ($entries as $entry) {
+ if ($entry->spanContext !== null) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private function metricTypeIcon(MetricType $type) : string
+ {
+ return match ($type) {
+ MetricType::COUNTER => '^',
+ MetricType::UP_DOWN_COUNTER => '~',
+ MetricType::GAUGE => 'o',
+ MetricType::HISTOGRAM => '#',
+ };
+ }
+
+ /**
+ * @param array|bool|\DateTimeImmutable|float|int|string $value
+ *
+ * @return array|bool|float|int|string
+ */
+ private function normalizeLogAttributeValue(mixed $value) : array|bool|float|int|string
+ {
+ if ($value instanceof \DateTimeImmutable) {
+ return $value->format(\DateTimeInterface::RFC3339_EXTENDED);
+ }
+
+ if (\is_array($value)) {
+ return \array_map(
+ static fn ($item) => $item instanceof \DateTimeImmutable
+ ? $item->format(\DateTimeInterface::RFC3339_EXTENDED)
+ : $item,
+ $value
+ );
+ }
+
+ return $value;
+ }
+
+ private function printSpan(Span $span) : void
+ {
+ $headerLines = $this->buildSpanHeaderLines($span);
+ $resourceLines = $this->buildSpanResourceLines($span->resource());
+ $scopeLines = $this->buildSpanScopeLines($span);
+ $attributeLines = $this->buildSpanAttributeLines($span);
+ $eventLines = $this->buildSpanEventLines($span);
+ $linkLines = $this->buildSpanLinkLines($span);
+ $droppedLines = $this->buildSpanDroppedCountsLines($span);
+
+ $allLines = \array_merge($headerLines, $resourceLines, $scopeLines, $attributeLines, $eventLines, $linkLines, $droppedLines);
+ $width = $this->calculateLineWidth($allLines, self::SPAN_MIN_WIDTH);
+
+ $buffer = $this->output->border($width) . PHP_EOL;
+ $buffer .= $this->output->row($this->output->bold($headerLines[0]), $width) . PHP_EOL;
+ $buffer .= $this->output->border($width) . PHP_EOL;
+
+ for ($i = 1; $i < \count($headerLines); $i++) {
+ $buffer .= $this->output->row($headerLines[$i], $width) . PHP_EOL;
+ }
+
+ if (\count($resourceLines) > 0) {
+ $buffer .= $this->output->border($width) . PHP_EOL;
+
+ foreach ($resourceLines as $line) {
+ $buffer .= $this->output->row($line, $width) . PHP_EOL;
+ }
+ }
+
+ if (\count($scopeLines) > 0) {
+ foreach ($scopeLines as $line) {
+ $buffer .= $this->output->row($line, $width) . PHP_EOL;
+ }
+ }
+
+ if (\count($attributeLines) > 0) {
+ $buffer .= $this->output->border($width) . PHP_EOL;
+
+ foreach ($attributeLines as $line) {
+ $buffer .= $this->output->row($line, $width) . PHP_EOL;
+ }
+ }
+
+ if (\count($eventLines) > 0) {
+ $buffer .= $this->output->border($width) . PHP_EOL;
+
+ foreach ($eventLines as $line) {
+ $buffer .= $this->output->row($line, $width) . PHP_EOL;
+ }
+ }
+
+ if (\count($linkLines) > 0) {
+ $buffer .= $this->output->border($width) . PHP_EOL;
+
+ foreach ($linkLines as $line) {
+ $buffer .= $this->output->row($line, $width) . PHP_EOL;
+ }
+ }
+
+ if (\count($droppedLines) > 0) {
+ $buffer .= $this->output->border($width) . PHP_EOL;
+
+ foreach ($droppedLines as $line) {
+ $buffer .= $this->output->row($line, $width) . PHP_EOL;
+ }
+ }
+
+ $buffer .= $this->output->border($width);
+
+ $this->output->write($buffer);
+ }
+}
diff --git a/src/lib/telemetry/src/Flow/Telemetry/Provider/Console/ConsoleLogExporter.php b/src/lib/telemetry/src/Flow/Telemetry/Provider/Console/ConsoleLogExporter.php
deleted file mode 100644
index e28710aab..000000000
--- a/src/lib/telemetry/src/Flow/Telemetry/Provider/Console/ConsoleLogExporter.php
+++ /dev/null
@@ -1,349 +0,0 @@
-output = new ConsoleOutput($colors, $outputStream);
- }
-
- /**
- * @param array $entries
- */
- public function export(array $entries) : bool
- {
- if (\count($entries) === 0) {
- return true;
- }
-
- $formattedRecords = $this->formatEntries($entries);
- $bodyWidth = $this->calculateBodyWidth($formattedRecords);
- $hasTrace = $this->hasAnySpanContext($entries);
- $resourceLines = $this->buildResourceLines($entries[0]->resource);
- $scopeLine = $this->buildScopeLine($entries[0]);
- $width = $this->calculateTotalWidth($bodyWidth, $hasTrace);
-
- foreach ($resourceLines as $resourceLine) {
- $resourceWidth = \mb_strlen($this->output->stripColors($resourceLine)) + 4;
- $width = \max($width, $resourceWidth);
- }
-
- $buffer = $this->output->border($width) . PHP_EOL;
- $buffer .= $this->output->row($this->output->bold('LOGS'), $width) . PHP_EOL;
-
- if (\count($resourceLines) > 0) {
- foreach ($resourceLines as $resourceLine) {
- $buffer .= $this->output->row($resourceLine, $width) . PHP_EOL;
- }
- }
-
- if ($scopeLine !== null) {
- $buffer .= $this->output->row($scopeLine, $width) . PHP_EOL;
- }
-
- $buffer .= $this->output->border($width) . PHP_EOL;
- $buffer .= $this->output->row($this->buildHeaderLine($bodyWidth, $hasTrace), $width) . PHP_EOL;
- $buffer .= $this->output->border($width) . PHP_EOL;
-
- foreach ($formattedRecords as $record) {
- $buffer .= $this->output->row($this->buildLine($record, $bodyWidth, $hasTrace), $width) . PHP_EOL;
- }
-
- $buffer .= $this->output->border($width);
-
- $this->output->write($buffer);
-
- return true;
- }
-
- /**
- * @return array
- */
- public function transports() : array
- {
- return [new VoidTransport()];
- }
-
- private function buildHeaderLine(int $bodyWidth, bool $hasTrace) : string
- {
- $timestamp = $this->output->pad('Timestamp', self::TIMESTAMP_WIDTH);
- $level = $this->output->pad('Level', self::SEVERITY_WIDTH);
- $message = $this->output->pad('Message', $bodyWidth);
-
- $line = $this->output->bold($timestamp) . ' | ';
- $line .= $this->output->bold($level) . ' | ';
- $line .= $this->output->bold($message);
-
- if ($hasTrace) {
- $trace = $this->output->pad('Trace', self::TRACE_WIDTH);
- $line .= ' | ' . $this->output->bold($trace);
- }
-
- return $line;
- }
-
- /**
- * @param array{timestamp: string, severity: string, severityRaw: Severity, body: string, trace: string, droppedAttributeCount: int} $record
- */
- private function buildLine(array $record, int $bodyWidth, bool $hasTrace) : string
- {
- $timestamp = $this->output->pad($record['timestamp'], self::TIMESTAMP_WIDTH);
- $severity = $this->output->pad($record['severity'], self::SEVERITY_WIDTH);
- $body = $this->output->truncate($record['body'], $bodyWidth);
- $body = $this->output->pad($body, $bodyWidth);
-
- $line = $this->output->gray($timestamp) . ' | ';
- $line .= $this->colorBySeverity($severity, $record['severityRaw']) . ' | ';
- $line .= $body;
-
- if ($this->options->showDroppedAttributeCount && $record['droppedAttributeCount'] > 0) {
- $line .= ' ' . $this->output->yellow('[dropped:' . $record['droppedAttributeCount'] . ']');
- }
-
- if ($hasTrace) {
- $trace = $this->output->pad($record['trace'], self::TRACE_WIDTH);
- $line .= ' | ' . ($record['trace'] !== '' ? $this->output->dim($trace) : $trace);
- }
-
- return $line;
- }
-
- /**
- * @return array
- */
- private function buildResourceLines(TelemetryResource $resource) : array
- {
- if ($resource->isEmpty()) {
- return [];
- }
-
- if (!$this->options->showResourceAttributes) {
- $parts = [];
- $serviceName = $resource->get('service.name');
-
- if (\is_string($serviceName)) {
- $parts[] = $serviceName;
- }
-
- $serviceVersion = $resource->get('service.version');
-
- if (\is_string($serviceVersion)) {
- $parts[] = 'v' . $serviceVersion;
- }
-
- if (\count($parts) === 0) {
- return [];
- }
-
- return ['Resource: ' . $this->output->dim(\implode(' ', $parts))];
- }
-
- $lines = [];
- $lines[] = $this->output->bold('Resource:');
-
- $attributes = $resource->all();
- $maxKeyLength = 0;
-
- foreach (\array_keys($attributes) as $key) {
- $maxKeyLength = \max($maxKeyLength, \mb_strlen($key));
- }
-
- foreach ($attributes as $key => $value) {
- $keyStr = $this->output->pad($key, $maxKeyLength);
- $valueStr = $this->output->formatValue($value);
- $lines[] = ' ' . $this->output->cyan($keyStr) . ' = ' . $valueStr;
- }
-
- return $lines;
- }
-
- private function buildScopeLine(LogEntry $entry) : ?string
- {
- if (!$this->options->showInstrumentationScope) {
- return null;
- }
-
- $scope = $entry->scope;
-
- return 'Scope: ' . $this->output->dim($scope->name . ' v' . $scope->version);
- }
-
- /**
- * @param array $records
- */
- private function calculateBodyWidth(array $records) : int
- {
- $maxLength = 0;
-
- foreach ($records as $record) {
- $maxLength = \max($maxLength, \mb_strlen($record['body']));
- }
-
- if ($this->maxBodyLength !== null) {
- return \min($maxLength, $this->maxBodyLength);
- }
-
- return $maxLength;
- }
-
- private function calculateTotalWidth(int $bodyWidth, bool $hasTrace) : int
- {
- $width = 4;
- $width += self::TIMESTAMP_WIDTH;
- $width += 3;
- $width += self::SEVERITY_WIDTH;
- $width += 3;
- $width += $bodyWidth;
-
- if ($hasTrace) {
- $width += 3;
- $width += self::TRACE_WIDTH;
- }
-
- return \max(self::MIN_WIDTH, $width);
- }
-
- private function colorBySeverity(string $text, Severity $severity) : string
- {
- return match ($severity) {
- Severity::TRACE => $this->output->dim($text),
- Severity::DEBUG => $this->output->gray($text),
- Severity::INFO => $this->output->green($text),
- Severity::WARN => $this->output->yellow($text),
- Severity::ERROR => $this->output->red($text),
- Severity::FATAL => $this->output->bold($this->output->red($text)),
- };
- }
-
- /**
- * @param array|bool|\DateTimeImmutable|float|int|string> $attributes
- */
- private function formatAttributes(array $attributes) : string
- {
- if (\count($attributes) === 0) {
- return '';
- }
-
- $parts = [];
-
- foreach ($attributes as $key => $value) {
- $parts[] = $key . ': ' . $this->output->formatValue($this->normalizeValue($value));
- }
-
- return '{' . \implode(', ', $parts) . '}';
- }
-
- /**
- * @param array $entries
- *
- * @return array