Skip to content

Commit c8f22d1

Browse files
authored
Merge pull request #2 from MacPaw/feat/init-basic-version
Init basic version
2 parents f4152f6 + bb1fa9e commit c8f22d1

14 files changed

+588
-2
lines changed

.github/workflows/ci.yaml

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
name: CI
2+
3+
on:
4+
pull_request:
5+
push:
6+
branches: [ main, develop ]
7+
8+
jobs:
9+
run:
10+
runs-on: ubuntu-latest
11+
strategy:
12+
fail-fast: false
13+
matrix:
14+
php:
15+
- '8.3'
16+
coverage: ['none']
17+
symfony-versions:
18+
- '6.4.*'
19+
- '7.0.*'
20+
include:
21+
- description: 'Log Code Coverage'
22+
php: '8.3'
23+
symfony-versions: '^7.0'
24+
coverage: 'xdebug'
25+
26+
name: PHP ${{ matrix.php }} Symfony ${{ matrix.symfony-versions }} ${{ matrix.description }}
27+
steps:
28+
- name: Checkout
29+
uses: actions/checkout@v2
30+
31+
- uses: actions/cache@v2
32+
with:
33+
path: ~/.composer/cache/files
34+
key: ${{ matrix.php }}-${{ matrix.symfony-versions }}
35+
36+
- name: Setup PHP
37+
uses: shivammathur/setup-php@v2
38+
with:
39+
php-version: ${{ matrix.php }}
40+
coverage: ${{ matrix.coverage }}
41+
42+
- name: Add PHPUnit matcher
43+
run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
44+
45+
- name: Set composer cache directory
46+
id: composer-cache
47+
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
48+
49+
- name: Cache composer
50+
uses: actions/[email protected]
51+
with:
52+
path: ${{ steps.composer-cache.outputs.dir }}
53+
key: ${{ runner.os }}-${{ matrix.php }}-${{ matrix.symfony-versions }}-composer-${{ hashFiles('composer.json') }}
54+
restore-keys: ${{ runner.os }}-${{ matrix.php }}-${{ matrix.symfony-versions }}-composer
55+
56+
- name: Update Symfony version
57+
if: matrix.symfony-versions != ''
58+
run: |
59+
composer require symfony/dependency-injection:${{ matrix.symfony-versions }} --no-update --no-scripts
60+
composer require symfony/config:${{ matrix.symfony-versions }} --no-update --no-scripts
61+
composer require --dev symfony/yaml:${{ matrix.symfony-versions }} --no-update --no-scripts
62+
composer require symfony/http-kernel:${{ matrix.symfony-versions }} --no-update --no-scripts
63+
composer require open-telemetry/symfony-sdk-bundle --no-update --no-scripts
64+
- name: Install dependencies
65+
run: composer install --ignore-platform-req=ext-opentelemetry
66+
67+
- name: Run PHPUnit tests
68+
run: vendor/bin/phpunit
69+
if: matrix.coverage == 'none'
70+
71+
- name: PHPUnit tests and Log Code coverage
72+
run: vendor/bin/phpunit --coverage-clover=coverage.xml
73+
if: matrix.coverage == 'xdebug'
74+
75+
- name: Upload coverage reports to Codecov
76+
if: matrix.coverage == 'xdebug'
77+
uses: codecov/[email protected]
78+
with:
79+
token: ${{ secrets.CODECOV_TOKEN }}

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/vendor/

README.md

