Skip to main content

Logging

CellularJS provide a lightweight package for logging called @cellularjs/logger. The main purpose of this package is providing a unify logger interface that decouple your code from underlying logger implementation.

The way it work is similar to SLF4J. Logger interface is not subject to change, but you can easily create a new logger implementation that suit your needs(Eg: OTEL, write log to file,...).

Installation:

yarn add @cellularjs/logger

Or

npm install @cellularjs/logger

1. Logger#

1.1. getLogger#

You can use getLogger function to get new logger instance. The new logger instance MUST inherit log level, metadata from its parent.

import { getLogger, LogLevel } from '@cellularjs/logger';
const logger = getLogger('UserProfile', { trace: '****' });logger.setLogLevel(LogLevel.DEBUG);
logger.debug('debug message');logger.info('info message');logger.warn('warn message');logger.error('error message', { error: 'stack trace', trace: '****#**' });logger.fatal('fatal message', { trace: '****#***' });

Output(default format):

2022-11-02T15:49:16.188Z DEBUG - UserProfile: debug message{ trace: '****' }2022-11-02T15:49:16.188Z INFO  - UserProfile: info message{ trace: '****' }2022-11-02T15:49:16.188Z WARN  - UserProfile: warn message{ trace: '****' }2022-11-02T15:49:16.188Z ERROR - UserProfile: error message{ error: 'stack trace', trace: '****#**' }2022-11-02T15:49:16.188Z FATAL - UserProfile: fatal message{ trace: '****#***' }

1.2. LogLevel#

CellularJS Logger support 5 levels of severity, the lower value the higher severity.

LevelValueRecommended content
LogLevel.FATAL0Critical issue, something important is not working(Eg: failed to connect to database).
LogLevel.ERROR1Error, but it is not critical to take an immediate action.
LogLevel.WARN2Something unusual happened(Eg: API limit request exceed,..).
LogLevel.INFO3Normal event occur(Eg: incoming request,...).
LogLevel.DEBUG4Detail information for debugging(Eg: SQL query, operation steps,...).
note

By default, the log level of root logger is LogLevel.INFO, so if new logger is created from the root logger, it will inherit this log level. You can change the log level at runtime by using childLogger.setLogLevel.

import { getLogger, LogLevel } from '@cellularjs/logger';
const logger = getLogger('UserProfile');
logger.debug('debug message'); // ignored(because default log level is LogLevel.INFO)logger.info('info message'); // work
//  With log level, you can filter what should be logged or ignored.logger.setLogLevel(LogLevel.ERROR);
logger.debug('debug message'); // ignoredlogger.info('info message'); // ignoredlogger.warn('warn message'); // ignoredlogger.error('error message'); // worklogger.fatal('fatal message'); // work

2. Custom logger#

The default logger is very simple. For advanced usage, you can plug-in other logger such as winston, pino,...

Eg: use winston as underlying logger.

import { createLogger } from 'winston';import { setLoggerFactory, LoggerFactory, Logger, LogMessage, LogMeta } from '@cellularjs/logger';
const winston = createLogger({  level: 'debug',  // ...});
class WinstonLogger implements Logger {  // It is not a MUST, but for seamless developer experience,  // you SHOULD use LogLevel.INFO as default log level.  private logLevel = LogLevel.INFO;
  constructor(private source?: string, private meta: LogMeta = {}) {}
  from(source: string, meta?: LogMeta): Logger {    const newLogger = new WinstonLogger(source, { ...this.meta, ...meta });
    newLogger.setLogLevel(this.level);
    return newLogger;  }
  info(msg: LogMessage, meta?: LogMeta) {    if (this.logLevel < LogLevel.INFO) return;
    winston.info(msg.toString(), { ...this.meta, ...meta });  }
  ... other stuff ...}
class WinstonLoggerFactory implements LoggerFactory {  private rootLogger = new WinstonLogger();
  getLogger(source: string, meta: LogMeta) {    const activeLogger: Logger = this.rootLogger;
    return activeLogger.from(source || activeLogger.getSource(), meta);  }}
// register WinstonLoggerFactory as logger factorysetLoggerFactory(new WinstonLoggerFactory());
// now you can get your new logger support by winston.const myLogger = getLogger();
myLogger.info('...');
tip

For example about logger implementation, have a look at CellularJS simple logger