Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Datadog Symfony Laravel Package

okvpn/datadog-symfony

Symfony bundle for Datadog observability. Configure DogStatsD metrics, APM tracing, and event/log forwarding from your Symfony app, with container integration and environment-based settings for production monitoring.

View on GitHub
Deep Wiki
Context7

Getting Started

The 1.0.0 release adds support for Symfony 7.0 and — more importantly for day-to-day work — multiple DataDog clients with DSN-based configuration. This is the package you want when you need to ship statsd/DogStatsD metrics from PHP without dragging in a heavyweight client library.

Minimal setup:

  1. Install via Composer and register the bundle/provider. On Symfony 7 you can now register it without patching service definitions; on Laravel you'll wire the bundle's services into the container yourself (it is not a first-party Laravel package — expect to write a small service provider that maps the okvpn_datadog.clients config into bindings).
  2. Start with a single client using a DSN, not the legacy array config:
# config/packages/okvpn_datadog.yaml
okvpn_datadog:
    clients:
        default: 'datadog://127.0.0.1/namespase?tags=tg1,tg2'
  1. Inject the client (typically Okvpn\Bundle\DatadogBundle\Client / the named service) and emit your first metric: increment('orders.created'), timing('query.duration', 42), gauge('queue.depth', 12).

First use case: replace ad-hoc error_log/microtime() instrumentation on a hot path with a single named client, then add a null://null client in the test environment so CI never opens a UDP socket.

Where to look first: the bundle's DependencyInjection/Configuration.php (it is the source of truth for the clients map and DSN parsing), followed by the client factory. The DSN is the whole contract — host is the agent address, the first path segment is the namespace, and tags is a comma-separated query param. Read the factory before you write config; guessing at namespace placement is the most common onboarding mistake.

Implementation Patterns

Named clients per concern. The headline feature of 1.0.0 is that you are no longer limited to one global client. Model this explicitly:

okvpn_datadog:
    clients:
        default: 'datadog://datadog-agent:8125/app?tags=env:prod,region:eu'
        billing: 'datadog://datadog-agent:8125/billing?tags=env:prod'
        'null': null://null
        mock: mock://mock

Then inject the client you need by name rather than reaching for a global. This maps cleanly onto Laravel's contextual binding — bind billing into the billing service and keep everything else on default. Per-client namespaces mean you get free tag-based segregation in the DataDog UI without inventing a prefix convention in application code.

DSN as the environment seam. Because the entire client is described by a URL, environment switching becomes config, not code:

  • dev: datadog://127.0.0.1:8125/app
  • test/CI: null://null (accepts writes, sends nothing — fast and hermetic)
  • local benchmarking: mock://mock (in-memory, so you can assert on emitted metrics in your own test suite)

Keep the DSN in an env var (DATADOG_DSN) and reference it from the YAML. This is the single highest-value habit with this package: it makes the "do we send metrics in tests?" question disappear.

Wrap emission in a thin domain layer. Don't call the client from controllers. Introduce something like MetricsRecorder that takes the client, owns the metric names, tag vocabulary, and sampling, and is the only thing your app talks to. When you later add a second backend or want to switch client names, you change one class. This also keeps the package out of your business logic, which matters because it is a Symfony bundle at heart and you may not always be on it.

Use mock://mock for behavioural tests. The 1.0.0 mock transport is the extension point tests have been missing. In a feature test, swap the binding to the mock client, run the request, and assert that orders.created was incremented with env:test. This is far more useful than asserting on a real UDP send (which you can't observe anyway).

Multi-client fan-out for migration. During a namespace/tag migration, define default and default_v2 pointing at the same agent with different namespace/tags, emit to both for a release, compare, then delete one. Cheap, reversible, and exactly what multiple clients was built for.

Instrument at boundaries, not in loops. Emit timings around I/O (HTTP client calls, DB-heavy repository methods, queue jobs), not inside inner loops. DogStatsD is fire-and-forget UDP — volume is cheap but cardinality and hot-path overhead are not.

Gotchas and Tips

UDP is fire-and-forget — failures are silent. There is no exception when the agent is down or the DNS name is wrong. If metrics "aren't showing up," check in this order: (1) is the DSN host reachable and the port right (8125, not 8126 — that's the APM trace port), (2) is the environment actually using datadog:// and not silently falling back to null://null, (3) are you looking at the right namespace in DataDog (the path segment in the DSN).

null://null and mock://mock are quoted keys for a reason. 'null' is a reserved-ish word in YAML-adjacent parsing and null unquoted may be coerced to PHP null rather than the string null. Always quote client names that collide with scalars. The same applies if you name a client true, yes, or on.

The namespace is a path segment, not a tag. datadog://host/namespase?tags=a,b — everything after the first / (up to ?) is the metric prefix, and DataDog renders it as namespase.metric.name. Setting a namespace and prefixing metric names in code double-prefixes them (app.app.orders.created). Pick one place; the DSN is usually the better one.

tags in the DSN are global to that client. Per-metric tags are added at call time and merge with these. Watch for duplicate keys — behaviour on collision is not guaranteed to match your intent, and in DataDog the last-write-wins semantics can differ between the agent and the intake. Keep the global tag set small and stable (env, region, service); put high-cardinality values (user IDs, order IDs) nowhere near tags — that's the cardinality footgun that inflates your bill.

Namespace collision with Symfony 7 autowiring. With Symfony 7 support in 1.0.0, named clients are registered as distinct services. If you have more than one client and type-hint the client interface without a name, autowiring will pick default and may fail loudly if default isn't defined. Always explicitly select a client — via #[Autowire(service: 'okvpn_datadog.clients.billing')] on Symfony, or an explicit binding on Laravel. Do not rely on the first-defined client being the implicit one.

This is a Symfony bundle; Laravel integration is DIY. The package historically targets Symfony's DI and config tree. There is no config/datadog.php or facade. On Laravel you will need your own service provider to translate the clients map into singletons, and your own config file — don't expect php artisan vendor:publish to do anything. Budget an hour for that adapter and keep it boring.

Extension points. The DSN factory is the natural seam: to add a transport (e.g. a logging client that writes metric lines to a channel), register a scheme and a factory keyed on it rather than subclassing the client. For anything more invasive — sampling, batching, buffering — wrap the client in a decorator and register that as the service, so the package's internals stay untouched and upgrades stay cheap.

Debugging tip. Point one environment at mock://mock and dump the recorded metrics at the end of a request in a middleware; you'll see every metric name, tag, and type exactly as it would be emitted, without needing a running agent. This is the fastest way to catch tag typos and accidental double-prefixing before they cost you a dashboard rewrite.

Pin the version. 1.0.0 is the first release declaring Symfony 7 support and simultaneously restructures configuration around clients/DSN. If you are upgrading from a pre-1.0 array-based config, treat the config rewrite as the migration — the old top-level keys are likely no longer read, and a stale config will simply produce no metrics rather than an error. Pin ^1.0 and do the config change in the same commit as the dependency bump.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect