Skip to content

Commit

Permalink
Merge pull request #12 from keepsuit/refactoring-tracer
Browse files Browse the repository at this point in the history
Refactoring tracer api
  • Loading branch information
cappuc authored Feb 9, 2024
2 parents 06a1c40 + 19a15f3 commit d2d4fe8
Show file tree
Hide file tree
Showing 17 changed files with 225 additions and 198 deletions.
2 changes: 1 addition & 1 deletion phpstan.neon
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
parameters:
level: 7
level: 8
paths:
- src
- config
Expand Down
11 changes: 3 additions & 8 deletions src/Facades/Tracer.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@
namespace Keepsuit\LaravelOpenTelemetry\Facades;

use Illuminate\Support\Facades\Facade;
use OpenTelemetry\API\Trace\SpanBuilderInterface;
use Keepsuit\LaravelOpenTelemetry\Support\SpanBuilder;
use OpenTelemetry\API\Trace\SpanInterface;
use OpenTelemetry\API\Trace\SpanKind;
use OpenTelemetry\Context\Context;
use OpenTelemetry\Context\ContextInterface;
use OpenTelemetry\Context\ScopeInterface;
Expand All @@ -17,13 +16,9 @@
* @method static ScopeInterface|null activeScope()
* @method static ContextInterface currentContext()
* @method static array propagationHeaders(?ContextInterface $context = null)
* @method static SpanBuilderInterface build(string $name)
* @method static SpanInterface start(string $name, int $spanKind = SpanKind::KIND_INTERNAL)
* @method static mixed measure(string $name, \Closure $callback)
* @method static mixed measureAsync(string $name, \Closure $callback)
* @method static SpanInterface recordExceptionToSpan(SpanInterface $span, \Throwable $exception)
* @method static Context|null extractContextFromPropagationHeaders(array $headers)
* @method static void setRootSpan(SpanInterface $span)
* @method static SpanBuilder newSpan(string $name)
* @method static void updateLogContext()
*/
class Tracer extends Facade
{
Expand Down
21 changes: 9 additions & 12 deletions src/Instrumentation/QueryInstrumentation.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,17 @@ public function recordQuery(QueryExecuted $event): void
default: fn () => ''
);

$span = Tracer::build(sprintf('sql %s', $operationName))
$span = Tracer::newSpan(sprintf('sql %s', $operationName))
->setSpanKind(SpanKind::KIND_CLIENT)
->setStartTimestamp($this->getEventStartTimestampNs($event->time))
->startSpan();

$span->setAttributes([
TraceAttributes::DB_SYSTEM => $event->connection->getDriverName(),
TraceAttributes::DB_NAME => $event->connection->getDatabaseName(),
TraceAttributes::DB_OPERATION => $operationName,
TraceAttributes::DB_STATEMENT => $event->sql,
TraceAttributes::DB_USER => $event->connection->getConfig('username'),
TraceAttributes::SERVER_ADDRESS => $event->connection->getConfig('host'),
TraceAttributes::SERVER_PORT => $event->connection->getConfig('port'),
]);
->setAttribute(TraceAttributes::DB_SYSTEM, $event->connection->getDriverName())
->setAttribute(TraceAttributes::DB_NAME, $event->connection->getDatabaseName())
->setAttribute(TraceAttributes::DB_OPERATION, $operationName)
->setAttribute(TraceAttributes::DB_STATEMENT, $event->sql)
->setAttribute(TraceAttributes::DB_USER, $event->connection->getConfig('username'))
->setAttribute(TraceAttributes::SERVER_ADDRESS, $event->connection->getConfig('host'))
->setAttribute(TraceAttributes::SERVER_PORT, $event->connection->getConfig('port'))
->start();

$span->end();
}
Expand Down
16 changes: 7 additions & 9 deletions src/Instrumentation/QueueInstrumentation.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
use Illuminate\Queue\QueueManager;
use Keepsuit\LaravelOpenTelemetry\Facades\Tracer;
use OpenTelemetry\API\Trace\SpanKind;
use OpenTelemetry\Context\Context;

class QueueInstrumentation implements Instrumentation
{
Expand All @@ -31,20 +30,19 @@ protected function recordJobStart(): void
app('events')->listen(JobProcessing::class, function (JobProcessing $event) {
$context = Tracer::extractContextFromPropagationHeaders($event->job->payload());

$span = Tracer::build(sprintf('%s process', $event->job->resolveName()))
$span = Tracer::newSpan(sprintf('%s process', $event->job->resolveName()))
->setSpanKind(SpanKind::KIND_CONSUMER)
->setParent($context)
->startSpan();

$span->setAttribute('messaging.system', config(sprintf('queue.connections.%s.driver', $event->connectionName)))
->setAttribute('messaging.system', config(sprintf('queue.connections.%s.driver', $event->connectionName)))
->setAttribute('messaging.operation', 'process')
->setAttribute('messaging.destination.kind', 'queue')
->setAttribute('messaging.destination.name', $event->job->getQueue())
->setAttribute('messaging.destination.template', $event->job->resolveName());
->setAttribute('messaging.destination.template', $event->job->resolveName())
->start();

Tracer::setRootSpan($span);
$span->activate();

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

Expand All @@ -64,8 +62,8 @@ protected function recordJobEnd(): void

$span->recordException($event->exception);

$scope?->detach();
$span->end();
$scope->detach();
});
}

