Skip to content

feat(metrics): add PHP runtime metrics package#645

Open
intuibase wants to merge 7 commits into
open-telemetry:mainfrom
intuibase:feat/runtime-metrics
Open

feat(metrics): add PHP runtime metrics package#645
intuibase wants to merge 7 commits into
open-telemetry:mainfrom
intuibase:feat/runtime-metrics

Conversation

@intuibase

@intuibase intuibase commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Adds open-telemetry/opentelemetry-metrics-runtime — a new contrib package that auto-registers OTel observable instruments for PHP runtime internals: memory usage/peak/limit, garbage collection stats (including PHP 8.3+ timing fields), OPcache memory and hit-rate, and CPU time via getrusage().

  • OPcache and CPU metrics are skipped when underlying functions are unavailable
  • Supports PHP 8.1–8.5

Adds open-telemetry/opentelemetry-metrics-runtime — a new contrib package
that auto-registers OTel observable instruments for PHP runtime internals:
memory usage/peak/limit, garbage collection stats (including PHP 8.3+ timing
fields), OPcache memory and hit-rate, and CPU time via getrusage().

- Disable entire package: OTEL_PHP_DISABLED_INSTRUMENTATIONS=metrics-runtime
- Disable metric groups: OTEL_PHP_DISABLED_METRICS=opcache,cpu
- OPcache and CPU metrics are skipped when underlying functions are unavailable
- Supports PHP 8.1–8.5; phan stub created via post-install-cmd on PHP 8.5+
@intuibase intuibase requested a review from a team as a code owner June 29, 2026 10:38
@codecov

codecov Bot commented Jun 29, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.21339% with 21 lines in your changes missing coverage. Please review.
✅ Project coverage is 81.17%. Comparing base (00e96a9) to head (7af0b6e).
⚠️ Report is 8 commits behind head on main.

Files with missing lines Patch % Lines
src/Metrics/Runtime/src/OpcacheMetrics.php 82.92% 14 Missing ⚠️
src/Metrics/Runtime/src/MemoryMetrics.php 85.71% 5 Missing ⚠️
src/Metrics/Runtime/src/RuntimeMetrics.php 88.23% 2 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff              @@
##               main     #645      +/-   ##
============================================
+ Coverage     80.46%   81.17%   +0.70%     
+ Complexity     1669     1477     -192     
============================================
  Files           116      103      -13     
  Lines          6265     5582     -683     
============================================
- Hits           5041     4531     -510     
+ Misses         1224     1051     -173     
Flag Coverage Δ
Context/Swoole 0.00% <ø> (ø)
Exporter/Instana 49.80% <ø> (ø)
Instrumentation/AwsSdk 82.14% <ø> (ø)
Instrumentation/CakePHP 20.42% <ø> (ø)
Instrumentation/CodeIgniter 79.31% <ø> (ø)
Instrumentation/Curl 86.88% <ø> (ø)
Instrumentation/Doctrine 92.82% <ø> (ø)
Instrumentation/ExtAmqp 88.80% <ø> (ø)
Instrumentation/Guzzle 76.25% <ø> (ø)
Instrumentation/HttpAsyncClient 78.94% <ø> (ø)
Instrumentation/HttpConfig 28.76% <ø> (ø)
Instrumentation/IO 0.00% <ø> (ø)
Instrumentation/Laravel ?
Instrumentation/Magento2 88.12% <ø> (?)
Instrumentation/MongoDB 76.84% <ø> (ø)
Instrumentation/MySqli ?
Instrumentation/OpenAIPHP 86.71% <ø> (ø)
Instrumentation/PostgreSql 91.36% <ø> (ø)
Instrumentation/Psr14 77.41% <ø> (ø)
Instrumentation/Psr15 89.74% <ø> (ø)
Instrumentation/Psr16 97.43% <ø> (ø)
Instrumentation/Psr18 79.41% <ø> (ø)
Instrumentation/Psr6 97.56% <ø> (ø)
Instrumentation/ReactPHP 99.41% <ø> (?)
Instrumentation/Session 94.28% <ø> (ø)
Instrumentation/Slim 84.21% <ø> (ø)
Metrics/Runtime 91.21% <91.21%> (?)
Propagation/CloudTrace 90.69% <ø> (ø)
Propagation/Instana 98.07% <ø> (ø)
Propagation/ServerTiming 94.73% <ø> (ø)
Propagation/TraceResponse 94.73% <ø> (ø)
ResourceDetectors/Azure 91.66% <ø> (ø)
ResourceDetectors/DigitalOcean 100.00% <ø> (ø)
Sampler/Xray 78.38% <ø> (ø)
Shims/OpenTracing 92.99% <ø> (ø)
SqlCommenter 95.58% <ø> (ø)
Utils/Test 87.79% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/Metrics/Runtime/src/CpuMetrics.php 100.00% <100.00%> (ø)
...c/Metrics/Runtime/src/GarbageCollectionMetrics.php 100.00% <100.00%> (ø)
...strumentationConfigurationRuntimeMetricsConfig.php 100.00% <100.00%> (ø)
src/Metrics/Runtime/src/RuntimeMetricsConfig.php 100.00% <100.00%> (ø)
...rics/Runtime/src/RuntimeMetricsInstrumentation.php 100.00% <100.00%> (ø)
src/Metrics/Runtime/src/RuntimeMetrics.php 88.23% <88.23%> (ø)
src/Metrics/Runtime/src/MemoryMetrics.php 85.71% <85.71%> (ø)
src/Metrics/Runtime/src/OpcacheMetrics.php 82.92% <82.92%> (ø)

... and 30 files with indirect coverage changes


Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 00e96a9...7af0b6e. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Comment thread src/Metrics/Runtime/src/CpuMetrics.php Outdated
Comment thread src/Metrics/Runtime/src/MemoryMetrics.php
Comment thread src/Metrics/Runtime/src/CpuMetrics.php Outdated
Comment thread src/Metrics/Runtime/src/GarbageCollectionMetrics.php Outdated
Comment thread src/Metrics/Runtime/README.md Outdated
Comment thread src/Metrics/Runtime/composer.json Outdated
Comment thread src/Metrics/Runtime/_register.php Outdated
Comment thread src/Metrics/Runtime/README.md Outdated
- Use batchObserve() in CpuMetrics, GarbageCollectionMetrics and
  OpcacheMetrics so getrusage()/gc_status()/opcache_get_status() are
  called only once per collection cycle
- Handle missing ru_nvcsw/ru_nivcsw keys on OSes that omit them (macOS)
- Fix instrument units to singular form per OTel semconv: {run},
  {object}, {switch}, {hit}, {miss}, {script}, {string}
- Replace custom parseMemoryLimit() with ini_parse_quantity(); add
  symfony/polyfill-php82 to support PHP 8.1
- Register via Instrumentation SPI instead of autoload.files so metrics
  are registered after SdkAutoloader initialises Globals; remove
  _register.php
- Use separate meters per group (io.opentelemetry.contrib.php.runtime.
  {group}) to allow disabling via OTEL_CONFIG_FILE meter configurators
  or views; keep OTEL_PHP_DISABLED_METRICS as a lightweight fallback
- Depend on open-telemetry/api ^1.1 instead of sdk; move sdk to
  require-dev
@intuibase intuibase requested a review from Nevay June 30, 2026 13:07
@intuibase

Copy link
Copy Markdown
Contributor Author

Thanks for the thorough review @Nevay! All points addressed.

@ChrisLightfootWild ChrisLightfootWild left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome stuff.

Could we add a .gitattributes also to this package? 💪

Comment thread src/Metrics/Runtime/composer.json Outdated
Comment thread src/Metrics/Runtime/src/CpuMetrics.php Outdated
Comment thread src/Metrics/Runtime/src/CpuMetrics.php Outdated
Comment thread src/Metrics/Runtime/src/RuntimeMetrics.php Outdated
Comment thread src/Metrics/Runtime/src/MemoryMetrics.php Outdated
Comment thread src/Metrics/Runtime/src/CpuMetrics.php
Comment thread src/Metrics/Runtime/src/GarbageCollectionMetrics.php Outdated
Comment thread src/Metrics/Runtime/src/RuntimeMetrics.php Outdated
intuibase added 3 commits July 6, 2026 14:55
- Only observe ru_nvcsw/ru_nivcsw when getrusage() actually reports them
  (absent on some OSes) instead of defaulting to 0
- Merge voluntary/involuntary context switches into the standard
  process.context_switches metric with a process.context_switch.type
  attribute, so it stays meaningful when that attribute is dropped
- Split memory usage into additive emalloc/overhead components instead of
  overlapping real/emalloc values, for the same reason
- Mark per-group metric classes as @internal
- Drop the process.runtime. prefix from PHP-specific metrics per the
  runtime metrics semantic conventions
- Pass an instrumentation version to each meter via InstalledVersions
- Add a _register.php fallback (via ServiceLoader::register()) so the
  instrumentation still registers when the tbachert/spi composer plugin
  is not allowed to run
…TATIONS

Reuse the standard OTEL_PHP_DISABLED_INSTRUMENTATIONS env var
(metrics-runtime-memory/-gc/-opcache/-cpu, or metrics-runtime/all for
everything) instead of the ad-hoc OTEL_PHP_DISABLED_METRICS variable.
->observe(static function (ObserverInterface $observer): void {
$emalloc = memory_get_usage(false);
$observer->observe($emalloc, ['memory.type' => 'emalloc']);
$observer->observe(memory_get_usage(true) - $emalloc, ['memory.type' => 'overhead']);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should call memory_get_usage(true) immediately after memory_get_usage(false), calling $observer->observe() might allocate additional memory.

'Memory limit configured in php.ini (-1 means unlimited)',
)
->observe(static function (ObserverInterface $observer): void {
$limit = ini_get('memory_limit') ?: '-1';

@Nevay Nevay Jul 6, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm learning towards not reporting this value if unlimited to keep avg/min/... aggregations useful.

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