Skip to content

vs Nebula Logger — keep, improve, drop

Nebula Logger is the best open-source articulation of what developer logging should feel like on a metadata platform, which is why the CAOS design is measured against it. But “measured against” is not “cloned from.” Much of Nebula’s architecture is scaffolding to overcome Apex and Salesforce platform limits — scaffolding CAOS does not need because it owns the kernel. This page is the opinionated verdict: keep the ergonomics, improve what the kernel does better, drop the workarounds.

The guiding principle: a Nebula capability that serves the developer is kept or improved; a Nebula capability that exists to serve the platform’s constraints is dropped.

Keep — the ergonomics developers actually want

Section titled “Keep — the ergonomics developers actually want”

These are Nebula’s genuine contributions, and CAOS keeps them because they answer developer needs, not platform limits.

Kept capability Nebula form CAOS form
A clean per-level API Logger.info(), .error(), .warn(), … log.info() / log.error() / …
Structured fields, not string soup .setField(LogEntryEvent__e.X__c, v) typed fields object
Scenarios — name the unit of work Logger.setScenario(), LoggerScenario__c first-class scenario field + per-scenario config
Tags — cross-cutting classification .addTag(), LoggerTag__c, LogEntryTagRule__mdt typed tags + rule-based tagging as config
Per-scenario level & retention overrides LogScenarioRule__mdt scenario-keyed configuration
Data masking of sensitive values LogEntryDataMaskRule__mdt masking against data classification
Retention date + purge LogRetentionDate__c, LogBatchPurger retentionDate + scheduled purge job
Browse logs as records Logger Console app, Related Log Entries LWC a standard CAOS Logger package over the log objects’ list views / record pages
Related-record association .setRecord() relatedRecord, defaulted to the triggering row

Improve — where owning the kernel does better

Section titled “Improve — where owning the kernel does better”

Same intent as Nebula, executed better because CAOS is the runner, not a framework running on top of one.

Nebula’s developer attaches context by hand — .setRecord(), .setField(), .addTag() — and the framework enriches what Limits and UserInfo expose at save time. A CAOS entry already carries its save phase, the participant that raised it, the budget spent to that point, the caller’s access, and the correlation id with nothing attached, because the kernel is executing the save and already tracks all of it. Less to type, more captured.

Nebula records Limits snapshots as best it can from Apex. CAOS attaches the governor’s actual running tally — fuel, DML, rows scanned/returned, outward calls — and the phase-run counter proving single-pass execution, because those are the kernel’s own working state. “This transaction spent 84/150 DML and 6.2s/10s CPU across SHAPE→COMMIT” is a field copy, not instrumentation.

Nebula exposes a SaveMethod enum (EVENT_BUS / QUEUEABLE / REST / SYNCHRONOUS_DML) because no single delivery mode is right under all rollback and mixed-DML conditions, so the developer must choose. CAOS routes by transaction outcome automatically — committed saves log atomically with the record, rolled-back saves log out of band — so there is nothing to choose and no wrong choice to make.

Logs are first-class objects, not an app that ships objects

Section titled “Logs are first-class objects, not an app that ships objects”

Nebula must package a console app, custom list views, quick actions, and an LWC because Log__c is an app object that does not arrive with a UI. CAOS log objects are defined by the same schema as any object, so a standard package delivers list views, record pages, reports, and the query view over them with no bespoke logging components — while FLS, sharing, and the capture/correlation engine come from the kernel and the object model themselves.

Correlation that actually spans the platform

Section titled “Correlation that actually spans the platform”

Nebula correlates within its own objects via a transaction id. CAOS threads one correlation id through the developer log, the execution trace, the data history, the metadata audit, and the error envelope — so a Log is a pivot into the whole story of a transaction, not just into other log rows.

Nebula offers seven levels — ERROR, WARN, INFO, DEBUG, FINE, FINER, FINEST — inherited from Java/System.LoggingLevel. In practice FINE/FINER/FINEST are used inconsistently and the boundary between them is arbitrary. CAOS uses five — ERROR > WARN > INFO > DEBUG > TRACE — collapsing the three fine-grained levels into one TRACE. Fewer levels means threshold configuration is comprehensible and cross-team log discipline is achievable. This is a small, opinionated simplification, reversible if a real need for finer gradation appears.

Drop — the Salesforce workarounds we refuse to port

Section titled “Drop — the Salesforce workarounds we refuse to port”

Each of these exists because of an Apex or Salesforce platform constraint. CAOS does not share the constraint, so porting the workaround would add indirection to buy a property the kernel already has.

The LogEntryEvent__e platform-event pipeline

Section titled “The LogEntryEvent__e platform-event pipeline”

Dropped. This is the biggest drop and the clearest. Nebula routes all logging through a platform event because Apex insert of a log record is rolled back with the transaction, and a “Publish Immediately” platform event is the only Apex primitive that escapes a rollback. CAOS’s transactional outbox and out-of-band telemetry sink already cross that boundary natively, so there is nothing to transport across a boundary the platform won’t otherwise let you cross. Dropping it removes a whole transport object (LogEntryEvent__e), a subscriber (LogEntryEventHandler), an unavoidable async-consistency window on every log, and publish-limit/heap pressure.

Dropped. A direct consequence of the drop above: with no platform event and a kernel that knows the outcome, there is no menu of delivery modes to expose. In particular Nebula’s SYNCHRONOUS_DML — which logs immediately but is rolled back on failure — is the exact footgun the whole design exists to avoid, and it only exists because someone sometimes needs synchronous logs and the platform event can’t give them. CAOS gives atomic-on-commit logs by default, so the trade-off disappears.

Dropped. Nebula buffers entries in memory and requires an explicit saveLog() to publish them; a forgotten flush silently loses every entry. CAOS binds persistence to the transaction boundary, so there is no unflushed state and no call to forget. The most common Nebula support issue — “my logs didn’t save” — is designed out.

suspendSaving() / resumeSaving() / flushBuffer()

Section titled “suspendSaving() / resumeSaving() / flushBuffer()”

Dropped as public API. These exist to manage the in-memory buffer and to dodge platform-event publish limits in loops. With outcome-routed delivery and no publish-limit pressure, buffer management is the kernel’s concern, not the developer’s. The underlying need — don’t let logging blow the resource budget — is kept, but as an automatic budget cap, not a manual suspend/resume dance.

Dropped as a separate mechanism; capability kept. Nebula’s Big Object archive exists because Salesforce meters standard-object storage and Big Objects are the cheap-but-awkward escape hatch — with a different query model and, per Nebula’s own issue tracker, heap pain during archival. CAOS folds “retain past the hot window without hard-deleting” into its tiered retention, one object with one query path. We keep the capability and drop the second storage model.

Dropped. Nebula can be configured to throw if you log without first setting a scenario — a guardrail against context-free Apex logs. CAOS entries are never context-free (phase, participant, correlation are always present), so forcing a scenario before you may log adds friction without adding signal. Scenarios stay optional and defaulted.

The Apex plugin framework as the primary extension model

Section titled “The Apex plugin framework as the primary extension model”

Dropped in that form. Nebula’s LoggerPlugin.Triggerable Apex/Flow plugins are how you extend an app-layer framework that can’t otherwise reach into logging. CAOS extends logging through its own effect and integration model and configuration data — a Slack notification on ERROR, for instance, is an ordinary post-commit effect, not a logger-specific plugin interface. The use cases (notify on severe logs, enrich entries) are kept as platform-native extension; the bespoke plugin SPI is not.

Nebula Logger is excellent, and roughly half of it is genuinely about developer ergonomics — the level API, scenarios, tags, structured fields, retention, browsable records. CAOS keeps all of that, and improves it by supplying context the kernel already holds. The other half is scaffolding for Apex’s inability to log across a rollback — the platform event, the save methods, the manual flush, the buffer management, the Big Object. CAOS drops that half wholesale, because the kernel already solves rollback-safe delivery. The result is more succinct for the developer and richer in captured context at the same time — the specific outcome that is only possible when logging is built into the kernel instead of bolted onto a platform.