UNPKG

6.1 kBJavaScriptView Raw
1/**
2 * @module logger
3 * Basic logging features
4 */
5
6var util = require('util');
7var _ = require('lodash');
8
9var properties = require('./properties');
10var uuid = require('./utils/uuid');
11
12const levels = {trace: 0, debug: 1, info: 2, warn: 3, err: 4, error: 4};
13var loggers = {};
14var enabled = {};
15
16/**
17 * Provides a level for the specified namespace
18 * @param namespace
19 * @returns {Logger}
20 */
21var manager = module.exports = function (namespace) {
22 var logger = loggers[namespace];
23 if (!logger) {
24 logger = new Logger(namespace);
25 loggers[namespace] = logger;
26 _.forEach(_.keys(enabled), function (item) {
27 if ((new RegExp(item)).test(namespace)) {
28 logger.enabled = true;
29 logger.level = enabled[item] || 'info';
30 }
31 });
32 }
33 return logger;
34};
35manager.getLogger = manager;
36
37/**
38 * Enable a logger
39 * @param namespace {string} namespace of the logger to enable
40 * @param [level] {string} level
41 */
42manager.enable = function (namespace, level) {
43 var regex = namespace2regex(namespace);
44 enabled[regex] = level;
45 var namespaces = matchingNamespaces(regex);
46 _.forEach(namespaces, function (item) {
47 var logger = loggers[item];
48 if (logger) {
49 logger.enabled = true;
50 if (level) logger.level = level;
51 }
52 });
53};
54
55/**
56 * Disable a logger
57 * @param namespace {string} namespace of the logger to disable
58 */
59manager.disable = function (namespace) {
60 var regex = namespace2regex(namespace);
61 delete enabled[regex];
62 var namespaces = matchingNamespaces(regex);
63 _.forEach(namespaces, function (item) {
64 var logger = loggers[item];
65 if (logger) logger.enabled = false;
66 });
67};
68
69/**
70 * Set a logger level
71 * @param namespace {string} namespace of the logger whose level has to be set
72 * @param level {string} level
73 */
74manager.level = function (namespace, level) {
75 var regex = namespace2regex(namespace);
76 var namespaces = matchingNamespaces(regex);
77 _.forEach(namespaces, function (item) {
78 var logger = loggers[item];
79 if (logger) logger.level = level;
80 });
81};
82
83/**
84 * Generate a regex from a namespace
85 * @param {string} namespace
86 * @returns {string}
87 */
88function namespace2regex(namespace) {
89 return '^' + namespace.replace('*', '.*?') + '$';
90}
91
92/**
93 * Find matching namespaces
94 * @param {string} regex
95 * @returns {Array}
96 */
97function matchingNamespaces(regex) {
98 var re = new RegExp(regex);
99 return _.filter(_.keys(loggers), function (key) {
100 return re.test(key);
101 });
102}
103
104/**
105 * Logger
106 * @param namespace {string} logger namespace
107 * @constructor
108 */
109function Logger(namespace) {
110 this.namespace = namespace;
111 this.enabled = false;
112 this.level = 'info';
113}
114
115/**
116 * Calls log with given level
117 * @returns {string} error unique id
118 */
119_.forEach(_.keys(levels), function (level) {
120 Logger.prototype[level] = function () {
121 return internalLog(this, level, Array.prototype.slice.call(arguments));
122 };
123});
124
125/**
126 * Builds log with given params
127 * @param level {string} log level
128 * @param code {string} unique error code
129 * @param message {string} message
130 * @param [techData] {object} technical data
131 * @param [userData] {object} use data
132 */
133Logger.prototype.makeLog = function (level, code, message, techData, userData) {
134 if (levels[level] < levels[this.level]) return {};
135
136 var messages = [];
137 messages.push('[' + code + '] ' + message);
138 if (techData) messages.push({techData: techData});
139 if (userData) messages.push({userData: userData});
140
141 var errid = internalLog(this, level, messages);
142 return {errid: errid, code: code, data: userData};
143};
144
145/**
146 * Logs given messages into stdout
147 * @param logger {Logger} logger
148 * @param level {string} log level
149 * @param messages {Array} messages to log
150 * @returns {string} error unique id
151 */
152var internalLog = function (logger, level, messages) {
153 var errors = [];
154 if (level === 'error' || level === 'err' || level === 'warn') {
155 errors.push(formatError(new Error('Current Stack')));
156 }
157 _.forEach(messages, function (message) {
158 if (util.isError(message)) {
159 errors.push(message);
160 } else if (_.isObject(message) || _.isArray(message)) {
161 var extractedErrors = extractErrors(message, 3);
162 if (!_.isEmpty(extractedErrors)) {
163 errors = errors.concat(extractedErrors);
164 }
165 }
166 });
167 if (!_.isEmpty(errors)) messages.push({errors: errors});
168 messages.push({container: {ID: properties.ID, name: properties.name, netInfo: properties.netInfo}});
169
170 var errid = uuid();
171 if (logger.enabled && levels[level] >= levels[logger.level]) {
172 try {
173 manager.log(logger.namespace, level, messages);
174 } catch (err) {
175 console.error('hubiquitus log processing error', err, err.stack);
176 }
177 }
178 return errid;
179};
180
181/**
182 * Effective logger; can be overriden
183 * @param namespace {string} namespace
184 * @param level {string} log level
185 * @param messages {array} messages to log
186 */
187manager.log = function (namespace, level, messages) {
188 var log = '[' + namespace + '][' + level + '][' + new Date() + ']';
189 _.forEach(messages, function (message) {
190 log += '\n';
191 if (_.isObject(message) || _.isArray(message)) {
192 log += util.inspect(message, {depth: 10});
193 } else {
194 log += message;
195 }
196 });
197 console.log(log + '\n');
198};
199
200/**
201 * Formats an error
202 * @param {Error} err
203 * @return {Object} formatted error
204 */
205function formatError(err) {
206 return {err: ('Error ' + err.message), stack: err.stack};
207}
208
209/**
210 * Extract errors from array|object
211 * @param {Object|Array} data
212 * @param {Number} depth
213 * @return {Array} errors
214 */
215function extractErrors(data, depth) {
216 depth = depth || 1;
217 var currentDepth = 0;
218
219 return (function extract(data) {
220 currentDepth++;
221 var errors = [];
222 _.forEach(data, function (item, name) {
223 if (util.isError(item)) {
224 errors.push(formatError(item));
225 } else if (currentDepth < depth && (_.isObject(data) || _.isArray(data))) {
226 var extractedErrors = extract(item);
227 if (!_.isEmpty(extractedErrors)) {
228 errors = errors.concat(extractedErrors);
229 }
230 }
231 });
232 return errors;
233 })(data);
234}