README
Isomorphic Logger
Tiny isomorphic logger that has the same semantics on the server and on the client with multi-channel support and modular structure.
import {Logger, createConsoleProcessor} from '@grabrinc/isomorphic-logger';
const logger = new Logger;
logger.channel(createConsoleProcessor());
logger.log('Hello world!', {foo: 'bar'}); // → Prints "Hello world! {foo: 'bar'}" to console
Logging
These methods are available on Logger
instance and log messages at corresponding log level:
trace(...messages)
debug(...messages)
info(...messages)
there's a convinient aliaslog(...messages)
warn(...messages)
error(...messages)
Each method accepts an arbitrary number of arguments as console.log
does.
Logging Level
Setting log level on Logger
instance allows to limit verbosity of the output:
import {LogLevel} from '@grabrinc/isomorphic-logger';
// Now messages with warn level or higher are logged.
logger.setLevel(LogLevel.WARN);
Following log levels are available out-of-the-box:
LogLevel.TRACE
LogLevel.DEBUG
LogLevel.INFO
LogLevel.WARN
LogLevel.ERROR
LogLevel.OFF
no messages would be logged with this level.
You can create your own log level via instantinating LogLevel
class:
logger.setLevel(new LogLevel(150));
Logging Level Test
If you want to perform heavy computations when particular logging level is set, you can use logging level test methods:
if (logger.isDebugEnabled()) {
// Do heavy stuff here
logger.debug('Computation results');
}
These methods are availeble on Logger
instance:
isTraceEnabled()
isDebugEnabled()
isInfoEnabled()
isWarnEnabled()
isErrorEnabled()
Channels
To set up a logger instance you need to define at least one channel.
Channel consists of processors that are executed one after another and can be asynchronous.
import {
Logger,
createStackTraceTransformProcessor,
createDateAndLevelPrependProcessor,
createThrottleProcessor,
createConsoleProcessor
} from '@grabrinc/isomorphic-logger';
import Sentry from 'raven-js';
logger.channel(
createStackTraceTransformProcessor(), // Converts error objects to string representing stack trace.
createDateAndLevelPrependProcessor(), // Prepends every message with date and time.
createThrottleProcessor({delay: 500, length: 10}), // Batch logged messages.
createConsoleProcessor() // Write batched messages to console.
);
logger.channel(
createMessageConcatProcessor(), // Concat all messages into a single string.
createErrorWrapProcessor(), // Wrap message into an Error object and trim excessive stack frames.
createSentryProcessor(Sentry) // Send messages to Sentry.
);
logger.log('Hello there!') // This is logged to both console and Sentry
Even if the channel contains an asynchronous processor, messages are guaranteed to be logged in the original order.
Logger itself is also a processor, so you can nest one logger into another:
const errorLogger = new Logger();
errorLogger.setLevel(LogLevel.ERROR);
errorLogger.channel(createSentryProcessor(Sentry));
const logger = new Logger();
logger.setLevel(LogLevel.TRACE);
logger.channel(createConsoleProcessor());
logger.channel(errorLogger);
logger.log('Foo'); // This is logged in the console only
logger.error('Oh snap!') // This is logged in the console and send to Sentry
Available Processors
Following processors are available at the moment:
createAggregateProcessor()
createConsoleProcessor()
createDateAndLevelPrependProcessor()
createErrorWrapProcessor()
createInspectProcessor()
createMessageConcatProcessor()
createSentryProcessor()
createStackTraceTransformProcessor()
createThrottleProcessor()
There are also server-only processors available which can be imported from @grabrinc/isomorphic-logger/server
:
createHighlightProcessor()
createFileAppendProcessor()
Unstable!createRollingFileAppendProcessor()
Unstable!
How to create a custom processor?
A processor is a function that receives a set of records:
type Record = {
level: LogLevel,
messages: Array<*>
}
function myCustomProcessor(records: Record[]): Promise<Record[]> | Record[] | Promise<null> | null {
return records;
}
Or an object that has process
function property:
const myCustomProcessor = {
process(records: Record[]): Promise<Record[]> | Record[] | Promise<null> | null {
return records;
}
}
A processor should do some stuff with messages and return a new set of records that is passed to the next processor.
If processor returns false value than next processor is not invoked.
A processor can return Promise
that is awaited before proceeding to next processor.
If you need to ensure logging was completed before continuing code execution you can:
await logger.error('Wait for this messages to log!', error);
Declarative Logger Configuration
Logger can be created from JSON configuration:
import {parseLoggerConfig, ProcessorFactories} from '@grabrinc/isomorphic-logger';
const loggerConfig = {
level: 'TRACE',
channels: [
[
{type: 'throttle', options: {delay: 1000, length: 10}}
{type: 'extractStackTrace'},
{type: 'highlight'},
{type: 'console'}
],
[
{
type: 'logger',
options: {
level: 'ERROR',
channels: [
[
{type: 'prependDateAndLevel'},
{type: 'console'}
]
]
}
}
]
]
};
const logger = parseLoggerConfig(loggerConfig, ProcessorFactories);
License
The code is available under MIT license.