Skip to content

Commit

Permalink
Merge pull request #11 from worksome/feature/otel-config
Browse files Browse the repository at this point in the history
feat: update to entirely rely on OTEL config
  • Loading branch information
owenvoke authored Jul 25, 2024
2 parents 3e1ea57 + cda0794 commit d2d5146
Show file tree
Hide file tree
Showing 11 changed files with 178 additions and 115 deletions.
3 changes: 1 addition & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
stability: [prefer-lowest, prefer-stable]
include:
- laravel: 10.*
testbench: 8.*
testbench: ^8.21
- laravel: 11.*
testbench: 9.*

Expand All @@ -34,7 +34,6 @@ jobs:
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick, fileinfo
coverage: none

- name: Setup problem matchers
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ You can publish the config file with:
```shell
php artisan vendor:publish --tag="laravel-telemetry-config"
```

## Usage

This package will work out of the box with a default OTLP exporter configuration.
Expand Down
17 changes: 17 additions & 0 deletions UPGRADE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Upgrade Guide

## Upgrading To 0.5 From 0.4.x

### Configuration Changes

The `telemetry.enabled` configuration key has been moved and negated to `sdk.disabled` to match the underlying
OpenTelemetry SDK.

Please update this value in your configuration if it has been published.

```diff
- 'enabled' => env('LARAVEL_TELEMETRY_ENABLED', true),
+ 'sdk' => [
+ 'disabled' => ! env('LARAVEL_TELEMETRY_ENABLED', true)
+ ],
```
15 changes: 8 additions & 7 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,20 @@
],
"require": {
"php": "^8.2",
"illuminate/contracts": "^10.0 || ^11.0",
"illuminate/contracts": "^10.48 || ^11.0",
"open-telemetry/api": "^1.0.3",
"open-telemetry/sdk": "^1.0.8",
"open-telemetry/exporter-otlp": "^1.0.3",
"open-telemetry/exporter-otlp": "^1.0.4",
"php-http/guzzle7-adapter": "^1.0"
},
"require-dev": {
"google/protobuf": "^3.25",
"larastan/larastan": "^2.9",
"nunomaduro/collision": "^7.10 || ^8.1",
"larastan/larastan": "^2.8",
"orchestra/testbench": "^8.15 || ^9.0",
"pestphp/pest": "^2.33",
"pestphp/pest-plugin-laravel": "^2.2",
"worksome/coding-style": "^2.8"
"orchestra/testbench": "^8.21.1 || ^9.2",
"pestphp/pest": "^2.34",
"pestphp/pest-plugin-laravel": "^2.4",
"worksome/coding-style": "^2.11"
},
"autoload": {
"psr-4": {
Expand Down
4 changes: 3 additions & 1 deletion config/telemetry.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
// The Configuration is based on OpenTelemetry's naming convention.
return [

'enabled' => env('LARAVEL_TELEMETRY_ENABLED', true),
'sdk' => [
'disabled' => ! env('LARAVEL_TELEMETRY_ENABLED', true)
],

'exporter' => [
'otlp' => [
Expand Down
26 changes: 11 additions & 15 deletions src/ConfigConfigurationResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,28 @@

namespace Worksome\LaravelTelemetry;

use Illuminate\Contracts\Config\Repository;
use OpenTelemetry\SDK\Common\Configuration\Resolver\ResolverInterface;

/**
* Resolves Open Telemetry configuration via Laravel config.
*
* The `config()` function is intentionally used to ensure that the latest config is always used.
*/
class ConfigConfigurationResolver implements ResolverInterface
{
private const PREFIX = 'telemetry';

public function __construct(
private readonly Repository $config,
) {
public function retrieveValue(string $variableName)
{
return config()->get($this->variableNameToConfigKey($variableName));
}

private function getKey(string $variableName): string
public function hasVariable(string $variableName): bool
{
return config()->has($this->variableNameToConfigKey($variableName));
}

private function variableNameToConfigKey(string $variableName): string
{
$names = collect(explode('_', $variableName))
->map(fn(string $key) => strtolower($key))
Expand All @@ -28,14 +34,4 @@ private function getKey(string $variableName): string

return implode('.', [self::PREFIX, ...$names]);
}

public function retrieveValue(string $variableName)
{
return $this->config->get($this->getKey($variableName));
}

public function hasVariable(string $variableName): bool
{
return $this->config->has($this->getKey($variableName));
}
}
85 changes: 25 additions & 60 deletions src/LaravelTelemetryServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,14 @@
use Illuminate\Queue\Events\WorkerStopping;
use Illuminate\Support\ServiceProvider;
use OpenTelemetry\API\LoggerHolder;
use OpenTelemetry\API\Logs\LoggerProviderInterface;
use OpenTelemetry\API\Metrics\MeterProviderInterface;
use OpenTelemetry\API\Trace\TracerProviderInterface;
use OpenTelemetry\SDK\Common\Attribute\Attributes;
use OpenTelemetry\SDK\Common\Configuration\Resolver\CompositeResolver;
use OpenTelemetry\SDK\Common\Instrumentation\InstrumentationScopeFactory;
use OpenTelemetry\SDK\Common\Time\ClockFactory;
use OpenTelemetry\SDK\Metrics\Exemplar\ExemplarFilter\WithSampledTraceExemplarFilter;
use OpenTelemetry\SDK\Metrics\MeterProvider;
use OpenTelemetry\SDK\Logs\LoggerProviderFactory;
use OpenTelemetry\SDK\Logs\LoggerProviderInterface as LoggerProviderSdkInterface;
use OpenTelemetry\SDK\Metrics\MeterProviderFactory;
use OpenTelemetry\SDK\Metrics\MeterProviderInterface as MeterProviderSdkInterface;
use OpenTelemetry\SDK\Metrics\MetricReader\ExportingReader;
use OpenTelemetry\SDK\Metrics\StalenessHandler\NoopStalenessHandlerFactory;
use OpenTelemetry\SDK\Metrics\View\CriteriaViewRegistry;
use OpenTelemetry\SDK\Registry;
use OpenTelemetry\SDK\Resource\ResourceInfoFactory;
use OpenTelemetry\SDK\Trace\TracerProviderFactory;
use OpenTelemetry\SDK\Trace\TracerProviderInterface as TracerProviderSdkInterface;
use Psr\Log\LoggerInterface;
Expand All @@ -31,35 +25,19 @@ class LaravelTelemetryServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->app->singleton(MeterProviderSdkInterface::class, function () {
if (! $this->app->make(Repository::class)->get('telemetry.enabled')) {
return null;
}
$this->app->singleton(LoggerProviderSdkInterface::class, function () {
return (new LoggerProviderFactory())->create();
});

return new MeterProvider(
null,
ResourceInfoFactory::defaultResource(),
ClockFactory::getDefault(),
Attributes::factory(),
new InstrumentationScopeFactory(Attributes::factory()),
[
new ExportingReader(
Registry::metricExporterFactory('otlp')->create(),
),
],
new CriteriaViewRegistry(),
new WithSampledTraceExemplarFilter(),
new NoopStalenessHandlerFactory(),
);
$this->app->bind(LoggerProviderInterface::class, LoggerProviderSdkInterface::class);

$this->app->singleton(MeterProviderSdkInterface::class, function () {
return (new MeterProviderFactory())->create();
});

$this->app->bind(MeterProviderInterface::class, MeterProviderSdkInterface::class);

$this->app->singleton(TracerProviderSdkInterface::class, function () {
if (! $this->app->make(Repository::class)->get('telemetry.enabled')) {
return null;
}

return (new TracerProviderFactory())->create();
});

Expand All @@ -77,42 +55,18 @@ public function boot(): void
'telemetry',
);

$this->app->beforeResolving(MeterProviderInterface::class, function () {
if (! $this->app->make(Repository::class)->get('telemetry.enabled')) {
return;
}

/** @var LoggerInterface $logger */
$logger = $this->app->get(LoggerInterface::class);
/** @var ConfigConfigurationResolver $configResolver */
$configResolver = $this->app->get(ConfigConfigurationResolver::class);
LoggerHolder::set($logger);
CompositeResolver::instance()->addResolver($configResolver);
});

$this->app->beforeResolving(TracerProviderInterface::class, function () {
if (! $this->app->make(Repository::class)->get('telemetry.enabled')) {
return;
}

/** @var LoggerInterface $logger */
$logger = $this->app->get(LoggerInterface::class);
/** @var ConfigConfigurationResolver $configResolver */
$configResolver = $this->app->get(ConfigConfigurationResolver::class);
LoggerHolder::set($logger);
CompositeResolver::instance()->addResolver($configResolver);
});
$this->prepareConfigResolver();

$this->callAfterResolving(Dispatcher::class, function (Dispatcher $event) {
if (! $this->app->make(Repository::class)->get('telemetry.enabled')) {
if ($this->app->make(Repository::class)->get('telemetry.sdk.disabled')) {
return;
}

$event->listen(WorkerStopping::class, WorkerStoppingFlush::class);
});

$this->app->terminating(function () {
if (! $this->app->make(Repository::class)->get('telemetry.enabled')) {
if ($this->app->make(Repository::class)->get('telemetry.sdk.disabled')) {
return;
}

Expand All @@ -129,4 +83,15 @@ public function boot(): void
}
});
}

private function prepareConfigResolver(): void
{
/** @var LoggerInterface $logger */
$logger = $this->app->get(LoggerInterface::class);

LoggerHolder::set($logger);
CompositeResolver::instance()->addResolver(
new ConfigConfigurationResolver()
);
}
}
5 changes: 0 additions & 5 deletions tests/ExampleTest.php

This file was deleted.

105 changes: 105 additions & 0 deletions tests/Feature/LaravelTelemetryServiceProviderTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
<?php

declare(strict_types=1);

use Illuminate\Contracts\Config\Repository as ConfigRepository;
use OpenTelemetry\API\Logs\LoggerInterface;
use OpenTelemetry\API\Logs\LoggerProviderInterface;
use OpenTelemetry\API\Logs\NoopLogger;
use OpenTelemetry\API\Metrics\MeterInterface;
use OpenTelemetry\API\Metrics\MeterProviderInterface;
use OpenTelemetry\API\Metrics\Noop\NoopMeter;
use OpenTelemetry\API\Trace\NoopTracer;
use OpenTelemetry\API\Trace\TracerInterface;
use OpenTelemetry\API\Trace\TracerProviderInterface;
use OpenTelemetry\SDK\Logs\LoggerProviderInterface as LoggerProviderSdkInterface;
use OpenTelemetry\SDK\Metrics\MeterProviderInterface as MeterProviderSdkInterface;
use OpenTelemetry\SDK\Sdk;
use OpenTelemetry\SDK\Trace\TracerProviderInterface as TracerProviderSdkInterface;

it('registers the default config', function () {
/** @var ConfigRepository $config */
$config = $this->app->get(ConfigRepository::class);

expect($config->get('telemetry'))
->sdk->disabled->toBeFalse()
->exporter->otlp->endpoint->toBe('http://localhost:4318');
});

it('can update the config at runtime', function () {
/** @var ConfigRepository $config */
$config = $this->app->get(ConfigRepository::class);

expect(Sdk::isDisabled())->toBeFalse();

$config->set('telemetry.sdk.disabled', true);

expect(Sdk::isDisabled())->toBeTrue();
});

it('registers the logger provider', function () {
expect(
$this->app->get(LoggerProviderInterface::class)
)
->toBeInstanceOf(LoggerProviderInterface::class)
->toBeInstanceOf(LoggerProviderSdkInterface::class);
});

it('registers the metric provider', function () {
expect(
$this->app->get(MeterProviderInterface::class)
)
->toBeInstanceOf(MeterProviderInterface::class)
->toBeInstanceOf(MeterProviderSdkInterface::class);
});

it('registers the tracer provider', function () {
expect(
$this->app->get(TracerProviderInterface::class)
)
->toBeInstanceOf(TracerProviderInterface::class)
->toBeInstanceOf(TracerProviderSdkInterface::class);
});

it('can get a logger from the logger provider', function () {
expect(
$this->app->get(LoggerProviderInterface::class)->getLogger('test')
)
->toBeInstanceOf(LoggerInterface::class);
});

it('can get a meter from the meter provider', function () {
expect(
$this->app->get(MeterProviderInterface::class)->getMeter('test')
)
->toBeInstanceOf(MeterInterface::class);
});

it('can get a tracer from the tracer provider', function () {
expect(
$this->app->get(TracerProviderInterface::class)->getTracer('test')
)
->toBeInstanceOf(TracerInterface::class);
});

it('returns no-op instances when the SDK is disabled', function () {
/** @var ConfigRepository $config */
$config = $this->app->get(ConfigRepository::class);

$config->set('telemetry.sdk.disabled', true);

expect(
$this->app->get(LoggerProviderInterface::class)->getLogger('test')
)
->toBeInstanceOf(NoopLogger::class);

expect(
$this->app->get(MeterProviderInterface::class)->getMeter('test')
)
->toBeInstanceOf(NoopMeter::class);

expect(
$this->app->get(TracerProviderInterface::class)->getTracer('test')
)
->toBeInstanceOf(NoopTracer::class);
});
4 changes: 3 additions & 1 deletion tests/Pest.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

use Worksome\LaravelTelemetry\Tests\TestCase;

uses(TestCase::class)->in(__DIR__);
uses(TestCase::class)->in('Feature');
Loading

0 comments on commit d2d5146

Please sign in to comment.