Expand Down
4 changes: 2 additions & 2 deletions src/Instrumentation/RedisInstrumentation.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ public function recordCommand(CommandExecuted $event): void
{
$traceName = sprintf('redis %s %s', $event->connection->getName(), $event->command);

$span = Tracer::build($traceName)
$span = Tracer::newSpan($traceName)
->setSpanKind(SpanKind::KIND_CLIENT)
->setStartTimestamp($this->getEventStartTimestampNs($event->time))
->startSpan();
->start();

if ($span->isRecording()) {
$span->setAttribute(TraceAttributes::DB_SYSTEM, 'redis')
Expand Down
3 changes: 3 additions & 0 deletions src/LaravelOpenTelemetryServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -155,5 +155,8 @@ private function configureEnvironmentVariables(): void
$envRepository = Env::getRepository();

$envRepository->set(OTELVariables::OTEL_SERVICE_NAME, config('opentelemetry.service_name'));

// Disable debug scopes wrapping
$_SERVER['OTEL_PHP_DEBUG_SCOPES_DISABLED'] = 1;
}
}
7 changes: 4 additions & 3 deletions src/Support/CarbonClock.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Carbon\Carbon;
use Carbon\CarbonImmutable;
use Carbon\CarbonInterface;
use OpenTelemetry\SDK\Common\Time\ClockInterface;
use OpenTelemetry\SDK\Common\Time\SystemClock;

Expand All @@ -19,14 +20,14 @@ public function __construct()
public function now(): int
{
if (Carbon::hasTestNow()) {
return (int) CarbonImmutable::now()->getPreciseTimestamp(6) * 1000;
return static::carbonToNanos(CarbonImmutable::now());
}

return $this->systemClock->now();
}

