Logging Levels
Logging levels are arranged in increasing order of severity — from detailed debugging (0) to critical failures (7). Filtering works on an inclusive principle: when a certain level is set, messages of that level and all higher levels are logged.
How Level Filtering Works
// Example 1: ERROR level
const $logger = new Logger('app');
$logger.pushHandler(new ConsoleHandler(LogLevel.ERROR));
// These logs WILL be output:
$logger.error('Loading error'); // ✓ level 4 >= 4
$logger.critical('Critical error'); // ✓ level 5 >= 4
$logger.alert('Alert'); // ✓ level 6 >= 4
$logger.emergency('Emergency'); // ✓ level 7 >= 4
// These logs WILL NOT be output:
$logger.debug('Debug'); // ✗ level 0 < 4
$logger.info('Info'); // ✗ level 1 < 4
$logger.warning('Warning'); // ✗ level 3 < 4
Main Types and Interfaces
LogRecord Log record structure:
{
channel: string, // Logger channel name
level: LogLevel, // Numeric level
levelName: LogLevelName, // Level name (e.g., 'DEBUG')
message: string, // Message text
context: Record<string, any>, // Contextual data
extra: Record<string, any>, // Additional data (added by processors)
timestamp: Date // Timestamp
}
LoggerInterface Main logger interface:
log(level: LogLevel, message: string, context?: Record<string, any>): Promise<void>
debug(message: string, context?: Record<string, any>): Promise<void>
info(message: string, context?: Record<string, any>): Promise<void>
notice(message: string, context?: Record<string, any>): Promise<void>
warning(message: string, context?: Record<string, any>): Promise<void>
error(message: string, context?: Record<string, any>): Promise<void>
critical(message: string, context?: Record<string, any>): Promise<void>
alert(message: string, context?: Record<string, any>): Promise<void>
emergency(message: string, context?: Record<string, any>): Promise<void>
Handler Log handler:
handle(record: LogRecord): Promise<boolean> // Process record
isHandling(level: LogLevel): boolean // Check level support
shouldBubble(): boolean // Whether to continue chain
setFormatter(formatter: Formatter): void // Set formatter
getFormatter(): Formatter | null // Get formatter
Formatter for converting LogRecord:
format(record: LogRecord): any
Processor for modifying LogRecord:
(record: LogRecord) => LogRecord
Main Classes
AbstractLogger Abstract base class implementing convenience methods (debug, info, etc.).
Logger Main logger implementation:
constructor(channel: string) // Create logger with specified channel
pushHandler(handler: Handler): this // Add handler
popHandler(): Handler | null // Remove last handler
setHandlers(handlers: Handler[]): this // Set list of handlers
pushProcessor(processor: Processor): this // Add processor
log(level, message, context): Promise<void> // Main logging method
NullLogger Stub for cases when logging is not required. Performs no operations.
Logger Factory
LoggerFactory Static methods for creating loggers:
createNullLogger(): LoggerInterface
createForBrowser(channel: string, isDevMode: boolean = false): LoggerInterface
createForBrowserDevelopment(channel: string, level: LogLevel = LogLevel.DEBUG): LoggerInterface
createForBrowserProduction(channel: string, level: LogLevel = LogLevel.ERROR): LoggerInterface
forcedLog(logger, action, message, context): Promise<void> // Forced logging
Processors
Processors modify log records before processing:
Built-in processors:
memoryUsageProcessor- adds memory usagepidProcessor- adds process ID
Creating a custom processor:
const customProcessor: Processor = (record) => {
record.extra.customField = 'value'
return record
}
Formatters
AbstractFormatter Base formatter class with date formatting support.
LineFormatter Formats logs into a string with template support:
// Default: '[{channel}] {levelName}: {message} {context} {extra} {date}'
new LineFormatter(formatString, dateFormat)
Available placeholders:
{channel}- channel name{levelName}- level name{message}- message{context}- context in JSON{extra}- extra data in JSON{timestamp}- unix timestamp{date}- date in specified format
JsonFormatter: Formats logs into a JSON string.
TelegramFormatter: Formats a log entry for sending to Telegram. Supports HTML markup with escaped special characters.
Handlers
AbstractHandler Base handler class with logging level and bubbling support.
ConsoleHandler Outputs logs to browser console with styling support for different levels.
ConsoleV2Handler Improved version of ConsoleHandler with more readable output.
MemoryHandler Stores logs in memory (useful for testing and debugging):
const handler = new MemoryHandler(LogLevel.DEBUG, { limit: 1000 })
const records = handler.getRecords() // Get all records
handler.clear() // Clear records
ConsolaAdapter Adapter for Consola.
WinstonAdapter Adapter for Winston.
StreamHandler Node.js stream handler for writing logs to streams.
TelegramHandlerSends logs to Telegram chat. The browser displays a warning in the console. In server-side, sends a message via the Telegram Bot API.
Usage
Basic Example
import { LoggerFactory } from '@bitrix24/b24jssdk'
const devMode = typeof import.meta !== 'undefined' && (import.meta.dev || import.meta.env?.DEV)
const $logger = LoggerFactory.createForBrowser('Example:getCrmItem', devMode)
$logger.info('User logged in', { userId: 123 })
Advanced Configuration
import { Logger, ConsoleV2Handler, LineFormatter } from '@bitrix24/b24jssdk'
const $logger = new Logger('app')
const handler = new ConsoleV2Handler(LogLevel.DEBUG)
handler.setFormatter(new LineFormatter('[{levelName}] {message}'))
$logger
.pushHandler(handler)
.pushProcessor(memoryUsageProcessor)
.pushProcessor(pidProcessor)
$logger.warning('Low memory', { freeMemory: '10MB' })
Creating a Custom Handler
class CustomHandler extends AbstractHandler {
async handle(record: LogRecord): Promise<boolean> {
// Send to server, write to file, etc.
return true // Returns true if record was processed
}
}
Features
- Asynchronous: All logging methods return a Promise.
- Chain of responsibility: Handlers are called sequentially.
- Bubbling: If a handler returns
trueandshouldBubble() === false, the chain is interrupted. - Channels: Logs are grouped by channels for filtering.
- Context: Support for structured data via the
contextparameter.
Usage Recommendations
Development
- Use
LoggerFactory.createForBrowserDevelopmentwith levelDEBUG - Add processors for debugging information
Production
- Use
LoggerFactory.createForBrowserProductionwith levelERRORor higher - Limit the number of handlers for performance
- Consider creating custom handlers for sending logs to server
Context
// Good:
$logger.error('Failed to process order', {
orderId: 123,
userId: 456,
error: error.message
})
// Bad:
$logger.error(`Failed to process order ${orderId} for user ${userId}: ${error.message}`)