Logger Type
Description
Section titled “Description”This example demonstrates the Logger interface for consistent logging across the AlgoKit Utils SDK. Topics covered:
- Logger type definition with all log levels
- Console-based Logger implementation
- No-op (silent) logger for production
- Custom formatting logger
- Compatibility with common logging patterns
Prerequisites
Section titled “Prerequisites”- No LocalNet required
Run This Example
Section titled “Run This Example”From the repository root:
cd examplesnpm run example common/06-logger.ts/** * Example: Logger Type * * This example demonstrates the Logger interface for consistent logging * across the AlgoKit Utils SDK. * * Topics covered: * - Logger type definition with all log levels * - Console-based Logger implementation * - No-op (silent) logger for production * - Custom formatting logger * - Compatibility with common logging patterns * * Prerequisites: * - No LocalNet required */
import type { Logger } from '@algorandfoundation/algokit-utils/common'import { printHeader, printInfo, printStep, printSuccess } from '../shared/utils.js'
// ============================================================================// Main Example// ============================================================================
printHeader('Logger Type Example')
// ============================================================================// Step 1: Logger Type Definition// ============================================================================printStep(1, 'Logger Type Definition')
printInfo('The Logger type defines a standard logging interface:')printInfo('')printInfo(' type Logger = {')printInfo(' error(message: string, ...optionalParams: unknown[]): void')printInfo(' warn(message: string, ...optionalParams: unknown[]): void')printInfo(' info(message: string, ...optionalParams: unknown[]): void')printInfo(' verbose(message: string, ...optionalParams: unknown[]): void')printInfo(' debug(message: string, ...optionalParams: unknown[]): void')printInfo(' }')printInfo('')printInfo('Log levels (from most to least severe):')printInfo(' 1. error - Critical errors that need immediate attention')printInfo(' 2. warn - Warning conditions that should be reviewed')printInfo(' 3. info - Informational messages for normal operations')printInfo(' 4. verbose - Detailed tracing for troubleshooting')printInfo(' 5. debug - Developer debugging information')printSuccess('Logger interface provides 5 standard log levels')
// ============================================================================// Step 2: Console-based Logger Implementation// ============================================================================printStep(2, 'Console-based Logger Implementation')
const consoleLogger: Logger = { error: (message: string, ...optionalParams: unknown[]) => { console.error(`[ERROR] ${message}`, ...optionalParams) }, warn: (message: string, ...optionalParams: unknown[]) => { console.warn(`[WARN] ${message}`, ...optionalParams) }, info: (message: string, ...optionalParams: unknown[]) => { console.info(`[INFO] ${message}`, ...optionalParams) }, verbose: (message: string, ...optionalParams: unknown[]) => { console.log(`[VERB] ${message}`, ...optionalParams) }, debug: (message: string, ...optionalParams: unknown[]) => { console.debug(`[DEBUG] ${message}`, ...optionalParams) },}
printInfo('Created a console-based Logger implementation')printInfo('Demonstrating each log level:')console.log('') // blank line for readability
consoleLogger.error('Database connection failed', { code: 'ECONNREFUSED', port: 5432 })consoleLogger.warn('API rate limit approaching', { current: 95, limit: 100 })consoleLogger.info('Transaction submitted', { txId: 'ABC123...' })consoleLogger.verbose('Processing block', { round: 12345, txCount: 7 })consoleLogger.debug('Raw response payload', { bytes: 1024 })
console.log('') // blank line for readabilityprintSuccess('All 5 log levels demonstrated')
// ============================================================================// Step 3: No-op (Silent) Logger// ============================================================================printStep(3, 'No-op (Silent) Logger')
const nullLogger: Logger = { error: () => {}, warn: () => {}, info: () => {}, verbose: () => {}, debug: () => {},}
printInfo('Created a no-op (silent) logger')printInfo('Silent loggers are useful for:')printInfo(' - Production environments where logging is disabled')printInfo(' - Unit tests where log output is not wanted')printInfo(' - Default parameter values when no logger is provided')printInfo('')printInfo('Calling silent logger (no output expected):')nullLogger.error('This error will not be logged')nullLogger.warn('This warning will not be logged')nullLogger.info('This info will not be logged')nullLogger.verbose('This verbose message will not be logged')nullLogger.debug('This debug message will not be logged')printSuccess('No-op logger created - produces no output')
// ============================================================================// Step 4: Custom Formatting Logger// ============================================================================printStep(4, 'Custom Formatting Logger')
function createTimestampLogger(prefix: string): Logger { const formatMessage = (level: string, message: string): string => { const timestamp = new Date().toISOString() return `${timestamp} [${prefix}] ${level.padEnd(7)} ${message}` }
return { error: (message: string, ...optionalParams: unknown[]) => { console.error(formatMessage('ERROR', message), ...optionalParams) }, warn: (message: string, ...optionalParams: unknown[]) => { console.warn(formatMessage('WARN', message), ...optionalParams) }, info: (message: string, ...optionalParams: unknown[]) => { console.info(formatMessage('INFO', message), ...optionalParams) }, verbose: (message: string, ...optionalParams: unknown[]) => { console.log(formatMessage('VERBOSE', message), ...optionalParams) }, debug: (message: string, ...optionalParams: unknown[]) => { console.debug(formatMessage('DEBUG', message), ...optionalParams) }, }}
const appLogger = createTimestampLogger('MyApp')
printInfo('Created a logger with custom formatting:')printInfo(' - ISO timestamp prefix')printInfo(' - Application name prefix')printInfo(' - Padded log level for alignment')printInfo('')printInfo('Demonstrating custom formatted output:')console.log('') // blank line for readability
appLogger.info('Application started')appLogger.debug('Configuration loaded', { env: 'development' })appLogger.warn('Deprecated API endpoint called')
console.log('') // blank line for readabilityprintSuccess('Custom formatting logger created and demonstrated')
// ============================================================================// Step 5: Level-filtered Logger// ============================================================================printStep(5, 'Level-filtered Logger')
type LogLevel = 'error' | 'warn' | 'info' | 'verbose' | 'debug'
const LOG_LEVEL_PRIORITY: Record<LogLevel, number> = { error: 0, warn: 1, info: 2, verbose: 3, debug: 4,}
function createFilteredLogger(minLevel: LogLevel): Logger { const minPriority = LOG_LEVEL_PRIORITY[minLevel]
const shouldLog = (level: LogLevel): boolean => { return LOG_LEVEL_PRIORITY[level] <= minPriority }
return { error: (message: string, ...optionalParams: unknown[]) => { if (shouldLog('error')) console.error(`[ERROR] ${message}`, ...optionalParams) }, warn: (message: string, ...optionalParams: unknown[]) => { if (shouldLog('warn')) console.warn(`[WARN] ${message}`, ...optionalParams) }, info: (message: string, ...optionalParams: unknown[]) => { if (shouldLog('info')) console.info(`[INFO] ${message}`, ...optionalParams) }, verbose: (message: string, ...optionalParams: unknown[]) => { if (shouldLog('verbose')) console.log(`[VERB] ${message}`, ...optionalParams) }, debug: (message: string, ...optionalParams: unknown[]) => { if (shouldLog('debug')) console.debug(`[DEBUG] ${message}`, ...optionalParams) }, }}
printInfo('Created a level-filtered logger factory')printInfo('Filter levels control which messages are logged:')printInfo(' - "error" -> only errors')printInfo(' - "warn" -> errors + warnings')printInfo(' - "info" -> errors + warnings + info')printInfo(' - "verbose" -> all except debug')printInfo(' - "debug" -> all messages')printInfo('')
printInfo('Testing with minLevel="warn" (only error and warn):')const warnLogger = createFilteredLogger('warn')console.log('') // blank line for readabilitywarnLogger.error('This error IS logged')warnLogger.warn('This warning IS logged')warnLogger.info('This info is NOT logged')warnLogger.debug('This debug is NOT logged')
console.log('') // blank line for readabilityprintSuccess('Level-filtered logger demonstrated')
// ============================================================================// Step 6: Logger Interface Compatibility// ============================================================================printStep(6, 'Logger Interface Compatibility')
printInfo('The Logger interface is compatible with common logging libraries:')printInfo('')printInfo(' 1. Console API - native JavaScript console methods')printInfo(' const logger: Logger = { ...console, verbose: console.log }')printInfo('')printInfo(' 2. Winston - popular Node.js logging library')printInfo(' const winston = require("winston")')printInfo(' const logger: Logger = winston.createLogger(...)')printInfo('')printInfo(' 3. Pino - fast JSON logger for Node.js')printInfo(' const pino = require("pino")')printInfo(' const logger: Logger = pino()')printInfo('')printInfo(' 4. Custom implementations - as shown in this example')printInfo('')
// Demonstrate that native console can be adapted as a LoggerprintInfo('Demonstrating console adapted as Logger:')const nativeConsoleLogger: Logger = { ...console, verbose: console.log.bind(console), // Console doesn't have verbose, so use log}console.log('') // blank line for readabilitynativeConsoleLogger.info('Native console.info works as Logger.info')nativeConsoleLogger.debug('Native console.debug works as Logger.debug')
console.log('') // blank line for readabilityprintSuccess('Console can be adapted to Logger interface (needs verbose wrapper)')
// ============================================================================// Step 7: Using Logger in Functions// ============================================================================printStep(7, 'Using Logger in Functions')
function simulateTransaction(txId: string, logger: Logger = nullLogger): void { logger.info(`Starting transaction: ${txId}`) logger.debug('Validating transaction parameters') logger.verbose('Serializing transaction data') logger.info(`Transaction ${txId} completed successfully`)}
printInfo('Created a function that accepts Logger as optional parameter')printInfo('When no logger is provided, it defaults to nullLogger (silent)')printInfo('')
printInfo('Calling with no logger (silent):')simulateTransaction('TX-001')printInfo(' (no output produced)')printInfo('')
printInfo('Calling with consoleLogger:')console.log('') // blank line for readabilitysimulateTransaction('TX-002', consoleLogger)
console.log('') // blank line for readabilityprintSuccess('Logger can be used as optional dependency injection')
// ============================================================================// Summary// ============================================================================printStep(8, 'Summary')
printInfo('Logger interface provides:')printInfo(' - 5 standard log levels (error, warn, info, verbose, debug)')printInfo(' - Support for additional parameters (objects, arrays, etc.)')printInfo(' - Compatibility with console, Winston, Pino, and others')printInfo(' - Easy to implement custom formatters and filters')printInfo(' - No-op logger for disabling output')printInfo('')printSuccess('Logger Type Example completed!')