diff --git a/composer.json b/composer.json index 3af9fab2..86d0586b 100644 --- a/composer.json +++ b/composer.json @@ -34,7 +34,9 @@ "psr/http-server-middleware": "^1.0", "middlewares/fast-route": "^1.2.1", "middlewares/request-handler": "^1.4.0", - "squizlabs/php_codesniffer": "3.*" + "squizlabs/php_codesniffer": "3.*", + "illuminate/http": "^7.20", + "illuminate/routing": "^7.20" }, "config": { "sort-packages": true diff --git a/src/Zipkin/Instrumentation/Laravel/Middleware.php b/src/Zipkin/Instrumentation/Laravel/Middleware.php new file mode 100644 index 00000000..27446cd7 --- /dev/null +++ b/src/Zipkin/Instrumentation/Laravel/Middleware.php @@ -0,0 +1,107 @@ +tracer = $tracing->getTracing()->getTracer(); + $this->extractor = $tracing->getTracing()->getPropagation()->getExtractor(new RequestHeaders()); + $this->parser = $tracing->getParser(); + $this->requestSampler = $tracing->getRequestSampler(); + } + + public static function createFromTracing(Tracing $tracing): self + { + return new self(new HttpServerTracing($tracing, new DefaultParser)); + } + + public function handle(Request $request, Closure $next) + { + $extractedContext = ($this->extractor)($request); + + $span = $this->nextSpan($extractedContext, $request); + $scopeCloser = $this->tracer->openScope($span); + + if ($span->isNoop()) { + try { + return $next($request); + } finally { + $span->finish(); + $scopeCloser(); + } + } + + $span->setKind(Kind\SERVER); + $spanCustomizer = new SpanCustomizerShield($span); + $span->setName($this->parser->spanName($request)); + $this->parser->request($request, $span->getContext(), $spanCustomizer); + + try { + $response = $next($request); + $this->parser->response($response, $span->getContext(), $spanCustomizer); + return $response; + } catch (Throwable $e) { + $span->setError($e); + throw $e; + } finally { + $span->finish(); + $scopeCloser(); + } + } + + private function nextSpan(?SamplingFlags $extractedContext, Request $request): Span + { + if ($extractedContext instanceof TraceContext) { + return $this->tracer->joinSpan($extractedContext); + } + + $extractedContext = $extractedContext ?? DefaultSamplingFlags::createAsEmpty(); + if ($this->requestSampler === null) { + return $this->tracer->nextSpan($extractedContext); + } + + return $this->tracer->nextSpanWithSampler( + $this->requestSampler, + [$request], + $extractedContext + ); + } +} diff --git a/src/Zipkin/Instrumentation/Laravel/Propagation/RequestHeaders.php b/src/Zipkin/Instrumentation/Laravel/Propagation/RequestHeaders.php new file mode 100644 index 00000000..dd543b34 --- /dev/null +++ b/src/Zipkin/Instrumentation/Laravel/Propagation/RequestHeaders.php @@ -0,0 +1,21 @@ +hasHeader($key) ? $carrier->header($key)[0] : null; + } +} diff --git a/src/Zipkin/Instrumentation/Laravel/Request.php b/src/Zipkin/Instrumentation/Laravel/Request.php new file mode 100644 index 00000000..da8df12c --- /dev/null +++ b/src/Zipkin/Instrumentation/Laravel/Request.php @@ -0,0 +1,55 @@ +delegate = $delegate; + } + + public function getMethod(): string + { + return $this->delegate->getMethod(); + } + + public function getPath(): ?string + { + return $this->delegate->getPathInfo() ?: '/'; + } + + public function getUrl(): string + { + return $this->delegate->getUri(); + } + + public function getHeader(string $name): string + { + return $this->delegate->headers->get($name); + } + + /** + * @return IlluminateHttpRequest + */ + public function unwrap() + { + return $this->delegate; + } + + public function getRoute(): ?string + { + + return $this->delegate->route()->uri; + } +} diff --git a/src/Zipkin/Instrumentation/Laravel/Response.php b/src/Zipkin/Instrumentation/Laravel/Response.php new file mode 100644 index 00000000..caa13c40 --- /dev/null +++ b/src/Zipkin/Instrumentation/Laravel/Response.php @@ -0,0 +1,33 @@ +delegate = $delegate; + } + + public function getStatusCode(): int + { + return $this->delegate->getStatusCode(); + } + + /** + * @return HttpFoundationRequest + */ + public function unwrap() + { + return $this->delegate; + } +}