Skip to content

Commit

Permalink
custom span builder
Browse files Browse the repository at this point in the history
  • Loading branch information
cappuc committed Feb 9, 2024
1 parent 6d72738 commit e4c2d09
Show file tree
Hide file tree
Showing 11 changed files with 166 additions and 101 deletions.
10 changes: 5 additions & 5 deletions src/Facades/Tracer.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

namespace Keepsuit\LaravelOpenTelemetry\Facades;

use Closure;
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 @@ -18,9 +18,9 @@
* @method static ContextInterface currentContext()
* @method static array propagationHeaders(?ContextInterface $context = null)
* @method static Context|null extractContextFromPropagationHeaders(array $headers)
* @method static SpanBuilderInterface build(string $name)
* @method static SpanInterface start(string $name, int $spanKind = SpanKind::KIND_INTERNAL, ?ContextInterface $context = null)
* @method static mixed measure(string $name, \Closure $callback, int $spanKind = SpanKind::KIND_INTERNAL)
* @method static SpanBuilder newSpan(string $name)
* @method static SpanInterface start(string $name)
* @method static mixed measure(string $name, Closure $callback)
*/
class Tracer extends Facade
{
Expand Down
19 changes: 8 additions & 11 deletions src/Instrumentation/QueryInstrumentation.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,21 +29,18 @@ 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))
->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'))
->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'),
]);

$span->end();
}
}
14 changes: 8 additions & 6 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,14 +30,17 @@ protected function recordJobStart(): void
app('events')->listen(JobProcessing::class, function (JobProcessing $event) {
$context = Tracer::extractContextFromPropagationHeaders($event->job->payload());

$span = Tracer::start(sprintf('%s process', $event->job->resolveName()), SpanKind::KIND_CONSUMER, $context);
$span->activate();

$span->setAttribute('messaging.system', config(sprintf('queue.connections.%s.driver', $event->connectionName)))
$span = Tracer::newSpan(sprintf('%s process', $event->job->resolveName()))
->setSpanKind(SpanKind::KIND_CONSUMER)
->setParent($context)
->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())
->startSpan();

$span->activate();
});
}

Expand Down
2 changes: 1 addition & 1 deletion src/Instrumentation/RedisInstrumentation.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ 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();
Expand Down
2 changes: 1 addition & 1 deletion src/LaravelOpenTelemetryServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ protected function initTracer(): void
),
),
'http' => new OtlpSpanExporter(
// @phpstan-ignore-next-line
// @phpstan-ignore-next-line
(new OtlpHttpTransportFactory())->create(
(new HttpEndpointResolver())->resolveToString(config('opentelemetry.exporters.http.endpoint'), Signals::TRACE),
'application/x-protobuf'
Expand Down
26 changes: 11 additions & 15 deletions src/Support/HttpClient/GuzzleTraceMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,16 @@ 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(),
])
->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())
->startSpan();

$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
8 changes: 5 additions & 3 deletions src/Support/HttpServer/TraceRequestMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
use OpenTelemetry\API\Trace\StatusCode;
use OpenTelemetry\SemConv\TraceAttributes;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\StreamedResponse;

class TraceRequestMiddleware
{
Expand Down Expand Up @@ -52,7 +51,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;

return Tracer::start(name: $route, spanKind: SpanKind::KIND_SERVER, context: $context)
return Tracer::newSpan($route)
->setSpanKind(SpanKind::KIND_SERVER)
->setParent($context)
->setAttribute(TraceAttributes::URL_FULL, $request->fullUrl())
->setAttribute(TraceAttributes::URL_PATH, $request->path() === '/' ? $request->path() : '/'.$request->path())
->setAttribute(TraceAttributes::URL_QUERY, $request->getQueryString())
Expand All @@ -64,7 +65,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());
->setAttribute(TraceAttributes::NETWORK_PEER_ADDRESS, $request->ip())
->startSpan();
}

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

namespace Keepsuit\LaravelOpenTelemetry\Support;

