A Zipkin integration for Symfony applications
composer require jcchavezs/zipkin-instrumentation-symfony
This Symfony bundle provides a middleware that can be used to trace
HTTP requests. In order to use it, it is important that you declare
the middleware by adding this to app/config/services.yml
or any other
dependency injection declaration.
services:
tracing_middleware:
class: ZipkinBundle\Middleware
arguments:
- "@zipkin.default_tracing"
- "@logger"
tags:
- { name: kernel.event_listener, event: kernel.request, priority: 256 }
- { name: kernel.event_listener, event: kernel.terminate }
- { name: kernel.event_listener, event: kernel.exception }
@zipkin.default_tracing
is a Zipkin\DefaultTracing
instance which is being
built based on the configuration (add this to app/config/config.yml
):
zipkin:
noop: false # if set to true, no request will be traced
service_name: my_service # the name of the service
sampler:
type: percentage
percentage: 0.1
Besides always
, and never
there are three other sampling strategies: by path, by route and
by percentage, however it is also possible yo use your own sampler.
It is important to mention that the sampling decision is made on two situations: a) when a new trace is being started, b) when the extracted context does not include a sampling decision.
This is for those cases where you want to make a sampling decision based on the url path:
zipkin:
...
sampler:
type: path
path:
included_paths:
- "/my/resource/[0-9]"
excluded_paths:
- "/another/path/"
This is for those cases where you want to make a sampling decision based on the symfony route:
zipkin:
...
sampler:
type: route
path:
included_paths:
- "my_route"
excluded_paths:
- "another_route"
This one is for those cases where you want to sample only a percentage of the requests (a.k.a "Sampling rate")
zipkin:
...
sampler:
type: percentage
percentage: 0.1
You can pass a custom sampler as long as it implements the Zipkin\Sampler
interface.
Check the Custom Tracing section for more details.
By default, the bundle reports to Log
reporter which wraps @logger
.
This is the most common use case, it reports to a HTTP backend of Zipkin
zipkin:
...
reporter:
type: http
http:
endpoint_url: http://zipkin:9411/api/v2/spans
You can add tags to every span being created by the tracer. This functionality is useful when you need to add tags like instance name.
services:
tracing_middleware:
class: ZipkinBundle\Middleware
arguments:
- "@zipkin.default_tracing"
- "@logger"
- { instance: %instance_name% }
tags:
- { name: kernel.event_listener, event: kernel.request, priority: 256 }
- { name: kernel.event_listener, event: kernel.terminate }
- { name: kernel.event_listener, event: kernel.exception }
Although this bundle provides a tracer based on the configuration parameters
under the zipkin
node, you can inject your own tracing component
to the
middleware as long as it implements the Zipkin\Tracing
interface:
services:
tracing_middleware:
class: ZipkinBundle\Middleware
arguments:
- "@my_own_tracing"
- "@logger"
tags:
- { name: kernel.event_listener, event: kernel.request, priority: 256 }
- { name: kernel.event_listener, event: kernel.terminate }
- { name: kernel.event_listener, event: kernel.exception }
Span customizers allow the user to add custom information to the span based on
the request. For example by default the span name is being defined by the HTTP
verb. This approach is a not so bad option seeking for low cardinality in span
naming. A more useful approach is to use the route path: /user/{user_id}
however
including the @router
in the middleware is expensive and reduces its performance
thus the best is to precompile (aka cache warm up) a map of name => path
in cache
that can be used to resolve the path in runtime.
services:
zipkin.span_customizer.by_path_namer:
class: ZipkinBundle\SpanCustomizers\ByPathNamer\SpanCustomizer
factory: [ZipkinBundle\SpanCustomizers\ByPathNamer\SpanCustomizer, 'create']
arguments:
- "%kernel.cache_dir%"
zipkin.span_customizer.by_path_namer.cache_warmer:
class: ZipkinBundle\SpanCustomizers\ByPathNamer\CacheWarmer
arguments:
- "@router"
tags:
- { name: kernel.cache_warmer, priority: 0 }
tracing_middleware:
class: ZipkinBundle\Middleware
arguments:
- "@zipkin.default_tracing"
- "@logger"
- { instance: %instance_name% }
- "@zipkin.span_customizer.by_path_namer"
tags:
- { name: kernel.event_listener, event: kernel.request, priority: 256 }
- { name: kernel.event_listener, event: kernel.terminate }
- { name: kernel.event_listener, event: kernel.exception }
All contribution and feedback are welcome.
Run the unit tests with:
composer test
On every build we run a end to end (E2E) test against a symfony application. This test run in our CI tests but it can be also reproduced in local by:
- Go to
tests/Integration
- Run
make SYMFONY_VERSION={{SYMFONY_VERSION}} build
to build the test application (by default newest version) - Run
make run-zipkin
to start zipkin sever - Run
make run-app
to start the test application - Hit the application
curl -i http://localhost:8000/_health
- Check that traces are in zipkin (
http://localhost:9411/zipkin/
)