@edge/log
v1.6.2
Published
Logging utility library with powerful adaptor middleware
Downloads
263
Readme
Log
Logging utility library with powerful adaptor middleware.
Usage
The @edge/log
package uses adaptors that act as logging middleware. The most basic adaptor is the standard input/output adaptor StdioAdaptor
which essentially acts like an enhanced console log. Adaptors can be configured and passed in at instantiation, along with the optional parameters name, level, and context, or attached with the use(adaptor)
method.
Install with NPM:
$ npm install @edge/log --save
Basic usage
This example shows basic console logging.
Note, the default log level is Info
so you won't see the debug message:
import { Log, StdioAdaptor } from '@edge/log'
const log = new Log()
log.use(new StdioAdaptor())
log.debug('debugging')
log.info('for your information')
log.warn('achtung! warning!')
log.error('unfortunately, an error occurred')
Full usage
It is possible to use any number of parameters when instantiating the log:
import { Log, LogLevel, LogtailAdaptor, StdioAdaptor } from '@edge/log'
const name = 'example'
const level = LogLevel.Debug
const context = { timestamp: Date.now() }
const stdioAdaptor = new StdioAdaptor()
const logtailAdaptor = new LogtailAdaptor(process.env.LOGTAIL_SOURCE_TOKEN)
const adaptors = [stdioAdaptor, logtailAdaptor]
const log = new Log(adaptors, name, level, context)
log.info('example', { cool: true })
Log levels
There are four log levels, exposed via the LogLevel
type.
export enum LogLevel { Debug, Info, Warn, Error }
The default LogLevel
is Info
, but you can set this when creating your Log
instance. All messages equal to and greater than the current log level will be processed, and the others will be ignored.
import { Log, LogLevel, StdioAdaptor } from '@edge/log'
const log = new Log(LogLevel.Warn)
log.use(new StdioAdaptor())
log.debug('you won\'t see me')
log.info('or me')
log.warn('but you will see me')
log.error('and me')
You can change the log level at runtime by using the setLogLevel(level)
method:
import { Log, LogLevel, StdioAdaptor } from '@edge/log'
const log = new Log(LogLevel.Warn)
log.use(new StdioAdaptor())
log.debug('you won\'t see me')
log.setLogLevel(LogLevel.Info)
log.info('but you will see me now')
Name
You can assign a name to Log
instances, for example:
const log = new Log('readme')
log.use(new StdioAdaptor())
log.info('this is an example')
You can extend the name with the extend
method:
const log = new Log('readme')
log.use(new StdioAdaptor())
log.info('this is an example')
const eventLog = log.extend('event')
eventLog.info('the name of this log will be readme:event')
Context
You can pass in context along with your log message. In the case of StdioAdaptor
, this is stringified and output. For LogtailAdaptor
, it is passed along to Logtail. How it is utilised in down to the adaptors. You can also attach context to a Log
instance itself, therefore including it with every message.
Per message context
For example, you could attach debug data to a message:
const log = new Log([new StdioAdaptor()], LogLevel.Debug)
log.debug('debug context example', { debugData: [1, 2, 3] })
Or perhaps an error:
try {
// imagine something bad happens here
throw new Error('something bad happened')
}
catch (err: any) {
if (err instanceof Error) log.error('an error was caught', err)
else log.error('an unknown error was caught')
}
Per instance context
Context can also be attached to the Log
instance itself, so that it is included with every message. This can be done in one of two ways.
At instantiation:
import { Log, StdioAdaptor } from '@edge/log'
const log = new Log({
instance: generateInstanceID(),
someFlag: true
})
log.use(new StdioAdaptor())
log.info('example')
Or by extending an existing Log
instance:
import { Log, StdioAdaptor } from '@edge/log'
const log = new Log([new StdioAdaptor()])
const event = { eventID: 528 }
const eventLog = log.extend(event)
eventLog.info('event started')
Merging contexts
Contexts are automatically merged. This means you can extend a Log
instance with some context, then add to it within a message, and also extend it with further context.
import { Log, StdioAdaptor } from '@edge/log'
const log = new Log({
instance: generateInstanceID(),
someFlag: true
})
log.use(new StdioAdaptor())
const eventLog = log.extend({ eventName: 'testEvent' })
eventLog.info('event started', { eventStartDate: new Date() })
The above example would start with the log
instance and the context:
{
instance: 'ed5eb32b',
someFlag: true
}
Then the eventLog
instance would have the context:
{
instance: 'ed5eb32b',
someFlag: true,
eventName: 'testEvent'
}
And the info message would have the context:
{
instance: 'ed5eb32b',
someFlag: true,
eventName: 'testEvent',
eventStartDate: '2021-10-04T20:16:49.988Z'
}
Adaptors
Adaptors form the powerful middleware layer of @edge/log
. At their most basic, they are objects with four methods: debug
, info
, warn
, and error
, and each of these methods take three parameters: the Log
instance, a log message as a string, and an optional context object. See custom adaptors below for more details.
LogtailAdaptor
LogtailAdaptor
pushes your log messages to Logtail. For this, you'll need an API key, or what Logtail call a Source Token.
It takes two parameters, logtailSourceToken
and an optional enableNameInjection
(default: true
).
Usage:
const log = new Log()
const logtail = new LogtailAdaptor(process.env.LOGTAIL_SOURCE_TOKEN)
log.use(logtail)
Note: if you set a Name on the
Log
instance, the Logtail adaptor will inject this into the context that it uploads. Avoid setting aname
field on the root of the context object or disable name injection like so:
const log = new Log()
const logtail = new LogtailAdaptor(process.env.LOGTAIL_SOURCE_TOKEN, false)
log.use(logtail)
StdioAdaptor
StdioAdaptor
is a simple adaptor that outputs logs to stdout (and if you configure it, stderr).
It takes one parameter, useStderr
. If this is set, errors will be written to stderr instead of stdout.
// Errors will be written to stdout
const stdoutOnly = new StdioAdaptor()
// Errors will be written to stderr
const stderrToo = new StdioAdaptor(true)
Logging errors
StdioAdaptor
handles error logging differently based on how the error is supplied.
If the error is provided as the message (first argument) or context (second argument) then it will be presented as a stack in log output. For example:
log.error(new Error("some error"))
// 14:34:12.992 ERR Error: some error
// Error: some error
// at Object.<anonymous> (***/tests/index.ts:30:4)
// [...etc]
log.error("my msg", new Error("some error"))
// 14:34:12.992 ERR my msg
// Error: some error
// at Object.<anonymous> (***/tests/index.ts:30:4)
// [...etc]
However, if an error is provided as a property of the context, it will be presented as an ordinary object:
const err = new Error("some error")
log.error("my msg", { err })
// 14:38:08.031 ERR my msg {"err":{"message":"some error","name":"Error","stack":"Error: some error\n at Object.<anonymous> (***/tests/index.ts:30:4) [...etc]"}}
Using multiple adaptors
You can easily use multiple adaptors. You can either pass them in at instantiation:
import { Log, LogtailAdaptor, StdioAdaptor } from '@edge/log'
const log = new Log([
new StdioAdaptor(),
new LogtailAdaptor(process.env.LOGTAIL_SOURCE_TOKEN)
])
log.debug('debugging')
Or attach them with the use
method:
import { Log, LogtailAdaptor, StdioAdaptor } from '@edge/log'
const log = new Log()
log.use(new StdioAdaptor())
log.use(new LogtailAdaptor(process.env.LOGTAIL_SOURCE_TOKEN))
log.debug('debugging')
Custom adaptors
It is possible, encouraged even, to write and use custom adaptors. Adaptors must conform to the following type, which simply has four methods.
export type Adaptor = {
debug: (log: Log, message: string, context?: Record<string, unknown>) => void
info: (log: Log, message: string, context?: Record<string, unknown>) => void
warn: (log: Log, message: string, context?: Record<string, unknown>) => void
error: (log: Log, message: string, context?: Record<string, unknown>) => void
}
For example
import { Adaptor, Log } from '@edge/log'
class ConsoleLogAdaptor implements Adaptor {
debug(log: Log, message: string, context?: Record<string, unknown>): void {
console.log('DEBUG', message, log.name, context)
}
info(log: Log, message: string, context?: Record<string, unknown>): void {
console.log('INFO', message, log.name, context)
}
warn(log: Log, message: string, context?: Record<string, unknown>): void {
console.log('WARN', message, log.name, context)
}
error(log: Log, message: string, context?: Record<string, unknown>): void {
console.log('ERROR', message, log.name, context)
}
}
const log = new Log('custom log example')
log.use(new ConsoleLogAdaptor())
log.info('hello from the console')
Contributing
Interested in contributing to the project? Amazing! Before you do, please have a quick look at our Contributor Guidelines where we've got a few tips to help you get started.
License
Edge is the infrastructure of Web3. A peer-to-peer network and blockchain providing high performance decentralised web services, powered by the spare capacity all around us.
Copyright notice (C) 2021 Edge Network Technologies Limited [email protected] All rights reserved
This product is part of Edge. Edge is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version ("the GPL").
If you wish to use Edge outside the scope of the GPL, please contact us at [email protected] for details of alternative license arrangements.
This product may be distributed alongside other components available under different licenses (which may not be GPL). See those components themselves, or the documentation accompanying them, to determine what licenses are applicable.
Edge is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
The GNU General Public License (GPL) is available at: https://www.gnu.org/licenses/gpl-3.0.en.html A copy can be found in the file GPL.md distributed with these files.
This copyright notice MUST APPEAR in all copies of the product!