Skip to content
This repository was archived by the owner on Dec 5, 2025. It is now read-only.

Commit 5bf6058

Browse files
committed
wip
1 parent a502761 commit 5bf6058

File tree

5 files changed

+136
-27
lines changed

5 files changed

+136
-27
lines changed

src/Middlewares/MeasureRequest.php

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -50,31 +50,7 @@ public function handle(Request $request, Closure $next, ?string $name = null)
5050
}
5151
}
5252

53-
protected function recordHeaders(SpanInterface $span, Request|Response $http): SpanInterface
54-
{
55-
$prefix = match (true) {
56-
$http instanceof Request => 'http.request.header.',
57-
$http instanceof Response => 'http.response.header.',
58-
};
59-
60-
foreach ($http->headers->all() as $key => $value) {
61-
$key = strtolower($key);
62-
63-
if (! static::headerIsAllowed($key)) {
64-
continue;
65-
}
66-
67-
$value = static::headerIsSensitive($key) ? ['*****'] : $value;
6853

69-
if (is_array($value)) {
70-
$value = implode(', ', $value);
71-
}
72-
73-
$span->setAttribute($prefix.$key, $value);
74-
}
75-
76-
return $span;
77-
}
7854

7955
protected static function httpHostName(Request $request): string
8056
{
@@ -103,7 +79,6 @@ public function getRequestSpanAttributes(Request $request): array
10379
TraceAttributes::SERVER_PORT => $request->getPort(),
10480
TraceAttributes::CLIENT_PORT => $request->server('REMOTE_PORT'),
10581
TraceAttributes::USER_AGENT_ORIGINAL => $request->userAgent(),
106-
TraceAttributes::HTTP_FLAVOR => $request->server('SERVER_PROTOCOL'),
10782
];
10883
}
10984
}

src/OpenTelemetryServiceProvider.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,12 @@
55
namespace Overtrue\LaravelOpenTelemetry;
66

77
use Illuminate\Contracts\Http\Kernel;
8+
use Illuminate\Http\Client\PendingRequest;
89
use Illuminate\Support\ServiceProvider;
10+
use OpenTelemetry\API\Common\Time\Clock;
911
use Overtrue\LaravelOpenTelemetry\Middlewares\MeasureRequest;
12+
use Overtrue\LaravelOpenTelemetry\Support\CarbonClock;
13+
use Overtrue\LaravelOpenTelemetry\Support\GuzzleTraceMiddleware;
1014
use Overtrue\LaravelOpenTelemetry\Support\Measure;
1115

1216
class OpenTelemetryServiceProvider extends ServiceProvider
@@ -25,6 +29,11 @@ public function boot(): void
2529
$this->injectHttpMiddleware(app(Kernel::class));
2630
}
2731

