Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,16 @@ The most robust observability solution for Salesforce experts. Built 100% native

---

## AI Agent Skills

Nebula Logger includes [Agent Skills](https://www.skills.sh/docs) that help AI coding agents (Claude Code, GitHub Copilot, Cursor, and others) install, configure, and use Nebula Logger with the recommended patterns.

```bash
npx skills add jongpie/NebulaLogger
```

---

## Features

1. A unified logging tool that supports easily adding log entries across the Salesforce platform, using:
Expand Down
21 changes: 21 additions & 0 deletions skills.sh.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"$schema": "https://skills.sh/schemas/skills.sh.schema.json",
"notGrouped": "bottom",
"groupings": [
{
"title": "Setup",
"description": "Installation and configuration of Nebula Logger in a Salesforce org.",
"skills": ["nebula-logger-install"]
},
{
"title": "Usage",
"description": "Writing logging code across Apex, LWC, Aura, Flow, and OmniStudio.",
"skills": ["nebula-logger-apex-logging"]
},
{
"title": "Governance",
"description": "Best practices, logging levels, and governor limit considerations.",
"skills": ["nebula-logger-best-practices"]
}
]
}
200 changes: 200 additions & 0 deletions skills/nebula-logger-apex-logging/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
---
name: nebula-logger-apex-logging
description: Use this skill when the user wants to implement or update Nebula Logger usage in Apex, LWC, Aura, Flow, or OmniStudio. Covers logging APIs, save patterns, record association, scenarios, tags, async transaction linking, and save method selection.
---

# Writing Logging Code with Nebula Logger

## Core Apex Pattern

Use level-specific methods to add entries, then persist once with `Logger.saveLog()`.

```apex
public with sharing class InvoiceService {
public static void processInvoice(Id invoiceId) {
try {
Logger.info('Starting invoice processing for ' + invoiceId);
Logger.debug('Loading invoice and related records');

// Business logic here

Logger.info('Invoice processing completed successfully');
} catch (Exception ex) {
Logger.error('Invoice processing failed', ex);
throw ex;
} finally {
Logger.saveLog();
}
}
}
```

Supported level methods include `error()`, `warn()`, `info()`, `debug()`, `fine()`, `finer()`, and `finest()`.

## Exception Logging with Stack Trace

Use exception overloads to capture the full exception context.

```apex
try {
update accountsToUpdate;
} catch (DmlException ex) {
Logger.exception('Failed to update accounts', ex);
Logger.saveLog();
throw ex;
}
```

## Attach Records Instead of String IDs

Prefer structured record association over embedding IDs in free text.

```apex
Account acct = [SELECT Id, Name FROM Account WHERE Id = :accountId LIMIT 1];

Logger
.warn('Credit check returned warnings')
.setRecord(acct)
.addTag('credit-check')
.addTag('customer-onboarding');

Logger.saveLog();
```

You can also use list/map overloads such as `setRecord(List<SObject>)` and `setRecord(Map<Id, SObject>)`.

## Tags and Scenarios

Use scenarios for coarse process grouping and tags for fine-grained slicing.

```apex
Logger.setScenario('Order Fulfillment');

Logger
.info('Picking inventory started')
.addTag('inventory')
.addTag('fulfillment');

Logger.saveLog();
```

## Relate Async Child Transactions

Batch/queueable/scheduled executions run in separate transactions. Link children to a parent transaction ID.

```apex
public with sharing class ParentQueueable implements Queueable {
public void execute(QueueableContext context) {
String rootTxId = Logger.getTransactionId();
Logger.info('Parent job started');
Logger.saveLog();

System.enqueueJob(new ChildQueueable(rootTxId));
}
}

public with sharing class ChildQueueable implements Queueable {
private final String parentLogTransactionId;

public ChildQueueable(String parentLogTransactionId) {
this.parentLogTransactionId = parentLogTransactionId;
}

public void execute(QueueableContext context) {
Logger.setParentLogTransactionId(this.parentLogTransactionId);
Logger.info('Child job linked to parent log transaction');
Logger.saveLog();
}
}
```

## LWC Pattern

Use the `logger` module and call `getLogger()` once per component instance.

```js
import { LightningElement } from 'lwc';
import { getLogger } from 'c/logger';

export default class PaymentPanel extends LightningElement {
logger = getLogger();

connectedCallback() {
this.logger.setScenario('Payment UI');
this.logger.info('Payment panel initialized');
this.logger.saveLog();
}

async handleSave() {
try {
this.logger.debug('Submitting payment request');
// async work
this.logger.info('Payment submitted');
} catch (error) {
this.logger.error('Payment submit failed').setExceptionDetails(error);
} finally {
await this.logger.saveLog();
}
}
}
```

## Aura Pattern

Add the logger component in markup and call it via `component.find('logger')`.