+77-2
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,77 @@
1-
# symfony-otel-bundle
2-
OpenTelemetry client Bundle
1+
# Symfony OpenTelemetry client Bundle
2+
This bundle provides configured official otel bundle for Symfony application under the hood.
3+
In addition, it provides a general way to configure telemetry collection via configuration list of Kernel listeners.
4+
5+
## Setup bundle
6+
Enable bundle in your Symfony application:
7+
```php
8+
return [
9+
Macpaw\SymfonyOtelBundle\SymfonyOtelBundle::class => ['all' => true],
10+
// ...
11+
];
12+
```
13+
14+
## Configuration understanding
15+
This bundle is a decoration under [https://github.com/opentelemetry-php/contrib-sdk-bundle](https://github.com/opentelemetry-php/contrib-sdk-bundle) to simplify integration with the official bundle and provide generic kernel listeners for data tracing.
16+
If you want to get more information about configuration, please refer to the official bundle documentation.
17+
18+
## Setup bundle
19+
20+
## Kernel event listeners
21+
Example of kernel event listener implementation can be found in `Macpaw\SymfonyOtelBundle\Span\ExecutionTimeSpanTracer` class.
22+
When specific listener need to be configured, you need to add it to `span_tracers` list in configuration after implementation.
23+
24+
### Example
25+
26+
1. Create a custom span tracer class:
27+
28+
```php
29+
namespace Macpaw\SymfonyOtelBundle\Span;
30+
31+
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
32+
use OpenTelemetry\API\Trace\Span;
33+
34+
class CustomSpanTracer implements EventSubscriberInterface
35+
{
36+
public function onKernelRequest(RequestEvent $event): void
37+
{
38+
$span = Span::startSpan('custom_span');
39+
// Add custom tracing logic here
40+
$span->end();
41+
}
42+
43+
public static function getSubscribedEvents(): array
44+
{
45+
return [
46+
KernelEvents::REQUEST => 'onKernelRequest',
47+
];
48+
}
49+
}
50+
```
51+
52+
2. Register the custom span tracer in the Symfony configuration:
53+
54+
```yaml
55+
# config/packages/symfony_otel.yaml
56+
symfony_otel:
57+
span_tracers:
58+
- App\Span\CustomSpanTracer
59+
```
60+
61+
## Environment Variables
62+
63+
This bundle supports the following OpenTelemetry SDK environment variables for configuration:
64+
65+
- `OTEL_RESOURCE_ATTRIBUTES`: Key-value pairs to be used as resource attributes.
66+
- `OTEL_SERVICE_NAME`: The name of the service.
67+
- `OTEL_TRACES_EXPORTER`: The exporter to be used for traces.
68+
- `OTEL_METRICS_EXPORTER`: The exporter to be used for metrics.
69+
- `OTEL_LOGS_EXPORTER`: The exporter to be used for logs.
70+
- `OTEL_EXPORTER_OTLP_ENDPOINT`: The endpoint for the OTLP exporter.
71+
- `OTEL_EXPORTER_OTLP_HEADERS`: Headers to be sent with each OTLP request.
72+
- `OTEL_EXPORTER_OTLP_TIMEOUT`: Timeout for OTLP requests.
73+
- `OTEL_PROPAGATORS`: Propagators to be used for context propagation.
74+
- `OTEL_TRACES_SAMPLER`: The sampler to be used for traces.
75+
- `OTEL_TRACES_SAMPLER_ARG`: Arguments for the trace sampler.
76+
77+
For a complete list and detailed descriptions, please refer to the [OpenTelemetry SDK Environment Variables documentation](https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables/).

Resources/config/otel_bundle.yml

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
otel_bundle:
2+
tracer_name: '%env(OTEL_TRACER_NAME)%'
3+
service_name: '%env(OTEL_SERVICE_NAME)%'
4+
span_tracers:
5+
- { class: 'Macpaw\SymfonyOtelBundle\Span\ExecutionTimeSpanTracer', tag: 'kernel.event_subscriber' }

Resources/config/services.yml

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
services:
2+
_defaults:
3+
autowire: true
4+
autoconfigure: true
5+
6+
Macpaw\SymfonyOtelBundle\Service\TraceService:
7+
arguments:
8+
$tracerProvider: '@OpenTelemetry\SDK\Trace\TracerProviderInterface'
9+
10+
otpl.tracerProviderFactory:
11+
class: OpenTelemetry\SDK\Trace\TracerProviderFactory
12+
13+
OpenTelemetry\SDK\Trace\TracerProviderInterface:
14+
class: OpenTelemetry\SDK\Trace\TracerProvider
15+
factory: [ '@otpl.tracerProviderFactory', 'create' ]
16+
tags:
17+
- { name: 'otpl.tracerProvider' }
18+
19+
OpenTelemetry\Context\Propagation\TextMapPropagatorInterface:
20+
factory: [ '@OpenTelemetry\SDK\Propagation\PropagatorFactory', 'create' ]
21+
22+
OpenTelemetry\SDK\Propagation\PropagatorFactory:
23+
class: OpenTelemetry\SDK\Propagation\PropagatorFactory

composer.json

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{
2+
"name": "macpaw/symfony-otel-bundle",
3+
"type": "library",
4+
"license": "MIT",
5+
"autoload": {
6+
"psr-4": {
7+
"Macpaw\\SymfonyOtelBundle\\": "src/"
8+
}
9+
},
10+
"authors": [
11+
{
12+
"name": "Taras Ziabukhin",
13+
"email": "[email protected]"
14+
}
15+
],
16+
"minimum-stability": "stable",
17+
"require": {
18+
"php": "^8.2",
19+
"ext-opentelemetry": "*",
20+
"symfony/dependency-injection": "^6.4|^7.0",
21+
"symfony/config": "^6.4|^7.0",
22+
"symfony/yaml": "^6.4|^7.0",
23+
"open-telemetry/symfony-sdk-bundle": "^0.0.25",
24+
"symfony/http-client": "^6.4|^7.0",
25+
"nyholm/psr7": "^1.8",
26+
"guzzlehttp/promises": "^2.0",
27+
"php-http/httplug": "^2.4",
28+
"symfony/http-kernel": "^6.4|^7.0"
29+
},
30+
"config": {
31+
"allow-plugins": {
32+
"php-http/discovery": true,
33+
"tbachert/spi": true
34+
}
35+
},
36+
"require-dev": {
37+
"phpunit/phpunit": "^11.4"
38+
}
39+
}

phpunit.xml

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!-- https://phpunit.readthedocs.io/en/latest/configuration.html -->
3+
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.1/phpunit.xsd"
5+
bootstrap="vendor/autoload.php"
6+
colors="true">
7+
<php>
8+
<env name="OTEL_PROPAGATORS" value="baggage,tracecontext"/>
9+
</php>
10+
<testsuites>
11+
<testsuite name="Unit Tests">
12+
<directory>tests</directory>
13+
</testsuite>
14+
</testsuites>
15+
<source>
16+
<include>
17+
<directory>./src/Service</directory>
18+
<directory>./src/Span</directory>
19+
</include>
20+
<exclude>
21+
<directory suffix="Exception.php">src</directory>
22+
<directory suffix="php">src/DependencyInjection</directory>
23+
</exclude>
24+
</source>
25+
</phpunit>
+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
namespace Macpaw\SymfonyOtelBundle\DependencyInjection;
4+
5+
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
6+
use Symfony\Component\Config\Definition\ConfigurationInterface;
7+
8+
class Configuration implements ConfigurationInterface
9+
{
10+
11+
public function getConfigTreeBuilder(): TreeBuilder
12+
{
13+
$tree = new TreeBuilder(SymfonyOtelExtension::NAME);
14+
$rootNode = $tree->getRootNode();
15+
16+
$rootNode
17+
->children()
18+
->scalarNode('tracer_name')
19+
->cannotBeEmpty()
20+
->defaultValue('test-template')
21+
->end()
22+
->scalarNode('service_name')
23+
->cannotBeEmpty()
24+
->defaultValue('test-service')
25+
->end()
26+
->arrayNode('span_tracers')
27+
->defaultValue([])
28+
->arrayPrototype()
29+
->children()
30+
->scalarNode('class')->isRequired()->end()
31+
->scalarNode('tag')->isRequired()->end()
32+
->end()
33+
->end()
34+
->end()
35+
->end();
36+
37+
return $tree;
38+
}
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Macpaw\SymfonyOtelBundle\DependencyInjection;
6+
7+
use Macpaw\SymfonyOtelBundle\Span\ExecutionTimeSpanTracer;
8+
use Symfony\Component\DependencyInjection\ContainerBuilder;
9+
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
10+
use Symfony\Component\DependencyInjection\Definition;
11+
12+
class SymfonyOtelCompilerPass implements CompilerPassInterface
13+
{
14+
15+
public function process(ContainerBuilder $container): void
16+
{
17+
$listeners = $container->getParameter('otel_bundle.span_tracers');
18+
19+
foreach ($listeners as $listener) {
20+
$definition = $container->hasDefinition($listener['class']) ?
21+
$container->getDefinition($listener['class']) : new Definition($listener['class']);
22+
23+
$definition->setAutowired(true);
24+
25+
if ($listener['class'] === ExecutionTimeSpanTracer::class) {
26+
$definition->setArguments([
27+
'$tracerName' => $container->getParameter('otel_bundle.tracer_name'),
28+
]);
29+
}
30+
31+
$definition->addTag($listener['tag']);
32+
33+
$container->setDefinition($listener['class'], $definition);
34+
}
35+
36+
}
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Macpaw\SymfonyOtelBundle\DependencyInjection;
6+
7+
use Exception;
8+
use Symfony\Component\Config\FileLocator;
9+
use Symfony\Component\DependencyInjection\ContainerBuilder;
10+
use Symfony\Component\DependencyInjection\Extension\Extension;
11+
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
12+
13+
class SymfonyOtelExtension extends Extension
14+
{
15+
public const NAME = 'otel_bundle';
16+
17+
/**
18+
* @param array<string, mixed> $configs
19+
*
20+
* @throws Exception
21+
*/
22+
public function load(array $configs, ContainerBuilder $container): void
23+
{
24+
$loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/../../Resources/config'));
25+
$loader->load('services.yml');
26+
27+
$configuration = $this->getConfiguration($configs, $container);
28+
$configs = $this->processConfiguration($configuration, $configs);
29+
30+
$container->setParameter('otel_bundle.tracer_name', $configs['tracer_name']);
31+
$container->setParameter('otel_bundle.tracer_name', $configs['tracer_name']);
32+
$container->setParameter('otel_bundle.span_tracers', $configs['span_tracers']);
33+
}
34+
35+
/**
36+
* @param array<string, mixed> $config
37+
*/
38+
public function getConfiguration(array $config, ContainerBuilder $container): Configuration
39+
{
40+
return new Configuration();
41+
}
42+
43+
public function getAlias(): string
44+
{
45+
return self::NAME;
46+
}
47+
}

0 commit comments

Comments
 (0)