Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: use NoopTracerProvider if Sdk is disabled #55

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
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
1 change: 0 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
"require-dev": {
"doctrine/dbal": "^3.0",
"friends-of-phpspec/phpspec-expect": "^4.0",
"open-telemetry/transport-grpc": "^1.0",
"open-telemetry/gen-otlp-protobuf": "^1.0",
"php-http/httplug": "^2.3",
"phpspec/phpspec": "^7.5",
Expand Down
35 changes: 21 additions & 14 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,30 @@
### Features

#### Tracing

- Using the official [OpenTelemetry SDK](https://github.com/open-telemetry/opentelemetry-php)
- Minimal auto-instrumentation for **requests**, console **commands**, **consumers** and **doctrine**
- Trace context propagation from incoming **requests** to **consumers**, **outgoing http calls** and **databases** (using [`sqlcommenter`](https://google.github.io/sqlcommenter/))
- Configurable blacklisting of requests by path to avoid useless traces, eg. `/metrics` or `/_healthz`
- Automatic log inclusion with configurable log level and channels

#### Metrics

- Using the [Prometheus Client](https://github.com/PromPHP/prometheus_client_php)
- Minimal auto-instrumentation for common **request**, **consumer** and **message** metrics (see list of provided [default metrics](./docs/metrics/default-metrics.md)).
- Autoconfigurable metric providers, see below.

#### Logging

- Adds trace context to logs for correlation, with customizable keys.

#### Baggage

- Using the official [OpenTelemetry SDK](https://github.com/open-telemetry/opentelemetry-php)
- Baggage propagation from incoming **requests** to **consumers** and **outgoing http calls**

#### Health

- A simple endpoint to expose application health (default: `/_healthz`)
- Autoconfigurable healthcheck interface to add healthchecks to be made for global application health

Expand All @@ -30,7 +35,9 @@
```sh
composer require worldia/instrumentation-bundle <your-exporter>
```

You will aso need to install an [exporter implementation](https://packagist.org/packages/open-telemetry/exporter-otlp?query=open-telemetry%2Fexporter-) and `APCu` is required by the prometheus exporter.

```

Add to ```bundles.php```:
Expand All @@ -41,8 +48,8 @@ return [
];
```

**Minimal configuration**
See the complete [configuration reference here](./docs/config-reference.md) or run ```bin/console config:dump-reference instrumentation```.
**Minimal configuration**
See the complete [configuration reference here](./docs/config-reference.md) or run `bin/console config:dump-reference instrumentation`.

```yaml
// docker-compose.yaml
Expand Down Expand Up @@ -71,18 +78,18 @@ instrumentation: ~
### Usage

- **Tracing**
- [Simple tracing example](./docs/tracing/simple-trace.md)
- [Simple tracing example using the static API](./docs/tracing/static-usage.md)
- [Add Urls to your traces in error messages](./docs/tracing/add-urls-to-your-traces.md)
- [Use upstream request id as trace id](./docs/tracing/upstream-request-id.md)
- [Customize operation (span) name for a message](./docs/tracing/custom-operation-name-for-message.md)
- [Link strategy for a message](./docs/tracing/link-strategy-for-message.md)
- [Propagating trace/baggage context in HTTP requests](./docs/tracing/propagating-context.md)
- [Add request / response bodies as span attributes for HTTP requests](./docs/tracing/message-bodies.md)
- [Simple tracing example](./docs/tracing/simple-trace.md)
- [Simple tracing example using the static API](./docs/tracing/static-usage.md)
- [Add Urls to your traces in error messages](./docs/tracing/add-urls-to-your-traces.md)
- [Use upstream request id as trace id](./docs/tracing/upstream-request-id.md)
- [Customize operation (span) name for a message](./docs/tracing/custom-operation-name-for-message.md)
- [Link strategy for a message](./docs/tracing/link-strategy-for-message.md)
- [Propagating trace/baggage context in HTTP requests](./docs/tracing/propagating-context.md)
- [Add request / response bodies as span attributes for HTTP requests](./docs/tracing/message-bodies.md)
- **Metrics**
- [Adding a metric](./docs/metrics/adding-a-metric.md)
- [Using Redis as storage adapter](./docs/metrics/using-redis-as-storage.md) (Recommended)
- [Adding a metric](./docs/metrics/adding-a-metric.md)
- [Using Redis as storage adapter](./docs/metrics/using-redis-as-storage.md) (Recommended)
- **Logging**
- [Customizing trace context log keys](./docs/logging/custom-keys.md)
- [Customizing trace context log keys](./docs/logging/custom-keys.md)
- **Health**
- [Adding a healthcheck](./docs/health/adding-a-healthcheck.md)
- [Adding a healthcheck](./docs/health/adding-a-healthcheck.md)
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ public function it_gets_controller_and_route(ResourceInfo $info, AttributesInter
private function getMinimalInfo(): array
{
return [
'db_driver' => sprintf('doctrine/dbal-%s', InstalledVersions::getVersion('doctrine/dbal')),
'framework' => sprintf('symfony-%s', Kernel::VERSION),
'db_driver' => \sprintf('doctrine/dbal-%s', InstalledVersions::getVersion('doctrine/dbal')),
'framework' => \sprintf('symfony-%s', Kernel::VERSION),
];
}
}
40 changes: 21 additions & 19 deletions src/Bridge/GoogleCloud/Logging/Formatter/StdOutFormatter.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,30 +45,32 @@ protected function normalize(mixed $data, int $depth = 0): mixed
$data['severity'] = $data['level_name'];
unset($data['level_name']);

// Map tracing
if (isset($data['context']['trace'])) {
$data['logging.googleapis.com/trace'] = 'projects/'.$this->project.'/traces/'.$data['context']['trace'];
}
if (isset($data['context']['span'])) {
$data['logging.googleapis.com/spanId'] = $data['context']['span'];
}
if (isset($data['context']['sampled'])) {
$data['logging.googleapis.com/trace_sampled'] = $data['context']['sampled'];
}
if (isset($data['context']['operation'])) {
$data['logging.googleapis.com/operation'] = $data['context']['operation'];
if (\is_array($data['context'])) {
// Map tracing
if (isset($data['context']['trace'])) {
$data['logging.googleapis.com/trace'] = 'projects/'.$this->project.'/traces/'.$data['context']['trace'];
}
if (isset($data['context']['span'])) {
$data['logging.googleapis.com/spanId'] = $data['context']['span'];
}
if (isset($data['context']['sampled'])) {
$data['logging.googleapis.com/trace_sampled'] = $data['context']['sampled'];
}
if (isset($data['context']['operation'])) {
$data['logging.googleapis.com/operation'] = $data['context']['operation'];
}
unset($data['context']['trace'], $data['context']['span'], $data['context']['sampled'], $data['context']['operation']);

if ($exception = $data['context']['exception'] ?? false) {
$data['message'] = $exception['message'];
$data['context'] = array_merge($data['context'], $exception['context']);
unset($data['context']['exception']);
}
}
unset($data['context']['trace'], $data['context']['span'], $data['context']['sampled'], $data['context']['operation']);

// Map channel
$data['logging.googleapis.com/labels'] = ['channel' => $data['channel']];
unset($data['channel']);

if ($exception = $data['context']['exception'] ?? false) {
$data['message'] = $exception['message'];
$data['context'] = array_merge($data['context'], $exception['context']);
unset($data['context']['exception']);
}
}

return $data;
Expand Down
4 changes: 2 additions & 2 deletions src/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public function getConfigTreeBuilder(): TreeBuilder
->scalarPrototype()->end()
->beforeNormalization()
->ifTrue(fn ($v) => !isset($v[ResourceAttributes::SERVICE_NAME]))
->thenInvalid(sprintf('You must provide the "%s" attribute in resource info.', ResourceAttributes::SERVICE_NAME))
->thenInvalid(\sprintf('You must provide the "%s" attribute in resource info.', ResourceAttributes::SERVICE_NAME))
->end()
->end()

Expand Down Expand Up @@ -91,7 +91,7 @@ public function getConfigTreeBuilder(): TreeBuilder
->enumNode('level')
->defaultValue(Level::Info)
->values(Level::cases())
->info(sprintf('One of the %s levels.', Level::class))
->info(\sprintf('One of the %s levels.', Level::class))
->end()
->arrayNode('channels')
->defaultValue([])
Expand Down
12 changes: 6 additions & 6 deletions src/DependencyInjection/Extension.php
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ public function process(ContainerBuilder $container): void

foreach ($providedMetrics as $name => $metric) {
if (isset($metrics[$name])) {
throw new \RuntimeException(sprintf('A metric named %s is already registered.', $name));
throw new \RuntimeException(\sprintf('A metric named %s is already registered.', $name));
}
$metrics[$name] = $metric;
}
Expand All @@ -133,13 +133,13 @@ public function process(ContainerBuilder $container): void
}

foreach ($connectionsToTrace as $connection) {
$serviceId = sprintf('doctrine.dbal.%s_connection', $connection);
$serviceId = \sprintf('doctrine.dbal.%s_connection', $connection);

if (!\in_array($serviceId, $connections, true)) {
throw new \InvalidArgumentException(sprintf('No such connection: "%s".', $connection));
throw new \InvalidArgumentException(\sprintf('No such connection: "%s".', $connection));
}

$configDef = $container->getDefinition(sprintf('%s.configuration', $serviceId));
$configDef = $container->getDefinition(\sprintf('%s.configuration', $serviceId));

$middlewares = [];
foreach ($configDef->getMethodCalls() as $call) {
Expand Down Expand Up @@ -252,7 +252,7 @@ protected function loadTracing(array $config, ContainerBuilder $container): void

foreach (['blacklist', 'methods'] as $property) {
if (isset($config[$feature][$property])) {
$container->setParameter(sprintf('tracing.%s.%s', $feature, $property), $config[$feature][$property]);
$container->setParameter(\sprintf('tracing.%s.%s', $feature, $property), $config[$feature][$property]);
}
}
}
Expand Down Expand Up @@ -281,7 +281,7 @@ protected function loadLogging(array $config, ContainerBuilder $container): void
}

foreach ($config['handlers'] as $handler) {
if ($container->hasDefinition(sprintf('monolog.handler.%s', $handler))) {
if ($container->hasDefinition(\sprintf('monolog.handler.%s', $handler))) {
$container->getDefinition(\Instrumentation\Logging\Processor\TraceContextProcessor::class)
->addTag('monolog.processor', ['handler' => $handler]);
}
Expand Down
7 changes: 7 additions & 0 deletions src/DependencyInjection/config/tracing/tracing.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use Instrumentation\Tracing\Propagation\RegexIncomingTraceHeaderResolver;
use Instrumentation\Tracing\Sampling\TogglableSampler;
use Instrumentation\Tracing\Serializer\Normalizer\ErrorNormalizer;
use Instrumentation\Tracing\TogglableTracerProvider;
use Instrumentation\Tracing\TraceUrlGeneratorInterface;
use Instrumentation\Tracing\Twig\Extension\TracingExtension;
use OpenTelemetry\API\Trace\TracerProviderInterface;
Expand Down Expand Up @@ -90,6 +91,12 @@
])
->public()

->set(TogglableTracerProvider::class)
->decorate(TracerProviderInterface::class)
->args([
service('.inner'),
])

->set(MainSpanContextInterface::class, MainSpanContext::class)

->set(TracingHandler::class)
Expand Down
2 changes: 1 addition & 1 deletion src/Http/HttpMessageHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public static function formatHeadersForSpanAttribute(array $headers): string
$lines = [];
foreach ($headers as $name => $values) {
foreach ($values as $value) {
$lines[] = sprintf('%s: %s', mb_strtolower($name), $value);
$lines[] = \sprintf('%s: %s', mb_strtolower($name), $value);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/Http/TracedResponse.php
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ public static function stream(HttpClientInterface $client, iterable $responses,

foreach ($responses as $r) {
if (!$r instanceof self) {
throw new \TypeError(sprintf('"%s::stream()" expects parameter 1 to be an iterable of TracedResponse objects, "%s" given.', TracingHttpClient::class, get_debug_type($r)));
throw new \TypeError(\sprintf('"%s::stream()" expects parameter 1 to be an iterable of TracedResponse objects, "%s" given.', TracingHttpClient::class, get_debug_type($r)));
}

$traceableMap[$r->response] = $r;
Expand Down
2 changes: 1 addition & 1 deletion src/Http/TracingHttpClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ protected function getExtraSpanAttributes(array|null $attributes): array
}

if (!\is_array($attributes)) {
throw new \RuntimeException(sprintf('Extra span attributes must be a comma separated list of attributes or an array. %s given.', get_debug_type($attributes)));
throw new \RuntimeException(\sprintf('Extra span attributes must be a comma separated list of attributes or an array. %s given.', get_debug_type($attributes)));
}

return $attributes;
Expand Down
7 changes: 5 additions & 2 deletions src/Metrics/CounterAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

class CounterAdapter implements CounterInterface
{
use IterableAttributesTrait;

public function __construct(
private string $name,
private string $description,
Expand All @@ -20,11 +22,12 @@ public function __construct(
}

/**
* @param int $amount
* @param array{labels: array<string,mixed>} $attributes
* @param int $amount
* @param iterable<string,array<string,mixed>> $attributes
*/
public function add($amount, iterable $attributes = [], $context = null): void
{
$attributes = $this->normalizeAttributes($attributes);
/** @var array<string> $labelNames */
$labelNames = array_keys($attributes['labels'] ?? []);
$labelValues = array_values($attributes['labels'] ?? []);
Expand Down
2 changes: 1 addition & 1 deletion src/Metrics/EventSubscriber/RequestEventSubscriber.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public function onTerminate(Event\TerminateEvent $event): void
}

$time = microtime(true) - $event->getRequest()->server->get('REQUEST_TIME_FLOAT');
$code = sprintf('%sxx', substr((string) $event->getResponse()->getStatusCode(), 0, 1));
$code = \sprintf('%sxx', substr((string) $event->getResponse()->getStatusCode(), 0, 1));
$operation = $this->mainSpanContext?->getOperationName() ?: 'unknown';

$this->registry->getGauge('requests_handling')->dec();
Expand Down
3 changes: 3 additions & 0 deletions src/Metrics/HistogramAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

class HistogramAdapter implements HistogramInterface
{
use IterableAttributesTrait;

public function __construct(
private string $name,
private string $description,
Expand All @@ -25,6 +27,7 @@ public function __construct(
*/
public function record($amount, iterable $attributes = [], $context = null): void
{
$attributes = $this->normalizeAttributes($attributes);
/** @var array<string> $labelNames */
$labelNames = array_keys($attributes['labels'] ?? []);
$labelValues = array_values($attributes['labels'] ?? []);
Expand Down
26 changes: 26 additions & 0 deletions src/Metrics/IterableAttributesTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

/*
* This file is part of the worldia/instrumentation-bundle package.
* (c) Worldia <[email protected]>
*/

namespace Instrumentation\Metrics;

trait IterableAttributesTrait
{
/**
* @param iterable<string,array<string,mixed>> $attributes
*
* @return array<string,array<string,mixed>>
*/
public function normalizeAttributes(iterable $attributes): array
{
$newAttr = [];
foreach ($attributes as $key => $value) {
$newAttr[$key] = $value;
}

return $newAttr;
}
}
8 changes: 4 additions & 4 deletions src/Metrics/Meter.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public function createCounter(string $name, string|null $unit = null, string|nul
*/
public function createObservableCounter(string $name, string|null $unit = null, string|null $description = null, $advisory = [], callable ...$callbacks): ObservableCounterInterface
{
throw new \LogicException(sprintf('Method %s is not implemented', __METHOD__));
throw new \LogicException(\sprintf('Method %s is not implemented', __METHOD__));
}

/**
Expand Down Expand Up @@ -95,7 +95,7 @@ public function createHistogram(string $name, string|null $unit = null, string|n
*/
public function createObservableGauge(string $name, string|null $unit = null, string|null $description = null, $advisory = [], callable ...$callbacks): ObservableGaugeInterface
{
throw new \LogicException(sprintf('Method %s is not implemented', __METHOD__));
throw new \LogicException(\sprintf('Method %s is not implemented', __METHOD__));
}

/**
Expand Down Expand Up @@ -131,14 +131,14 @@ public function createUpDownCounter(string $name, string|null $unit = null, stri
*/
public function createObservableUpDownCounter(string $name, string|null $unit = null, string|null $description = null, $advisory = [], callable ...$callbacks): ObservableUpDownCounterInterface
{
throw new \LogicException(sprintf('Method %s is not implemented', __METHOD__));
throw new \LogicException(\sprintf('Method %s is not implemented', __METHOD__));
}

public function batchObserve(
callable $callback,
AsynchronousInstrument $instrument,
AsynchronousInstrument ...$instruments
): ObservableCallbackInterface {
throw new \LogicException(sprintf('Method %s is not implemented', __METHOD__));
throw new \LogicException(\sprintf('Method %s is not implemented', __METHOD__));
}
}
2 changes: 1 addition & 1 deletion src/Metrics/Registry.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ private function getMetric(string $name): MetricDefinition
{
if (!isset($this->instantiated[$name])) {
if (!isset($this->metrics[$name])) {
throw new \InvalidArgumentException(sprintf('No metric registered with that name: "%s".', $name));
throw new \InvalidArgumentException(\sprintf('No metric registered with that name: "%s".', $name));
}

/**
Expand Down
Loading
Loading