```html
<aura:component>
<c:logger aura:id="logger" />
</aura:component>
```

```javascript
({
doInit: function (component) {
const logger = component.find('logger');
logger.info('Aura component initialized');
logger.saveLog();
}
});
```

## Flow Pattern

Use Nebula Logger invocable actions in this sequence:

1. Add one or more log entries using `Add Log Entry`, `Add Log Entry for an SObject Record`, or `Add Log Entry for an SObject Record Collection`.
2. End the flow path with `Save Log`.

Flow variable references can be passed in messages and inputs using standard merge syntax such as `{!recordId}`.

## OmniStudio Pattern

Use `CallableLogger` as a Remote Action and provide action/input maps.

```apex
Type loggerType = Type.forName('Nebula', 'CallableLogger') ?? Type.forName('CallableLogger');
if (loggerType != null) {
System.Callable logger = (System.Callable) loggerType.newInstance();

Map<String, Object> args = new Map<String, Object>{
'loggingLevel' => 'INFO',
'message' => 'OmniStudio remote action completed',
'saveLog' => true,
'tags' => new List<String>{'omnistudio', 'remote-action'}
};

logger.call('newEntry', args);
}
```

## Save Method Selection Guide

`Logger.SaveMethod` values in this repo are `EVENT_BUS`, `QUEUEABLE`, `REST`, and `SYNCHRONOUS_DML`.

| Save Method | Use when | Trade-off |
| --- | --- | --- |
| `EVENT_BUS` (default) | General-purpose app logging | Depends on platform event capacity |
| `QUEUEABLE` | You want to defer work and reduce synchronous CPU pressure | Adds async dependency and queueable execution timing |
| `REST` | You need to avoid mixed-DML constraints in current context | Requires callout path and valid session context |
| `SYNCHRONOUS_DML` | You need immediate persistence and can tolerate rollback risk | Log inserts are rolled back if transaction fails |
62 changes: 62 additions & 0 deletions skills/nebula-logger-best-practices/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
---
name: nebula-logger-best-practices
description: Use this skill when the user wants to review, harden, or standardize Nebula Logger usage across a Salesforce team. Covers operational logging standards, environment-aware settings, limit-aware design, and governance guardrails.
---

# Nebula Logger Best Practices and Governance

## Team-Wide Practices

Use these defaults when reviewing pull requests or designing logging conventions.

1. Always call `Logger.saveLog()`.
2. Place `Logger.saveLog()` in `finally` blocks for transactional code paths.
3. Use logging levels strategically. Fine-grained levels such as `FINE`, `FINER`, and `FINEST` can be combined with Logger Settings to avoid unnecessary logging overhead in performance-sensitive code paths and enable deeper diagnostics only when needed. Reserve higher-level logging (`ERROR`, `WARN`, and `INFO`) for information that is operationally significant.
4. Tune `LoggerSettings__c.LoggingLevel__c` by environment.
5. Use `Logger.setScenario()` for business-process grouping.
6. Associate records with `.setRecord(...)` instead of embedding IDs in message strings.
7. Define a controlled tag taxonomy (for example: `domain:*`, `feature:*`, `incident:*`).
8. Configure scheduled purging and retention windows (`DefaultLogPurgeAction__c`, `DefaultNumberOfDaysToRetainLogs__c`).

## Environment-Aware Logging Levels

Treat logging verbosity as an operational control, not as a code constant.

| Environment | Suggested baseline | Reason |
| --- | --- | --- |
| Production | `ERROR` or `WARN` | Reduce noise and storage impact while preserving incident signal |
| UAT | `INFO` | Validate business flows without excessive detail |
| QA | `DEBUG` or `FINE` | Support defect reproduction and integration testing |
| Sandbox/Dev | `FINE` to `FINEST` (time-boxed) | Deep diagnostics during active development |

Use hierarchy overrides (user/profile) for temporary incident windows, then revert.

## Review Checklist for Existing Code

- Does each execution path persist logs (`saveLog`) exactly once?
- Are exceptions logged with stack trace context?
- Are record references attached via `.setRecord(...)`?
- Are scenarios and tags consistent with team taxonomy?
- Is save method choice explicit when not using default `EVENT_BUS`?

## Governor and Capacity Considerations

Nebula Logger helps observability, but it still runs inside Salesforce governor boundaries.

| Constraint | Impact on logging | Mitigation |
| --- | --- | --- |
| Data storage | High-volume logs can consume custom object storage quickly | Enforce retention policy and purge schedule; lower verbosity in production |
| Platform event daily limit | `EVENT_BUS` volume can hit org event allocations | Use selective logging and switch targeted jobs to `QUEUEABLE`/`SYNCHRONOUS_DML` when needed |
| SOQL queries | Extra enrichment queries in logging paths can compound limits | Reuse queried records and avoid logging-only query fanout |
| CPU time | Heavy serialization/tagging in loops increases CPU usage | Log aggregate milestones, not every iteration, and prefer async save paths |
| Async job allocation | Overusing `QUEUEABLE` can compete with business async work | Reserve queueable saves for high-cost or mixed-DML-sensitive contexts |
| Heap size | Large payloads/exceptions can inflate transaction memory | Truncate oversized payloads and avoid dumping full object graphs |

