JavaScript message logging system
- Features
- Simplest setup
- Typical setup
- Configuration
- Log targets
- Log number
- Logger arguments
- LogLevel
- Logger
- Logger methods
- LogEntry
- Creating custom message formatter
- Creating custom log target
- Demo
To use the logger you need to first set it up. The best way to do that is to create a separate JavaScript module.
The simplest logger setup would look like this:
logging/online-shop-logger.js
``javascript
import { Log } from '@intermattory/logging';
const log = Log()
log.start()
export const Logger = log.Logger
`
you can than use it like so:
`javascript
import { Logger } from 'logging/online-shop-logger.js'
function ShoppingCart(items) {
const logger = Logger(ShoppingCart)
logger.info('Initializing shopping cart with items', items)
}
`
By default, the Logger logs all messages to the console.
logging/online-shop-logger.js
`javascript
import { Log, ArrayTarget, ConsoleTarget } from '@intermattory/logging';
import { Config } from './config.js';
import { LogServiceTarget } from './logging/log-service.js'
const consoleTarget = ConsoleTarget()
const arrayTarget = ArrayTarget()
const configuration = {
DEV: {
logTargets: [
{
target: consoleTarget,
level: LogLevel.ALL
},
{
target: arrayTarget,
level: LogLevel.ALL
}
]},
STAGE: {
logTargets: [
{
target: consoleTarget,
level: LogLevel.WARN,
filter: logEntry => logEntry.module !== 'authorization'
},
{
target: arrayTarget,
level: LogLevel.ALL
}
]},
PROD: {
logTargets: [
{
target: consoleTarget,
level: LogLevel.ERROR,
filter: logEntry => logEntry.module !== 'authorization'
},
{
target: arrayTarget,
level: LogLevel.ALL
},
{
target: LogServiceTarget(),
level: LogLevel.WARN
}
]}
}
const log = Log(configuration[Config.environment])
log.start()
export const Logger = (source, module) => log.Logger(source, { module, application: 'Web application' })
export const getLogs = () => arrayTarget.array
`
Usage example:
`javascript
import { Logger } from 'logging/online-shop-logger.js'
function ShoppingCart(items) {
const logger = Logger(ShoppingCart, 'eCommerce')
logger.info('Initializing shopping cart with items', items)
const getDiscount = items => service.getDiscount(items)
.then(discount => {
logger.debug('Discount:', discount)
if (discount > 50)
logger.warn('Discount calculation formula is probably wrong')
})
.catch(error => {
const logEntry = logger.error.withError(error, 'Service failed to calculate the discount')
showAlert(Your discount could not be calculated. Please try again later. See log #${logEntry.logNumber} for details)`
})
// ...
}ServiceTarget
Intermattory logging doesn't provide the . If you need one, check Creating custom log target.
By default, the logger logs all messages to the console
`javascript`
const DEFAULT_LOGGER_CONFIGURATION = {
logNumberGenerator: DEFAULT_LOG_NUMBER_GENERATOR,
logTargets: [
{
target: ConsoleTarget(),
level: LogLevel.ALL
}
]
}
to change that, pass your configuration to Log's "constructor":`javascript`
const consoleTarget = ConsoleTarget()
const arrayTarget = ArrayTarget()
const configuration = {
logTargets: [
{
target: consoleTarget,
level: LogLevel.ALL,
filter: logEntry => logEntry.source !== 'IgnoredSource'
},
{
target: arrayTarget,
level: LogLevel.INFO
}
]}
const log = Log(configuration)
log.start()
ArrayTarget is useful to list the logs in GUI. Check out the demo to see it in action.arrayTarget.array
Get the array from .
ConsoleTarget can be initialized with an optional messageFormatter argument.ConsoleTarget
It lets you set the template of a message and customize information it contains. uses TemplateMessageFormatter as default.
You can also create your own formatter - check out Creating custom message formatter.
You can create your own log targets. Check out Creating custom log target to find out how.
Use filter field to exclude the messages you don't want to log. It expects a function which returns true or false.
Filtering doesn't affect the log number incrementation.
By default, the log number is an integer incremented by 1 with every new log entry. Counting starts at the moment you call log.start().
You can customize it by setting the logNumberGenerator field to a function which generates unique IDs.number
The function takes the default integer log number and a date as arguments. It can return a or string.
Logger does not validate the result of the function, so make sure you return unique values.
All log targets use the same log number generator.
function takes 2 arguments: source and optional parameters.The
parameters are sort of tags which can help you filter the log entries.
In different projects or even modules of the same project, you may need to log different information.
Sometimes you may want to log only the source of the message, sometimes the module name or message category.
You can define your Logger arguments by wrapping the original log.Logger in a Higher Order Function.
You can also create multiple loggers with different signature:logging/online-shop-logger.js
`javascript
const log = Log()
log.start()export const Logger = (source) => log.Logger(source, { application: 'Web application', workstationId: getWorkstaionId() })
export const ModuleLogger = (source, module) => log.Logger(source, { module, application: 'Web application' })
export const CategoryLogger = (source, category) => log.Logger(source, { category })
`LogLevel
Log level is a number.LogLevel object provides predefined log levels:
* LogLevel.ALL = 0
* LogLevel.DEBUG = 100
* LogLevel.INFO = 200
* LogLevel.WARN = 300
* LogLevel.ERROR = 400
* LogLevel.FATAL = 1000
Logger
Once you instantiated the Log you can use the Logger:`javascript
const log = Log()
const logger = log.Logger('LogSource')
`Logger function takes 2 arguments:
- mandatory source (function or string)
- optional parameters (object)#### source
If you pass a function as source, the logger will log its name.
Be aware that if you minify your code, function names may be minified too and a string may be more reliable.
#### parameters
This can be anything. If you would like to log more information and don't want to pass them every time you log a message,
you can pass them as
parameters to the Logger:
`javascript
const logger = Logger(ShoppingCart, { module: 'eCommerce'})
`---
NOTE
OK, it can't be anything...
You can give your parameters any valid JavaScript name except
date, logNumber, level, error, message, source.
These are standard fields which describe every log entry.---
$3
Here are all available methods:
`javascript
const logEntry = logger.log(customLevel, 'Message')
`
`javascript
const logEntry = logger.fatal('Message')
`
`javascript
const logEntry = logger.error('Message')
`
`javascript
const logEntry = logger.warn('Message')
`
`javascript
const logEntry = logger.info('Message')
`
`javascript
const logEntry = logger.debug('Message')
`
With error:
`javascript
const logEntry = logger.log.withError(error, LogLevel.ERROR, 'Message')
`
`javascript
const logEntry = logger.fatal.withError(error, 'Message')
`
`javascript
const logEntry = logger.error.withError(error, 'Message')
`
`javascript
const logEntry = logger.warn.withError(error, 'Message')
`
`javascript
const logEntry = logger.info.withError(error, 'Message')
`
`javascript
const logEntry = logger.debug.withError(error, 'Message')
`LogEntry
Every method returns a log entry, which of the following structure:
`
{
logNumber: number | string,
level: string,
message: [],
error?: {*},
date: Date,
source: function | string
}
`This is very useful, if after an action, you need to give the user a feedback with some technical details, like the log number or error:
`javascript
const logEntry = logger.error.withError(error, 'Parsing error')
showAlert(An error occurred while opening the document (${logEntry.errorMessage}). For technical details go to the log No.${logEntry.logNumber})
`TemplateMessageFormatter
`javascript
import { TemplateMessageFormatter, ConsoleTarget } from '@inttermattory/logging'const consoleMessageFormatFunction = (entry, formattedEntry) =>
#${formattedLogEntry.logNumber} [${formattedLogEntry.level}][${formattedLogEntry.time}]${formattedLogEntry.parameters ? [${formattedLogEntry.parameters}] : ''} ${formattedLogEntry.source}: ${formattedLogEntry.message}${logEntry.error ? ${formattedLogEntry.errorName}
stack trace:
${formattedEntry.stackTrace}
: ''}
const consoleMessageTemplate = TemplateMessageFormatter(consoleMessageFormatFunction)
const consoleTarget = ConsoleTarget(consoleMessageTemplate)
`The default formatter is stored in
TemplateMessageFormatter.DEFAULT_MESSAGE_TEMPLATE
and here is how it looks like:
`javascript
(logEntry, formattedLogEntry) => #${formattedLogEntry.logNumber} [${formattedLogEntry.level}][${formattedLogEntry.time}]${formattedLogEntry.parameters ? [${formattedLogEntry.parameters}] : ''} ${formattedLogEntry.source}: ${formattedLogEntry.message}${logEntry.error ? ' ' + formattedLogEntry.error: ''}
`Creating custom message formatter
A message formatter has a very simple interface:
It must implement a format method and return an array. format takes one argument: a log entry
Example log entry:
`
{
date: [object Date],
logNumber: 11,
error: [object Error],
level: 300,
message: [object Array],
source: 'Demo',
...parameters
}
`Your formatter may look like this:
`javascript
const MyMessageFormatter = {
format(entry) {
let message = []; // your implementation
return message;
}
}
`
It is important that the format method returns an array, even if the formatted message is a string.Creating custom log target
Every log target must have log method, which takes 1 argument: log entry object.
Example log entry:
`
{
date: [object Date],
logNumber: 11,
error: [object Error],
level: 300,
message: [object Array],
source: 'Demo',
...parameters
}
`
Your log target may have the following shape:
`javascript
const MyLogTarget = {
log(entry) {
// your implementation
}
}
`
You may want to use a message formatter. If so, please refer to ConsoleTargetDemo
https://intermattory.bitbucket.io/logging/What's included?
* ArrayTarget
* ConsoleTarget
* TemplateMessageFormatterWhat's not included (yet)?
* Colors
* FileTargetExtending
You can easily extend package features:
* Creating custom log target
* Creating custom message formatter
Tests
Run tests with `npm test``MIT