public function nanoTime(): int
public static function carbonToNanos(CarbonInterface $carbon): int
{
return $this->now();
return (int) $carbon->getPreciseTimestamp(6) * 1000;
}
}
28 changes: 12 additions & 16 deletions src/Support/HttpClient/GuzzleTraceMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,17 @@ public static function make(): Closure
{
return static function (callable $handler): callable {
return static function (RequestInterface $request, array $options) use ($handler) {
$span = Tracer::build(sprintf('HTTP %s', $request->getMethod()))
$span = Tracer::newSpan(sprintf('HTTP %s', $request->getMethod()))
->setSpanKind(SpanKind::KIND_CLIENT)
->setAttributes([
TraceAttributes::URL_FULL => sprintf('%s://%s%s', $request->getUri()->getScheme(), $request->getUri()->getHost(), $request->getUri()->getPath()),
TraceAttributes::URL_PATH => $request->getUri()->getPath(),
TraceAttributes::URL_QUERY => $request->getUri()->getQuery(),
TraceAttributes::HTTP_REQUEST_METHOD => $request->getMethod(),
TraceAttributes::HTTP_REQUEST_BODY_SIZE => $request->getBody()->getSize(),
TraceAttributes::URL_SCHEME => $request->getUri()->getScheme(),
TraceAttributes::SERVER_ADDRESS => $request->getUri()->getHost(),
TraceAttributes::SERVER_PORT => $request->getUri()->getPort(),
])
->startSpan();
->setAttribute(TraceAttributes::URL_FULL, sprintf('%s://%s%s', $request->getUri()->getScheme(), $request->getUri()->getHost(), $request->getUri()->getPath()))
->setAttribute(TraceAttributes::URL_PATH, $request->getUri()->getPath())
->setAttribute(TraceAttributes::URL_QUERY, $request->getUri()->getQuery())
->setAttribute(TraceAttributes::HTTP_REQUEST_METHOD, $request->getMethod())
->setAttribute(TraceAttributes::HTTP_REQUEST_BODY_SIZE, $request->getBody()->getSize())
->setAttribute(TraceAttributes::URL_SCHEME, $request->getUri()->getScheme())
->setAttribute(TraceAttributes::SERVER_ADDRESS, $request->getUri()->getHost())
->setAttribute(TraceAttributes::SERVER_PORT, $request->getUri()->getPort())
->start();

$context = $span->storeInContext(Tracer::currentContext());

Expand All @@ -41,10 +39,8 @@ public static function make(): Closure
assert($promise instanceof PromiseInterface);

return $promise->then(function (Response $response) use ($span) {
$span->setAttributes([
TraceAttributes::HTTP_RESPONSE_STATUS_CODE => $response->getStatusCode(),
TraceAttributes::HTTP_REQUEST_BODY_SIZE => $response->getHeader('Content-Length')[0] ?? null,
]);
$span->setAttribute(TraceAttributes::HTTP_RESPONSE_STATUS_CODE, $response->getStatusCode())
->setAttribute(TraceAttributes::HTTP_REQUEST_BODY_SIZE, $response->getHeader('Content-Length')[0] ?? null);

if ($response->getStatusCode() >= 400) {
$span->setStatus(StatusCode::STATUS_ERROR);
Expand Down
17 changes: 7 additions & 10 deletions src/Support/HttpServer/TraceRequestMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ public function handle(Request $request, Closure $next): mixed
$span = $this->startTracing($request);
$scope = $span->activate();

Tracer::updateLogContext();

try {
$response = $next($request);

Expand All @@ -33,7 +35,8 @@ public function handle(Request $request, Closure $next): mixed

return $response;
} catch (\Throwable $exception) {
Tracer::recordExceptionToSpan($span, $exception);
$span->recordException($exception)
->setStatus(StatusCode::STATUS_ERROR);

throw $exception;
} finally {
Expand All @@ -50,14 +53,9 @@ protected function startTracing(Request $request): SpanInterface
$route = rescue(fn () => Route::getRoutes()->match($request)->uri(), $request->path(), false);
$route = str_starts_with($route, '/') ? $route : '/'.$route;

$span = Tracer::build(name: $route)
return Tracer::newSpan($route)
->setSpanKind(SpanKind::KIND_SERVER)
->setParent($context)
->startSpan();

Tracer::setRootSpan($span);

$span
->setAttribute(TraceAttributes::URL_FULL, $request->fullUrl())
->setAttribute(TraceAttributes::URL_PATH, $request->path() === '/' ? $request->path() : '/'.$request->path())
->setAttribute(TraceAttributes::URL_QUERY, $request->getQueryString())
Expand All @@ -69,9 +67,8 @@ protected function startTracing(Request $request): SpanInterface
->setAttribute(TraceAttributes::SERVER_PORT, $request->getPort())
->setAttribute(TraceAttributes::USER_AGENT_ORIGINAL, $request->userAgent())
->setAttribute(TraceAttributes::NETWORK_PROTOCOL_VERSION, $request->getProtocolVersion())
->setAttribute(TraceAttributes::NETWORK_PEER_ADDRESS, $request->ip());

return $span;
->setAttribute(TraceAttributes::NETWORK_PEER_ADDRESS, $request->ip())
->start();
}

protected function recordHttpResponseToSpan(SpanInterface $span, Response $response): void
Expand Down
113 changes: 113 additions & 0 deletions src/Support/SpanBuilder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
<?php

namespace Keepsuit\LaravelOpenTelemetry\Support;

use Carbon\CarbonInterface;
use Closure;
use Illuminate\Foundation\Bus\PendingDispatch;
use OpenTelemetry\API\Trace\SpanBuilderInterface;
use OpenTelemetry\API\Trace\SpanContextInterface;
use OpenTelemetry\API\Trace\SpanInterface;
use OpenTelemetry\API\Trace\SpanKind;
use OpenTelemetry\Context\ContextInterface;
use Throwable;

class SpanBuilder
{
public function __construct(
protected SpanBuilderInterface $spanBuilder
) {
}

public function setParent(?ContextInterface $context): SpanBuilder
{
$this->spanBuilder->setParent($context);

return $this;
}

public function addLink(SpanContextInterface $context, iterable $attributes = []): SpanBuilder
{
$this->spanBuilder->addLink($context, $attributes);

return $this;
}

public function setAttribute(string $key, mixed $value): SpanBuilder
{
$this->spanBuilder->setAttribute($key, $value);

return $this;
}

/**
* @param iterable<string,mixed> $attributes
*/
public function setAttributes(iterable $attributes): SpanBuilder
{
$this->spanBuilder->setAttributes($attributes);

return $this;
}

/**
* @param CarbonInterface|int $timestamp A carbon instance or a timestamp in nanoseconds
*/
public function setStartTimestamp(CarbonInterface|int $timestamp): SpanBuilder
{
if ($timestamp instanceof CarbonInterface) {
$timestamp = CarbonClock::carbonToNanos($timestamp);
}

$this->spanBuilder->setStartTimestamp($timestamp);

return $this;
}

/**
* @phpstan-param SpanKind::KIND_* $spanKind
*/
public function setSpanKind(int $spanKind): SpanBuilder
{
$this->spanBuilder->setSpanKind($spanKind);

return $this;
}

public function start(): SpanInterface
{
return $this->spanBuilder->startSpan();
}

/**
* @template U
*
* @param Closure(SpanInterface $span): U $callback
* @return (U is PendingDispatch ? null : U)
*
* @throws Throwable
*/
public function measure(Closure $callback): mixed
{
$span = $this->start();
$scope = $span->activate();

try {
$result = $callback($span);

// Fix: Dispatch is effective only on destruct
if ($result instanceof PendingDispatch) {
$result = null;
}

return $result;
} catch (Throwable $exception) {
$span->recordException($exception);

throw $exception;
} finally {
$span->end();
$scope->detach();
}
}
}
Loading

0 comments on commit d2d4fe8

Please sign in to comment.