Skip to content

[php]: add endpoints to support OpenTelemetry logs#6646

Open
rachelyangdog wants to merge 12 commits into
mainfrom
rachel.yang/php-otel-logs
Open

[php]: add endpoints to support OpenTelemetry logs#6646
rachelyangdog wants to merge 12 commits into
mainfrom
rachel.yang/php-otel-logs

Conversation

@rachelyangdog
Copy link
Copy Markdown
Contributor

@rachelyangdog rachelyangdog commented Mar 30, 2026

Motivation

Summary

Enables the OpenTelemetry logs parametric tests (tests/parametric/test_otel_logs.py) for dd-trace-php starting at v1.20.0, which ships the OTel logs feature.

Adds three new endpoints to the PHP parametric server so it can act as an OTel logs test client:

  • POST /otel/logger/create — constructs a Monolog Logger wrapped around an OTel Handler so the real LogsIntegration PSR-3 hook fires (not just direct SDK emission, which would pass FR09 trivially).
  • POST /otel/logger/write — maps the test client's level string to PSR-3, activates the requested span scope, and writes through Monolog.
  • POST /log/otel/flush — force-flushes the LoggerProvider.

The server constructs the LoggerProvider explicitly (LogsExporterFactory + BatchLogRecordProcessor + ResourceInfoFactory::defaultResource()) rather than relying on OTEL_PHP_AUTOLOAD_ENABLED, mirroring how user code would wire this up and avoiding side effects from unrequested Tracer/Meter auto-instrumentation. dd-trace-php's DatadogResolver still fills in OTEL_EXPORTER_OTLP_LOGS_ENDPOINT from the agent host, and the resource detectors populate Service/Environment/Host from DD_SERVICE / DD_ENV / DD_VERSION / DD_HOSTNAME.

Adds composer dependencies:

  • open-telemetry/exporter-otlp — OTLP HTTP/protobuf transport
  • open-telemetry/opentelemetry-logger-monolog — PSR-3 → OTel handler bridge
  • pins open-telemetry/api: ^1.0.0 <1.4.0 and open-telemetry/sdk: 1.4.0 for compatibility

Changes

Workflow

  1. ⚠️ Create your PR as draft ⚠️
  2. Work on you PR until the CI passes
  3. Mark it as ready for review
    • Test logic is modified? -> Get a review from RFC owner.
    • Framework is modified, or non obvious usage of it -> get a review from R&P team

🚀 Once your PR is reviewed and the CI green, you can merge it!

🛟 #apm-shared-testing 🛟

Reviewer checklist

  • Anything but tests/ or manifests/ is modified ? I have the approval from R&P team
  • A docker base image is modified?
    • the relevant build-XXX-image label is present
  • A scenario is added, removed or renamed?

@rachelyangdog rachelyangdog requested review from a team as code owners March 30, 2026 14:19
@rachelyangdog rachelyangdog marked this pull request as draft March 30, 2026 14:19
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 30, 2026

CODEOWNERS have been resolved as:

manifests/php.yml                                                       @DataDog/apm-php @DataDog/asm-php
utils/build/docker/php/parametric/composer.json                         @DataDog/apm-php @DataDog/system-tests-core
utils/build/docker/php/parametric/composer.lock                         @DataDog/apm-php @DataDog/system-tests-core
utils/build/docker/php/parametric/server.php                            @DataDog/apm-php @DataDog/system-tests-core

rachelyangdog and others added 2 commits March 30, 2026 10:31
…ridge

- server.php: replace manual LogsExporterFactory + BatchLogRecordProcessor +
  SDKLoggerProvider wiring with Globals::loggerProvider(), aligning with the
  Node.js and Python parametric servers. Drop the hardcoded putenv that
  forced OTEL_EXPORTER_OTLP_ENDPOINT to port 4318 and prevented the tracer's
  DatadogResolver from picking the correct port per protocol.
- /log/otel/flush: wrap forceFlush in try/catch and return get_class on the
  provider, matching the Python/Node.js shape.
- Dockerfile: set DD_AUTOLOAD_NO_COMPILE=true so ddtrace loads the per-file
  bridge (src/bridge/_files_*.php) rather than the stale bundled
  src/bridge/_generated_*.php from the released package, allowing local PR
  source overrides in /binaries/src to take effect.
