graham-campbell/logger-core
Core logging utilities for Laravel applications, providing a PSR-3-compatible logger foundation, context handling, and reusable components for Graham Campbell's logger packages. Lightweight and easy to integrate into existing Laravel projects.
Architecture fit. This is a shallow utility, not a framework component: a trait (plus a nullable LoggerInterface property, a lazy getLogger(), and a setLogger()) that a consuming class mixes in to obtain PSR-3 logging without hard-depending on Laravel's Log facade or logger(). It sits beneath our code, is composed by our classes rather than called directly, and ships no service provider, facade, config file, or publishable assets. That gives a small integration surface and a contained blast radius — but it also means the package delivers nothing on its own. It is the shared foundation of the Graham Campbell logger family, not the thing that makes logging work in a Laravel app. The TPM-relevant consequence is coupling: mixing the trait into our classes imports vendor-chosen method names, visibility, and a mutable logger property into our own class hierarchy, in exchange for roughly fifteen lines of logic we could own outright.
Integration feasibility. Mechanically trivial — composer require, a use statement, and (for Lumen, which does not auto-discover) an explicit $app->register(...) in bootstrap/app.php. The v1.1.0 changelog asserts "official lumen support," but provides no version matrix, no registration snippet, and no feature-parity claim, so treat it as a historical note rather than a compatibility guarantee. The real feasibility verdict is driven by lifecycle signals, not mechanics: the source repository is GrahamDeprecated/Laravel-Logger-Core, the score is 0.3, stars and dependents are zero, and the only evidence of investment is "added a couple of tests." For planning purposes this is an archived artifact, and any integration plan must assume we are the maintainer from day one.
Technical risk. High and asymmetric. Functional risk is low — a thin trait is unlikely to fail catastrophically. Lifecycle risk is high: an unmaintained dependency inside the logging/observability path is precisely the kind of thing that pins a framework upgrade. If a class in our codebase uses this trait and Laravel or PHP moves a major, there is no upstream patch and no security response, so we either fork or we don't upgrade. Secondary risks worth sizing:
psr/log major drift. LoggerInterface signatures and LogLevel changed across v1/v2/v3; a loose constraint can produce runtime signature incompatibilities under strict types that no test catches until production.setLogger() is never called, getLogger() lazily builds an unconfigured logger. Nothing throws; records simply never reach our channels or production JSON formatter.Psr\Log\LoggerAwareTrait risks duplicate property/method signatures — a fatal error at autoload.Key questions. (1) Is this already present transitively via any package we run, and if so, which version and under what psr/log constraint? (2) Which PHP/Laravel/Lumen versions are actually in scope, and does the package's constraint resolve cleanly against each? (3) Do we have queued or serialized workloads that would carry a logger instance? (4) Do we need structured context and redaction guarantees — and if so, who certifies them here? (5) Who owns the fork if we adopt and upstream is gone?
Stack fit. Fits anywhere PSR-3 fits: Laravel, Lumen, and plain PHP scripts. It is not a fit for ordinary application code in a Laravel app, where Log::, logger(), or constructor-injecting Psr\Log\LoggerInterface already provides channels, handlers, and context for free. The legitimate niche is package/SDK authoring — classes that must log without assuming the framework — and test seams where a NullLogger or TestLogger is injected to assert on records.
Build vs. buy — build. Scope is a nullable logger property, a lazy getter, and a setter. That is an afternoon of our own code with our own tests, our own psr/log constraint, and no supply-chain, versioning, or upgrade-tracking overhead. Adopting an external dependency for this buys capability we can produce internally and adds risk we cannot offload. Where we own the constructor, prefer explicit LoggerInterface injection over any trait — same decoupling, better static analysis, no shared mutable state.
Migration path. If we discover the trait in our code, plan a copy-and-remove, not an upgrade: (1) inventory transitive presence; (2) vendor the trait into an internal namespace with a fixed psr/log constraint; (3) swap the use statement at call sites; (4) composer remove graham-campbell/logger-core; (5) add the serialization guard (__sleep/__serialize dropping the logger) if any consuming class is queued. This preserves behavior, drops the abandoned dependency, and puts the version constraint under our control.
Compatibility. There is no published version matrix, no CI evidence, and no support window. If we proceed at all, we validate against our exact PHP/Laravel/Lumen matrix on a throwaway branch, and we specifically exercise the Lumen path — Lumen's reduced container means auto-discovery-style features can silently no-op rather than fail loudly.
Sequencing. Lowest-effort, highest-value first: (1) add the package to the service template's dependency deny-list / guidance note so it does not propagate by default; (2) record the decision in an ADR alongside the deprecated status, zero dependents, and thin test coverage; (3) if the trait pattern is attractive, implement an internal equivalent with handler/formatter/channel support delegated to Monolog and Laravel config, plus a test that asserts the injected logger is the container's instance; (4) only then consider replacing any existing usage.
Maintenance. We become the effective maintainer on day one. No upstream patches, no security advisories, no compatibility work for new Laravel or PHP releases. Budget a recurring line item for "review at each framework major," and expect that review to end in a fork or a removal.
Support. No community buffer: zero dependents means no one else surfaces a bug before we do. Nothing in the package maps log levels, interpolates context, or redacts sensitive fields — if a log line carries a user ID or token, the guarantees come from Monolog and our config, not from here. Support expectations should be set accordingly, including with any auditor asking about dependency provenance.
Scaling. Compute cost is negligible. The scaling concern is per-instance logger proliferation: the lazy default is per-object, so short-lived object churn yields thousands of logger instances. Mitigate by resolving one logger from the container and pushing it in at the container boundary (service provider or small factory), never at call sites. Also avoid mixing it with LoggerAwareTrait.
Failure modes. Ranked by likelihood of reaching production: (1) silent default logger — setLogger() omitted, records vanish or land on stderr, no exception; add a non-production startup assertion that the instance is the container's Monolog logger. (2) Serialization failure — queued job carrying a Closure-bearing handler fails to unserialize; guard with __sleep/__serialize. (3) psr/log signature mismatch — surfaces at runtime under strict types after a dependency bump. (4) Trait collision — fatal at autoload. (5) Lumen no-op — configuration that works via Laravel auto-discovery silently does nothing.
Ramp-up. Minutes for a mid-level engineer: read the trait source rather than the README — it is a handful of lines and tells you more than the docs, which are partly aspirational. The real ramp cost is organizational, not technical: explaining to engineering why we are not standardizing on a package whose description sounds like a logging framework but is a logger trait, and holding the line when it appears transitively in a scaffolded service. Keep the ADR short and the deny-list entry visible; that is the whole adoption story.
How can I help you explore Laravel packages today?