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+ }
0 commit comments