Ledger logs central point for unified logging system across Ledger libraries
npx @tessl/cli install tessl/npm-ledgerhq--logs@6.13.0@ledgerhq/logs is a unified logging system for all Ledger libraries, providing centralized log dispatching and management across the Ledger Live ecosystem. It offers core logging functionality with the log() function for basic logging, a trace() function for capturing contextual information, and a LocalTracer class for maintaining persistent context across multiple log calls.
npm install @ledgerhq/logsimport { log, trace, listen, LocalTracer, type Log, type TraceContext } from "@ledgerhq/logs";For CommonJS:
const { log, trace, listen, LocalTracer } = require("@ledgerhq/logs");import { log, trace, listen, LocalTracer } from "@ledgerhq/logs";
// Basic logging
log("apdu-in", "Received APDU command", { command: "0x80040000" });
// Enhanced logging with context
trace({
type: "hw-connection",
message: "Device connected",
data: { deviceId: "nano-s-001" },
context: { sessionId: "abc123" }
});
// Listen to all log events
const unsubscribe = listen((logEntry) => {
console.log(`[${logEntry.type}] ${logEntry.message}`, logEntry.data);
});
// Tracer for maintaining context
const tracer = new LocalTracer("wallet-sync", { userId: "user123" });
tracer.trace("Starting sync", { accountCount: 5 });Core logging function for dispatching log events across the system.
/**
* Logs something
* @param type - A namespaced identifier of the log (not a level like "debug", "error" but more like "apdu-in", "apdu-out", etc...)
* @param message - A clear message of the log associated to the type
* @param data - Optional data associated to the log event
*/
function log(type: LogType, message?: string, data?: LogData): void;Advanced logging function that captures more context than the basic log function.
/**
* A simple tracer function, only expanding the existing log function
* @param params - Object containing trace information
*/
function trace(params: {
type: LogType;
message?: string;
data?: LogData;
context?: TraceContext;
}): void;Subscribe to log events with callback functions for processing or forwarding logs.
/**
* Adds a subscriber to the emitted logs
* @param cb - Callback function called for each future log() with the Log object
* @returns Function that can be called to unsubscribe the listener
*/
function listen(cb: Subscriber): Unsubscribe;Class-based tracer for maintaining persistent context across multiple log calls.
/**
* A simple tracer class, that can be used to avoid repetition when using the `trace` function
*/
class LocalTracer {
/**
* Creates a new LocalTracer instance
* @param type - A given type (not level) for the current local tracer ("hw", "withDevice", etc.)
* @param context - Anything representing the context where the log occurred
*/
constructor(type: LogType, context?: TraceContext);
/**
* Trace a message with the tracer's type and context
* @param message - Log message
* @param data - Optional additional data (Note: signature uses TraceContext but data is passed as LogData to trace function)
*/
trace(message: string, data?: TraceContext): void;
/**
* Get the current context
* @returns Current context or undefined
*/
getContext(): TraceContext | undefined;
/**
* Set the context (mutates instance)
* @param context - New context
*/
setContext(context?: TraceContext): void;
/**
* Merge additional context (mutates instance)
* @param contextToAdd - Context to merge with existing context
*/
updateContext(contextToAdd: TraceContext): void;
/**
* Get the current log type
* @returns Current log type
*/
getType(): LogType;
/**
* Set the log type (mutates instance)
* @param type - New log type
*/
setType(type: LogType): void;
/**
* Create a new instance with an updated type (immutable)
* @param type - New log type
* @returns New LocalTracer instance
*/
withType(type: LogType): LocalTracer;
/**
* Create a new instance with a new context (immutable)
* @param context - New context (can be undefined to reset)
* @returns New LocalTracer instance
*/
withContext(context?: TraceContext): LocalTracer;
/**
* Create a new instance with an updated context (immutable)
* @param contextToAdd - Context to merge with existing context
* @returns New LocalTracer instance
*/
withUpdatedContext(contextToAdd: TraceContext): LocalTracer;
}/**
* Context data structure for tracing system
*/
type TraceContext = Record<string, unknown>;
/**
* Data associated with log events
*/
type LogData = any;
/**
* Namespaced identifier for log types
*/
type LogType = string;
/**
* Core log object structure
*/
interface Log {
/** A namespaced identifier of the log (not a level like "debug", "error" but more like "apdu", "hw", etc...) */
type: LogType;
/** Optional log message */
message?: string;
/** Data associated to the log event */
data?: LogData;
/** Context data, coming for example from the caller's parent, to enable a simple tracing system */
context?: TraceContext;
/** Unique id among all logs */
id: string;
/** Date when the log occurred */
date: Date;
}
/**
* Function to unsubscribe from log events
*/
type Unsubscribe = () => void;
/**
* Callback function for log event subscribers
*/
type Subscriber = (log: Log) => void;import { LocalTracer } from "@ledgerhq/logs";
// Create a tracer for a specific operation
const syncTracer = new LocalTracer("account-sync", {
userId: "user123",
sessionId: "session456"
});
// Log start of operation
syncTracer.trace("Starting account synchronization");
// Log progress with additional data
syncTracer.trace("Fetching account data", { accountId: "acc789" });
// Create new tracer for sub-operation
const apiTracer = syncTracer.withType("api-call");
apiTracer.trace("Calling accounts endpoint", {
url: "/api/accounts",
method: "GET"
});
// Log completion
syncTracer.trace("Account synchronization completed", {
duration: 1250,
accountsUpdated: 3
});import { log, listen } from "@ledgerhq/logs";
// Set up log processing
const unsubscribe = listen((logEntry) => {
// Forward critical logs to monitoring service
if (logEntry.type.includes("error") || logEntry.type.includes("critical")) {
sendToMonitoring(logEntry);
}
// Store all logs in local database
saveToDatabase(logEntry);
// Debug output in development
if (process.env.NODE_ENV === "development") {
console.log(`[${logEntry.date.toISOString()}] ${logEntry.type}: ${logEntry.message}`);
}
});
// Emit various log types throughout application
log("device-connection", "Hardware wallet connected", { deviceType: "Nano S" });
log("transaction-broadcast", "Transaction sent to network", { txHash: "0x..." });
log("error-recovery", "Recovered from network error", { attempts: 3 });
// Clean up when done
unsubscribe();import { LocalTracer } from "@ledgerhq/logs";
class WalletService {
private tracer: LocalTracer;
constructor(userId: string) {
this.tracer = new LocalTracer("wallet-service", { userId });
}
async processTransaction(txData: any) {
// Create operation-specific tracer
const txTracer = this.tracer.withUpdatedContext({
operation: "process-transaction",
txId: txData.id
});
txTracer.trace("Starting transaction processing");
try {
// Validation step
const validationTracer = txTracer.withType("validation");
validationTracer.trace("Validating transaction data");
// Processing step
const processingTracer = txTracer.withType("processing");
processingTracer.trace("Processing transaction", { amount: txData.amount });
txTracer.trace("Transaction processed successfully");
} catch (error) {
txTracer.trace("Transaction processing failed", { error: error.message });
throw error;
}
}
}When used in browser environments, @ledgerhq/logs automatically exposes the listen function globally for debugging purposes:
// Available in browser console for debugging
window.__ledgerLogsListen((log) => {
console.log("Debug log:", log);
});