Skip to content

The Logger app

Two things are easy to conflate, so separate them first. The logging engine — threading one correlation id through the save, capturing the trace and budget at save-order boundaries, delivering entries rollback-safely, enforcing retention — is always-on kernel behavior. It runs whether or not anyone ever looks at a log. The Logger app is a way to browse what that engine stored: Logs and Log Entries as list views and record pages, Scenarios and Tags as their own tabs, and the correlation pivot from any Log into the three audit streams.

The app is not part of the engine. It is a standard installable package — an app definition, its tabs, list views, and record pages, all delivered as metadata over the log objects, exactly the way the Sales app or any other app is delivered. The kernel does not compile it in and does not special-case it. The engine captures and stores; the app is one package-delivered lens on what was stored. Remove the package and the engine keeps logging; the records are still there, still queryable over the query surface and the CLI — you have simply removed one browsing surface.

This page explains what the app package contains, why logs get an app rather than the setup capability, and why the package is almost entirely metadata.

Logs could have been surfaced through the setup capability — the platform-administration surface where an admin configures objects, permissions, and page layouts. They are deliberately not. The line is a clean one:

  • Setup configures the platform. Its audience is an administrator changing how the system behaves — defining fields, editing field-level security, wiring permission sets. Setup screens are editors. Setup is itself a capability a package masters, not an engine built-in.
  • The Logger app browses operational data. Its audience is a developer or support engineer reading what happened in a transaction that already ran. Nothing here is configured; records are opened, filtered, and pivoted through. That is an app, not an editor.

Routing logs through the setup capability would force every platform developer into the administration surface to answer an everyday operational question (“what did this save do?”). A dedicated app lets them browse the log objects and records directly, the same way the Sales app lets a salesperson browse accounts and opportunities. The two audiences and the two verbs — configure vs browse — are different, so the surfaces are delivered as different packages.

(The retention policy — default windows by level and scenario, the purge schedule — is configuration, and it lives with the setup capability and configuration data, consistent with the line above. And retention enforcement is the engine’s, always on. The Logger app browses logs; setup configures how long they live; the kernel is what actually purges them.)

The Logger package is not a program. It is an app definition — a name, a set of tabs, and their default list views and record pages — over objects that already exist in the kernel. Everything that would normally be “build the log UI” is inherited from the object model:

The app needs Where it comes from Logging-specific code
A tab listing Logs, with filters List views on the Log object none
A Log detail screen The Log record page none — layout only
Column/row narrowing per reader FLS + record access at read none
Reports on error rate, budget trends The report builder over Log fields none
Ad-hoc queries and export The query surface none
Scenarios / Tags management Tabs over those objects none

Because a Log is two ordinary objects, and those objects are kernel-owned, an app over them is assembled from the same primitives as any app on the platform. The only genuinely logging-specific piece of the surface is the correlation pivot — a record-page action that jumps from a Log to the execution trace, data history, and metadata audit sharing its correlationId — and even that reuses the audit surface’s existing cross-stream navigation rather than inventing one.

This is the concrete payoff of building logging into the engine’s object model. Nebula Logger must ship a “Logger Console” Lightning app, custom list views, a “Related Log Entries” component, and quick actions inside a managed package, because its Log__c is an app object that arrives with no UI. The Logger package here is a manifest, not a codebase: the engine owns the log objects, the capture, and the delivery; the package owns only the presentation.

Tab Over Purpose
Logs the Log object Browse transactions — saved views like my ERROR logs today and rolled-back saves in invoice-recalc this week; open one to its record page
Log Entries the Log Entry object Cross-transaction entry search — “every entry tagged pricing at WARN or above this week” — independent of which Log they belong to
Scenarios the scenario config See the units of work that are named, their per-scenario level threshold and retention override
Tags the tag vocabulary The typed, deploy-managed tag vocabulary and the rules that auto-apply them

Opening a Log shows what the engine already captured, laid out for reading:

  • Entries in execution order — the Log Entries for the transaction, ordered by the save phase they were raised in, each showing its level, message, participantKey, and budgetAtEntry.
  • The budget snapshot and phase runs — the governor’s tally (fuel, DML, rows, outward calls) and the phase-run counter, copied from the save result, so “this transaction spent 84/150 DML across SHAPE→COMMIT in a single pass” is visible without instrumentation.
  • The correlation pivot — a one-click jump to the execution trace, the data history, and the metadata audit for the same correlationId, and back from an error to its Log. This is the “walk one failure end to end” promise made navigable.

The same read is available as a thin CLI view — caos logs --correlation-id <correlationId> — which is a plain query over the Log object, not a separate log store. The app and the CLI show the same records because they are the same kernel-owned objects read through the same query surface. The CLI does not depend on the Logger package being installed; it reads the engine’s objects directly.

Once the package is installed, the Logger app is not universally visible. It appears for users who hold the diagnostics capabilities — diagnostics.view to open logs, diagnostics.decode to read sensitive failure detail, diagnostics.trace to raise verbose capture — the same grants that gate the execution trace. There is no separate “Logger app” permission surface: holding the diagnostics grants both admits a user to the objects and surfaces the app that browses them. A user without those capabilities does not see the app, and would see no rows if they navigated to it, because read access is enforced at the query surface — by the engine, not by hiding the app.

The advantage is the same one the whole observability design rests on, applied to the surface:

  • It inherits the platform, so it stays consistent. The Logger app uses the same list-view, record-page, report, and query machinery as every other app, so it looks and behaves like the rest of the platform and improves automatically when those primitives do. A bespoke console drifts from the platform and must be maintained against it.
  • Access is correct by construction. FLS and sharing narrow what the app shows because the engine narrows the underlying reads — not because the app re-implements a permission model. Nebula’s console must respect an access model layered on top of its objects; this app cannot show a row the reader could not query.
  • It is metadata over an engine-owned model, not a codebase. The package ships an app definition, not logging code. The capture, correlation, delivery, and retention it depends on already live in the kernel and run whether the package is present or not — so the package is small, versionable, and removable without touching the logging that keeps happening underneath. See how packages deliver UI and the developer surface for where this app definition sits among the metadata you author.