Logging
Description — Couchbase Lite JavaScript — Logging and Debugging
Overview
Couchbase Lite JavaScript uses LogTape for logging, providing structured, configurable logging that helps diagnose issues during development and production.
LogTape is easy to use, unobtrusive, and flexible. It’s designed to integrate with whatever logging system your application currently uses.
| If you don’t configure logging, LogTape logs nothing by default. |
Effective logging is essential for:
-
Debugging development issues
-
Monitoring production applications
-
Diagnosing replication problems
-
Understanding query performance
-
Tracking storage operations
Installation
LogTape must be installed separately from Couchbase Lite:
npm install @logtape/logtape
For additional LogTape documentation, see: LogTape Installation Guide
Log Categories
Couchbase Lite organizes logs using a hierarchical category system.
Root Category: CouchbaseLite (exported as LogCategory constant)
Subcategories:
-
DB- Database operations -
Query- Query execution and optimization -
Sync- Replication and synchronization
You can configure different log levels for each subcategory or route them to different sinks.
Quick Start
Here’s a simple example that configures Couchbase Lite to log messages at "info" priority and above to the console:
import * as cbl from '@couchbase/lite-js';
import * as logtape from '@logtape/logtape';
await logtape.configure({
sinks: {
console: logtape.getConsoleSink(),
},
loggers: [
{
category: cbl.LogCategory,
lowestLevel: 'info',
sinks: ['console'],
}
],
});
For more configuration examples, see: LogTape Quick Start
Log Levels
LogTape supports these log levels (from least to most verbose):
-
fatal- Critical errors that cause application failure -
error- Errors that prevent operations from completing -
warning- Potential issues that don’t prevent operation -
info- Informational messages about normal operations -
debug- Detailed information for debugging
|
Performance Consideration LogTape at debug level can generate a lot of logs, especially for:
Recommend: Use debug only during development or targeted troubleshooting. |
Configuring Logging
Basic Configuration
import { LogCategory } from '@couchbase/lite-js';
import { configure, getConsoleSink } from '@logtape/logtape';
await configure({
sinks: {
console: getConsoleSink(),
},
loggers: [
{
category: LogCategory,
lowestLevel: 'debug',
sinks: ['console'],
}
],
});
Category-Specific Logging
Configure different log levels for different Couchbase Lite subsystems:
import { LogCategory } from '@couchbase/lite-js';
import { configure, getConsoleSink } from '@logtape/logtape';
await configure({
sinks: {
console: getConsoleSink(),
},
loggers: [
// General Couchbase Lite logs at info level
{
category: LogCategory,
lowestLevel: 'info',
sinks: ['console'],
},
// Verbose database logs
{
category: [LogCategory, 'DB'],
lowestLevel: 'debug',
sinks: ['console'],
},
// Verbose query logs
{
category: [LogCategory, 'Query'],
lowestLevel: 'debug',
sinks: ['console'],
},
// Verbose sync logs
{
category: [LogCategory, 'Sync'],
lowestLevel: 'debug',
sinks: ['console'],
},
],
});
| 1 | Root Couchbase Lite category at info level |
| 2 | Database operations at debug level |
| 3 | Query operations at debug level |
| 4 | Replication/sync operations at debug level |
Log Sinks
LogTape sinks determine where log messages are sent.
Console Sink
The console sink outputs logs to the browser console:
import { configure, getConsoleSink } from '@logtape/logtape';
import { LogCategory } from '@couchbase/lite-js';
await configure({
sinks: {
console: getConsoleSink(),
},
loggers: [
{
category: LogCategory,
lowestLevel: 'info',
sinks: ['console'],
}
],
});
File Sink
For Node.js environments (like Electron), you can log to files:
import { getFileSink } from '@logtape/file';
import { configure } from '@logtape/logtape';
import { LogCategory } from '@couchbase/lite-js';
await configure({
sinks: {
file: getFileSink('cbl.log'),
},
loggers: [
{
category: LogCategory,
lowestLevel: 'debug',
sinks: ['file'],
}
],
});
| File sinks require Node.js and are not available in browser environments. |
Multiple Sinks
Send logs to multiple destinations:
import { configure, getConsoleSink } from '@logtape/logtape';
import { LogCategory } from '@couchbase/lite-js';
await configure({
sinks: {
console: getConsoleSink(),
remote: async (record) => {
// Send to remote logging service
await fetch('/api/logs', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(record),
});
},
},
loggers: [
{
category: LogCategory,
lowestLevel: 'info',
sinks: ['console', 'remote'],
}
],
});
Custom Log Sink
Create custom sinks for specialized logging needs:
import { configure } from '@logtape/logtape';
import { LogCategory } from '@couchbase/lite-js';
// Custom sink that stores logs in IndexedDB
const indexedDBSink = async (record) => {
const db = await openDB('logs', 1, {
upgrade(db) {
db.createObjectStore('entries', { autoIncrement: true });
},
});
await db.add('entries', {
timestamp: record.timestamp,
level: record.level,
category: record.category,
message: record.message,
});
};
await configure({
sinks: {
indexedDB: indexedDBSink,
},
loggers: [
{
category: LogCategory,
lowestLevel: 'info',
sinks: ['indexedDB'],
}
],
});
Sink Inheritance
LogTape uses a hierarchical category system where child loggers inherit sinks from parent loggers.
import { configure, getConsoleSink } from '@logtape/logtape';
import { LogCategory } from '@couchbase/lite-js';
await configure({
sinks: {
console: getConsoleSink(),
file: getFileSink('db.log'),
},
loggers: [
// Parent logger - all Couchbase Lite logs to console
{
category: LogCategory,
sinks: ['console'],
},
// Child logger - DB logs also go to file
{
category: [LogCategory, 'DB'],
sinks: ['file'], // Inherits 'console' from parent
},
],
});
To override inherited sinks instead of adding to them:
await configure({
sinks: {
console: getConsoleSink(),
file: getFileSink('sync.log'),
},
loggers: [
{
category: LogCategory,
sinks: ['console'],
},
{
category: [LogCategory, 'Sync'],
sinks: ['file'],
parentSinks: 'override', // Don't inherit console sink
},
],
});
Common Logging Scenarios
Debugging Database Operations
import { configure, getConsoleSink } from '@logtape/logtape';
import { LogCategory } from '@couchbase/lite-js';
await configure({
sinks: {
console: getConsoleSink(),
},
loggers: [
{
category: [LogCategory, 'DB'],
lowestLevel: 'debug',
sinks: ['console'],
}
],
});
// Now database operations will be logged
const database = await Database.open(config);
Debugging Queries
await configure({
sinks: {
console: getConsoleSink(),
},
loggers: [
{
category: [LogCategory, 'Query'],
lowestLevel: 'debug',
sinks: ['console'],
}
],
});
// Query execution will now be logged
const query = database.createQuery('SELECT * FROM tasks');
await query.execute(row => console.log(row));
Debugging Replication
await configure({
sinks: {
console: getConsoleSink(),
},
loggers: [
{
category: [LogCategory, 'Sync'],
lowestLevel: 'debug',
sinks: ['console'],
}
],
});
// Replication activity will now be logged
const replicator = new Replicator(config);
await replicator.start();
Production Logging
Production Configuration
In production, use less verbose logging:
import { configure, getConsoleSink } from '@logtape/logtape';
import { LogCategory } from '@couchbase/lite-js';
await configure({
sinks: {
console: getConsoleSink(),
remote: async (record) => {
// Only send errors and warnings to remote service
if (record.level === 'error' || record.level === 'fatal') {
await sendToLoggingService(record);
}
},
},
loggers: [
{
category: LogCategory,
lowestLevel: 'warning', // Only warnings and errors
sinks: ['console', 'remote'],
}
],
});
Error Tracking Integration
Integrate with error tracking services like Sentry:
import * as Sentry from '@sentry/browser';
import { configure, getConsoleSink } from '@logtape/logtape';
import { LogCategory } from '@couchbase/lite-js';
const sentrySink = (record) => {
if (record.level === 'error' || record.level === 'fatal') {
Sentry.captureException(new Error(record.message), {
level: record.level,
extra: {
category: record.category.join('.'),
timestamp: record.timestamp,
},
});
}
};
await configure({
sinks: {
console: getConsoleSink(),
sentry: sentrySink,
},
loggers: [
{
category: LogCategory,
lowestLevel: 'error',
sinks: ['console', 'sentry'],
}
],
});
Meta Logger
LogTape has a special "meta logger" that logs LogTape’s own internal operations. This is useful for debugging logging configuration issues.
The meta logger uses the category ["logtape", "meta"] and is automatically enabled when you call configure().
import { configure, getConsoleSink } from '@logtape/logtape';
import { LogCategory } from '@couchbase/lite-js';
await configure({
sinks: {
console: getConsoleSink(),
},
loggers: [
// Your app logging
{
category: LogCategory,
lowestLevel: 'info',
sinks: ['console'],
},
// LogTape internal logging
{
category: ['logtape', 'meta'],
lowestLevel: 'debug',
sinks: ['console'],
},
],
});
| Enabling the meta logger produces a large amount of internal logging. Use only while troubleshooting LogTape configuration issues. |
Using Browser DevTools
Console Features
Modern browsers provide rich console features for working with logs:
-
Filtering - Filter by log level or text
-
Stack traces - Click to jump to source code
-
Object inspection - Expand objects inline
-
Search - Search through console output
-
Timestamps - See when each log occurred
-
Copy - Right-click to copy log messages
Debugging Workflow
Systematic Approach
-
Enable logging for the relevant category
-
Reproduce the issue while logging is active
-
Collect logs from browser console or log files
-
Analyze logs for errors or unexpected behavior
-
Increase verbosity if needed (switch to
debuglevel) -
Fix the issue based on insights from logs
-
Verify fix with logging still enabled
Common Debugging Patterns
// Enable debug logging temporarily
await configure({
sinks: {
console: getConsoleSink(),
},
loggers: [
{
category: [LogCategory, 'Sync'],
lowestLevel: 'debug',
sinks: ['console'],
}
],
});
// Perform the operation
try {
await replicator.start();
// Check console for detailed sync logs
} catch (error) {
console.error('Replication failed:', error);
}
Troubleshooting with Logs
Common Issues and Log Patterns
Replication Not Working:
Enable Sync logging and look for:
* WebSocket connection errors
* Authentication failures (401 Unauthorized)
* Network connectivity issues
* Sync Gateway URL problems
Queries Running Slow:
Enable Query logging and look for: * "Scan collection" (no index used) * "Search index" (index used - faster) * Query execution times * Large result sets
Storage Quota Exceeded:
Enable DB logging and look for:
* QuotaExceededError messages
* Storage usage warnings
* Failed save operations
Document Conflicts:
Enable Sync logging and look for: * Conflict resolution messages * Which documents are conflicting * Automatic vs custom resolution
Advanced Topics
Conditional Logging
Enable verbose logging only in specific conditions:
const isDevelopment = process.env.NODE_ENV === 'development';
const logLevel = isDevelopment ? 'debug' : 'warning';
await configure({
sinks: {
console: getConsoleSink(),
},
loggers: [
{
category: LogCategory,
lowestLevel: logLevel,
sinks: ['console'],
}
],
});
Runtime Reconfiguration
You can reconfigure logging at runtime by calling configure() again:
// Enable verbose logging
window.enableDebugLogging = async () => {
await configure({
sinks: {
console: getConsoleSink(),
},
loggers: [
{
category: LogCategory,
lowestLevel: 'debug',
sinks: ['console'],
}
],
});
console.log('Debug logging enabled');
};
// Call from browser console: enableDebugLogging()