Simple pluggable async logger for node and browser alike.
Ever wanted a logger with a simple API that you could easily write a minimal plugin for? A logger which would work in both your browser and in your node environment?
Well, then Yolog might be something for you.
Simply use your favorite package manager that can pull packages from the npm repository!
npm i --save @jitesoft/yolog
yarn add @jitesoft/yolog
someothermanager --save-package-to-json-file @jitesoft/yolog
Yolog have three base files which you can make use of when it is compiled:
- index.js
- node.js
- browser.js
The index.js
is only the API (contains the Yolog
and YologPlugin
interfaces and such) and could possibly be useful
if you wish to create your own plugin but don't care about the other stuff.
The browser.js
file contains the same code as the index.js
and an extra ConsolePlugin
which is pre-initialized if
using the default
value from the package.
The node.js
file contains the same code as the index.js
and an extra ConsolePlugin
which is pre-initialized if
using the default
value from the package.
When including the script, there will be a global Yolog
object containing logger
(which is an instance of yolog
ready to use with the console plugin), Yolog
which is the logger itself, if you wish to create a new instance yourself.
ConsolePlugin
, which is a plugin that produces output to the console and the YologPlugin
class (which is used to create new plugins)
Simply put:
<script src="yolog/browser.js"></script>
<script>
const logger = Yolog.logger;
logger.debug('Weee!');
</script>
import { logger } from '@jitesoft/yolog';
import { logger } from '@jitesoft/yolog/browser'; // Browser specific
import { logger } from '@jitesoft/yolog/node'; // Node specific
logger.debug('Weee!')
const logger = require('@jitesoft/yolog').logger;
const logger = require('@jitesoft/yolog/browser').logger; // Browser specific
const logger = require('@jitesoft/yolog/node').logger; // Node specific
logger.debug('Weee!')
The Yolog class have a set of pre-defined tags which are used to output different type of logs with. It is possible to turn a
tag on and off via code, both in a plugin or in the Yolog instance itself.
The pre-defined tags are (name and default value):
{
"debug": { "enabled": true, "error": true },
"info": { "enabled": true, "error": true },
"warning": { "enabled": true, "error": true },
"error": { "enabled": true, "error": true },
"critical": { "enabled": true, "error": true },
"alert": { "enabled": true, "error": true },
"emergency": { "enabled": true, "error": true }
}
When using the constructor, Yolog will accept the key-value pairs as string : bool
,
defaulting error
to true and setting enabled
to the boolean value.
The error
key is used to allow yolog to know if it should pass the first error it encounters in the argument list as the error parameter when calling
its plugins. This allows the user to pass errors which could be printed out with a nice looking stack trace in the plugins.
Yolog uses the @jitesoft/sprinf package to enable message and argument
building. This due to not wanting to use a node-specific method as util.format
when building for cross env support.
Be sure to check out supported parameter types if you wish to do some more advanced parameterization!
Turning a tag on or off is done by calling the set(tag, state = null)
method on either the Yolog instance (if you wish that no output
should be done for that tag at all) or by the set(tag, state = null)
method of the plugin that you wish to specifically not get logs for
the tag from.
If no state is passed with the set
method, it will toggle the state.
If you wish to add a new tag to yolog and a plugin, the set
method can also be used. Just pass the tag that you wish
to create as the tag, and it will be set to the value that you pass as second argument.
You can also check the state of a given tag by using the get(tag)
method on either the Yolog instance or the plugin.
The Yolog instance have a built-in event handler which can be used to listen to given tags instead of logging them with a plugin.
The events will not use the same priority queue as the normal callbacks, and will be async emitted, so do not
build your logic around that expectation.
Each event should either return nothing or boolean value. If return value is false
it will not bubble to the next
queued priority batch, else they will run in batches depending on priority.
This also means that the callback could be async without the logger having any issues with it.
There are on
, off
and once
methods available and they work basically as any normal event handler should.
The events emitted looks like the following:
const event = {
data: {
message: "message" /* Message passed to the logger. */,
arguments: [ /* argument list passed to the logger. */ ],
timestamp: 123 /* Unix-timestamp (ms) when the logger was invoked. */,
tag: "tag" /* The tag that was invoked. */,
errorObject: new Error() /* An error object created in the yolog #log method. */
}
};
// That is...
console.log(event.data.message); // Will give you the message property.
The package that Yolog uses can be found here (@jitesoft/events) if you wish to check out more specific details.
Plugins are minimal classes which can be used to extend the logger to use other type of outputs!
When writing a new plugin, the Plugin
class in the base lib is used as a base class (if using es6 inheritance),
the only method that really have to be implemented is the log
method, the base class takes care of the rest!
import { YologPlugin } from '@jitesoft/yolog';
export default class MyPlugin extends YologPlugin {
/**
* Method called when a log message is intercepted and the plugin is listening to the given tag.
*
* @param {String} tag Tag which was used when logging the message.
* @param {Number} timestamp Timestamp (in ms) when the log was intercepted by the Yolog instance.
* @param {String} message Message that is passed to the plugin.
* @param {Error} error Error generated in the logger to be possible to use for call stack or for other reasons.
* @return Promise<void>
*/
async log (tag, timestamp, message, error) {
// Do your magic here! (return a promise or use the async/await keywords!)
return await something.something(message);
}
}
Plugin interface:
interface YologPluginInterface {
log (tag: string, timestamp: number, message: string, error): Promise<void>; /*Abstract, only method required to be implemented. */
set (tag: string, state: boolean|null): void;
get (tag: string): boolean|undefined;
/*get*/ active (): Array<string>;
enableError(...tag: Array<string>): this;
disableError(...tag: Array<string>): this;
}
To add a new plugin to the Yolog instance, call the addPlugin(plugin)
method, removal can be done with removePlugin(plugin)
.
The following list are plugins maintained and supported by Jitesoft.
@jitesoft/yolog-file-plugin
@jitesoft/yolog-sentry-plugin
@jitesoft/yolog-slack-plugin
@jitesoft/yolog-email-plugin
@jitesoft/yolog-json-plugin
More are under development.
Create a pull request for the readme file to include your plugin here (after a general audit)!
Observe:
Community developed plugins listed here are not under the control of Jitesoft and any damages they may or may not cause
is nothing that Jitesoft can be held liable for. As with everything, you should always audit the code you use before
using it in production!
The Yolog base API is quite simple, there are a few methods to customize the logger, while most of the configurations should be done in plugins.
Events
As mentioned above, the Yolog instance can be used as an event handler, the methods connected to this features are the following:
interface Yolog {
on(tag: string, handler: Function, priority: number): number;
off(tag: string, handler: number | Function): boolean;
once(tag: string, handler: Function, priority?: number): number;
}
The methods pretty much speak for themselves. On and Once both returns a number, the number
is the handle of the specific handler function. If the handler is to be removed, the handle
or the function itself can be passed to yolog through the off
method, removing it from the list of handlers.
Priority is a batch priority, that is, if multiple handlers have the same priority, they will be called
async at the same time. If either of them returns false, the event will not bubble to the next batch.
There is currently no built in way to not allow other handlers with the same priority to stop each other.
Events start their emit before the plugins are invoked, but yolog will not wait for them to finnish before starting to run the plugins. That way, you can not be sure that the plugins are invoked before the plugins, or the other way around.
Plugins
Adding and removing plugins is if possible more easy than using the events. The following methods are used for this:
interface Yolog {
addPlugin(plugin: YologPlugin): Yolog;
removePlugin(plugin: YologPlugin): Yolog;
readonly plugins: YologPlugin[];
}
Plugins are all called in batch when a log command is done, they have no priority over each other and the promise of the log method called will resolve when all are done.
The getter plugins
will return all the plugins that are loaded in the instance.
Tags
Tags is quite an important thing in yolog (and most logging...), there are a few pre-defined tags and methods that are used to invoke those. The predefined tags are:
const tags = [
'debug',
'info',
'warning',
'error',
'critical',
'alert',
'emergency'
];
You may call a specific log tag via the tagName(message: string, ...args): Promise<void>
methods, and if you wish to use
a custom tag, the custom(tag: string, message: string, ...args): Promise<void>
method is available.
Just make sure that the tag exists in the logger by adding it in the set
method!
interface Yolog {
set(tag: string, state?: boolean): Yolog;
get(tag: string): boolean;
}
The set
method adds a tag or updates existing tags states, if it's set to true
the tag will be available in yolog, if false, it will not
this way you can globally configure some tags to be on or off, if they are off, no event nor plugin will be invoked when the
method is used.
get
will return true
or false
depending on the state of the tag.
An example of where this can be a useful feature is when you wish to use environment specific tags:
if (process.env.NODE_ENV === 'production') {
yolog.set('debug', false);
yolog.set('emergency', true);
}
Active and available
There are two getters which can be used to get information about active and available tags, they both return lists of strings (the name of the tag) and are readonly:
interface Yolog {
readonly available: string[];
readonly active: string[];
}
The available
getter will return the names of all tags that are set in yolog, this includes all custom tags you have created.
The active
getter will return the names of all the tags that have state set to true
.
Error
interface Yolog {
enableError(...tag: Array<string>): this;
disableError(...tag: Array<string>): this;
}
The enable and disable error methods toggles the Yolog instance to create and send a error to the plugins (and events). Each plugin have their own controll of this too, while this is on a higher level. Tags passed will be toggled on or off, while calling the functions without any argument will set errors to on or off for all the tags instead of a single tag.
Just as with the base API, the plugins have their own tags
to turn off and on! This is to make it possible to have specific
loggin types on for some tags while others are not. For example: you might want to output debug
to console and
you wish to use an email plugin for the critical
logs.
If it was not possible to turn off most tags for the email plugin, you would receive a new email for each debug log made.
Active and available
The available
and active
getters works as with the yolog class, the first returns all tags that are available in the plugin,
the active
returns which are turned on.
interface YologPlugin {
readonly available: string[];
readonly active: string[];
}
Set and Get
The set and get methods are - just as with the Yolog class - used to turn tags on and off and to check if a given tag is on or off.
Calling set
will create a new tag if none exist, else update it.
log
The log method is the most important function of the Plugins. It is basically the only method that is required to be implemented to create a new plugin.
log
is an async method which takes a tag, timestamp, message and error. The message is the formatted message that Yolog passes
to all plugins. The tag is the tag that was used when the log call was made. Timestamp is intended to be a Unix timestamp, which
can be used inside the plugin to format a nice time string for output.
Since v 2.6.0
a new Error
argument is passed as the last argument of the log method. It can be used to output the callstack or similar
information.
Since v 3.0.0
the Error
argument depends on the input from the user due to the fact that stack traces in javascript is not working
as one expect them to work.
In future versions, if a better fix is found, this might change.
Error
interface YologPlugin {
enableError(...tag: Array<string>): this;
disableError(...tag: Array<string>): this;
}
The methods allow the user to toggle each plugin errors on or off (that is, passing of the Error
object through the log parameter at all).
Each plugin is responsible to make sure the error object is not null
before doing anything with its properties.
Calling the functions without any argument will set errors to on or off for all the tags instead of a single tag.
Any undocumented features are either in development or working as intended but not yet fully tested or documented. Use with care.
Yolog follows the Semantic Versioning 2.0.0
This basically means that no API breaking changes will occur without a new major version release, features might be added
during a minor release and patches are only fixes.
The source can be found at GitHub and GitLab.
First release of yolog
(back then under the name of node-yolog
) was back in 2014, so quite a while ago... It was developed as a tiny in-house
logger - by Johannes at Talkative Labs - just to add colours to console logs. So nothing great nor special, although it did make the console look better!
After the initial release, the work on the package went stale for quite a while, like... years!
It was finally picked up again in 2018 and rebuilt and re-branded as yolog
under the @jitesoft
org. The new version was not only for colourful
console outputs, but rather a small plugin system for logging with a basic API which was easy to use and extend.
Contributions in any form are very welcome!
Issues (bugs, feature requests, questions etc etc) can be posted in the GitHub issue tracker.
Pull requests will be reviewed and should contain tests and follow the code style.
If you wish to contribute by monetary means, feel free to click the sponsor
button on GitHub or head on over to our OpenCollective page!
The MIT License (MIT)
Copyright (c) 2014 - 2020 Johannes Tegnér / Jitesoft
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.