Lightning Web Components
Import getLogger from the c/logger module and call it once per component instance. The returned logger exposes the same conceptual API as the Apex Logger class - level methods, chainable enrichment, saveLog() - adapted for browser-side use.
Basic usage
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(); } }}Level methods
Every level available in Apex is available on the LWC logger. Each returns a builder that supports enrichment chaining.
this.logger.error('Critical failure');this.logger.warn('Recoverable issue');this.logger.info('Business milestone');this.logger.debug('Debug detail');this.logger.fine('Verbose detail');this.logger.finer('More verbose');this.logger.finest('Most verbose');Builder methods
Off the level call, chain any of:
.setRecord(recordOrId)- attach a record or ID..setExceptionDetails(error)- attach a JavaScriptError(message, stack, name)..setScenario(scenarioName)- scenario for this specific entry (usually you’d calllogger.setScenario(...)at the transaction level instead)..addTag(tagName)- add a tag.
this.logger.error('Payment submit failed').setRecord(this.accountId).setExceptionDetails(error).addTag('payment').addTag('checkout');saveLog is async
saveLog() returns a Promise. Await it when subsequent code depends on the save being complete - for example, in a finally block before returning from an async handler:
async handleSave() { try { // work } catch (error) { this.logger.error('Save failed').setExceptionDetails(error); } finally { await this.logger.saveLog(); }}If you don’t await it, the save still happens - but the network round-trip may not complete before your component tears down.
Reuse one logger per component
getLogger() returns a scoped logger instance. Call it once as a class field and reuse across handlers - do not call it on every log statement.
// Goodexport default class PaymentPanel extends LightningElement { logger = getLogger(); // ...}
// Wrong - creates a new scoped instance per callhandleClick() { getLogger().info('Clicked'); getLogger().saveLog();}Each getLogger() scope has its own buffer. Creating one per call fragments buffered entries across multiple saveLog() calls and creates unnecessary Log__c records.
Testing
For jest tests, mock the c/logger module:
const mockLogger = { setScenario: jest.fn(), info: jest.fn().mockReturnThis(), error: jest.fn().mockReturnThis(), setExceptionDetails: jest.fn().mockReturnThis(), addTag: jest.fn().mockReturnThis(), saveLog: jest.fn()};
jest.mock('c/logger', () => ({ getLogger: () => mockLogger}));Assert on the arguments passed to logger methods (toHaveBeenCalledWith(...)), not just that they were called. toHaveBeenCalled() alone lets regressions slip through where the message text is dropped or garbled. See Testing your instrumentation for the full pattern.
Where next
- Aura - the equivalent for Aura components.
- Testing your instrumentation - jest mocking patterns.
loggerLWC reference - the exported API.logEntryBuilderLWC reference - the builder returned by level methods.