UNPKG

gelf-pro

Version:

The Graylog Extended Log Format for the Node.js

206 lines (179 loc) 4.94 kB
/** * Licensed under the MIT License * * @author Kanstantsin A Kamkou (2ka.by) * @license http://www.opensource.org/licenses/mit-license.php The MIT License * @link https://github.com/kkamkou/node-gelf-pro */ 'use strict'; var os = require('os'), _ = require('lodash'); // default configuration and the class itself var gelf = Object.create(null, { config: { enumerable: true, writable: true, value: { fields: {}, filter: [], broadcast: [], transform: [], adapterName: 'udp', adapterOptions: { protocol: 'udp4', host: '127.0.0.1', port: 12201 }, levels: { emergency: 0, alert: 1, critical: 2, error: 3, warning: 4, notice: 5, info: 6, debug: 7 }, aliases: { log: 'debug', warn: 'warning' } } }, adapter: { writable: true // the adapter holder (opened on test purpose) } }); /** * Updates the config object * @param {Object} conf * @returns {gelf} */ gelf.setConfig = function (conf) { this.config = _.merge({}, this.config, conf); return this; }; /** * Creates and return adapter (singleton) * @returns {Object} */ gelf.getAdapter = function () { if (!this.adapter) { this.adapter = require('./adapter/' + this.config.adapterName); this.adapter.setOptions(this.config.adapterOptions); } return this.adapter; }; /** * Converts an object to a string, adds custom fields * @param {Object} obj * @returns {String} */ gelf.getStringFromObject = function (obj) { var result = { version: '1.1', short_message: 'No message', // eslint-disable-line timestamp: _.round(Date.now() / 1000, 3), host: os.hostname() }; // it is not possible to send the id field if (!_.isUndefined(obj.id)) { delete obj.id; } // some fields should be copied without change ['full_message', 'short_message', 'level', 'host', 'timestamp'].forEach(function (key) { if (!_.isUndefined(obj[key])) { result[key] = obj[key]; delete obj[key]; } }); // recursion function for key-value aggregation // 32766 bytes is the maximum length for a field var recursion = function (input, prefix) { _.forOwn(input, function (value, key) { if ((/[^\w-]/).test(key)) { console.warn(key + ': the key format is not valid'); } if (_.isPlainObject(value)) { return recursion(value, prefix ? [prefix, key].join('_') : key); } result[(prefix ? [null, prefix, key] : [null, key]).join('_')] = _.isFinite(value) ? value : _.truncate(_.toString(value), {length: 32765}); // 32765 + 1 }); }; recursion(obj); return JSON.stringify(result); }; /** * Sends a message * @param {String} message * @param {Function} cb * @returns {gelf} */ gelf.send = function (message, cb) { this.getAdapter().send(message, cb); return this; }; /** * Sends a formatted message * @param {String} message * @param {Number} lvl * @param {Object|Function} extra * @param {Function} cb */ gelf.message = function (message, lvl, extra, cb) { // it is possible to skip the extra variable if (_.isFunction(extra) && !cb) { cb = extra; extra = {}; } cb = cb || _.noop; // empty call, usually triggered by a thoughtless programmer if (_.isNil(message)) { return cb(null, 0); } // cleaning up a bogus call if (!_.isUndefined(extra) && !_.isObjectLike(extra)) { console.warn('[gelf-pro]', 'extra should be object-like or undefined'); extra = {}; } // trying to convert an error to readable message if (_.isError(message)) { /* istanbul ignore else */ if (_.isEmpty(extra) && message.stack) { extra = {full_message: message.stack}; } message = message.message.toString() || 'Error'; } if (_.isError(extra)) { extra = {error: {message: extra.message, stack: extra.stack}}; } extra = _.merge({short_message: message, level: lvl}, this.config.fields, extra || {}); // filtering if (this.config.filter.length && !_.overEvery(this.config.filter)(_.cloneDeep(extra))) { return cb(null, 0); } // transforming if (this.config.transform.length) { _.invokeMap(this.config.transform, _.call, null, extra); } // broadcasting if (this.config.broadcast.length) { _.invokeMap(this.config.broadcast, _.call, null, _.cloneDeep(extra)); } extra = this.getStringFromObject(extra); var self = this; process.nextTick(function () { self.send(extra, cb); }); }; // defining default functions like info(), error(), etc. _.forEach(gelf.config.levels, function (idx, lvl) { this[lvl] = function (message, extra, cb) { this.message(message, idx, extra, cb); }; }.bind(gelf)); // aliases to be console alike _.forEach(gelf.config.aliases, function (from, to) { this[to] = this[from]; }.bind(gelf)); module.exports = gelf;