developer.overheid.nl

Ontwikkelaarsportaal van de Nederlandse overheid

Ga naar hoofdinhoud

PHP voorbeeldimplementatie met Symfony

Een voorbeeld hoe Logboek Dataverwerkingen toe te passen met Symfony in PHP.

Nieuw project​

Maak een nieuw project aan aan met Composer:

composer create-project symfony/skeleton:"8.1.*" ldv-example-php
cd ldv-example-php

Voeg dependency's toe​

composer require open-telemetry/sdk:^1.15
composer require open-telemetry/exporter-otlp:^1.4

Logboek​

OpenTelemetry​

In TracerFactory wordt een OpenTelemetry Tracer geconfigureerd die alleen voor het registreren van verwerkingen gebruikt wordt.

src/Logboek/TracerFactory.php
<?php

namespace App\Logboek;

use OpenTelemetry\API\Trace\TracerInterface;
use OpenTelemetry\Contrib\Otlp\ContentTypes;
use OpenTelemetry\Contrib\Otlp\OtlpHttpTransportFactory;
use OpenTelemetry\Contrib\Otlp\SpanExporter;
use OpenTelemetry\SDK\Common\Attribute\Attributes;
use OpenTelemetry\SDK\Common\Util\ShutdownHandler;
use OpenTelemetry\SDK\Resource\ResourceInfo;
use OpenTelemetry\SDK\Trace\Sampler\AlwaysOnSampler;
use OpenTelemetry\SDK\Trace\SpanProcessor\SimpleSpanProcessor;
use OpenTelemetry\SDK\Trace\TracerProvider;
use OpenTelemetry\SemConv\Attributes\ServiceAttributes;

class TracerFactory
{
private const TRACER_NAME = 'logboek-dataverwerkingen';
private const TRACER_VERSION = '0.1.0';

public function __invoke(string $endpoint, string $appName, string $appVersion): TracerInterface
{
$resource = ResourceInfo::create(
Attributes::create([
ServiceAttributes::SERVICE_NAME => $appName,
ServiceAttributes::SERVICE_VERSION => $appVersion,
])
);

$transport = (new OtlpHttpTransportFactory())->create($endpoint, ContentTypes::JSON);
$exporter = new SpanExporter($transport);

$provider = TracerProvider::builder()
->addSpanProcessor(new SimpleSpanProcessor($exporter))
->setResource($resource)
->setSampler(new AlwaysOnSampler())
->build();

ShutdownHandler::register($provider->shutdown(...));

return $provider->getTracer(self::TRACER_NAME, self::TRACER_VERSION);
}
}

Registreren van verwerkingen​

Met een ProcessingOperator kunnen we eenvoudig verwerkingen starten en direct de juiste attributen toevoegen. Met een Processing kan het "data subject" gezet worden.

src/Logboek/ProcessingOperator.php
<?php

namespace App\Logboek;

use OpenTelemetry\API\Trace\Propagation\TraceContextPropagator;
use OpenTelemetry\API\Trace\SpanBuilderInterface;
use OpenTelemetry\API\Trace\TracerInterface;
use OpenTelemetry\Context\Context;
use OpenTelemetry\Context\ContextInterface;
use Symfony\Component\DependencyInjection\Attribute\Target;

class ProcessingOperator
{
private TraceContextPropagator $propergator;

public function __construct(#[Target('logboekTracer')] private readonly TracerInterface $tracer)
{
$this->propergator = TraceContextPropagator::getInstance();
}

public function startProccessingFromForeignOperation(string $name, string $activityId, array $traceContextCarrier): Processing
{
$remoteContext = $this->propergator->extract($traceContextCarrier);
$span = $this->builder($name, $activityId, $remoteContext)
->startSpan();

Context::storage()->attach($span->storeInContext($remoteContext));

return new Processing($span);
}

public function startProccessing(string $name, string $activityId): Processing
{
$context = Context::getCurrent();
$span = $this->builder($name, $activityId, $context)
->startSpan();

Context::storage()->attach($span->storeInContext($context));

return new Processing($span);
}

private function builder(string $name, string $activityId, ContextInterface $context): SpanBuilderInterface
{
return $this->tracer->spanBuilder($name)
->setParent($context)
->setAttribute(LogboekAttributes::PROCESSING_ACTIVITY_ID, $activityId);
}
}

Attribuut​

Met een attribuut kunnen we eenvoudig verwerkingen registreren.

src/Logboek/Attribute/LogboekProcessing.php
<?php

namespace App\Logboek\Attribute;

#[\Attribute(\Attribute::TARGET_METHOD)]
class LogboekProcessing
{
public function __construct(
public readonly string $name,
public readonly string $activityId,
public readonly bool $fromForeign = false
) {}
}

Applicatie​

Logboek configureren​

Voeg de Logboek-tracer toe en configureer de endpoint-URL.

config/services.yaml
parameters:
app.name: ldv-example-php
app.version: 0.1.0
logboek.endpoint: http://127.0.0.1:4318/v1/traces

services:
# ... bestaande services ...

OpenTelemetry\API\Trace\TracerInterface $logboekTracer:
class: OpenTelemetry\API\Trace\TracerInterface
factory: '@App\Logboek\TracerFactory'
arguments:
- '%logboek.endpoint%'
- '%app.name%'
- '%app.version%'

Verwerkingen definiëren​

Alles komt nu samen in de PersonsController.

De gemarkeerde regels laten zien hoe het LogboekProcessing-attribuut, en de ProcessingOperator-, en Processing-class gebruikt kunnen worden.

src/Controller/PersonsController.php
<?php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

use App\Logboek\Attribute\LogboekProcessing;
use App\Logboek\ProcessingOperator;

#[Route('/persons')]
class PersonsController extends AbstractController
{
public function __construct(private readonly ProcessingOperator $processingOperator) {}

#[Route('/{bsn}/is-minimal-18-years-old', name: 'person-is-minimal-18-years-old', methods: ['GET'])]
#[LogboekProcessing('check_18_or_over', 'urn:ldv:activity:1337', fromForeign: true)]
public function isMinimal18YearsOld(string $bsn): Response
{
$is18OrOver = $this->personIs18OrOver($bsn);

return $this->json(['is_18_or_over' => $is18OrOver]);
}

private function personIs18OrOver(string $bsn): bool
{
$processing = $this->processingOperator
->startProccessing('fetch_date_of_birth', 'urn:ldv:activity:1550')
->setDataSubject($bsn, "BSN");

// Simuleer het ophalen van de geboortedatum
usleep(42_000);

$processing->stopSuccessful();

return true;
}
}

Applicatie starten​

Start nu de applicatie:

php -S 127.0.0.1:8080 -t public

Roep de API aan:

curl http://127.0.0.1:8080/persons/999990342/is-minimal-18-years-old

Logboek inzien​

Als je de OpenTelemetry viewer gebruikt kun je de twee verwerkingen in het logboek zien.