elastic/ecs-logging
ECS Logging for PHP transforms application logs into structured logs that comply with Elastic Common Schema. Supports Monolog 3, works with Filebeat, Elasticsearch, and Kibana, and helps correlate logs with APM traces for better observability.
Architecture fit
elastic/ecs-logging is not a logging framework — it is a Monolog formatter (plus a small set of ECS type/value objects: Error, Service, User, Tracing). That is architecturally attractive for Laravel: Laravel already uses Monolog internally, so this package slots in as a rendering concern at the edge of the existing logging stack rather than requiring a replacement of Illuminate\Log. There is no transport, no agent, no daemon, and no Elasticsearch client bundled — which keeps coupling low and means an exit to a different shipper (Vector, Fluent Bit, OTel Collector) remains viable if the JSON is ECS-compliant.
The consequence of being formatter-only is that the package delivers half of an observability pipeline. The other half — Filebeat filestream input with parsers.ndjson (overwrite_keys, add_error_key, expand_keys) or the equivalent Kubernetes/Docker autodiscover annotations — is documented but must be built and owned by us. That is genuinely more work than a composer require.
Integration feasibility
Feasible with low engineering effort. The likely shape in Laravel is a dedicated channel in config/logging.php with a StreamHandler/RotatingFileHandler (or php://stderr for containerized deploys) and a formatter key pointing at Elastic\Monolog\Formatter\ElasticCommonSchemaFormatter. Context enrichment maps cleanly onto Laravel's existing Log::withContext() and per-call context arrays, since the formatter passes through additional keys — service, user, and trace context can therefore be populated from middleware (request ID, tenant ID, authenticated user, APM trace ID) without touching call sites.
Two feasibility caveats. First, the formatter is instantiated by the container from config, so any constructor arguments (service name/version, ECS version) need an explicit binding or a formatter_with-style config; this must be validated before promising a "config-only" rollout. Second, the documented context pattern uses typed objects (EcsError, Service, User, Tracing) rather than plain arrays — this is a developer-ergonomics decision, not a technical blocker, but it means either adopting those classes or writing thin wrappers.
Technical risk
Elastic\Monolog\Formatter\ElasticCommonSchemaFormatter; docs/reference/setup.md states Monolog v2.* only and shows Elastic\Monolog\v2\Formatter\ElasticCommonSchemaFormatter. Both namespaces appear to exist for historical reasons, but the guidance is contradictory. Which class is current, and which Monolog major is supported on our Laravel version, must be resolved from source, not docs, before commitment.ecs.version: 1.2.0. Elasticsearch index templates, Kibana Logs views, and downstream detections expect a specific ECS major; a formatter pinned to an old ECS version can silently produce fields that don't match current mappings.logs-* data views, and the trace-correlation story all assume the Elastic stack. If our log backend is Datadog or Splunk, the package still delivers value (ECS-shaped JSON is a reasonable neutral schema) but the documentation stops being useful.User::setEmail() and arbitrary context passthrough make it easy to write emails, tokens, or request bodies into structured, indexed, retained storage. User::setHash() exists for obfuscation but is opt-in. This is a compliance surface, not a bug.Key questions
Stack fit
Good fit for a Laravel 10/11 + PHP 8.1+ estate. Laravel's LogManager already builds Monolog handlers per channel, and the formatter config key gives a zero-code injection point. php://stderr + Filebeat as a sidecar/DaemonSet is the cleanest target for containerized deployments; RotatingFileHandler + Filebeat filestream is the fallback for VM/bare-metal.
Migration path
Incremental, channel-by-channel, with no big-bang cutover:
ecs channel, verify emitted JSON against the ECS logging spec and confirm the formatter namespace/monolog version.ndjson + expand_keys + overwrite_keys + add_error_key, confirm dotted keys (log.level, trace.id) expand into the expected hierarchy and map cleanly.service.name/service.version (from config), trace.id (from APM agent if present), user.id (hashed), labels.environment/tenant.id.Compatibility
The formatter is a leaf dependency with no Elasticsearch client requirement, so it will not conflict with our existing PSR-3/Monolog stack. The only hard constraint is the Monolog major. Verify composer why monolog/monolog across the estate before standardizing — a mixed Monolog 2/3 fleet means two formatter namespaces in play, which is a support cost, not a blocker.
Sequencing
Schema convention and PII policy must land before the pilot, not after — retrofitting redaction onto shipped logs is expensive. Ingestion (step 2) must precede any volume, because unstructured JSON landing in Elasticsearch without expand_keys produces unusable indices and a reindex. Trace correlation should be sequenced after structured logging is stable, since it depends on an APM agent being present.
Maintenance
Low and favorably shaped. Apache-2.0, formatter-only, no runtime agent, no network calls, no state. Version upgrades are composer update and a regenerated lock file. The real maintenance burden is not the package — it is the Filebeat/collector config, the index templates, and the field convention, all of which we now own. Budget platform-engineering time for those, not application-engineering time.
Counterweight: low release cadence and a small maintainer pool (Elastic's APM team). Patch latency for a JSON-escaping or timestamp bug could be measured in months. Mitigation is trivial here — the formatter is small enough that a fork-and-patch is a realistic fallback, which materially de-risks the low-activity signal.
Support
Upstream support is documentation plus GitHub issues; there is no SLA and adoption is thin enough that community answers will be scarce. Internal support load will concentrate in one or two engineers who build the pilot. Expect to answer "why is my log line broken JSON" questions during ramp — the add_error_key Filebeat setting exists precisely for that and should be enabled from day one.
Scaling
Cost and throughput both move in the wrong direction relative to plain text logs. Per-line JSON with a fixed envelope (@timestamp, ecs.version, log.logger, log.level) is meaningfully larger, and json_encode per record adds measurable CPU at high log volume. For high-throughput services, this argues for: raising log thresholds (this package is a strong forcing function for reducing INFO-level noise), sampling, and async/buffered handlers. Log storage cost should be modeled, not assumed — structured logs also make retention tiers and field-level exclusion possible, which can offset the size increase.
Failure modes
JsonSerializable) can produce a broken or truncated line. Downstream, one bad line per request can drop that event entirely. The EcsError/EcsThrowable wrappers exist to handle the most common case (exceptions) — they should be mandatory in error-path review.log or message can collide with the ECS envelope. Whether the formatter sanitizes or overwrites is worth an explicit test; the release notes mention label-key sanitization, implying this class of bug has occurred before.expand_keys, log.level lands as a literal dotted field name and breaks every Kibana query written against log.level. This is a deploy-time failure with no runtime error — it surfaces as an empty dashboard.@timestamp; older pins or forks may not have it. Verify UTC with explicit offset in emitted output.Ramp-up
Developer ramp-up is genuinely low — one config block, one middleware, one shared package. Half a day per team. The asymmetric cost is on the operations side: Filebeat configuration, index templates, data views, and field-convention governance represent a multi-week platform investment that must be funded separately. The honest TPM framing is that this package is a small application change that implies a medium-sized observability platform commitment — and the decision to proceed should be justified by the platform investment, not by the 21-star composer package.
How can I help you explore Laravel packages today?