## When Not to Use Nebula Logger

Nebula Logger is not a substitute for sound architecture.

- Do not use logging as a workaround for unclear domain design.
- Do not store secrets, credentials, or unnecessary PII in log messages.

Use Nebula Logger as a structured observability layer, combined with clean service boundaries, resilient error handling, and purposeful telemetry.
89 changes: 89 additions & 0 deletions skills/nebula-logger-install/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
---
name: nebula-logger-install
description: Use this skill when the user wants to install and configure Nebula Logger in a Salesforce org for the first time. Covers package selection, installation paths, permissions, LoggerSettings__c hierarchy, and first-run troubleshooting.
---

# Nebula Logger Installation and Initial Configuration

## Why Nebula Logger Instead of Only System.debug()

`System.debug()` is useful during active debugging, but it is not a durable observability strategy for production operations.

- Debug logs have short retention windows and are hard to query for historical analysis.
- Searching by business record, process scenario, or tags is limited compared to purpose-built logging objects.
- It does not provide a unified approach across Apex, LWC, Aura, Flow, and OmniStudio.

Nebula Logger gives teams a consistent, queryable logging model across Salesforce runtime contexts.

## Package Choice: Unlocked vs Managed

Nebula Logger supports both package types with the same core metadata, but the operating model differs.

| Area | Unlocked Package | Managed Package |
| --- | --- | --- |
| Namespace | None | `Nebula` |
| Release cadence | Faster patch cadence | Slower, stabilized cadence |
| Source visibility | Full source access in org | Packaged global API surface |
| Plugin framework | Available | Not currently available |
| Distribution model | GitHub-first OSS workflow | AppExchange-friendly for managed delivery |
| Typical recommendation | Best for most internal Salesforce teams | Best when managed namespace packaging is required |

If your team is not constrained by managed-package requirements, start with the unlocked package.

## Installation Options

Always confirm the latest version on:

- https://github.com/jongpie/NebulaLogger/releases

### Browser install links

- Unlocked sandbox: `https://test.salesforce.com/packaging/installPackage.apexp?p0=04tg70000009GaDAAU`
- Unlocked production: `https://login.salesforce.com/packaging/installPackage.apexp?p0=04tg70000009GaDAAU`
- Managed sandbox: `https://test.salesforce.com/packaging/installPackage.apexp?mgd=true&p0=04tg700000086RdAAI`
- Managed production: `https://login.salesforce.com/packaging/installPackage.apexp?mgd=true&p0=04tg700000086RdAAI`

### Salesforce CLI install

```bash
# Unlocked package (from README)
sf package install --wait 20 --security-type AdminsOnly --package 04tg70000009GaDAAU

# Managed package (from README)
sf package install --wait 30 --security-type AdminsOnly --package 04tg700000086RdAAI
```

## Permission Sets to Assign

Assign permission sets as part of rollout. Exact API names in the repo are:

| Permission Set | Purpose | Typical users |
| --- | --- | --- |
| `LoggerAdmin` | Full control of Nebula Logger data and features | Platform admins, support leads |
| `LoggerLogViewer` | Read-only access to logs and console features | Operations, QA, support analysts |
| `LoggerEndUser` | Limited day-to-day access with controlled visibility | Business users who need log visibility |
| `LoggerLogCreator` | Minimal metadata/object access needed to generate logs | Integration users, Experience Cloud component users |

## Configure LoggerSettings__c Hierarchy

Nebula Logger uses a hierarchy custom setting so behavior can be tuned at multiple scopes:

1. Org default baseline.
2. Profile-level override.
3. User-level override.

Prioritize these settings during onboarding:

| Field | What it controls |
| --- | --- |
| `IsEnabled__c` | Global on/off switch for logging behavior at the effective hierarchy level |
| `LoggingLevel__c` | Effective minimum logging level for the user/profile/org context |
| `DefaultSaveMethod__c` | Default save strategy used by `Logger.saveLog()` |
| `DefaultLogPurgeAction__c` | Default cleanup behavior for old logs |
| `DefaultNumberOfDaysToRetainLogs__c` | Retention window for generated logs |

Note: older references may mention `DefaultLoggingLevel__c`; in this codebase the active field is `LoggingLevel__c`.

## First Troubleshooting Check

If logs are not created after deployment, check permission set assignment first, especially `LoggerEndUser` (or `LoggerLogCreator` for component/integration contexts). Missing permissions are the most common post-install issue.