32+
PendingRequest::macro('withTrace', function () {
33+
/** @var PendingRequest $this */
34+
return $this->withMiddleware(GuzzleTraceMiddleware::make());
35+
});
36+
2837
foreach (config('otel.watchers') as $watcher) {
2938
$this->app->make($watcher)->register($this->app);
3039
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
<?php
2+
3+
namespace Overtrue\LaravelOpenTelemetry\Support;
4+
5+
use Closure;
6+
use GuzzleHttp\Promise\PromiseInterface;
7+
use OpenTelemetry\API\Globals;
8+
use OpenTelemetry\API\Trace\SpanInterface;
9+
use OpenTelemetry\API\Trace\SpanKind;
10+
use OpenTelemetry\API\Trace\StatusCode;
11+
use OpenTelemetry\Context\Context;
12+
use OpenTelemetry\SemConv\TraceAttributes;
13+
use Overtrue\LaravelOpenTelemetry\Traits\InteractWithHttpHeaders;
14+
use Psr\Http\Message\RequestInterface;
15+
use Psr\Http\Message\ResponseInterface;
16+
17+
class GuzzleTraceMiddleware
18+
{
19+
use InteractWithHttpHeaders;
20+
21+
public static function make(): Closure
22+
{
23+
return static function (callable $handler): callable {
24+
return static function (RequestInterface $request, array $options) use ($handler) {
25+
$span = \Overtrue\LaravelOpenTelemetry\Facades\Measure::span(sprintf('HTTP %s', $request->getMethod()))
26+
->setSpanKind(SpanKind::KIND_CLIENT)
27+
->setAttribute(TraceAttributes::URL_FULL, sprintf('%s://%s%s', $request->getUri()->getScheme(), $request->getUri()->getHost(), $request->getUri()->getPath()))
28+
->setAttribute(TraceAttributes::URL_PATH, $request->getUri()->getPath())
29+
->setAttribute(TraceAttributes::URL_QUERY, $request->getUri()->getQuery())
30+
->setAttribute(TraceAttributes::NETWORK_PROTOCOL_VERSION, $request->getProtocolVersion())
31+
->setAttribute(TraceAttributes::NETWORK_PEER_ADDRESS, $request->getUri()->getHost())
32+
->setAttribute(TraceAttributes::HTTP_REQUEST_METHOD, $request->getMethod())
33+
->setAttribute(TraceAttributes::HTTP_REQUEST_BODY_SIZE, $request->getBody()->getSize())
34+
->setAttribute(TraceAttributes::URL_SCHEME, $request->getUri()->getScheme())
35+
->setAttribute(TraceAttributes::SERVER_ADDRESS, $request->getUri()->getHost())
36+
->setAttribute(TraceAttributes::SERVER_PORT, $request->getUri()->getPort())
37+
->setAttribute(TraceAttributes::CLIENT_PORT, $request->getHeader('REMOTE_PORT'))
38+
->setAttribute(TraceAttributes::USER_AGENT_ORIGINAL, $request->getHeader('User-Agent'))
39+
->start();
40+
41+
static::recordHeaders($span, $request);
42+
43+
$context = $span->storeInContext(Context::getCurrent());
44+
45+
foreach (\Overtrue\LaravelOpenTelemetry\Facades\Measure::propagationHeaders($context) as $key => $value) {
46+
$request = $request->withHeader($key, $value);
47+
}
48+
49+
$promise = $handler($request, $options);
50+
assert($promise instanceof PromiseInterface);
51+
52+
return $promise->then(function (ResponseInterface $response) use ($span) {
53+
$span->setAttribute(TraceAttributes::HTTP_RESPONSE_STATUS_CODE, $response->getStatusCode());
54+
55+
if (($contentLength = $response->getHeader('Content-Length')[0] ?? null) !== null) {
56+
$span->setAttribute(TraceAttributes::HTTP_RESPONSE_BODY_SIZE, $contentLength);
57+
}
58+
59+
static::recordHeaders($span, $response);
60+
61+
if ($response->getStatusCode() >= 400) {
62+
$span->setStatus(StatusCode::STATUS_ERROR);
63+
}
64+
65+
$span->end();
66+
67+
return $response;
68+
});
69+
};
70+
};
71+
}
72+
73+
protected static function recordHeaders(SpanInterface $span, RequestInterface|ResponseInterface $http): SpanInterface
74+
{
75+
$prefix = match (true) {
76+
$http instanceof RequestInterface => 'http.request.header.',
77+
$http instanceof ResponseInterface => 'http.response.header.',
78+
};
79+
80+
foreach ($http->getHeaders() as $key => $value) {
81+
$key = strtolower($key);
82+
83+
if (! static::headerIsAllowed($key)) {
84+
continue;
85+
}
86+
87+
$value = static::headerIsSensitive($key) ? ['*****'] : $value;
88+
89+
$span->setAttribute($prefix.$key, $value);
90+
}
91+
92+
return $span;
93+
}
94+
}

src/Support/Measure.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@
88
use OpenTelemetry\API\Trace\Span;
99
use OpenTelemetry\API\Trace\SpanContextValidator;
1010
use OpenTelemetry\API\Trace\SpanInterface;
11+
use OpenTelemetry\API\Trace\TracerInterface;
1112
use OpenTelemetry\Context\Context;
1213
use OpenTelemetry\Context\ContextInterface;
14+
use OpenTelemetry\Context\Propagation\TextMapPropagatorInterface;
1315
use OpenTelemetry\Context\ScopeInterface;
1416

1517
class Measure
@@ -62,7 +64,7 @@ public function end(string|int|null $name = null): void
6264
}
6365
}
6466

65-
public function tracer()
67+
public function tracer(): TracerInterface
6668
{
6769
return Globals::tracerProvider()->getTracer(config('app.name'));
6870
}
@@ -84,7 +86,7 @@ public function traceId(): ?string
8486
return SpanContextValidator::isValidTraceId($traceId) ? $traceId : null;
8587
}
8688

87-
public function propagator()
89+
public function propagator(): TextMapPropagatorInterface
8890
{
8991
return Globals::propagator();
9092
}

src/Traits/InteractWithHttpHeaders.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22

33
namespace Overtrue\LaravelOpenTelemetry\Traits;
44

5+
use Illuminate\Http\Request;
56
use Illuminate\Support\Arr;
67
use Illuminate\Support\Str;
8+
use OpenTelemetry\API\Trace\SpanInterface;
9+
use Symfony\Component\HttpFoundation\Response;
710

811
trait InteractWithHttpHeaders
912
{
@@ -59,4 +62,30 @@ protected function normalizeHeaders(array $headers): array
5962
fn (string $header) => strtolower(trim($header)),
6063
);
6164
}
65+
66+
protected function recordHeaders(SpanInterface $span, Request|Response $http): SpanInterface
67+
{
68+
$prefix = match (true) {
69+
$http instanceof Request => 'http.request.header.',
70+
$http instanceof Response => 'http.response.header.',
71+
};
72+
73+
foreach ($http->headers->all() as $key => $value) {
74+
$key = strtolower($key);
75+
76+
if (! static::headerIsAllowed($key)) {
77+
continue;
78+
}
79+
80+
$value = static::headerIsSensitive($key) ? ['*****'] : $value;
81+
82+
if (is_array($value)) {
83+
$value = implode(', ', $value);
84+
}
85+
86+
$span->setAttribute($prefix.$key, $value);
87+
}
88+
89+
return $span;
90+
}
6291
}

0 commit comments

Comments
 (0)