以下の内容はhttps://koudenpa.hatenablog.com/entry/2025/03/23/203242より取得しました。


Laravel LighthouseのGraphQLをOpenTelemetryトレース

LaravelLighthouseを使ってGraphQLサーバをホスティングしている。

Mackerelでトレーシング機能が提供されてから「そのうちトレースしよう」と思っていたのだけれど、ついにやったとこ重い腰を上げてトレースを送った。

Laravel本体には自動計装ライブラリがあるが、Lighthouseにはないので何となくGraphQLの処理の具合を見られるようにしたのでメモっておく。

OpenTelemetryは色々従うべき仕様や推奨される実装があるっぽいが、その辺を読み込んでいると年が明けるので「何となくGraphQLの処理の具合を見られる」が満たされれば良しとした。

三者に提供するライブラリではない場合はこのくらいでよいのが気楽だ。

Lighthouseにはトレース用の仕組みが用意されているので、OpenTelemetryでのTrace用のSpanを構築するDriverを実装して使うようにした。

結果、何となくGraphQLの処理の具合を見られるようになった。

なんとなくトレースできてる様子


PHP、Laravelのトレースはできている前提で、LaravelがロードするService ProviderにNuwave\Lighthouse\Tracing\TracingServiceProvider::classを追加、config/lighthouse.phpに実装したDriverを使用するように設定すればよい。

lighthouse/src/lighthouse.php at d9fb0ee2f00c52dd7af0d3ecff073b9fb9c95d87 · nuwave/lighthouse · GitHub

<?php declare(strict_types=1);
// config/lighthouse.php にトレース向けのドライバを設定

return [
    // ~ 略 ~
    'tracing' => [
        'driver' => App\GraphQL\OTelTracing::class,
    ],
];

実装にあたっての参考はこの辺り。

※追記: Subscriptionを使っているとhandleStartRequestが呼ばれずにexecutionされるパスがあるようで、そうするとエラーするスニペットになっている(後で直すかも)

<?php

namespace App\GraphQL;

use Illuminate\Support\Carbon;
use Nuwave\Lighthouse\Events\BuildExtensionsResponse;
use Nuwave\Lighthouse\Events\StartExecution;
use Nuwave\Lighthouse\Events\StartRequest;
use Nuwave\Lighthouse\Execution\ExtensionsResponse;
use Nuwave\Lighthouse\Execution\ResolveInfo;
use Nuwave\Lighthouse\Tracing\Tracing;
use Nuwave\Lighthouse\Tracing\TracingUtilities;

use OpenTelemetry\API\Globals;
use OpenTelemetry\API\Trace\SpanInterface;
use OpenTelemetry\API\Trace\SpanKind;
use OpenTelemetry\API\Trace\TracerInterface;
use OpenTelemetry\SemConv\TraceAttributes;

class OTelTracing implements Tracing
{
    use TracingUtilities;
    private TracerInterface $tracer;
    private SpanInterface $rootSpan;
    private StartRequest $startRequest;
    private int|float $executionStartPrecise;
    private float $executionStartUnixtime;

    public function handleStartRequest(StartRequest $startRequest): void
    {
        $this->startRequest = $startRequest;
        $this->executionStartPrecise = $this->timestamp();
        $this->executionStartUnixtime = microtime(true);
        $tracerProvider = Globals::tracerProvider();
        $this->tracer = $tracerProvider->getTracer('graphql-server');
    }

    public function handleStartExecution(StartExecution $startExecution): void
    {
        $this->rootSpan = $this->tracer
            ->spanBuilder("GraphQL Execution")
            ->setSpanKind(SpanKind::KIND_INTERNAL)
            ->setAttributes([
                TraceAttributes::GRAPHQL_OPERATION_NAME => $startExecution->operationName,
                TraceAttributes::GRAPHQL_DOCUMENT => $this->startRequest->request->getContent(),
            ])
            ->startSpan();
    }

    public function handleBuildExtensionsResponse(BuildExtensionsResponse $buildExtensionsResponse): ?ExtensionsResponse
    {
        $this->rootSpan->end();
        return null;
    }

    public function record(ResolveInfo $resolveInfo, float|int $start, float|int $end): void
    {
        // 10ms以下は間引いておく
        if ($this->diffTimeInNanoseconds($start, $end) < 10 * 1000 + 1000) {
            return;
        }
        $this->tracer
            ->spanBuilder("GraphQL Resolver")
            ->addLink($this->rootSpan->getContext())
            ->setSpanKind(SpanKind::KIND_INTERNAL)
            ->setAttributes([
                'graphql.resolver' => $resolveInfo->parentType . '#' . $resolveInfo->fieldName,
            ])
            ->setStartTimestamp($this->toNano($this->executionStartUnixtime) + $this->diffTimeInNanoseconds($this->executionStartPrecise, $start))
            ->startSpan()
            ->end($this->toNano($this->executionStartUnixtime) + $this->diffTimeInNanoseconds($this->executionStartPrecise, $end));
    }

    private function toNano(float $value): int
    {
        return (int) ($value * 1000 * 1000 * 1000);
    }
}



以上の内容はhttps://koudenpa.hatenablog.com/entry/2025/03/23/203242より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14