- Remove GrpcTransport.php: orphaned shim, never registered with the OTel
  Registry and required PECL ext-grpc which isn't installed in the image.
  gRPC support belongs as an opt-in dep on the user side (parallels
  dd-trace-py's opentelemetry-exporter-otlp[grpc]).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@datadog-prod-us1-5
Copy link
Copy Markdown

datadog-prod-us1-5 Bot commented May 7, 2026

Tests

🎉 All green!

🧪 All tests passed
❄️ No new flaky tests detected

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 05e290b | Docs | Datadog PR Page | Give us feedback!

rachelyangdog and others added 4 commits May 7, 2026 15:52
Mirrors the metrics-style pattern: dd-trace-php's DatadogResolver supplies
agent-aware defaults (OTEL_EXPORTER_OTLP_LOGS_ENDPOINT, etc.) and the
parametric server, standing in for user code, builds the LoggerProvider
itself with LogsExporterFactory + BatchLogRecordProcessor +
ResourceInfoFactory::defaultResource. Avoids relying on an
OTEL_PHP_AUTOLOAD_ENABLED override in the tracer, which would also stand up
unrequested Tracer/Meter providers and OTel auto-instrumentations.

Wraps construction in try/catch so the server keeps responding when the
configured protocol's transport isn't available (e.g. grpc without
ext-grpc + open-telemetry/transport-grpc), letting that test fail cleanly
on missing payloads instead of erroring at server startup.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Test_FR06_OTLP_Protocols: gRPC requires user-installed
  open-telemetry/transport-grpc + ext-grpc; dd-trace-php does not bundle
  either (parallels dd-trace-py's opt-in posture for grpc). HTTP/protobuf
  is exercised via FR01/FR05/FR08, so no real coverage loss.
- Test_FR10_Timeout_Configuration / Test_FR11_Telemetry: OTel config
  telemetry tracking (otel.log_records count metric, OTEL_EXPORTER_OTLP_*
  configuration reporting) is out of scope for the initial logs PR; will
  follow up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Manifest validator requires sorted keys. Test_FR06 now precedes Test_FR07.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@rachelyangdog rachelyangdog changed the title WIP: php endpoints to support opentelemtry logs [php]: add endpoints to support OpenTelemetry logs May 11, 2026
Comment thread utils/build/docker/php/parametric/Dockerfile
Comment thread utils/build/docker/php/parametric/server.php
Comment thread utils/build/docker/php/parametric/server.php Outdated
Comment thread run.sh Outdated
@rachelyangdog rachelyangdog marked this pull request as ready for review May 11, 2026 19:27
rachelyangdog and others added 4 commits May 11, 2026 15:27
Per Bob's review feedback: the FR09 log-injection test was passing
trivially before because the parametric server emitted OTel LogRecords
directly via the SDK Logger — so dd-trace-php's LogsIntegration PSR-3
hook (the real log-injection path) never fired, and the
shouldSkipForOtelLogs guard was never actually validated.

This change adds open-telemetry/opentelemetry-logger-monolog to the
parametric server's composer and routes /otel/logger/{create,write}
through a Monolog\Logger with an OTel Handler attached, so the PSR-3
hook in LogsIntegration runs and the integration's OTel-routed-Monolog
detection is genuinely exercised.

Two small wrinkles in the OTel Monolog handler that needed handling:

- The handler calls $provider->getLogger($monologChannel) with no scope
  params, so FR13 (version / schema_url / attributes) would lose what
  create_logger sent. Wrap the SDK provider in an anonymous class that
  stores the create_logger scope params and re-injects them when the
  handler asks for a Logger.

- The handler writes $record->level_name through verbatim as
  severity_text, so Monolog's "WARNING"/"CRITICAL" surface instead of
  OTel-canonical "WARN"/"FATAL" (FR12 [warning] failure).
  Monolog\LogRecord::with() doesn't allow overriding level_name (it's
  derived from the Level enum, not a stored property), so subclass the
  handler and re-implement write() with our own severity map. The
  subclass keeps `instanceof OpenTelemetry\Contrib\Logs\Monolog\Handler`
  true so LogsIntegration's detection still matches.

Full parametric suite still: 24 passed, 5 xfailed (out of scope: grpc +
telemetry), 1 xpassed (FR06 http_protobuf — collateral of the
class-level missing_feature gate), 1 skipped.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Removes two changes that only helped local PR-iteration and shouldn't
ship in the parametric image:

- ENV DD_AUTOLOAD_NO_COMPILE=true (made ddtrace load src/bridge/_files_*.php
  instead of the bundled _generated_*.php, so /binaries/src overrides
  could take effect without regenerating the bundle).
- The cp /binaries/src/. -> dd-trace-sources/src/ hunk (the override
  mechanism itself).

Local developers iterating on dd-trace-php PHP-level sources should now
either regenerate _generated_*.php locally (tooling/generation, composer
generate) and rsync that with the rest of src/, or ship a full tarball.
The parametric image is back to production-faithful: it runs the released
package's bundle, not a developer's loose source files.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comment thread run.sh Outdated
…c cap

dd-trace-php v1.20.0 ships the OTel logs feature, so the parametric
test_otel_logs.py module is enabled from that version. Existing per-class
missing_feature overrides (FR06 gRPC, FR07 DD_HOSTNAME, FR10/FR11
telemetry) stay in place as exclusions.

Also drops the PHP-specific pytest_numprocesses=4 cap from run.sh — that
was a local-iteration helper and shouldn't ship with the PR.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants