Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions .github/workflows/metrics-runtime.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: "[CI] Metrics Runtime"

on:
push:
branches: [main]
paths:
- 'src/Metrics/Runtime/**'

pull_request:
paths:
- 'src/Metrics/Runtime/**'

# To call from https://github.com/opentelemetry-php/contrib-auto-metrics-runtime repo/forks.
workflow_call:
inputs:
package-root:
type: string
required: true

jobs:
CI:
strategy:
fail-fast: false
matrix:
php-version:
- 8.1
- 8.2
- 8.3
- 8.4

uses: ./.github/workflows/php-contrib.yml
with:
job-name: "PHP ${{ matrix.php-version }}"
codecov-flags: 'Metrics/Runtime'
package-root: ${{ inputs.package-root || 'src/Metrics/Runtime' }}
php-version: ${{ matrix.php-version }}
1 change: 1 addition & 0 deletions .github/workflows/php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ jobs:
'Instrumentation/Symfony',
'Instrumentation/Yii',
'Logs/Monolog',
# 'Metrics/Runtime', # Experiment: moved to metrics-runtime.yml
'Propagation/CloudTrace',
'Propagation/Instana',
'Propagation/ServerTiming',
Expand Down
2 changes: 2 additions & 0 deletions .gitsplit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ splits:
target: "https://${GH_TOKEN}@github.com/opentelemetry-php/contrib-logger-monolog.git"
- prefix: "src/MetaPackages/opentelemetry"
target: "https://${GH_TOKEN}@github.com/opentelemetry-php/opentelemetry-meta.git"
- prefix: "src/Metrics/Runtime"
target: "https://${GH_TOKEN}@github.com/opentelemetry-php/contrib-metrics-runtime.git"
- prefix: "src/Propagation/CloudTrace"
target: "https://${GH_TOKEN}@github.com/opentelemetry-php/contrib-propagator-cloudtrace.git"
- prefix: "src/Propagation/Instana"
Expand Down
4 changes: 4 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"OpenTelemetry\\Contrib\\Resource\\Detector\\Azure\\": "src/ResourceDetectors/Azure/src",
"OpenTelemetry\\Contrib\\Resource\\Detector\\Container\\": "src/ResourceDetectors/Container/src",
"OpenTelemetry\\Contrib\\Resource\\Detector\\DigitalOcean\\": "src/ResourceDetectors/DigitalOcean/src",
"OpenTelemetry\\Contrib\\Metrics\\Runtime\\": "src/Metrics/Runtime/src",
"OpenTelemetry\\Contrib\\Sampler\\RuleBased\\": "src/Sampler/RuleBased/src",
"OpenTelemetry\\Contrib\\Shim\\OpenTracing\\": "src/Shims/OpenTracing/src",
"OpenTelemetry\\Contrib\\SqlCommenter\\": "src/SqlCommenter/src",
Expand Down Expand Up @@ -93,6 +94,7 @@
"src/Instrumentation/Yii/_register.php",
"src/Propagation/CloudTrace/_register.php",
"src/Propagation/Instana/_register.php",
"src/Metrics/Runtime/_register.php",
"src/ResourceDetectors/Azure/_register.php",
"src/ResourceDetectors/Container/_register.php",
"src/ResourceDetectors/DigitalOcean/_register.php"
Expand Down Expand Up @@ -130,6 +132,7 @@
"OpenTelemetry\\Tests\\Propagation\\CloudTrace\\": "src/Propagation/CloudTrace/tests",
"OpenTelemetry\\Tests\\Resource\\Detector\\Azure\\": "src/ResourceDetectors/Azure/tests",
"OpenTelemetry\\Contrib\\Resource\\Detector\\DigitalOcean\\": "src/ResourceDetectors/DigitalOcean/tests",
"OpenTelemetry\\Contrib\\Metrics\\Runtime\\Tests\\": "src/Metrics/Runtime/tests",
"OpenTelemetry\\Tests\\Contrib\\SqlCommenter\\": "src/SqlCommenter/tests",
"OpenTelemetry\\Tests\\Contrib\\Symfony\\": "src/Symfony/tests",
"OpenTelemetry\\TestUtils\\Tests\\": "src/Utils/Test/tests"
Expand Down Expand Up @@ -179,6 +182,7 @@
"open-telemetry/opentelemetry-propagation-instana": "self.version",
"open-telemetry/opentelemetry-propagation-server-timing": "self.version",
"open-telemetry/opentelemetry-propagation-traceresponse": "self.version",
"open-telemetry/opentelemetry-metrics-runtime": "self.version",
"open-telemetry/opentelemetry-sqlcommenter": "self.version",
"open-telemetry/opentracing-shim": "self.version",
"open-telemetry/sampler-rule-based": "self.version",
Expand Down
15 changes: 15 additions & 0 deletions src/Metrics/Runtime/.gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
* text=auto

*.md diff=markdown
*.php diff=php

/.gitattributes export-ignore
/.github export-ignore
/.gitignore export-ignore
/.phan export-ignore
/.php-cs-fixer.php export-ignore
/coverage.clover export-ignore
/phpstan.neon.dist export-ignore
/phpunit.xml.dist export-ignore
/psalm.xml.dist export-ignore
/tests export-ignore
9 changes: 9 additions & 0 deletions src/Metrics/Runtime/.github/workflows/contrib-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
name: Contrib CI Pipeline
on:
workflow_dispatch:

jobs:
CI:
uses: open-telemetry/opentelemetry-php-contrib/.github/workflows/metrics-runtime.yml@main
with:
package-root: '.'
2 changes: 2 additions & 0 deletions src/Metrics/Runtime/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/vendor/
/var/
62 changes: 62 additions & 0 deletions src/Metrics/Runtime/.phan/config.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php

use Phan\Issue;

return [
'target_php_version' => '8.1',

'allow_missing_properties' => false,
'null_casts_as_any_type' => false,
'null_casts_as_array' => true,
'array_casts_as_null' => true,
'scalar_implicit_cast' => false,
'scalar_array_key_cast' => true,
'scalar_implicit_partial' => [],
'strict_method_checking' => false,
'strict_object_checking' => false,
'strict_param_checking' => false,
'strict_property_checking' => false,
'strict_return_checking' => false,
'ignore_undeclared_variables_in_global_scope' => true,
'ignore_undeclared_functions_with_known_signatures' => true,
'backward_compatibility_checks' => false,
'check_docblock_signature_return_type_match' => false,
'prefer_narrowed_phpdoc_param_type' => true,
'prefer_narrowed_phpdoc_return_type' => true,
'analyze_signature_compatibility' => true,
'phpdoc_type_mapping' => [],
'dead_code_detection' => false,
'unused_variable_detection' => false,
'redundant_condition_detection' => false,
'assume_real_types_for_internal_functions' => false,
'quick_mode' => false,
'generic_types_enabled' => true,
'globals_type_map' => [],
'minimum_severity' => Issue::SEVERITY_LOW,
'suppress_issue_types' => [],
'exclude_file_regex' => '@^vendor/.*/(tests?|Tests?)/@',
'exclude_file_list' => [
'vendor/composer/composer/src/Composer/InstalledVersions.php',
],
'exclude_analysis_directory_list' => [
'vendor/',
],
'enable_include_path_checks' => true,
'processes' => 1,
'analyzed_file_extensions' => [
'php',
],
'autoload_internal_extension_signatures' => [],
'plugins' => [
'AlwaysReturnPlugin',
'PregRegexCheckerPlugin',
'UnreachableCodePlugin',
],
'directory_list' => [
'src',
'vendor',
],
'file_list' => [
'_register.php',
],
];
43 changes: 43 additions & 0 deletions src/Metrics/Runtime/.php-cs-fixer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

$finder = PhpCsFixer\Finder::create()
->exclude('vendor')
->exclude('var/cache')
->in(__DIR__);

$config = new PhpCsFixer\Config();
return $config->setRules([
'concat_space' => ['spacing' => 'one'],
'declare_equal_normalize' => ['space' => 'none'],
'is_null' => true,
'modernize_types_casting' => true,
'ordered_imports' => true,
'php_unit_construct' => true,
'single_line_comment_style' => true,
'yoda_style' => false,
'@PSR2' => true,
'array_syntax' => ['syntax' => 'short'],
'blank_line_after_opening_tag' => true,
'blank_line_before_statement' => true,
'cast_spaces' => true,
'declare_strict_types' => true,
'type_declaration_spaces' => true,
'include' => true,
'lowercase_cast' => true,
'new_with_parentheses' => true,
'no_extra_blank_lines' => true,
'no_leading_import_slash' => true,
'echo_tag_syntax' => true,
'no_unused_imports' => true,
'no_useless_else' => true,
'no_useless_return' => true,
'phpdoc_order' => true,
'phpdoc_scalar' => true,
'phpdoc_types' => true,
'short_scalar_cast' => true,
'blank_lines_before_namespace' => true,
'single_quote' => true,
'trailing_comma_in_multiline' => true,
])
->setRiskyAllowed(true)
->setFinder($finder);
144 changes: 144 additions & 0 deletions src/Metrics/Runtime/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
[![Releases](https://img.shields.io/badge/releases-purple)](https://github.com/open-telemetry/opentelemetry-php-contrib/releases)
[![Issues](https://img.shields.io/badge/issues-pink)](https://github.com/open-telemetry/opentelemetry-php/issues)
[![Source](https://img.shields.io/badge/source-contrib-green)](https://github.com/open-telemetry/opentelemetry-php-contrib/tree/main/src/Metrics/Runtime)
[![Latest Version](http://poser.pugx.org/open-telemetry/opentelemetry-metrics-runtime/v/unstable)](https://packagist.org/packages/open-telemetry/opentelemetry-metrics-runtime/)
[![Stable](http://poser.pugx.org/open-telemetry/opentelemetry-metrics-runtime/v/stable)](https://packagist.org/packages/open-telemetry/opentelemetry-metrics-runtime/)

This is a read-only subtree split of https://github.com/open-telemetry/opentelemetry-php-contrib.

# OpenTelemetry PHP Runtime Metrics

Collects PHP runtime metrics — memory, garbage collection, OPcache, and CPU usage — and exposes them as OpenTelemetry observable instruments.

## Requirements

* PHP 8.1+
* OpenTelemetry API

## Installation

```bash
composer require open-telemetry/opentelemetry-metrics-runtime
```

## Usage

### Automatic (recommended)

The package registers itself via [SPI](https://github.com/Nevay/spi) as an `Instrumentation`. When the OpenTelemetry SDK calls `SdkAutoloader::autoload()`, metrics are registered automatically with the configured `MeterProvider`. No additional code is required.

```shell
OTEL_PHP_AUTOLOAD_ENABLED=true
OTEL_METRICS_EXPORTER=otlp
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
```

### Manual

If you need to control when metrics are registered:

```php
use OpenTelemetry\Contrib\Metrics\Runtime\RuntimeMetrics;
use OpenTelemetry\Contrib\Metrics\Runtime\RuntimeMetricsConfig;

RuntimeMetrics::register($meterProvider);

// Optionally, disable individual groups (memory, gc, opcache, cpu):
RuntimeMetrics::register($meterProvider, new RuntimeMetricsConfig(disabled: ['opcache', 'cpu']));
```

## Metrics

### Memory (`memory`)

| Metric | Type | Unit | Description |
|--------|------|------|-------------|
| `php.memory.usage` | UpDownCounter | `By` | Current memory usage. Reported for `emalloc` (PHP internal allocation) and `overhead` (additional OS allocation beyond `emalloc`) via the `memory.type` attribute; the two sum to the real OS allocation. |
| `php.memory.peak_usage` | UpDownCounter | `By` | Peak memory usage since script start. Same `memory.type` attribute breakdown. |
| `php.memory.limit` | Gauge | `By` | Memory limit from `php.ini`. `-1` means unlimited. |

### Garbage Collection (`gc`)

| Metric | Type | Unit | Description |
|--------|------|------|-------------|
| `php.gc.runs` | Counter | `{run}` | Total number of GC cycles run. |
| `php.gc.collected` | Counter | `{object}` | Total number of objects collected. |
| `php.gc.roots` | Gauge | `{object}` | Current number of objects in the root buffer. |
| `php.gc.threshold` | Gauge | `{object}` | Number of roots required to trigger a GC cycle. |
| `php.gc.collector_time` | Counter | `s` | Cumulative time spent in the GC collector. **PHP 8.3+** |
| `php.gc.destructor_time` | Counter | `s` | Cumulative time spent running destructors during GC. **PHP 8.3+** |
| `php.gc.free_time` | Counter | `s` | Cumulative time spent freeing memory during GC. **PHP 8.3+** |

### OPcache (`opcache`)

Registered only when OPcache is enabled (`opcache.enable=1`). In CLI context, also requires `opcache.enable_cli=1`.

| Metric | Type | Unit | Description |
|--------|------|------|-------------|
| `php.opcache.memory_used` | UpDownCounter | `By` | Memory used by cached scripts. |
| `php.opcache.memory_free` | UpDownCounter | `By` | Free memory in the OPcache buffer. |
| `php.opcache.memory_wasted` | UpDownCounter | `By` | Wasted (fragmented) memory — requires restart to reclaim. |
| `php.opcache.hits` | Counter | `{hit}` | Total cache hits. |
| `php.opcache.misses` | Counter | `{miss}` | Total cache misses. |
| `php.opcache.hit_rate` | Gauge | `%` | Cache hit rate percentage. |
| `php.opcache.cached_scripts` | Gauge | `{script}` | Number of scripts currently in cache. |
| `php.opcache.interned_strings.memory_used` | UpDownCounter | `By` | Memory used by interned strings. |
| `php.opcache.interned_strings.memory_free` | UpDownCounter | `By` | Free memory in the interned strings buffer. |
| `php.opcache.interned_strings.count` | Gauge | `{string}` | Number of interned strings currently stored. |

### CPU (`cpu`)

Registered only on platforms where `getrusage()` is available (Linux, macOS, Windows).

| Metric | Type | Unit | Description |
|--------|------|------|-------------|
| `process.cpu.time` | Counter | `s` | CPU time consumed. Reported for `user` and `system` modes via the `cpu.mode` attribute. |
| `process.context_switches` | Counter | `{context_switch}` | Number of times the process has been context switched. Reported for `voluntary` and `involuntary` switches via the `process.context_switch.type` attribute. |

## Configuration

### Disable the entire package

```shell
OTEL_PHP_DISABLED_INSTRUMENTATIONS=metrics-runtime
```

### Disable individual metric groups

**Option 1: via `OTEL_PHP_DISABLED_INSTRUMENTATIONS`** — lightweight, no config file needed. Each group is addressed as `metrics-runtime-{group}`:

```shell
# Disable OPcache and CPU metrics only
OTEL_PHP_DISABLED_INSTRUMENTATIONS=metrics-runtime-opcache,metrics-runtime-cpu

# Disable GC metrics only
OTEL_PHP_DISABLED_INSTRUMENTATIONS=metrics-runtime-gc
```

| Group | Instrumentation name |
|-------|----------------------|
| `memory` | `metrics-runtime-memory` |
| `gc` | `metrics-runtime-gc` |
| `opcache` | `metrics-runtime-opcache` |
| `cpu` | `metrics-runtime-cpu` |

**Option 2: via `OTEL_CONFIG_FILE`** — standard OTel SDK configuration, supports fine-grained control (e.g. filtering by attributes). Each group uses its own meter named `io.opentelemetry.contrib.php.runtime.{group}`, which can be targeted with meter configurators or views:

```yaml
# otel-config.yaml
meter_provider:
views:
- selector:
meter_name: io.opentelemetry.contrib.php.runtime.opcache
stream:
aggregation:
drop:
```

```shell
OTEL_CONFIG_FILE=/path/to/otel-config.yaml
```

## Maintainer

[@intuibase](https://github.com/intuibase)
14 changes: 14 additions & 0 deletions src/Metrics/Runtime/_register.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

declare(strict_types=1);

use Nevay\SPI\ServiceLoader;
use OpenTelemetry\API\Configuration\ConfigEnv\EnvComponentLoader;
use OpenTelemetry\API\Instrumentation\AutoInstrumentation\Instrumentation;
use OpenTelemetry\Contrib\Metrics\Runtime\InstrumentationConfigurationRuntimeMetricsConfig;
use OpenTelemetry\Contrib\Metrics\Runtime\RuntimeMetricsInstrumentation;

// @phan-suppress-next-line PhanTemplateTypeConstraintViolation
ServiceLoader::register(Instrumentation::class, RuntimeMetricsInstrumentation::class);
// @phan-suppress-next-line PhanTemplateTypeConstraintViolation
ServiceLoader::register(EnvComponentLoader::class, InstrumentationConfigurationRuntimeMetricsConfig::class);
Loading
Loading