{"id":20027744,"url":"https://github.com/vinelab/tracing-laravel","last_synced_at":"2025-04-06T08:14:06.182Z","repository":{"id":38418242,"uuid":"201451012","full_name":"Vinelab/tracing-laravel","owner":"Vinelab","description":"Distributed tracing (OpenTracing) for Laravel made easy","archived":false,"fork":false,"pushed_at":"2024-02-05T02:54:52.000Z","size":157,"stargazers_count":79,"open_issues_count":6,"forks_count":22,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-03-30T06:09:48.392Z","etag":null,"topics":["distributed-tracing","jaeger","laravel","opentracing","tracing","zipkin"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Vinelab.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-08-09T11:02:13.000Z","updated_at":"2025-02-09T14:29:36.000Z","dependencies_parsed_at":"2024-06-19T05:27:06.892Z","dependency_job_id":"9dc4e4b8-c43a-424a-ab67-1cce5e63252f","html_url":"https://github.com/Vinelab/tracing-laravel","commit_stats":null,"previous_names":[],"tags_count":32,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Vinelab%2Ftracing-laravel","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Vinelab%2Ftracing-laravel/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Vinelab%2Ftracing-laravel/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Vinelab%2Ftracing-laravel/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Vinelab","download_url":"https://codeload.github.com/Vinelab/tracing-laravel/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247451665,"owners_count":20940944,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["distributed-tracing","jaeger","laravel","opentracing","tracing","zipkin"],"created_at":"2024-11-13T09:11:35.278Z","updated_at":"2025-04-06T08:14:06.152Z","avatar_url":"https://github.com/Vinelab.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Laravel Tracing\n\n  - [Introduction](#introduction)\n  - [Requirements](#requirements)\n  - [Installation](#installation)\n  - [Driver Prerequisites](#driver-prerequisites)\n    - [Zipkin](#zipkin)\n    - [Jaeger](#jaeger)\n    - [Null](#null)\n  - [Usage](#usage)\n    - [Creating Spans](#creating-spans)\n    - [Customizing Spans](#customizing-spans)\n    - [Retrieving Spans](#retrieving-spans)\n    - [Controlling Spans](#controlling-spans)\n  - [Flushing Spans](#flushing-spans)\n    - [Logging](#logging)\n    - [Middleware](#middleware)\n    - [Console Commands](#console-commands)\n    - [Queue Jobs](#queue-jobs)\n    - [Context Propagation](#context-propagation)\n  - [Custom Drivers](#custom-drivers)\n    - [Writing New Driver](#writing-new-driver)\n    - [Registering New Driver](#registering-new-driver)\n  - [Usage With Lumen](#usage-with-lumen)\n  - [Integrations](#integrations)\n    - [Lucid Architecture](#lucid-architecture)\n\n## Introduction\n\nDistributed tracing is the process of tracking the activity resulting from a request to an application. With this feature, you can:\n\n- Trace the path of a request as it travels across a complex system\n- Discover the latency of the components (services) along that path\n- Know which component in the path is creating a bottleneck\n- Inspect payloads that are being sent between components\n- Build execution graph for each separate component and more\n\nSimply put, distributed tracing is **a knowledge tool**. One of the most important perks of having it in your project is that deveopers can learn the system by simply following the traces it makes.\n\nSee how Uber is using distributed tracing to make sense of large number of microservices and interactions within their product:\n\n[![](http://img.youtube.com/vi/WRntQsUajow/0.jpg)](http://www.youtube.com/watch?v=WRntQsUajow \"\")\n\nA distributed trace is composed of multiple spans, which represent time spent in services or resources of those services.\n\nEach **Span** has the following:\n\n- Operation name\n- Start timestamp\n- Finish timestamp\n- Set of zero or more key:value tags to enable lookup and record additional information\n- Set of zero or more logs paired with a timestamp\n- References to related Spans (e.g. a parent)\n\nSpans are typically displayed for your view as a time axis where each span can be unfolded to inpect additional details:\n\n![image](https://i.gyazo.com/ee0065123c9d7536279e9e0f9ad60610.png)\n\nThe **Tracer** interface (available via `Trace` facade) creates Spans and understands how to Inject (serialize) and Extract (deserialize) them across process boundaries.\n\nSee [OpenTracing spec](https://opentracing.io/specification/) for more details on semantics behind distributed tracing.\n\n## Requirements\n\nThis package requires **PHP \u003e= 7.1** and **Laravel 5.5 or later**. We also offer limited Lumen support (basic tracing and http middleware).\n\n## Installation\n\nFirst, install the package via Composer:\n\n```sh\ncomposer require vinelab/tracing-laravel\n```\n\nAfter installation, you can publish the package configuration using the `vendor:publish` command. This command will publish the `tracing.php` configuration file to your config directory:\n\n```sh\nphp artisan vendor:publish --provider=\"Vinelab\\Tracing\\TracingServiceProvider\"\n```\n\nYou may configure the driver and service name in your `.env` file:\n\n```sh\nTRACING_DRIVER=zipkin\nTRACING_SERVICE_NAME=orders\n```\n\nYou should also add credentials for your respective driver as described in the section below.\n\nFor Lumen, see [dedicated section](#usage-with-lumen) with installation instructions.\n\n## Driver Prerequisites\n\n### Zipkin\n\nUse the following environment variables to configure Zipkin:\n\n```sh\nZIPKIN_HOST=localhost\nZIPKIN_PORT=9411\n```\n\nIf the collector is unreachable via a given hostname, you might see debug messages about that printed with each request. If you want to ignore these in production, simply edit your logging configuration to omit `debug` level messages.\n\n### Jaeger\n\nJaeger is not \"officially\" supported because of the lack of stable instrumentation for PHP.\n\nHowever, you can still post spans to Jaeger collector with Zipkin driver using [Zipkin compatible HTTP endpoint](https://www.jaegertracing.io/docs/1.11/features/#backwards-compatibility-with-zipkin). In fact, that's the recommended way to use this library since Jaeger's UI is just that much more convenient than Zipkin's.\n\nThere are some downsides, however:\n- you won't be able to avail of some Jaeger specific features like contextualized logging since Zipkin only supports tags and time annotations\n- HTTP is your only choice of transport (no UDP option)\n\nWe'll consider improving Jaeger support once its instrumetation matures.\n\n### Null\n\nThe package also includes `null` driver that discards created spans.\n\n## Usage\n\nYou will work with tracing via a `Trace` facade provided by this package.\n\n### Creating Spans\n\nStarting new trace is as simple as calling `startSpan` method with name for a logical operation the span represents:\n\n```php\n$span = Trace::startSpan('Create Order');\n```\n\nOften, you need to continue an existing trace which is why `startSpan` also accepts additional parameter for span context. **SpanContext** may be propagated via various channels including HTTP requests, AMQP messages, arrays or even another span:\n\n```php\n$spanContext = Trace::extract($request, Formats::ILLUMINATE_HTTP);\n\n$rootSpan = Trace::startSpan('Create Order', $spanContext);\n\n$childSpan = Trace::startSpan('Validate Order', $rootSpan-\u003egetContext())\n```\n\nThe possibilities are limitless. Refer to [Context Propagation](#context-propagation) section for more details.\n\n### Customizing Spans\n\nOverride span name:\n\n```php\n$span-\u003esetName('Create Order');\n```\n\nAdd tags, which may be used as lookup keys (to search span on UI) or additional details:\n\n```php\n$span-\u003etag('shipping_method', $shipping_method);\n```\n\n### Retrieving Spans\n\nYou can retrieve the current span, which is also your most recently created span:\n\n```php\n$span = Trace::getCurrentSpan()\n```\n\nThe first span you create when processing a request in the service is called a root span (not to mix with the global root span of the trace):\n\n\u003e After you call [flush](#flushing-spans), the root span is reset.\n\n```php\n$span = Trace::getRootSpan()\n```\n\n### Controlling Spans\n\nYou may finish the span by calling `finish` on it. Span duration is derived by subtracting the start timestamp from this:\n\n```php\n$span-\u003efinish()\n```\n\nYou can log additional data between span start and finish. For example, `annotate` creates a time-stamped event to explain latencies:\n\n```php\n$span-\u003eannotate('Order Validated')\n```\n\n## Flushing Spans\n\nFlush refers to the process of sending all pending spans to the transport. It will also reset the state of the tracer including the active spans and UUID:\n\n```php\nTrace::flush()\n```\n\nMost of the time though you don't need to explicitly call `flush`.  Since PHP is designed to die after each request, we already handle finishing the root span and calling flush upon application shutdown for you.\n\nIt's only when processing requests continuously in a loop (e.g. AMQP channels) that you must resort to calling `flush` manually.\n\n### Logging\n\nEach root span is associated with a unique identifier that can be used to lookup its trace. It is recommended you include it as part of [context](https://github.com/laravel/framework/blob/v5.8.31/src/Illuminate/Foundation/Exceptions/Handler.php#L151) when reporting errors to bridge the gap between different parts of your monitoring stack:\n\n```php\n// Illuminate\\Foundation\\Exceptions\\Handler\n\n/**\n  * Get the default context variables for logging.\n  *\n  * @return array\n  */\nprotected function context()\n{\n    return array_filter([\n        'userId' =\u003e Auth::id(),\n        'uuid' =\u003e Trace::getUUID(),\n    ]);\n}\n```\n\n[Custom drivers](#custom-drivers) may also support logging structured data (not available in Zipkin) which can be used for integrating tracing with a Log facade:\n\n```php\nuse Illuminate\\Support\\Facades\\Event;\n\n/**\n * Bootstrap any application services.\n *\n * @return void\n */\npublic function boot()\n{\n    Event::listen(MessageLogged::class, function (MessageLogged $e) {\n        Tracer::getCurrentSpan()-\u003elog((array) $e);\n    });\n}\n```\n\n### Middleware\n\nThis package includes a `\\Vinelab\\Tracing\\Middleware\\TraceRequests` middleware to take care of continuing the trace from incoming HTTP request.\n\nYou should register middleware class in the `$middleware` property of your `app/Http/Kernel.php` class.\n\nThe middleware adds the following **tags** on a root span:\n- `type` (http)\n- `request_method`\n- `request_path`\n- `request_uri`\n- `request_headers`\n- `request_ip`\n- `request_input`\n- `response_status`\n- `response_headers`\n- `response_content`\n\n\u003e Request and response body are only included for whitelisted content-types. See `logging.content_types` option in your `config/tracing.php`.\n\nYou can override the default name of the span (which is `VERB /path/for/route`) in the controller:\n\n```php\nTrace::getRootSpan()-\u003esetName('Create Order')\n```\n\n### Console Commands\n\n\u003e Lumen does not support this feature, but you can still create traces for commands manually using tracer instance.\n\nLet your console commsands be traced by adding `Vinelab\\Tracing\\Contracts\\ShouldBeTraced` interface to your class.\n\nThe container span will include the following tags:\n\n- `type` (cli)\n- `argv`\n\nThe span will be named after the console command. You can override the default name of the span in the command itself:\n\n```php\nTrace::getRootSpan()-\u003esetName('Mark Orders Expired')\n```\n\n### Queue Jobs\n\n\u003e Lumen does not support this feature, but you can still create traces for jobs manually using tracer instance.\n\nLet your queue jobs be traced by adding `Vinelab\\Tracing\\Contracts\\ShouldBeTraced` interface to your job class.\n\nThe container span will include the following tags:\n\n- `type` (queue)\n- `connection_name` (i.e. sync, redis etc.)\n- `queue_name`\n- `job_input`\n\nAs the name implies, `job_input` allows you to view your job's contructor parameters as JSON. Serialization of objects to this JSON string can be controlled by implementing one of the following interfaces: `Arrayable`, `Jsonable`, `JsonSerializable`, or a `__toString` method. A fallback behavior is to print all your object's public properties.\n\n\u003e Constructor arguments must be saved as a class property with the same name (see ProcessPodcast example below).\n\nThe span will be named after the queue job class. You can override the default name of the span in the job itself:\n\n```php\napp('tracing.queue.span')-\u003esetName('Process Podcast')\n```\n\nNote here that the queue span may not necessarily be the root span of the trace. You would usually want the queue to continue the trace from where it left of when the job was dispatched. You can achieve this by simply giving SpanContext to the job's constructor:\n\n```php\nclass ProcessPodcast implements ShouldQueue\n{\n    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;\n\n    protected $podcast;\n\n    protected $spanContext;\n\n    public function __construct(Podcast $podcast, SpanContext $spanContext)\n    {\n        $this-\u003epodcast = $podcast;\n        $this-\u003espanContext = $spanContext;\n    }\n\n    public function handle(AudioProcessor $processor)\n    {\n        // Process uploaded podcast...\n    }\n}\n```\n\nThe job above can be dispatched like so:\n\n```php\nProcessPodcast::dispatch($podcast, Trace::getRootSpan()-\u003egetContext());\n```\n\nThe rest will be handled automatically. Note that SpanContext will be excluded from logged `job_input`.\n\n**This package doesn't automatically handle tracing of queued closures and queued event listeners.** You can still trace them manually by opening and closing spans. Improving support for these features might be considered for future versions of the package.\n\n### Context Propagation\n\nAs we talked about previously, the tracer understands how to inject and extract trace context across different applications (services).\n\nWe have already seen the example of extracting trace from HTTP request:\n\n```php\n$spanContext = Trace::extract($request, Formats::ILLUMINATE_HTTP);\n```\n\nOf course, you don't need to do this manually because this package already includes a [middleware](#middleware) to handle this for you, but the trace may not necessarily come from HTTP request.\n\nThe second parameter is a format descriptor that tells us how to deserialize tracing headers from given carrier. By default, the following formats are supported:\n\n```php\nuse Vinelab\\Tracing\\Propagation\\Formats;\n\n$spanContext = Trace::extract($carrier, Formats::TEXT_MAP);\n$spanContext = Trace::extract($carrier, Formats::PSR_REQUEST);\n$spanContext = Trace::extract($carrier, Formats::ILLUMINATE_HTTP);\n$spanContext = Trace::extract($carrier, Formats::AMQP);\n$spanContext = Trace::extract($carrier, Formats::GOOGLE_PUBSUB);\n```\n\nYou may also add your own format using `registerExtractionFormat` method.\n\n```php\nTrace::registerExtractionFormat(\"pubsub\", new PubSubExtractor());\n```\n\nThe injection format must implement `Vinelab\\Tracing\\Contracts\\Extractor`. Refer to default Zipkin implementation for example.\n\n```php\ninterface Extractor\n{\n    public function extract($carrier): ?SpanContext;\n}\n```\n\nNaturally, you can also inject existing trace context from the **current span** into a given carrier so that another service can continue the trace:\n\n```php\n$message = Trace::inject($message, Formats::AMQP);\n\n$channel-\u003ebasic_publish($message, $this-\u003eexchangeName, $routingKey);\n```\n\nBy default, the following formats are supported:\n\n```php\nuse Vinelab\\Tracing\\Propagation\\Formats;\n\n$carrier = Trace::inject($carrier, Formats::TEXT_MAP);\n$carrier = Trace::inject($carrier, Formats::PSR_REQUEST);\n$carrier = Trace::inject($carrier, Formats::ILLUMINATE_HTTP);\n$carrier = Trace::inject($carrier, Formats::AMQP);\n$carrier = Trace::inject($carrier, Formats::GOOGLE_PUBSUB);\n$carrier = Trace::inject($carrier, Formats::VINELAB_HTTP);\n```\n\nYou may also add your own format using `registerInjectionFormat` method.\n\nThe injection format must implement `Vinelab\\Tracing\\Contracts\\Injector`. Refer to default Zipkin implementation for example.\n\n```php\ninterface Injector\n{\n    public function inject(SpanContext $spanContext, \u0026$carrier): void;\n}\n```\n\nYou can also use `injectContext` method if you need to pass span context explicitly:\n\n```php\n$carrier = Trace::injectContext($carrier, Formats::TEXT_MAP, $span-\u003egetContext());\n```\n\n---\n**IMPORTANT**: You don't need to create a custom propagation format if you need to get something done quickly. You can always avail of the default `TEXT_MAP` format to inject or extract tracing headers from an associative array.\n\n## Custom Drivers\n\n### Writing New Driver\n\nNew drivers must adhere to `Vinelab\\Tracing\\Contracts\\Tracer` contract. Refer to the default ZipkinTracer imlementation for example.\n\n```php\nuse Vinelab\\Tracing\\Contracts\\Extractor;\nuse Vinelab\\Tracing\\Contracts\\Injector;\nuse Vinelab\\Tracing\\Contracts\\Span;\nuse Vinelab\\Tracing\\Contracts\\SpanContext;\n\npublic function startSpan(string $name, SpanContext $spanContext = null, ?int $timestamp = null): Span;\npublic function getRootSpan(): ?Span;\npublic function getCurrentSpan(): ?Span;\npublic function getUUID(): ?string;\npublic function extract($carrier, string $format): ?SpanContext;\npublic function inject($carrier, string $format);\npublic function injectContext($carrier, string $format, SpanContext $spanContext);\npublic function registerExtractionFormat(string $format, Extractor $extractor): array;\npublic function registerInjectionFormat(string $format, Injector $injector): array;\npublic function flush(): void;\n```\n\n### Registering New Driver\n\nOnce you have written your custom driver, you may register it using the extend method of the `TracingDriverManager`. You should call the `extend` method from the `boot` method of your `AppServiceProvider` or any other service provider used by your application. For example, if you have written a `JaegerTracer`, you may register it like so:\n\n```php\nuse Vinelab\\Tracing\\TracingDriverManager;\n\n/**\n * Bootstrap any application services.\n *\n * @return void\n */\npublic function boot()\n{\n    resolve(TracingDriverManager::class)-\u003eextend('jaeger', function () {\n        return new JaegerTracer;\n    });\n}\n```\n\nOnce your driver has been registered, you may specify it as your tracing driver in your environment variables:\n\n```\nTRACING_DRIVER=jaeger\n```\n\n## Usage With Lumen\n\nYou need to register service provider manually in `bootstrap/app.php` file:\n\n```php\n$app-\u003eregister(Vinelab\\Tracing\\TracingServiceProvider::class);\n```\n\nYou should also register middleware in the same file:\n\n```php\n$app-\u003emiddleware([\n    Vinelab\\Tracing\\Middleware\\TraceRequests::class,\n]);\n```\n\nAdd the following lines to the end of `public/index.php` file:\n\n```php\n$tracer = app(Vinelab\\Tracing\\Contracts\\Tracer::class);\n\noptional($tracer-\u003egetRootSpan())-\u003efinish();\n$tracer-\u003eflush();\n```\n\nFinally, you may also want to copy over `config/tracing.php` from this repo if you need to customize default settings.\n\nIf you don't use facades in your Lumen project, you can resolve tracer instance from container like this:\n\n```php\nuse Vinelab\\Tracing\\Contracts\\Tracer;\n\napp(Tracer::class)-\u003estartSpan('Create Order')\n```\n\nNote that Lumen currently doesn't support automatic tracing for console commands and jobs because it doesn't dispatch some events and terminating callbacks. However, you can still create traces manually where you need them.\n\n## Integrations\n\n### Lucid Architecture\n\nThis package includes optional `Vinelab\\Tracing\\Integration\\Concerns\\TracesLucidArchitecture` trait to enable tracing for [Lucid projects](https://github.com/lucid-architecture/laravel-microservice):\n\n```php\nclass TracingServiceProvider extends ServiceProvider\n{\n    use TracesLucidArchitecture;\n\n    /**\n     * Bootstrap any application services.\n     *\n     * @return void\n     */\n    public function boot()\n    {\n        $this-\u003etraceLucidArchitecture();\n    }\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvinelab%2Ftracing-laravel","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvinelab%2Ftracing-laravel","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvinelab%2Ftracing-laravel/lists"}