clay-log
v1.5.5
Published
An isomorphic logging module for Clay projects
Downloads
3,141
Readme
Clay Log
An ismorphic logging module for Clay projects built around PinoJS
Why use Clay Log?
The purpose of this logging module is to wrap the instantiation of Pino for your Clay projects so that it can be used in multiple environments and output logs for the both local debugging (pretty printing) and log aggregators (such as the ELK stack). Use cases include:
- Server-side
- Amphora
- Amphora Renderers (Amphora HTML)
- Amphora plugins (Amphora Search)
- Clay-related microservicestog
model.js
files
- Client-side
- Kiln
- Kiln plugins (Clay Space Edit)
model.js
files
Setup
Instantiate the clay-log
module.
const clayLog = require('clay-log');
var log = clayLog.init({
name: 'amphora'
});
logger('info', 'some cool message', { additional: 'info' });
Want to attach some piece of data to every log? Use the meta
property. It accepts an Object, like so:
var log = clayLog.init({
name: 'amphora',
meta: {
important: 'information'
}
});
Maybe you're wanting to get a new instance of a logger with its own associated metadata? Use the meta
method. It accepts an Object as an argument and will spawn a new logger.
var loggerOne = clayLog.init({
name: 'amphora',
meta: {
important: 'information'
}
});
var loggerTwo = clayLog.meta({ another: 'piece of info' });
loggerOne('info', 'some cool message', { additional: 'info' });
loggerTwo('info', 'some different cool message');
If you'd like human-readable logs, Use the pretty
property.
var loggerOne = clayLog.init({
name: 'some-cli-tool',
pretty: true
});
If you're using the module in a command-line tool, it's useful to be able to specify where the logs should output to. By default, they will output to stdout
, but this can be changed by the output
property.
var loggerOne = clayLog.init({
name: 'some-cli-tool',
output: process.stderr
});
Usage
Once you have a logging instance you're free to begin logging. Logging levels are the same as the default PinoJS levels:
info
trace
debug
warn
error
To use simply do the following:
var loggingInstance = clayLog.init({
name: 'coolClayProject',
meta: {
important: 'information'
}
});
loggingInstance('info', 'my cool message!', { additional: 'info' });
The arguments are in the following order:
- Logging level
- Message (String)
- Accompanying log information (Object)
Safety Considerations :warning:
The safest way to use the third position argument (data
) is to wrap an object
rather than pass an object directly. The object provided should have enumerable properties.
Passing an object directly may result in a thrown error (if it's not really an object).
const obj = 'string';
log('info', 'this is unsafe', obj); // throws
Passing an object directly may result in mutation. Be mindful of this when utilizing plug-ins.
const obj = { key: 'value' };
log('info', 'this is unsafe', obj);
obj._label // 'INFO'
These are safer ways to provide the data
argument.
log('info', 'this is safer', { obj });
log('info', 'this is safer', { ...obj });
log('error', 'this is safer', { stack: err.stack });
Setting Minimum Log Level
For production instances it may not be necessary to log the same messages you do in dev environments. By default, Clay Log will only display info
and above level logs, but this can be configured with an environment variable. Set process.env.LOG
to a corresponding log level and that will be the minimum level that appears.
Errors
Want to be lazy? If you pass in an Error
object as the only argument and everything will get formatted properly OR just pass an Error
as a second argument and you'll be good.
var loggingInstance = clayLog.init({
name: 'coolClayProject',
meta: {
important: 'information'
}
});
loggingInstance(new Error('oh no!'));
loggingInstance('error', new Error('oh no!'));
_label
Property
This property on the output log message is meant to make the logs more human searchable when using grep or importing into an ELK-like tool. Rather than making people remember the association between level
and the different levels meanings, we supply a human-readable property.
Environment Variables
| Config Value | Decription |
| ----------------------- | -------------------------------------------------- |
| LOG
| The minimum log level to log trace,debug,info,warn,error
. [Default: info
] |
| CLAY_LOG_PRETTY
| Set to a 'truthy' value to enable pretty-printing. |
| CLAY_LOG_PLUGINS
| Comma-delimited list of plug-ins to enabled. Plug-ins are applied in the order listed. |
| CLAY_LOG_PLUGINS_PATH
| Absolute path (or path relative to the CWD) for directory containing additional plug-ins. |
Pretty Printing
If you don't pass in a pretty
property, pretty printing will controlled by the CLAY_LOG_PRETTY
environment variable. The logs will be printed in human readable form. Otherwise they will be regular PinoJS JSON strings.
Plug-ins
A "plug-in" for clay-log
is a wrapper function that is executed just before pino.<level>
.
Plug-ins can be used to:
- Rename keys in JSON message.
- Manipulate values in a JSON message.
- Provide additional data in a JSON message.
- Take additional action based on log level or other message attributes.
An empty or unset value for CLAY_LOG_PLUGINS
will disable plugins (the default).
Additional plug-ins can be loaded from CLAY_LOG_PLUGINS_PATH
.
:warning: :warning: :warning:
- As plug-in code is potentially executed for every message logged, there is a performance penalty to using them. :warning:
- Plug-ins will only execute on log levels greater-then-or-equal-to what is set in
LOG=
. - Plug-ins from
CLAY_LOG_PLUGINS_PATH
will take priority over core plug-ins, be mindful of any naming conflicts. - If a plug-in specified in
CLAY_LOG_PLUGINS
is not found a warning message will be logged but application execution will not halt. - Plug-ins are only supported server-side at the moment, they will not be loaded on the client.
Core Plug-ins
Heap Logging
If CLAY_LOG_PLUGINS
includes "heap" the following additional heap statistics will
be included:
{
"does_zap_garbage": 0,
"heap_size_limit": 0,
"malloced_memory": 0,
"number_of_detatched_contexts": 0,
"number_of_native_contexts": 0,
"peak_malloced_memory": 0,
"total_available_size": 0,
"total_heap_size": 0,
"total_heap_size_executable": 0,
"total_physical_size": 0,
"used_heap_size": 0
}
Sentry Reporting
If CLAY_LOG_PLUGINS
includes "sentry" all error
level logs will be reported to Sentry.
:warning: This plug-in requires @sentry/node
to be installed as a peer dependency and SENTRY_DSN
to be set as an environment variable.
Writing Core Plug-Ins
A plug-in is a wrapper function that accepts two arguments:
data
: The extra context object that will be included in a log.msg
: The string summary of the log message.
A plug-in can be used to enhance data
with additional context or to take additional
actions (like reporting errors to another service).
Here is an example plug-in called env
that will add the NODE_ENV
to every log line with
the warn
or error
level:
# ./plugins/env.js
// This utility should be used in all plug-ins. It provides an abstraction
// around the process used to wrap functions.
const { wrap } = require('./_utils');
// This is where your plug-in code will be defined. Anything in this block is
// executed **before** clay-log logs the message.
function wrapper(data, msg) {
data.env = process.env.NODE_ENV;
}
// The export of a plug-in will always use the format `wrap(<plug-in-func>, [<levels>])`.
// Omitting `[<levels>]` will apply the plug-in to all active log-levels.
module.exports = wrap(wrapper, ['warn', 'error']);
Writing Custom Plug-Ins
A "custom" plug-in is a clay-log extension that lives outside of the clay-log
project.
Use a custom plug-in when extending or modifying log data with needs that are specific
to your use-case but not useful for a broader audience.
To write a custom plugin:
- Designate where you will store your plug-ins in your project, eg.
./utils/clay-log-plugins
. - Set the
CLAY_LOG_PLUGINS_PATH
environment variable to./utils/clay-log-plugins
. - Write your plug-in code.
# ./utils/clay-log-plugins/rename.js
// The utility wrapper import path changes a bit when developing a custom plug-in.
const { wrap } = require('clay-log/plugins/_utils');
// This is where your plug-in code will be defined. Anything in this block is
// executed **before** clay-log logs the message.
function wrapper(data, msg) {
data.message = data.msg;
delete data['msg'];
}
// The export of a plug-in will always use the format `wrap(<plug-in-func>, [<levels>])`.
// Omitting `[<levels>]` will apply the plug-in to all active log-levels.
module.exports = wrap(wrapper, ['warn', 'error']);
- Set
CLAY_LOG_PLUGINS=rename
(or whatever custom plug-in names you've used).