use Closure;
use Illuminate\Foundation\Bus\PendingDispatch;
use OpenTelemetry\API\Trace\SpanBuilderInterface;
use OpenTelemetry\API\Trace\SpanContextInterface;
use OpenTelemetry\API\Trace\SpanInterface;
use Throwable;

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

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

return $this;
}

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

return $this;
}

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

return $this;
}

public function setAttributes(iterable $attributes): SpanBuilder
{
$this->spanBuilder->setAttributes($attributes);

return $this;
}

public function setStartTimestamp(int $timestampNanos): SpanBuilder
{
$this->spanBuilder->setStartTimestamp($timestampNanos);

return $this;
}

public function setSpanKind(int $spanKind): SpanBuilder
{
$this->spanBuilder->setSpanKind($spanKind);

return $this;
}

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

/**
* @template U
*
* @param Closure(SpanInterface $span): U $callback
* @return U
*
* @throws Throwable
*/
public function measure(Closure $callback): mixed
{
$span = $this->startSpan();
$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();
}
}
}
69 changes: 19 additions & 50 deletions src/Tracer.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,9 @@
namespace Keepsuit\LaravelOpenTelemetry;

use Closure;
use Exception;
use Illuminate\Foundation\Bus\PendingDispatch;
use Illuminate\Support\Facades\Log;
use OpenTelemetry\API\Trace\SpanBuilderInterface;
use Keepsuit\LaravelOpenTelemetry\Support\SpanBuilder;
use OpenTelemetry\API\Trace\SpanInterface;
use OpenTelemetry\API\Trace\SpanKind;
use OpenTelemetry\API\Trace\TracerInterface;
use OpenTelemetry\Context\Context;
use OpenTelemetry\Context\ContextInterface;
Expand All @@ -27,59 +24,31 @@ public function __construct(
/**
* @phpstan-param non-empty-string $name
*/
public function build(string $name): SpanBuilderInterface
public function newSpan(string $name): SpanBuilder
{
return $this->tracer->spanBuilder($name);
return new SpanBuilder($this->tracer->spanBuilder($name));
}

/**
* @phpstan-param non-empty-string $name
* @phpstan-param SpanKind::KIND_* $spanKind
*/
public function start(
string $name,
int $spanKind = SpanKind::KIND_INTERNAL,
?ContextInterface $context = null
): SpanInterface {
return $this->build($name)
->setSpanKind($spanKind)
->setParent($context)
->startSpan();
public function start(string $name): SpanInterface
{
return $this->newSpan($name)->startSpan();
}

/**
* @template U
*
* @param non-empty-string $name
* @param Closure(SpanInterface $span): U $callback
* @phpstan-param SpanKind::KIND_* $spanKind
*
* @throws Exception
* @return U
*
* @throws \Throwable
*/
public function measure(string $name, Closure $callback, int $spanKind = SpanKind::KIND_INTERNAL)
public function measure(string $name, Closure $callback)
{
$span = $this->start($name, $spanKind);
$scope = $span->activate();

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

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

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

throw $exception;
} finally {
$span->end();
$scope->detach();
}
return $this->newSpan($name)->measure($callback);
}

public function currentContext(): ContextInterface
Expand Down Expand Up @@ -131,14 +100,14 @@ public function isRecording(): bool
return false;
}

protected function setTraceIdForLogs(SpanInterface $span): void
{
if (config('opentelemetry.logs.inject_trace_id', true)) {
$field = config('opentelemetry.logs.trace_id_field', 'traceId');

Log::shareContext([
$field => $span->getContext()->getTraceId(),
]);
}
}
// protected function setTraceIdForLogs(SpanInterface $span): void
// {
// if (config('opentelemetry.logs.inject_trace_id', true)) {
// $field = config('opentelemetry.logs.trace_id_field', 'traceId');
//
// Log::shareContext([
// $field => $span->getContext()->getTraceId(),
// ]);
// }
// }
}
Loading

0 comments on commit e4c2d09

Please sign in to comment.