Line | Hits | Source |
---|---|---|
0 | 82 | "use strict"; |
1 | ||
2 | 82 | function Level(level, levelStr) { |
3 | 656 | this.level = level; |
4 | 656 | this.levelStr = levelStr; |
5 | } | |
6 | ||
7 | /** | |
8 | * converts given String to corresponding Level | |
9 | * @param {String} sArg String value of Level OR Log4js.Level | |
10 | * @param {Log4js.Level} defaultLevel default Level, if no String representation | |
11 | * @return Level object | |
12 | * @type Log4js.Level | |
13 | */ | |
14 | 82 | function toLevel(sArg, defaultLevel) { |
15 | ||
16 | 1726 | if (!sArg) { |
17 | 16 | return defaultLevel; |
18 | } | |
19 | ||
20 | 1710 | if (typeof sArg == "string") { |
21 | 1636 | var s = sArg.toUpperCase(); |
22 | 1636 | if (module.exports[s]) { |
23 | 1632 | return module.exports[s]; |
24 | } else { | |
25 | 4 | return defaultLevel; |
26 | } | |
27 | } | |
28 | ||
29 | 74 | return toLevel(sArg.toString()); |
30 | } | |
31 | ||
32 | 82 | Level.prototype.toString = function() { |
33 | 556 | return this.levelStr; |
34 | }; | |
35 | ||
36 | 82 | Level.prototype.isLessThanOrEqualTo = function(otherLevel) { |
37 | 1094 | if (typeof otherLevel === "string") { |
38 | 151 | otherLevel = toLevel(otherLevel); |
39 | } | |
40 | 1094 | return this.level <= otherLevel.level; |
41 | }; | |
42 | ||
43 | 82 | Level.prototype.isGreaterThanOrEqualTo = function(otherLevel) { |
44 | 103 | if (typeof otherLevel === "string") { |
45 | 7 | otherLevel = toLevel(otherLevel); |
46 | } | |
47 | 103 | return this.level >= otherLevel.level; |
48 | }; | |
49 | ||
50 | 82 | Level.prototype.isEqualTo = function(otherLevel) { |
51 | 78 | if (typeof otherLevel == "string") { |
52 | 3 | otherLevel = toLevel(otherLevel); |
53 | } | |
54 | 78 | return this.level === otherLevel.level; |
55 | }; | |
56 | ||
57 | 82 | module.exports = { |
58 | ALL: new Level(Number.MIN_VALUE, "ALL"), | |
59 | TRACE: new Level(5000, "TRACE"), | |
60 | DEBUG: new Level(10000, "DEBUG"), | |
61 | INFO: new Level(20000, "INFO"), | |
62 | WARN: new Level(30000, "WARN"), | |
63 | ERROR: new Level(40000, "ERROR"), | |
64 | FATAL: new Level(50000, "FATAL"), | |
65 | OFF: new Level(Number.MAX_VALUE, "OFF"), | |
66 | toLevel: toLevel | |
67 | }; |
Line | Hits | Source |
---|---|---|
0 | 45 | "use strict"; |
1 | 45 | var dateFormat = require('./date_format') |
2 | , os = require('os') | |
3 | , eol = os.EOL || '\n' | |
4 | , util = require('util') | |
5 | , replacementRegExp = /%[sdj]/g | |
6 | , layoutMakers = { | |
7 | 9 | "messagePassThrough": function() { return messagePassThroughLayout; }, |
8 | 1 | "basic": function() { return basicLayout; }, |
9 | 1 | "colored": function() { return colouredLayout; }, |
10 | 1 | "coloured": function() { return colouredLayout; }, |
11 | "pattern": function (config) { | |
12 | 1 | return patternLayout(config && config.pattern, config && config.tokens); |
13 | } | |
14 | } | |
15 | , colours = { | |
16 | ALL: "grey", | |
17 | TRACE: "blue", | |
18 | DEBUG: "cyan", | |
19 | INFO: "green", | |
20 | WARN: "yellow", | |
21 | ERROR: "red", | |
22 | FATAL: "magenta", | |
23 | OFF: "grey" | |
24 | }; | |
25 | ||
26 | 45 | function wrapErrorsWithInspect(items) { |
27 | 85 | return items.map(function(item) { |
28 | 159 | if ((item instanceof Error) && item.stack) { |
29 | 6 | return { inspect: function() { return util.format(item) + '\n' + item.stack; } }; |
30 | } else { | |
31 | 156 | return item; |
32 | } | |
33 | }); | |
34 | } | |
35 | ||
36 | 45 | function formatLogData(logData) { |
37 | 85 | var data = Array.isArray(logData) ? logData : Array.prototype.slice.call(arguments); |
38 | 85 | return util.format.apply(util, wrapErrorsWithInspect(data)); |
39 | } | |
40 | ||
41 | 45 | var styles = { |
42 | //styles | |
43 | 'bold' : [1, 22], | |
44 | 'italic' : [3, 23], | |
45 | 'underline' : [4, 24], | |
46 | 'inverse' : [7, 27], | |
47 | //grayscale | |
48 | 'white' : [37, 39], | |
49 | 'grey' : [90, 39], | |
50 | 'black' : [90, 39], | |
51 | //colors | |
52 | 'blue' : [34, 39], | |
53 | 'cyan' : [36, 39], | |
54 | 'green' : [32, 39], | |
55 | 'magenta' : [35, 39], | |
56 | 'red' : [31, 39], | |
57 | 'yellow' : [33, 39] | |
58 | }; | |
59 | ||
60 | 45 | function colorizeStart(style) { |
61 | 24 | return style ? '\x1B[' + styles[style][0] + 'm' : ''; |
62 | } | |
63 | 45 | function colorizeEnd(style) { |
64 | 24 | return style ? '\x1B[' + styles[style][1] + 'm' : ''; |
65 | } | |
66 | /** | |
67 | * Taken from masylum's fork (https://github.com/masylum/log4js-node) | |
68 | */ | |
69 | 45 | function colorize (str, style) { |
70 | 23 | return colorizeStart(style) + str + colorizeEnd(style); |
71 | } | |
72 | ||
73 | 45 | function timestampLevelAndCategory(loggingEvent, colour) { |
74 | 23 | var output = colorize( |
75 | formatLogData( | |
76 | '[%s] [%s] %s - ' | |
77 | , dateFormat.asString(loggingEvent.startTime) | |
78 | , loggingEvent.level | |
79 | , loggingEvent.categoryName | |
80 | ) | |
81 | , colour | |
82 | ); | |
83 | 23 | return output; |
84 | } | |
85 | ||
86 | /** | |
87 | * BasicLayout is a simple layout for storing the logs. The logs are stored | |
88 | * in following format: | |
89 | * <pre> | |
90 | * [startTime] [logLevel] categoryName - message\n | |
91 | * </pre> | |
92 | * | |
93 | * @author Stephan Strittmatter | |
94 | */ | |
95 | 45 | function basicLayout (loggingEvent) { |
96 | 21 | return timestampLevelAndCategory(loggingEvent) + formatLogData(loggingEvent.data); |
97 | } | |
98 | ||
99 | /** | |
100 | * colouredLayout - taken from masylum's fork. | |
101 | * same as basicLayout, but with colours. | |
102 | */ | |
103 | 45 | function colouredLayout (loggingEvent) { |
104 | 2 | return timestampLevelAndCategory( |
105 | loggingEvent, | |
106 | colours[loggingEvent.level.toString()] | |
107 | ) + formatLogData(loggingEvent.data); | |
108 | } | |
109 | ||
110 | 45 | function messagePassThroughLayout (loggingEvent) { |
111 | 31 | return formatLogData(loggingEvent.data); |
112 | } | |
113 | ||
114 | /** | |
115 | * PatternLayout | |
116 | * Format for specifiers is %[padding].[truncation][field]{[format]} | |
117 | * e.g. %5.10p - left pad the log level by 5 characters, up to a max of 10 | |
118 | * Fields can be any of: | |
119 | * - %r time in toLocaleTimeString format | |
120 | * - %p log level | |
121 | * - %c log category | |
122 | * - %m log data | |
123 | * - %d date in various formats | |
124 | * - %% % | |
125 | * - %n newline | |
126 | * - %x{<tokenname>} add dynamic tokens to your log. Tokens are specified in the tokens parameter | |
127 | * You can use %[ and %] to define a colored block. | |
128 | * | |
129 | * Tokens are specified as simple key:value objects. | |
130 | * The key represents the token name whereas the value can be a string or function | |
131 | * which is called to extract the value to put in the log message. If token is not | |
132 | * found, it doesn't replace the field. | |
133 | * | |
134 | * A sample token would be: { "pid" : function() { return process.pid; } } | |
135 | * | |
136 | * Takes a pattern string, array of tokens and returns a layout function. | |
137 | * @param {String} Log format pattern String | |
138 | * @param {object} map object of different tokens | |
139 | * @return {Function} | |
140 | * @author Stephan Strittmatter | |
141 | * @author Jan Schmidle | |
142 | */ | |
143 | 45 | function patternLayout (pattern, tokens) { |
144 | 42 | var TTCC_CONVERSION_PATTERN = "%r %p %c - %m%n"; |
145 | 42 | var regex = /%(-?[0-9]+)?(\.?[0-9]+)?([\[\]cdmnprx%])(\{([^\}]+)\})?|([^%]+)/; |
146 | ||
147 | 42 | pattern = pattern || TTCC_CONVERSION_PATTERN; |
148 | ||
149 | 42 | function categoryName(loggingEvent, specifier) { |
150 | 9 | var loggerName = loggingEvent.categoryName; |
151 | 9 | if (specifier) { |
152 | 7 | var precision = parseInt(specifier, 10); |
153 | 7 | var loggerNameBits = loggerName.split("."); |
154 | 7 | if (precision < loggerNameBits.length) { |
155 | 4 | loggerName = loggerNameBits.slice(loggerNameBits.length - precision).join("."); |
156 | } | |
157 | } | |
158 | 9 | return loggerName; |
159 | } | |
160 | ||
161 | 42 | function formatAsDate(loggingEvent, specifier) { |
162 | 7 | var format = dateFormat.ISO8601_FORMAT; |
163 | 7 | if (specifier) { |
164 | 6 | format = specifier; |
165 | // Pick up special cases | |
166 | 6 | if (format == "ISO8601") { |
167 | 1 | format = dateFormat.ISO8601_FORMAT; |
168 | 5 | } else if (format == "ABSOLUTE") { |
169 | 2 | format = dateFormat.ABSOLUTETIME_FORMAT; |
170 | 3 | } else if (format == "DATE") { |
171 | 1 | format = dateFormat.DATETIME_FORMAT; |
172 | } | |
173 | } | |
174 | // Format the date | |
175 | 7 | return dateFormat.asString(format, loggingEvent.startTime); |
176 | } | |
177 | ||
178 | 42 | function formatMessage(loggingEvent) { |
179 | 8 | return formatLogData(loggingEvent.data); |
180 | } | |
181 | ||
182 | 42 | function endOfLine() { |
183 | 4 | return eol; |
184 | } | |
185 | ||
186 | 42 | function logLevel(loggingEvent) { |
187 | 11 | return loggingEvent.level.toString(); |
188 | } | |
189 | ||
190 | 42 | function startTime(loggingEvent) { |
191 | 3 | return "" + loggingEvent.startTime.toLocaleTimeString(); |
192 | } | |
193 | ||
194 | 42 | function startColour(loggingEvent) { |
195 | 1 | return colorizeStart(colours[loggingEvent.level.toString()]); |
196 | } | |
197 | ||
198 | 42 | function endColour(loggingEvent) { |
199 | 1 | return colorizeEnd(colours[loggingEvent.level.toString()]); |
200 | } | |
201 | ||
202 | 42 | function percent() { |
203 | 1 | return '%'; |
204 | } | |
205 | ||
206 | 42 | function userDefined(loggingEvent, specifier) { |
207 | 5 | if (typeof(tokens[specifier]) !== 'undefined') { |
208 | 3 | if (typeof(tokens[specifier]) === 'function') { |
209 | 2 | return tokens[specifier](loggingEvent); |
210 | } else { | |
211 | 1 | return tokens[specifier]; |
212 | } | |
213 | } | |
214 | 2 | return null; |
215 | } | |
216 | ||
217 | 42 | var replacers = { |
218 | 'c': categoryName, | |
219 | 'd': formatAsDate, | |
220 | 'm': formatMessage, | |
221 | 'n': endOfLine, | |
222 | 'p': logLevel, | |
223 | 'r': startTime, | |
224 | '[': startColour, | |
225 | ']': endColour, | |
226 | '%': percent, | |
227 | 'x': userDefined | |
228 | }; | |
229 | ||
230 | 42 | function replaceToken(conversionCharacter, loggingEvent, specifier) { |
231 | 50 | return replacers[conversionCharacter](loggingEvent, specifier); |
232 | } | |
233 | ||
234 | 42 | function truncate(truncation, toTruncate) { |
235 | 50 | var len; |
236 | 50 | if (truncation) { |
237 | 5 | len = parseInt(truncation.substr(1), 10); |
238 | 5 | return toTruncate.substring(0, len); |
239 | } | |
240 | ||
241 | 45 | return toTruncate; |
242 | } | |
243 | ||
244 | 42 | function pad(padding, toPad) { |
245 | 50 | var len; |
246 | 50 | if (padding) { |
247 | 8 | if (padding.charAt(0) == "-") { |
248 | 4 | len = parseInt(padding.substr(1), 10); |
249 | // Right pad with spaces | |
250 | 4 | while (toPad.length < len) { |
251 | 9 | toPad += " "; |
252 | } | |
253 | } else { | |
254 | 4 | len = parseInt(padding, 10); |
255 | // Left pad with spaces | |
256 | 4 | while (toPad.length < len) { |
257 | 9 | toPad = " " + toPad; |
258 | } | |
259 | } | |
260 | } | |
261 | 50 | return toPad; |
262 | } | |
263 | ||
264 | 42 | return function(loggingEvent) { |
265 | 41 | var formattedString = ""; |
266 | 41 | var result; |
267 | 41 | var searchString = pattern; |
268 | ||
269 | 41 | while ((result = regex.exec(searchString))) { |
270 | 58 | var matchedString = result[0]; |
271 | 58 | var padding = result[1]; |
272 | 58 | var truncation = result[2]; |
273 | 58 | var conversionCharacter = result[3]; |
274 | 58 | var specifier = result[5]; |
275 | 58 | var text = result[6]; |
276 | ||
277 | // Check if the pattern matched was just normal text | |
278 | 58 | if (text) { |
279 | 8 | formattedString += "" + text; |
280 | } else { | |
281 | // Create a raw replacement string based on the conversion | |
282 | // character and specifier | |
283 | 50 | var replacement = |
284 | replaceToken(conversionCharacter, loggingEvent, specifier) || | |
285 | matchedString; | |
286 | ||
287 | // Format the replacement according to any padding or | |
288 | // truncation specified | |
289 | 50 | replacement = truncate(truncation, replacement); |
290 | 50 | replacement = pad(padding, replacement); |
291 | 50 | formattedString += replacement; |
292 | } | |
293 | 58 | searchString = searchString.substr(result.index + result[0].length); |
294 | } | |
295 | 41 | return formattedString; |
296 | }; | |
297 | ||
298 | } | |
299 | ||
300 | 45 | module.exports = { |
301 | basicLayout: basicLayout, | |
302 | messagePassThroughLayout: messagePassThroughLayout, | |
303 | patternLayout: patternLayout, | |
304 | colouredLayout: colouredLayout, | |
305 | coloredLayout: colouredLayout, | |
306 | layout: function(name, config) { | |
307 | 13 | return layoutMakers[name] && layoutMakers[name](config); |
308 | } | |
309 | }; |
Line | Hits | Source |
---|---|---|
0 | 45 | "use strict"; |
1 | 45 | exports.ISO8601_FORMAT = "yyyy-MM-dd hh:mm:ss.SSS"; |
2 | 45 | exports.ISO8601_WITH_TZ_OFFSET_FORMAT = "yyyy-MM-ddThh:mm:ssO"; |
3 | 45 | exports.DATETIME_FORMAT = "dd MM yyyy hh:mm:ss.SSS"; |
4 | 45 | exports.ABSOLUTETIME_FORMAT = "hh:mm:ss.SSS"; |
5 | ||
6 | 45 | function padWithZeros(vNumber, width) { |
7 | 456 | var numAsString = vNumber + ""; |
8 | 456 | while (numAsString.length < width) { |
9 | 184 | numAsString = "0" + numAsString; |
10 | } | |
11 | 456 | return numAsString; |
12 | } | |
13 | ||
14 | 45 | function addZero(vNumber) { |
15 | 399 | return padWithZeros(vNumber, 2); |
16 | } | |
17 | ||
18 | /** | |
19 | * Formats the TimeOffest | |
20 | * Thanks to http://www.svendtofte.com/code/date_format/ | |
21 | * @private | |
22 | */ | |
23 | 45 | function offset(date) { |
24 | // Difference to Greenwich time (GMT) in hours | |
25 | 57 | var os = Math.abs(date.getTimezoneOffset()); |
26 | 57 | var h = String(Math.floor(os/60)); |
27 | 57 | var m = String(os%60); |
28 | 57 | if (h.length == 1) { |
29 | 2 | h = "0" + h; |
30 | } | |
31 | 57 | if (m.length == 1) { |
32 | 57 | m = "0" + m; |
33 | } | |
34 | 57 | return date.getTimezoneOffset() < 0 ? "+"+h+m : "-"+h+m; |
35 | } | |
36 | ||
37 | 45 | exports.asString = function(/*format,*/ date) { |
38 | 57 | var format = exports.ISO8601_FORMAT; |
39 | 57 | if (typeof(date) === "string") { |
40 | 33 | format = arguments[0]; |
41 | 33 | date = arguments[1]; |
42 | } | |
43 | ||
44 | 57 | var vDay = addZero(date.getDate()); |
45 | 57 | var vMonth = addZero(date.getMonth()+1); |
46 | 57 | var vYearLong = addZero(date.getFullYear()); |
47 | 57 | var vYearShort = addZero(date.getFullYear().toString().substring(3,4)); |
48 | 57 | var vYear = (format.indexOf("yyyy") > -1 ? vYearLong : vYearShort); |
49 | 57 | var vHour = addZero(date.getHours()); |
50 | 57 | var vMinute = addZero(date.getMinutes()); |
51 | 57 | var vSecond = addZero(date.getSeconds()); |
52 | 57 | var vMillisecond = padWithZeros(date.getMilliseconds(), 3); |
53 | 57 | var vTimeZone = offset(date); |
54 | 57 | var formatted = format |
55 | .replace(/dd/g, vDay) | |
56 | .replace(/MM/g, vMonth) | |
57 | .replace(/y{1,4}/g, vYear) | |
58 | .replace(/hh/g, vHour) | |
59 | .replace(/mm/g, vMinute) | |
60 | .replace(/ss/g, vSecond) | |
61 | .replace(/SSS/g, vMillisecond) | |
62 | .replace(/O/g, vTimeZone); | |
63 | 57 | return formatted; |
64 | ||
65 | }; |
Line | Hits | Source |
---|---|---|
0 | 24 | "use strict"; |
1 | /* | |
2 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
3 | * you may not use this file except in compliance with the License. | |
4 | * You may obtain a copy of the License at | |
5 | * | |
6 | * http://www.apache.org/licenses/LICENSE-2.0 | |
7 | * | |
8 | * Unless required by applicable law or agreed to in writing, software | |
9 | * distributed under the License is distributed on an "AS IS" BASIS, | |
10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
11 | * See the License for the specific language governing permissions and | |
12 | * limitations under the License. | |
13 | */ | |
14 | ||
15 | /** | |
16 | * @fileoverview log4js is a library to log in JavaScript in similar manner | |
17 | * than in log4j for Java. The API should be nearly the same. | |
18 | * | |
19 | * <h3>Example:</h3> | |
20 | * <pre> | |
21 | * var logging = require('log4js'); | |
22 | * //add an appender that logs all messages to stdout. | |
23 | * logging.addAppender(logging.consoleAppender()); | |
24 | * //add an appender that logs "some-category" to a file | |
25 | * logging.addAppender(logging.fileAppender("file.log"), "some-category"); | |
26 | * //get a logger | |
27 | * var log = logging.getLogger("some-category"); | |
28 | * log.setLevel(logging.levels.TRACE); //set the Level | |
29 | * | |
30 | * ... | |
31 | * | |
32 | * //call the log | |
33 | * log.trace("trace me" ); | |
34 | * </pre> | |
35 | * | |
36 | * NOTE: the authors below are the original browser-based log4js authors | |
37 | * don't try to contact them about bugs in this version :) | |
38 | * @version 1.0 | |
39 | * @author Stephan Strittmatter - http://jroller.com/page/stritti | |
40 | * @author Seth Chisamore - http://www.chisamore.com | |
41 | * @since 2005-05-20 | |
42 | * @static | |
43 | * Website: http://log4js.berlios.de | |
44 | */ | |
45 | 24 | var events = require('events') |
46 | , fs = require('fs') | |
47 | , path = require('path') | |
48 | , util = require('util') | |
49 | , layouts = require('./layouts') | |
50 | , levels = require('./levels') | |
51 | , LoggingEvent = require('./logger').LoggingEvent | |
52 | , Logger = require('./logger').Logger | |
53 | , ALL_CATEGORIES = '[all]' | |
54 | , appenders = {} | |
55 | , loggers = {} | |
56 | , appenderMakers = {} | |
57 | , defaultConfig = { | |
58 | appenders: [ | |
59 | { type: "console" } | |
60 | ], | |
61 | replaceConsole: false | |
62 | }; | |
63 | ||
64 | /** | |
65 | * Get a logger instance. Instance is cached on categoryName level. | |
66 | * @param {String} categoryName name of category to log to. | |
67 | * @return {Logger} instance of logger for the category | |
68 | * @static | |
69 | */ | |
70 | 24 | function getLogger (categoryName) { |
71 | ||
72 | // Use default logger if categoryName is not specified or invalid | |
73 | 450 | if (typeof categoryName !== "string") { |
74 | 1 | categoryName = Logger.DEFAULT_CATEGORY; |
75 | } | |
76 | ||
77 | 450 | var appenderList; |
78 | 450 | if (!loggers[categoryName]) { |
79 | // Create the logger for this name if it doesn't already exist | |
80 | 42 | loggers[categoryName] = new Logger(categoryName); |
81 | 42 | if (appenders[categoryName]) { |
82 | 14 | appenderList = appenders[categoryName]; |
83 | 14 | appenderList.forEach(function(appender) { |
84 | 14 | loggers[categoryName].addListener("log", appender); |
85 | }); | |
86 | } | |
87 | 42 | if (appenders[ALL_CATEGORIES]) { |
88 | 23 | appenderList = appenders[ALL_CATEGORIES]; |
89 | 23 | appenderList.forEach(function(appender) { |
90 | 34 | loggers[categoryName].addListener("log", appender); |
91 | }); | |
92 | } | |
93 | } | |
94 | ||
95 | 450 | return loggers[categoryName]; |
96 | } | |
97 | ||
98 | /** | |
99 | * args are appender, then zero or more categories | |
100 | */ | |
101 | 24 | function addAppender () { |
102 | 81 | var args = Array.prototype.slice.call(arguments); |
103 | 81 | var appender = args.shift(); |
104 | 81 | if (args.length === 0 || args[0] === undefined) { |
105 | 47 | args = [ ALL_CATEGORIES ]; |
106 | } | |
107 | //argument may already be an array | |
108 | 81 | if (Array.isArray(args[0])) { |
109 | 2 | args = args[0]; |
110 | } | |
111 | ||
112 | 81 | args.forEach(function(category) { |
113 | 84 | addAppenderToCategory(appender, category); |
114 | ||
115 | 84 | if (category === ALL_CATEGORIES) { |
116 | 47 | addAppenderToAllLoggers(appender); |
117 | 37 | } else if (loggers[category]) { |
118 | 23 | loggers[category].addListener("log", appender); |
119 | } | |
120 | }); | |
121 | } | |
122 | ||
123 | 24 | function addAppenderToAllLoggers(appender) { |
124 | 47 | for (var logger in loggers) { |
125 | 80 | if (loggers.hasOwnProperty(logger)) { |
126 | 80 | loggers[logger].addListener("log", appender); |
127 | } | |
128 | } | |
129 | } | |
130 | ||
131 | 24 | function addAppenderToCategory(appender, category) { |
132 | 84 | if (!appenders[category]) { |
133 | 69 | appenders[category] = []; |
134 | } | |
135 | 84 | appenders[category].push(appender); |
136 | } | |
137 | ||
138 | 24 | function clearAppenders () { |
139 | 73 | appenders = {}; |
140 | 73 | for (var logger in loggers) { |
141 | 231 | if (loggers.hasOwnProperty(logger)) { |
142 | 231 | loggers[logger].removeAllListeners("log"); |
143 | } | |
144 | } | |
145 | } | |
146 | ||
147 | 24 | function configureAppenders(appenderList, options) { |
148 | 51 | clearAppenders(); |
149 | 51 | if (appenderList) { |
150 | 40 | appenderList.forEach(function(appenderConfig) { |
151 | 42 | loadAppender(appenderConfig.type); |
152 | 42 | var appender; |
153 | 42 | appenderConfig.makers = appenderMakers; |
154 | 42 | try { |
155 | 42 | appender = appenderMakers[appenderConfig.type](appenderConfig, options); |
156 | 41 | addAppender(appender, appenderConfig.category); |
157 | } catch(e) { | |
158 | 1 | throw new Error("log4js configuration problem for " + util.inspect(appenderConfig), e); |
159 | } | |
160 | }); | |
161 | } | |
162 | } | |
163 | ||
164 | 24 | function configureLevels(levels) { |
165 | 50 | if (levels) { |
166 | 11 | for (var category in levels) { |
167 | 10 | if (levels.hasOwnProperty(category)) { |
168 | 10 | getLogger(category).setLevel(levels[category]); |
169 | } | |
170 | } | |
171 | } | |
172 | } | |
173 | ||
174 | 24 | function setGlobalLogLevel(level) { |
175 | 2 | Logger.prototype.level = levels.toLevel(level, levels.TRACE); |
176 | } | |
177 | ||
178 | /** | |
179 | * Get the default logger instance. | |
180 | * @return {Logger} instance of default logger | |
181 | * @static | |
182 | */ | |
183 | 24 | function getDefaultLogger () { |
184 | 1 | return getLogger(Logger.DEFAULT_CATEGORY); |
185 | } | |
186 | ||
187 | 24 | var configState = {}; |
188 | ||
189 | 24 | function loadConfigurationFile(filename) { |
190 | 37 | if (filename) { |
191 | 12 | return JSON.parse(fs.readFileSync(filename, "utf8")); |
192 | } | |
193 | 25 | return undefined; |
194 | } | |
195 | ||
196 | 24 | function configureOnceOff(config, options) { |
197 | 51 | if (config) { |
198 | 51 | try { |
199 | 51 | configureAppenders(config.appenders, options); |
200 | 50 | configureLevels(config.levels); |
201 | ||
202 | 50 | if (config.replaceConsole) { |
203 | 1 | replaceConsole(); |
204 | } else { | |
205 | 49 | restoreConsole(); |
206 | } | |
207 | } catch (e) { | |
208 | 1 | throw new Error( |
209 | "Problem reading log4js config " + util.inspect(config) + | |
210 | ". Error was \"" + e.message + "\" (" + e.stack + ")" | |
211 | ); | |
212 | } | |
213 | } | |
214 | } | |
215 | ||
216 | 24 | function reloadConfiguration() { |
217 | 3 | var mtime = getMTime(configState.filename); |
218 | 4 | if (!mtime) return; |
219 | ||
220 | 2 | if (configState.lastMTime && (mtime.getTime() > configState.lastMTime.getTime())) { |
221 | 1 | configureOnceOff(loadConfigurationFile(configState.filename)); |
222 | } | |
223 | 2 | configState.lastMTime = mtime; |
224 | } | |
225 | ||
226 | 24 | function getMTime(filename) { |
227 | 8 | var mtime; |
228 | 8 | try { |
229 | 8 | mtime = fs.statSync(configState.filename).mtime; |
230 | } catch (e) { | |
231 | 1 | getLogger('log4js').warn('Failed to load configuration file ' + filename); |
232 | } | |
233 | 8 | return mtime; |
234 | } | |
235 | ||
236 | 24 | function initReloadConfiguration(filename, options) { |
237 | 5 | if (configState.timerId) { |
238 | 1 | clearInterval(configState.timerId); |
239 | 1 | delete configState.timerId; |
240 | } | |
241 | 5 | configState.filename = filename; |
242 | 5 | configState.lastMTime = getMTime(filename); |
243 | 5 | configState.timerId = setInterval(reloadConfiguration, options.reloadSecs*1000); |
244 | } | |
245 | ||
246 | 24 | function configure(configurationFileOrObject, options) { |
247 | 50 | var config = configurationFileOrObject; |
248 | 50 | config = config || process.env.LOG4JS_CONFIG; |
249 | 50 | options = options || {}; |
250 | ||
251 | 50 | if (config === undefined || config === null || typeof(config) === 'string') { |
252 | 36 | if (options.reloadSecs) { |
253 | 5 | initReloadConfiguration(config, options); |
254 | } | |
255 | 36 | config = loadConfigurationFile(config) || defaultConfig; |
256 | } else { | |
257 | 14 | if (options.reloadSecs) { |
258 | 1 | getLogger('log4js').warn( |
259 | 'Ignoring configuration reload parameter for "object" configuration.' | |
260 | ); | |
261 | } | |
262 | } | |
263 | 50 | configureOnceOff(config, options); |
264 | } | |
265 | ||
266 | 24 | var originalConsoleFunctions = { |
267 | log: console.log, | |
268 | debug: console.debug, | |
269 | info: console.info, | |
270 | warn: console.warn, | |
271 | error: console.error | |
272 | }; | |
273 | ||
274 | 24 | function replaceConsole(logger) { |
275 | 3 | function replaceWith(fn) { |
276 | 15 | return function() { |
277 | 10 | fn.apply(logger, arguments); |
278 | }; | |
279 | } | |
280 | 3 | logger = logger || getLogger("console"); |
281 | 3 | ['log','debug','info','warn','error'].forEach(function (item) { |
282 | 15 | console[item] = replaceWith(item === 'log' ? logger.info : logger[item]); |
283 | }); | |
284 | } | |
285 | ||
286 | 24 | function restoreConsole() { |
287 | 51 | ['log', 'debug', 'info', 'warn', 'error'].forEach(function (item) { |
288 | 255 | console[item] = originalConsoleFunctions[item]; |
289 | }); | |
290 | } | |
291 | ||
292 | 24 | function loadAppender(appender) { |
293 | 46 | var appenderModule; |
294 | 46 | try { |
295 | 46 | appenderModule = require('./appenders/' + appender); |
296 | } catch (e) { | |
297 | 1 | appenderModule = require(appender); |
298 | } | |
299 | 46 | module.exports.appenders[appender] = appenderModule.appender.bind(appenderModule); |
300 | 46 | appenderMakers[appender] = appenderModule.configure.bind(appenderModule); |
301 | } | |
302 | ||
303 | 24 | module.exports = { |
304 | getLogger: getLogger, | |
305 | getDefaultLogger: getDefaultLogger, | |
306 | ||
307 | addAppender: addAppender, | |
308 | loadAppender: loadAppender, | |
309 | clearAppenders: clearAppenders, | |
310 | configure: configure, | |
311 | ||
312 | replaceConsole: replaceConsole, | |
313 | restoreConsole: restoreConsole, | |
314 | ||
315 | levels: levels, | |
316 | setGlobalLogLevel: setGlobalLogLevel, | |
317 | ||
318 | layouts: layouts, | |
319 | appenders: {}, | |
320 | appenderMakers: appenderMakers, | |
321 | connectLogger: require('./connect-logger').connectLogger | |
322 | }; | |
323 | ||
324 | //set ourselves up | |
325 | 24 | configure(); |
326 |
Line | Hits | Source |
---|---|---|
0 | 41 | "use strict"; |
1 | 41 | var levels = require('./levels') |
2 | , util = require('util') | |
3 | , events = require('events') | |
4 | , DEFAULT_CATEGORY = '[default]'; | |
5 | ||
6 | /** | |
7 | * Models a logging event. | |
8 | * @constructor | |
9 | * @param {String} categoryName name of category | |
10 | * @param {Log4js.Level} level level of message | |
11 | * @param {Array} data objects to log | |
12 | * @param {Log4js.Logger} logger the associated logger | |
13 | * @author Seth Chisamore | |
14 | */ | |
15 | 41 | function LoggingEvent (categoryName, level, data, logger) { |
16 | 79 | this.startTime = new Date(); |
17 | 79 | this.categoryName = categoryName; |
18 | 79 | this.data = data; |
19 | 79 | this.level = level; |
20 | 79 | this.logger = logger; |
21 | } | |
22 | ||
23 | /** | |
24 | * Logger to log messages. | |
25 | * use {@see Log4js#getLogger(String)} to get an instance. | |
26 | * @constructor | |
27 | * @param name name of category to log to | |
28 | * @author Stephan Strittmatter | |
29 | */ | |
30 | 41 | function Logger (name, level) { |
31 | 46 | this.category = name || DEFAULT_CATEGORY; |
32 | ||
33 | 46 | if (level) { |
34 | 2 | this.setLevel(level); |
35 | } | |
36 | } | |
37 | 41 | util.inherits(Logger, events.EventEmitter); |
38 | 41 | Logger.DEFAULT_CATEGORY = DEFAULT_CATEGORY; |
39 | 41 | Logger.prototype.level = levels.TRACE; |
40 | ||
41 | 41 | Logger.prototype.setLevel = function(level) { |
42 | 213 | this.level = levels.toLevel(level, this.level || levels.TRACE); |
43 | }; | |
44 | ||
45 | 41 | Logger.prototype.removeLevel = function() { |
46 | 2 | delete this.level; |
47 | }; | |
48 | ||
49 | 41 | Logger.prototype.log = function() { |
50 | 79 | var args = Array.prototype.slice.call(arguments) |
51 | , logLevel = args.shift() | |
52 | , loggingEvent = new LoggingEvent(this.category, logLevel, args, this); | |
53 | 79 | this.emit("log", loggingEvent); |
54 | }; | |
55 | ||
56 | 41 | Logger.prototype.isLevelEnabled = function(otherLevel) { |
57 | 562 | return this.level.isLessThanOrEqualTo(otherLevel); |
58 | }; | |
59 | ||
60 | 41 | ['Trace','Debug','Info','Warn','Error','Fatal'].forEach( |
61 | function(levelString) { | |
62 | 246 | var level = levels.toLevel(levelString); |
63 | 246 | Logger.prototype['is'+levelString+'Enabled'] = function() { |
64 | 6 | return this.isLevelEnabled(level); |
65 | }; | |
66 | ||
67 | 246 | Logger.prototype[levelString.toLowerCase()] = function () { |
68 | 88 | if (this.isLevelEnabled(level)) { |
69 | 79 | var args = Array.prototype.slice.call(arguments); |
70 | 79 | args.unshift(level); |
71 | 79 | Logger.prototype.log.apply(this, args); |
72 | } | |
73 | }; | |
74 | } | |
75 | ); | |
76 | ||
77 | ||
78 | 41 | exports.LoggingEvent = LoggingEvent; |
79 | 41 | exports.Logger = Logger; |
Line | Hits | Source |
---|---|---|
0 | 24 | "use strict"; |
1 | 24 | var levels = require("./levels"); |
2 | 24 | var DEFAULT_FORMAT = ':remote-addr - -' + |
3 | ' ":method :url HTTP/:http-version"' + | |
4 | ' :status :content-length ":referrer"' + | |
5 | ' ":user-agent"'; | |
6 | /** | |
7 | * Log requests with the given `options` or a `format` string. | |
8 | * | |
9 | * Options: | |
10 | * | |
11 | * - `format` Format string, see below for tokens | |
12 | * - `level` A log4js levels instance. Supports also 'auto' | |
13 | * | |
14 | * Tokens: | |
15 | * | |
16 | * - `:req[header]` ex: `:req[Accept]` | |
17 | * - `:res[header]` ex: `:res[Content-Length]` | |
18 | * - `:http-version` | |
19 | * - `:response-time` | |
20 | * - `:remote-addr` | |
21 | * - `:date` | |
22 | * - `:method` | |
23 | * - `:url` | |
24 | * - `:referrer` | |
25 | * - `:user-agent` | |
26 | * - `:status` | |
27 | * | |
28 | * @param {String|Function|Object} format or options | |
29 | * @return {Function} | |
30 | * @api public | |
31 | */ | |
32 | ||
33 | 24 | function getLogger(logger4js, options) { |
34 | 13 | if ('object' == typeof options) { |
35 | 6 | options = options || {}; |
36 | 7 | } else if (options) { |
37 | 4 | options = { format: options }; |
38 | } else { | |
39 | 3 | options = {}; |
40 | } | |
41 | ||
42 | 13 | var thislogger = logger4js |
43 | , level = levels.toLevel(options.level, levels.INFO) | |
44 | , fmt = options.format || DEFAULT_FORMAT | |
45 | , nolog = options.nolog ? createNoLogCondition(options.nolog) : null; | |
46 | ||
47 | 13 | return function (req, res, next) { |
48 | // mount safety | |
49 | 23 | if (req._logging) return next(); |
50 | ||
51 | // nologs | |
52 | 30 | if (nolog && nolog.test(req.originalUrl)) return next(); |
53 | 16 | if (thislogger.isLevelEnabled(level) || options.level === 'auto') { |
54 | ||
55 | 15 | var start = new Date() |
56 | , statusCode | |
57 | , writeHead = res.writeHead | |
58 | , end = res.end | |
59 | , url = req.originalUrl; | |
60 | ||
61 | // flag as logging | |
62 | 15 | req._logging = true; |
63 | ||
64 | // proxy for statusCode. | |
65 | 15 | res.writeHead = function(code, headers){ |
66 | 11 | res.writeHead = writeHead; |
67 | 11 | res.writeHead(code, headers); |
68 | 11 | res.__statusCode = statusCode = code; |
69 | 11 | res.__headers = headers || {}; |
70 | ||
71 | //status code response level handling | |
72 | 11 | if(options.level === 'auto'){ |
73 | 5 | level = levels.INFO; |
74 | 8 | if(code >= 300) level = levels.WARN; |
75 | 7 | if(code >= 400) level = levels.ERROR; |
76 | } else { | |
77 | 6 | level = levels.toLevel(options.level, levels.INFO); |
78 | } | |
79 | }; | |
80 | ||
81 | // proxy end to output a line to the provided logger. | |
82 | 15 | res.end = function(chunk, encoding) { |
83 | 15 | res.end = end; |
84 | 15 | res.end(chunk, encoding); |
85 | 15 | res.responseTime = new Date() - start; |
86 | 15 | if (thislogger.isLevelEnabled(level)) { |
87 | 15 | if (typeof fmt === 'function') { |
88 | 1 | var line = fmt(req, res, function(str){ return format(str, req, res); }); |
89 | 2 | if (line) thislogger.log(level, line); |
90 | } else { | |
91 | 14 | thislogger.log(level, format(fmt, req, res)); |
92 | } | |
93 | } | |
94 | }; | |
95 | } | |
96 | ||
97 | //ensure next gets always called | |
98 | 16 | next(); |
99 | }; | |
100 | } | |
101 | ||
102 | /** | |
103 | * Return formatted log line. | |
104 | * | |
105 | * @param {String} str | |
106 | * @param {IncomingMessage} req | |
107 | * @param {ServerResponse} res | |
108 | * @return {String} | |
109 | * @api private | |
110 | */ | |
111 | ||
112 | 24 | function format(str, req, res) { |
113 | 14 | return str |
114 | .replace(':url', req.originalUrl) | |
115 | .replace(':method', req.method) | |
116 | .replace(':status', res.__statusCode || res.statusCode) | |
117 | .replace(':response-time', res.responseTime) | |
118 | .replace(':date', new Date().toUTCString()) | |
119 | .replace(':referrer', req.headers.referer || req.headers.referrer || '') | |
120 | .replace(':http-version', req.httpVersionMajor + '.' + req.httpVersionMinor) | |
121 | .replace( | |
122 | ':remote-addr', | |
123 | req.socket && | |
124 | (req.socket.remoteAddress || (req.socket.socket && req.socket.socket.remoteAddress)) | |
125 | ) | |
126 | .replace(':user-agent', req.headers['user-agent'] || '') | |
127 | .replace( | |
128 | ':content-length', | |
129 | (res._headers && res._headers['content-length']) || | |
130 | (res.__headers && res.__headers['Content-Length']) || | |
131 | '-' | |
132 | ) | |
133 | 1 | .replace(/:req\[([^\]]+)\]/g, function(_, field){ return req.headers[field.toLowerCase()]; }) |
134 | .replace(/:res\[([^\]]+)\]/g, function(_, field){ | |
135 | 1 | return res._headers ? |
136 | (res._headers[field.toLowerCase()] || res.__headers[field]) | |
137 | : (res.__headers && res.__headers[field]); | |
138 | }); | |
139 | } | |
140 | ||
141 | /** | |
142 | * Return RegExp Object about nolog | |
143 | * | |
144 | * @param {String} nolog | |
145 | * @return {RegExp} | |
146 | * @api private | |
147 | * | |
148 | * syntax | |
149 | * 1. String | |
150 | * 1.1 "\\.gif" | |
151 | * NOT LOGGING http://example.com/hoge.gif and http://example.com/hoge.gif?fuga | |
152 | * LOGGING http://example.com/hoge.agif | |
153 | * 1.2 in "\\.gif|\\.jpg$" | |
154 | * NOT LOGGING http://example.com/hoge.gif and | |
155 | * http://example.com/hoge.gif?fuga and http://example.com/hoge.jpg?fuga | |
156 | * LOGGING http://example.com/hoge.agif, | |
157 | * http://example.com/hoge.ajpg and http://example.com/hoge.jpg?hoge | |
158 | * 1.3 in "\\.(gif|jpe?g|png)$" | |
159 | * NOT LOGGING http://example.com/hoge.gif and http://example.com/hoge.jpeg | |
160 | * LOGGING http://example.com/hoge.gif?uid=2 and http://example.com/hoge.jpg?pid=3 | |
161 | * 2. RegExp | |
162 | * 2.1 in /\.(gif|jpe?g|png)$/ | |
163 | * SAME AS 1.3 | |
164 | * 3. Array | |
165 | * 3.1 ["\\.jpg$", "\\.png", "\\.gif"] | |
166 | * SAME AS "\\.jpg|\\.png|\\.gif" | |
167 | */ | |
168 | 24 | function createNoLogCondition(nolog) { |
169 | 4 | var regexp = null; |
170 | ||
171 | 4 | if (nolog) { |
172 | 4 | if (nolog instanceof RegExp) { |
173 | 1 | regexp = nolog; |
174 | } | |
175 | ||
176 | 4 | if (typeof nolog === 'string') { |
177 | 2 | regexp = new RegExp(nolog); |
178 | } | |
179 | ||
180 | 4 | if (Array.isArray(nolog)) { |
181 | 1 | var regexpsAsStrings = nolog.map( |
182 | function convertToStrings(o) { | |
183 | 2 | return o.source ? o.source : o; |
184 | } | |
185 | ); | |
186 | 1 | regexp = new RegExp(regexpsAsStrings.join('|')); |
187 | } | |
188 | } | |
189 | ||
190 | 4 | return regexp; |
191 | } | |
192 | ||
193 | 24 | exports.connectLogger = getLogger; |
Line | Hits | Source |
---|---|---|
0 | 19 | "use strict"; |
1 | 19 | var layouts = require('../layouts') |
2 | , consoleLog = console.log.bind(console); | |
3 | ||
4 | 19 | function consoleAppender (layout) { |
5 | 21 | layout = layout || layouts.colouredLayout; |
6 | 21 | return function(loggingEvent) { |
7 | 1 | consoleLog(layout(loggingEvent)); |
8 | }; | |
9 | } | |
10 | ||
11 | 19 | function configure(config) { |
12 | 20 | var layout; |
13 | 20 | if (config.layout) { |
14 | 1 | layout = layouts.layout(config.layout.type, config.layout); |
15 | } | |
16 | 20 | return consoleAppender(layout); |
17 | } | |
18 | ||
19 | 19 | exports.appender = consoleAppender; |
20 | 19 | exports.configure = configure; |
Line | Hits | Source |
---|---|---|
0 | 4 | "use strict"; |
1 | 4 | var fs = require('fs') |
2 | , stream | |
3 | , debug = require('../debug')('BaseRollingFileStream') | |
4 | , util = require('util') | |
5 | , semver = require('semver'); | |
6 | ||
7 | 4 | if (semver.satisfies(process.version, '>=0.10.0')) { |
8 | 3 | stream = require('stream'); |
9 | } else { | |
10 | 1 | stream = require('readable-stream'); |
11 | } | |
12 | ||
13 | 4 | module.exports = BaseRollingFileStream; |
14 | ||
15 | 4 | function BaseRollingFileStream(filename, options) { |
16 | 24 | debug("In BaseRollingFileStream"); |
17 | 24 | this.filename = filename; |
18 | 24 | this.options = options || { encoding: 'utf8', mode: parseInt('0644', 8), flags: 'a' }; |
19 | 24 | this.currentSize = 0; |
20 | ||
21 | 24 | function currentFileSize(file) { |
22 | 23 | var fileSize = 0; |
23 | 23 | try { |
24 | 23 | fileSize = fs.statSync(file).size; |
25 | } catch (e) { | |
26 | // file does not exist | |
27 | } | |
28 | 23 | return fileSize; |
29 | } | |
30 | ||
31 | 24 | function throwErrorIfArgumentsAreNotValid() { |
32 | 24 | if (!filename) { |
33 | 1 | throw new Error("You must specify a filename"); |
34 | } | |
35 | } | |
36 | ||
37 | 24 | throwErrorIfArgumentsAreNotValid(); |
38 | 23 | debug("Calling BaseRollingFileStream.super"); |
39 | 23 | BaseRollingFileStream.super_.call(this); |
40 | 23 | this.openTheStream(); |
41 | 23 | this.currentSize = currentFileSize(this.filename); |
42 | } | |
43 | 4 | util.inherits(BaseRollingFileStream, stream.Writable); |
44 | ||
45 | 4 | BaseRollingFileStream.prototype._write = function(chunk, encoding, callback) { |
46 | 29 | var that = this; |
47 | 29 | function writeTheChunk() { |
48 | 29 | debug("writing the chunk to the underlying stream"); |
49 | 29 | that.currentSize += chunk.length; |
50 | 29 | that.theStream.write(chunk, encoding, callback); |
51 | } | |
52 | ||
53 | 29 | debug("in _write"); |
54 | ||
55 | 29 | if (this.shouldRoll()) { |
56 | 9 | this.currentSize = 0; |
57 | 9 | this.roll(this.filename, writeTheChunk); |
58 | } else { | |
59 | 20 | writeTheChunk(); |
60 | } | |
61 | }; | |
62 | ||
63 | 4 | BaseRollingFileStream.prototype.openTheStream = function(cb) { |
64 | 32 | debug("opening the underlying stream"); |
65 | 32 | this.theStream = fs.createWriteStream(this.filename, this.options); |
66 | 32 | if (cb) { |
67 | 9 | this.theStream.on("open", cb); |
68 | } | |
69 | }; | |
70 | ||
71 | 4 | BaseRollingFileStream.prototype.closeTheStream = function(cb) { |
72 | 9 | debug("closing the underlying stream"); |
73 | 9 | this.theStream.end(cb); |
74 | }; | |
75 | ||
76 | 4 | BaseRollingFileStream.prototype.shouldRoll = function() { |
77 | 1 | return false; // default behaviour is never to roll |
78 | }; | |
79 | ||
80 | 4 | BaseRollingFileStream.prototype.roll = function(filename, callback) { |
81 | 1 | callback(); // default behaviour is not to do anything |
82 | }; | |
83 |
Line | Hits | Source |
---|---|---|
0 | 14 | "use strict"; |
1 | ||
2 | 14 | module.exports = function(label) { |
3 | 18 | var debug; |
4 | ||
5 | 18 | if (process.env.NODE_DEBUG && /\blog4js\b/.test(process.env.NODE_DEBUG)) { |
6 | 1 | debug = function(message) { |
7 | 1 | console.error('LOG4JS: (%s) %s', label, message); |
8 | }; | |
9 | } else { | |
10 | 17 | debug = function() { }; |
11 | } | |
12 | ||
13 | 18 | return debug; |
14 | }; |
Line | Hits | Source |
---|---|---|
0 | 2 | exports.RollingFileStream = require('./RollingFileStream'); |
1 | 2 | exports.DateRollingFileStream = require('./DateRollingFileStream'); |
Line | Hits | Source |
---|---|---|
0 | 2 | "use strict"; |
1 | 2 | var BaseRollingFileStream = require('./BaseRollingFileStream') |
2 | , debug = require('../debug')('RollingFileStream') | |
3 | , util = require('util') | |
4 | , path = require('path') | |
5 | , fs = require('fs') | |
6 | , async = require('async'); | |
7 | ||
8 | 2 | module.exports = RollingFileStream; |
9 | ||
10 | 2 | function RollingFileStream (filename, size, backups, options) { |
11 | 9 | this.size = size; |
12 | 9 | this.backups = backups || 1; |
13 | ||
14 | 9 | function throwErrorIfArgumentsAreNotValid() { |
15 | 9 | if (!filename || !size || size <= 0) { |
16 | 1 | throw new Error("You must specify a filename and file size"); |
17 | } | |
18 | } | |
19 | ||
20 | 9 | throwErrorIfArgumentsAreNotValid(); |
21 | ||
22 | 8 | RollingFileStream.super_.call(this, filename, options); |
23 | } | |
24 | 2 | util.inherits(RollingFileStream, BaseRollingFileStream); |
25 | ||
26 | 2 | RollingFileStream.prototype.shouldRoll = function() { |
27 | 22 | debug("should roll with current size %d, and max size %d", this.currentSize, this.size); |
28 | 22 | return this.currentSize >= this.size; |
29 | }; | |
30 | ||
31 | 2 | RollingFileStream.prototype.roll = function(filename, callback) { |
32 | 7 | var that = this, |
33 | nameMatcher = new RegExp('^' + path.basename(filename)); | |
34 | ||
35 | 7 | function justTheseFiles (item) { |
36 | 305 | return nameMatcher.test(item); |
37 | } | |
38 | ||
39 | 7 | function index(filename_) { |
40 | 164 | return parseInt(filename_.substring((path.basename(filename) + '.').length), 10) || 0; |
41 | } | |
42 | ||
43 | 7 | function byIndex(a, b) { |
44 | 41 | if (index(a) > index(b)) { |
45 | 14 | return 1; |
46 | 27 | } else if (index(a) < index(b) ) { |
47 | 24 | return -1; |
48 | } else { | |
49 | 3 | return 0; |
50 | } | |
51 | } | |
52 | ||
53 | 7 | function increaseFileIndex (fileToRename, cb) { |
54 | 28 | var idx = index(fileToRename); |
55 | 28 | debug('Index of ' + fileToRename + ' is ' + idx); |
56 | 28 | if (idx < that.backups) { |
57 | //on windows, you can get a EEXIST error if you rename a file to an existing file | |
58 | //so, we'll try to delete the file we're renaming to first | |
59 | 21 | fs.unlink(filename + '.' + (idx+1), function (err) { |
60 | //ignore err: if we could not delete, it's most likely that it doesn't exist | |
61 | 21 | debug('Renaming ' + fileToRename + ' -> ' + filename + '.' + (idx+1)); |
62 | 21 | fs.rename(path.join(path.dirname(filename), fileToRename), filename + '.' + (idx + 1), cb); |
63 | }); | |
64 | } else { | |
65 | 7 | cb(); |
66 | } | |
67 | } | |
68 | ||
69 | 7 | function renameTheFiles(cb) { |
70 | //roll the backups (rename file.n to file.n+1, where n <= numBackups) | |
71 | 7 | debug("Renaming the old files"); |
72 | 7 | fs.readdir(path.dirname(filename), function (err, files) { |
73 | 7 | async.forEachSeries( |
74 | files.filter(justTheseFiles).sort(byIndex).reverse(), | |
75 | increaseFileIndex, | |
76 | cb | |
77 | ); | |
78 | }); | |
79 | } | |
80 | ||
81 | 7 | debug("Rolling, rolling, rolling"); |
82 | 7 | async.series([ |
83 | this.closeTheStream.bind(this), | |
84 | renameTheFiles, | |
85 | this.openTheStream.bind(this) | |
86 | ], callback); | |
87 | ||
88 | }; |
Line | Hits | Source |
---|---|---|
0 | 2 | "use strict"; |
1 | 2 | var BaseRollingFileStream = require('./BaseRollingFileStream') |
2 | , debug = require('../debug')('DateRollingFileStream') | |
3 | , format = require('../date_format') | |
4 | , async = require('async') | |
5 | , fs = require('fs') | |
6 | , util = require('util'); | |
7 | ||
8 | 2 | module.exports = DateRollingFileStream; |
9 | ||
10 | 2 | function DateRollingFileStream(filename, pattern, options, now) { |
11 | 14 | debug("Now is " + now); |
12 | 14 | if (pattern && typeof(pattern) === 'object') { |
13 | 1 | now = options; |
14 | 1 | options = pattern; |
15 | 1 | pattern = null; |
16 | } | |
17 | 14 | this.pattern = pattern || '.yyyy-MM-dd'; |
18 | 14 | this.now = now || Date.now; |
19 | 14 | this.lastTimeWeWroteSomething = format.asString(this.pattern, new Date(this.now())); |
20 | 14 | this.baseFilename = filename; |
21 | 14 | this.alwaysIncludePattern = false; |
22 | ||
23 | 14 | if (options) { |
24 | 11 | if (options.alwaysIncludePattern) { |
25 | 2 | this.alwaysIncludePattern = true; |
26 | 2 | filename = this.baseFilename + this.lastTimeWeWroteSomething; |
27 | } | |
28 | 11 | delete options.alwaysIncludePattern; |
29 | 11 | if (Object.keys(options).length === 0) { |
30 | 9 | options = null; |
31 | } | |
32 | } | |
33 | 14 | debug("this.now is " + this.now + ", now is " + now); |
34 | ||
35 | 14 | DateRollingFileStream.super_.call(this, filename, options); |
36 | } | |
37 | 2 | util.inherits(DateRollingFileStream, BaseRollingFileStream); |
38 | ||
39 | 2 | DateRollingFileStream.prototype.shouldRoll = function() { |
40 | 7 | var lastTime = this.lastTimeWeWroteSomething, |
41 | thisTime = format.asString(this.pattern, new Date(this.now())); | |
42 | ||
43 | 7 | debug("DateRollingFileStream.shouldRoll with now = " + |
44 | this.now() + ", thisTime = " + thisTime + ", lastTime = " + lastTime); | |
45 | ||
46 | 7 | this.lastTimeWeWroteSomething = thisTime; |
47 | 7 | this.previousTime = lastTime; |
48 | ||
49 | 7 | return thisTime !== lastTime; |
50 | }; | |
51 | ||
52 | 2 | DateRollingFileStream.prototype.roll = function(filename, callback) { |
53 | 2 | var that = this; |
54 | ||
55 | 2 | debug("Starting roll"); |
56 | ||
57 | 2 | if (this.alwaysIncludePattern) { |
58 | 1 | this.filename = this.baseFilename + this.lastTimeWeWroteSomething; |
59 | 1 | async.series([ |
60 | this.closeTheStream.bind(this), | |
61 | this.openTheStream.bind(this) | |
62 | ], callback); | |
63 | } else { | |
64 | 1 | var newFilename = this.baseFilename + this.previousTime; |
65 | 1 | async.series([ |
66 | this.closeTheStream.bind(this), | |
67 | deleteAnyExistingFile, | |
68 | renameTheCurrentFile, | |
69 | this.openTheStream.bind(this) | |
70 | ], callback); | |
71 | } | |
72 | ||
73 | 2 | function deleteAnyExistingFile(cb) { |
74 | //on windows, you can get a EEXIST error if you rename a file to an existing file | |
75 | //so, we'll try to delete the file we're renaming to first | |
76 | 1 | fs.unlink(newFilename, function (err) { |
77 | //ignore err: if we could not delete, it's most likely that it doesn't exist | |
78 | 1 | cb(); |
79 | }); | |
80 | } | |
81 | ||
82 | 2 | function renameTheCurrentFile(cb) { |
83 | 1 | debug("Renaming the " + filename + " -> " + newFilename); |
84 | 1 | fs.rename(filename, newFilename, cb); |
85 | } | |
86 | ||
87 | }; |
Line | Hits | Source |
---|---|---|
0 | 1 | "use strict"; |
1 | 1 | var log4js = require('../log4js'); |
2 | ||
3 | 1 | function categoryFilter (excludes, appender) { |
4 | 3 | if (typeof(excludes) === 'string') excludes = [excludes]; |
5 | 2 | return function(logEvent) { |
6 | 7 | if (excludes.indexOf(logEvent.categoryName) === -1) { |
7 | 4 | appender(logEvent); |
8 | } | |
9 | }; | |
10 | } | |
11 | ||
12 | 1 | function configure(config) { |
13 | 1 | log4js.loadAppender(config.appender.type); |
14 | 1 | var appender = log4js.appenderMakers[config.appender.type](config.appender); |
15 | 1 | return categoryFilter(config.exclude, appender); |
16 | } | |
17 | ||
18 | 1 | exports.appender = categoryFilter; |
19 | 1 | exports.configure = configure; |
Line | Hits | Source |
---|---|---|
0 | 5 | "use strict"; |
1 | 5 | var layouts = require('../layouts') |
2 | , path = require('path') | |
3 | , fs = require('fs') | |
4 | , streams = require('../streams') | |
5 | , os = require('os') | |
6 | , eol = os.EOL || '\n' | |
7 | , openFiles = []; | |
8 | ||
9 | //close open files on process exit. | |
10 | 5 | process.on('exit', function() { |
11 | 1 | openFiles.forEach(function (file) { |
12 | 5 | file.end(); |
13 | }); | |
14 | }); | |
15 | ||
16 | /** | |
17 | * File Appender writing the logs to a text file. Supports rolling of logs by size. | |
18 | * | |
19 | * @param file file log messages will be written to | |
20 | * @param layout a function that takes a logevent and returns a string | |
21 | * (defaults to basicLayout). | |
22 | * @param logSize - the maximum size (in bytes) for a log file, | |
23 | * if not provided then logs won't be rotated. | |
24 | * @param numBackups - the number of log files to keep after logSize | |
25 | * has been reached (default 5) | |
26 | */ | |
27 | 5 | function fileAppender (file, layout, logSize, numBackups) { |
28 | 20 | var bytesWritten = 0; |
29 | 20 | file = path.normalize(file); |
30 | 20 | layout = layout || layouts.basicLayout; |
31 | 20 | numBackups = numBackups === undefined ? 5 : numBackups; |
32 | //there has to be at least one backup if logSize has been specified | |
33 | 20 | numBackups = numBackups === 0 ? 1 : numBackups; |
34 | ||
35 | 20 | function openTheStream(file, fileSize, numFiles) { |
36 | 20 | var stream; |
37 | 20 | if (fileSize) { |
38 | 9 | stream = new streams.RollingFileStream( |
39 | file, | |
40 | fileSize, | |
41 | numFiles | |
42 | ); | |
43 | } else { | |
44 | 11 | stream = fs.createWriteStream( |
45 | file, | |
46 | { encoding: "utf8", | |
47 | mode: parseInt('0644', 8), | |
48 | flags: 'a' } | |
49 | ); | |
50 | } | |
51 | 20 | stream.on("error", function (err) { |
52 | 1 | console.error("log4js.fileAppender - Writing to file %s, error happened ", file, err); |
53 | }); | |
54 | 20 | return stream; |
55 | } | |
56 | ||
57 | 20 | var logFile = openTheStream(file, logSize, numBackups); |
58 | ||
59 | // push file to the stack of open handlers | |
60 | 20 | openFiles.push(logFile); |
61 | ||
62 | 20 | return function(loggingEvent) { |
63 | 19 | logFile.write(layout(loggingEvent) + eol, "utf8"); |
64 | }; | |
65 | } | |
66 | ||
67 | 5 | function configure(config, options) { |
68 | 6 | var layout; |
69 | 6 | if (config.layout) { |
70 | 5 | layout = layouts.layout(config.layout.type, config.layout); |
71 | } | |
72 | ||
73 | 6 | if (options && options.cwd && !config.absolute) { |
74 | 1 | config.filename = path.join(options.cwd, config.filename); |
75 | } | |
76 | ||
77 | 6 | return fileAppender(config.filename, layout, config.maxLogSize, config.backups); |
78 | } | |
79 | ||
80 | 5 | exports.appender = fileAppender; |
81 | 5 | exports.configure = configure; |
Line | Hits | Source |
---|---|---|
0 | 3 | "use strict"; |
1 | 3 | var streams = require('../streams') |
2 | , layouts = require('../layouts') | |
3 | , path = require('path') | |
4 | , os = require('os') | |
5 | , eol = os.EOL || '\n' | |
6 | , openFiles = []; | |
7 | ||
8 | //close open files on process exit. | |
9 | 3 | process.on('exit', function() { |
10 | 1 | openFiles.forEach(function (file) { |
11 | 5 | file.end(); |
12 | }); | |
13 | }); | |
14 | ||
15 | /** | |
16 | * File appender that rolls files according to a date pattern. | |
17 | * @filename base filename. | |
18 | * @pattern the format that will be added to the end of filename when rolling, | |
19 | * also used to check when to roll files - defaults to '.yyyy-MM-dd' | |
20 | * @layout layout function for log messages - defaults to basicLayout | |
21 | */ | |
22 | 3 | function appender(filename, pattern, alwaysIncludePattern, layout) { |
23 | 14 | layout = layout || layouts.basicLayout; |
24 | ||
25 | 14 | var logFile = new streams.DateRollingFileStream( |
26 | filename, | |
27 | pattern, | |
28 | { alwaysIncludePattern: alwaysIncludePattern } | |
29 | ); | |
30 | 14 | openFiles.push(logFile); |
31 | ||
32 | 14 | return function(logEvent) { |
33 | 3 | logFile.write(layout(logEvent) + eol, "utf8"); |
34 | }; | |
35 | ||
36 | } | |
37 | ||
38 | 3 | function configure(config, options) { |
39 | 3 | var layout; |
40 | ||
41 | 3 | if (config.layout) { |
42 | 2 | layout = layouts.layout(config.layout.type, config.layout); |
43 | } | |
44 | ||
45 | 3 | if (!config.alwaysIncludePattern) { |
46 | 2 | config.alwaysIncludePattern = false; |
47 | } | |
48 | ||
49 | 3 | if (options && options.cwd && !config.absolute) { |
50 | 1 | config.filename = path.join(options.cwd, config.filename); |
51 | } | |
52 | ||
53 | 3 | return appender(config.filename, config.pattern, config.alwaysIncludePattern, layout); |
54 | } | |
55 | ||
56 | 3 | exports.appender = appender; |
57 | 3 | exports.configure = configure; |
Line | Hits | Source |
---|---|---|
0 | 7 | "use strict"; |
1 | 7 | var zlib = require('zlib'); |
2 | 7 | var layouts = require('../layouts'); |
3 | 7 | var levels = require('../levels'); |
4 | 7 | var dgram = require('dgram'); |
5 | 7 | var util = require('util'); |
6 | 7 | var debug = require('../debug')('GELF Appender'); |
7 | ||
8 | 7 | var LOG_EMERG=0; // system is unusable |
9 | 7 | var LOG_ALERT=1; // action must be taken immediately |
10 | 7 | var LOG_CRIT=2; // critical conditions |
11 | 7 | var LOG_ERR=3; // error conditions |
12 | 7 | var LOG_ERROR=3; // because people WILL typo |
13 | 7 | var LOG_WARNING=4; // warning conditions |
14 | 7 | var LOG_NOTICE=5; // normal, but significant, condition |
15 | 7 | var LOG_INFO=6; // informational message |
16 | 7 | var LOG_DEBUG=7; // debug-level message |
17 | ||
18 | 7 | var levelMapping = {}; |
19 | 7 | levelMapping[levels.ALL] = LOG_DEBUG; |
20 | 7 | levelMapping[levels.TRACE] = LOG_DEBUG; |
21 | 7 | levelMapping[levels.DEBUG] = LOG_DEBUG; |
22 | 7 | levelMapping[levels.INFO] = LOG_INFO; |
23 | 7 | levelMapping[levels.WARN] = LOG_WARNING; |
24 | 7 | levelMapping[levels.ERROR] = LOG_ERR; |
25 | 7 | levelMapping[levels.FATAL] = LOG_CRIT; |
26 | ||
27 | /** | |
28 | * GELF appender that supports sending UDP packets to a GELF compatible server such as Graylog | |
29 | * | |
30 | * @param layout a function that takes a logevent and returns a string (defaults to none). | |
31 | * @param host - host to which to send logs (default:localhost) | |
32 | * @param port - port at which to send logs to (default:12201) | |
33 | * @param hostname - hostname of the current host (default:os hostname) | |
34 | * @param facility - facility to log to (default:nodejs-server) | |
35 | */ | |
36 | 7 | function gelfAppender (layout, host, port, hostname, facility) { |
37 | 7 | var config, customFields; |
38 | 7 | if (typeof(host) === 'object') { |
39 | 7 | config = host; |
40 | 7 | host = config.host; |
41 | 7 | port = config.port; |
42 | 7 | hostname = config.hostname; |
43 | 7 | facility = config.facility; |
44 | 7 | customFields = config.customFields; |
45 | } | |
46 | ||
47 | 7 | host = host || 'localhost'; |
48 | 7 | port = port || 12201; |
49 | 7 | hostname = hostname || require('os').hostname(); |
50 | 7 | facility = facility || 'nodejs-server'; |
51 | 7 | layout = layout || layouts.messagePassThroughLayout; |
52 | ||
53 | 7 | var defaultCustomFields = customFields || {}; |
54 | ||
55 | 7 | var client = dgram.createSocket("udp4"); |
56 | ||
57 | 7 | process.on('exit', function() { |
58 | 2 | if (client) client.close(); |
59 | }); | |
60 | ||
61 | /** | |
62 | * Add custom fields (start with underscore ) | |
63 | * - if the first object passed to the logger contains 'GELF' field, | |
64 | * copy the underscore fields to the message | |
65 | * @param loggingEvent | |
66 | * @param msg | |
67 | */ | |
68 | 7 | function addCustomFields(loggingEvent, msg){ |
69 | ||
70 | /* append defaultCustomFields firsts */ | |
71 | 5 | Object.keys(defaultCustomFields).forEach(function(key) { |
72 | // skip _id field for graylog2, skip keys not starts with UNDERSCORE | |
73 | 2 | if (key.match(/^_/) && key !== "_id") { |
74 | 2 | msg[key] = defaultCustomFields[key]; |
75 | } | |
76 | }); | |
77 | ||
78 | /* append custom fields per message */ | |
79 | 5 | var data = loggingEvent.data; |
80 | 5 | if (!Array.isArray(data) || data.length === 0) return; |
81 | 5 | var firstData = data[0]; |
82 | ||
83 | 9 | if (!firstData.GELF) return; // identify with GELF field defined |
84 | 1 | Object.keys(firstData).forEach(function(key) { |
85 | // skip _id field for graylog2, skip keys not starts with UNDERSCORE | |
86 | 3 | if (key.match(/^_/) || key !== "_id") { |
87 | 3 | msg[key] = firstData[key]; |
88 | } | |
89 | }); | |
90 | ||
91 | /* the custom field object should be removed, so it will not be looged by the later appenders */ | |
92 | 1 | loggingEvent.data.shift(); |
93 | } | |
94 | ||
95 | 7 | function preparePacket(loggingEvent) { |
96 | 5 | var msg = {}; |
97 | 5 | addCustomFields(loggingEvent, msg); |
98 | 5 | msg.full_message = layout(loggingEvent); |
99 | 5 | msg.short_message = msg.full_message; |
100 | ||
101 | 5 | msg.version="1.0"; |
102 | 5 | msg.timestamp = msg.timestamp || new Date().getTime() / 1000 >> 0; |
103 | 5 | msg.host = hostname; |
104 | 5 | msg.level = levelMapping[loggingEvent.level || levels.DEBUG]; |
105 | 5 | msg.facility = facility; |
106 | 5 | return msg; |
107 | } | |
108 | ||
109 | 7 | function sendPacket(packet) { |
110 | 3 | try { |
111 | 3 | client.send(packet, 0, packet.length, port, host); |
112 | } catch(e) {} | |
113 | } | |
114 | ||
115 | 7 | return function(loggingEvent) { |
116 | 5 | var message = preparePacket(loggingEvent); |
117 | 5 | zlib.gzip(new Buffer(JSON.stringify(message)), function(err, packet) { |
118 | 5 | if (err) { |
119 | 1 | console.error(err.stack); |
120 | } else { | |
121 | 4 | if (packet.length > 8192) { |
122 | 1 | debug("Message packet length (" + packet.length + ") is larger than 8k. Not sending"); |
123 | } else { | |
124 | 3 | sendPacket(packet); |
125 | } | |
126 | } | |
127 | }); | |
128 | }; | |
129 | } | |
130 | ||
131 | 7 | function configure(config) { |
132 | 7 | var layout; |
133 | 7 | if (config.layout) { |
134 | 1 | layout = layouts.layout(config.layout.type, config.layout); |
135 | } | |
136 | 7 | return gelfAppender(layout, config); |
137 | } | |
138 | ||
139 | 7 | exports.appender = gelfAppender; |
140 | 7 | exports.configure = configure; |
Line | Hits | Source |
---|---|---|
0 | 4 | "use strict"; |
1 | 4 | var log4js = require('../log4js') |
2 | , layouts = require('../layouts') | |
3 | , Hook = require('hook.io').Hook | |
4 | , util = require('util'); | |
5 | ||
6 | 4 | var Logger = function createLogger(options) { |
7 | 1 | var self = this; |
8 | 1 | var actualAppender = options.actualAppender; |
9 | 1 | Hook.call(self, options); |
10 | 1 | self.on('hook::ready', function hookReady() { |
11 | 1 | self.on('*::' + options.name + '::log', function log(loggingEvent) { |
12 | 2 | deserializeLoggingEvent(loggingEvent); |
13 | 2 | actualAppender(loggingEvent); |
14 | }); | |
15 | }); | |
16 | }; | |
17 | 4 | util.inherits(Logger, Hook); |
18 | ||
19 | 4 | function deserializeLoggingEvent(loggingEvent) { |
20 | 2 | loggingEvent.startTime = new Date(loggingEvent.startTime); |
21 | 2 | loggingEvent.level.toString = function levelToString() { |
22 | 2 | return loggingEvent.level.levelStr; |
23 | }; | |
24 | } | |
25 | ||
26 | 4 | function initHook(hookioOptions) { |
27 | 4 | var loggerHook; |
28 | 4 | if (hookioOptions.mode === 'master') { |
29 | // Start the master hook, handling the actual logging | |
30 | 1 | loggerHook = new Logger(hookioOptions); |
31 | } else { | |
32 | // Start a worker, just emitting events for a master | |
33 | 3 | loggerHook = new Hook(hookioOptions); |
34 | } | |
35 | 4 | loggerHook.start(); |
36 | 4 | return loggerHook; |
37 | } | |
38 | ||
39 | 4 | function getBufferedHook(hook, eventName) { |
40 | 4 | var hookBuffer = []; |
41 | 4 | var hookReady = false; |
42 | 4 | hook.on('hook::ready', function emptyBuffer() { |
43 | 3 | hookBuffer.forEach(function logBufferItem(loggingEvent) { |
44 | 1 | hook.emit(eventName, loggingEvent); |
45 | }); | |
46 | 3 | hookReady = true; |
47 | }); | |
48 | ||
49 | 4 | return function log(loggingEvent) { |
50 | 6 | if (hookReady) { |
51 | 4 | hook.emit(eventName, loggingEvent); |
52 | } else { | |
53 | 2 | hookBuffer.push(loggingEvent); |
54 | } | |
55 | }; | |
56 | } | |
57 | ||
58 | 4 | function createAppender(hookioOptions) { |
59 | 4 | var loggerHook = initHook(hookioOptions); |
60 | 4 | var loggerEvent = hookioOptions.name + '::log'; |
61 | 4 | return getBufferedHook(loggerHook, loggerEvent); |
62 | } | |
63 | ||
64 | 4 | function configure(config) { |
65 | 4 | var actualAppender; |
66 | 4 | if (config.appender && config.mode === 'master') { |
67 | 1 | log4js.loadAppender(config.appender.type); |
68 | 1 | actualAppender = log4js.appenderMakers[config.appender.type](config.appender); |
69 | 1 | config.actualAppender = actualAppender; |
70 | } | |
71 | 4 | return createAppender(config); |
72 | } | |
73 | ||
74 | 4 | exports.appender = createAppender; |
75 | 4 | exports.configure = configure; |
Line | Hits | Source |
---|---|---|
0 | 1 | "use strict"; |
1 | 1 | var levels = require('../levels') |
2 | , log4js = require('../log4js'); | |
3 | ||
4 | 1 | function logLevelFilter (levelString, appender) { |
5 | 2 | var level = levels.toLevel(levelString); |
6 | 2 | return function(logEvent) { |
7 | 8 | if (logEvent.level.isGreaterThanOrEqualTo(level)) { |
8 | 4 | appender(logEvent); |
9 | } | |
10 | }; | |
11 | } | |
12 | ||
13 | 1 | function configure(config) { |
14 | 1 | log4js.loadAppender(config.appender.type); |
15 | 1 | var appender = log4js.appenderMakers[config.appender.type](config.appender); |
16 | 1 | return logLevelFilter(config.level, appender); |
17 | } | |
18 | ||
19 | 1 | exports.appender = logLevelFilter; |
20 | 1 | exports.configure = configure; |
Line | Hits | Source |
---|---|---|
0 | 6 | "use strict"; |
1 | 6 | var log4js = require('../log4js') |
2 | , net = require('net') | |
3 | , END_MSG = '__LOG4JS__'; | |
4 | ||
5 | /** | |
6 | * Creates a server, listening on config.loggerPort, config.loggerHost. | |
7 | * Output goes to config.actualAppender (config.appender is used to | |
8 | * set up that appender). | |
9 | */ | |
10 | 6 | function logServer(config) { |
11 | ||
12 | /** | |
13 | * Takes a utf-8 string, returns an object with | |
14 | * the correct log properties. | |
15 | */ | |
16 | 3 | function deserializeLoggingEvent(clientSocket, msg) { |
17 | 7 | var loggingEvent; |
18 | 7 | try { |
19 | 7 | loggingEvent = JSON.parse(msg); |
20 | 6 | loggingEvent.startTime = new Date(loggingEvent.startTime); |
21 | 6 | loggingEvent.level = log4js.levels.toLevel(loggingEvent.level.levelStr); |
22 | } catch (e) { | |
23 | // JSON.parse failed, just log the contents probably a naughty. | |
24 | 1 | loggingEvent = { |
25 | startTime: new Date(), | |
26 | categoryName: 'log4js', | |
27 | level: log4js.levels.ERROR, | |
28 | data: [ 'Unable to parse log:', msg ] | |
29 | }; | |
30 | } | |
31 | ||
32 | 7 | loggingEvent.remoteAddress = clientSocket.remoteAddress; |
33 | 7 | loggingEvent.remotePort = clientSocket.remotePort; |
34 | ||
35 | 7 | return loggingEvent; |
36 | } | |
37 | ||
38 | 3 | var actualAppender = config.actualAppender, |
39 | server = net.createServer(function serverCreated(clientSocket) { | |
40 | 3 | clientSocket.setEncoding('utf8'); |
41 | 3 | var logMessage = ''; |
42 | ||
43 | 3 | function logTheMessage(msg) { |
44 | 7 | if (logMessage.length > 0) { |
45 | 7 | actualAppender(deserializeLoggingEvent(clientSocket, msg)); |
46 | } | |
47 | } | |
48 | ||
49 | 3 | function chunkReceived(chunk) { |
50 | 13 | var event; |
51 | 13 | logMessage += chunk || ''; |
52 | 13 | if (logMessage.indexOf(END_MSG) > -1) { |
53 | 7 | event = logMessage.substring(0, logMessage.indexOf(END_MSG)); |
54 | 7 | logTheMessage(event); |
55 | 7 | logMessage = logMessage.substring(event.length + END_MSG.length) || ''; |
56 | //check for more, maybe it was a big chunk | |
57 | 7 | chunkReceived(); |
58 | } | |
59 | } | |
60 | ||
61 | 3 | clientSocket.on('data', chunkReceived); |
62 | 3 | clientSocket.on('end', chunkReceived); |
63 | }); | |
64 | ||
65 | 3 | server.listen(config.loggerPort || 5000, config.loggerHost || 'localhost'); |
66 | ||
67 | 3 | return actualAppender; |
68 | } | |
69 | ||
70 | 6 | function workerAppender(config) { |
71 | 3 | var canWrite = false, |
72 | buffer = [], | |
73 | socket; | |
74 | ||
75 | 3 | createSocket(); |
76 | ||
77 | 3 | function createSocket() { |
78 | 5 | socket = net.createConnection(config.loggerPort || 5000, config.loggerHost || 'localhost'); |
79 | 5 | socket.on('connect', function() { |
80 | 4 | emptyBuffer(); |
81 | 4 | canWrite = true; |
82 | }); | |
83 | 5 | socket.on('timeout', socket.end.bind(socket)); |
84 | //don't bother listening for 'error', 'close' gets called after that anyway | |
85 | 5 | socket.on('close', createSocket); |
86 | } | |
87 | ||
88 | 3 | function emptyBuffer() { |
89 | 4 | var evt; |
90 | 4 | while ((evt = buffer.shift())) { |
91 | 2 | write(evt); |
92 | } | |
93 | } | |
94 | ||
95 | 3 | function write(loggingEvent) { |
96 | 9 | socket.write(JSON.stringify(loggingEvent), 'utf8'); |
97 | 9 | socket.write(END_MSG, 'utf8'); |
98 | } | |
99 | ||
100 | 3 | return function log(loggingEvent) { |
101 | 9 | if (canWrite) { |
102 | 7 | write(loggingEvent); |
103 | } else { | |
104 | 2 | buffer.push(loggingEvent); |
105 | } | |
106 | }; | |
107 | } | |
108 | ||
109 | 6 | function createAppender(config) { |
110 | 6 | if (config.mode === 'master') { |
111 | 3 | return logServer(config); |
112 | } else { | |
113 | 3 | return workerAppender(config); |
114 | } | |
115 | } | |
116 | ||
117 | 6 | function configure(config, options) { |
118 | 1 | var actualAppender; |
119 | 1 | if (config.appender && config.mode === 'master') { |
120 | 1 | log4js.loadAppender(config.appender.type); |
121 | 1 | actualAppender = log4js.appenderMakers[config.appender.type](config.appender, options); |
122 | 1 | config.actualAppender = actualAppender; |
123 | } | |
124 | 1 | return createAppender(config); |
125 | } | |
126 | ||
127 | 6 | exports.appender = createAppender; |
128 | 6 | exports.configure = configure; |
Line | Hits | Source |
---|---|---|
0 | 6 | "use strict"; |
1 | 6 | var layouts = require("../layouts") |
2 | , mailer = require("nodemailer") | |
3 | , os = require('os'); | |
4 | ||
5 | /** | |
6 | * SMTP Appender. Sends logging events using SMTP protocol. | |
7 | * It can either send an email on each event or group several | |
8 | * logging events gathered during specified interval. | |
9 | * | |
10 | * @param config appender configuration data | |
11 | * config.sendInterval time between log emails (in seconds), if 0 | |
12 | * then every event sends an email | |
13 | * @param layout a function that takes a logevent and returns a string (defaults to basicLayout). | |
14 | */ | |
15 | 6 | function smtpAppender(config, layout) { |
16 | 6 | layout = layout || layouts.basicLayout; |
17 | 6 | var subjectLayout = layouts.messagePassThroughLayout; |
18 | 6 | var sendInterval = config.sendInterval*1000 || 0; |
19 | ||
20 | 6 | var logEventBuffer = []; |
21 | 6 | var sendTimer; |
22 | ||
23 | 6 | function sendBuffer() { |
24 | 8 | if (logEventBuffer.length > 0) { |
25 | ||
26 | 8 | var transport = mailer.createTransport(config.transport, config[config.transport]); |
27 | 8 | var firstEvent = logEventBuffer[0]; |
28 | 8 | var body = ""; |
29 | 8 | while (logEventBuffer.length > 0) { |
30 | 9 | body += layout(logEventBuffer.shift()) + "\n"; |
31 | } | |
32 | ||
33 | 8 | var msg = { |
34 | to: config.recipients, | |
35 | subject: config.subject || subjectLayout(firstEvent), | |
36 | text: body, | |
37 | headers: { "Hostname": os.hostname() } | |
38 | }; | |
39 | 8 | if (config.sender) { |
40 | 1 | msg.from = config.sender; |
41 | } | |
42 | 8 | transport.sendMail(msg, function(error, success) { |
43 | 8 | if (error) { |
44 | 1 | console.error("log4js.smtpAppender - Error happened", error); |
45 | } | |
46 | 8 | transport.close(); |
47 | }); | |
48 | } | |
49 | } | |
50 | ||
51 | 6 | function scheduleSend() { |
52 | 3 | if (!sendTimer) { |
53 | 2 | sendTimer = setTimeout(function() { |
54 | 2 | sendTimer = null; |
55 | 2 | sendBuffer(); |
56 | }, sendInterval); | |
57 | } | |
58 | } | |
59 | ||
60 | 6 | return function(loggingEvent) { |
61 | 9 | logEventBuffer.push(loggingEvent); |
62 | 9 | if (sendInterval > 0) { |
63 | 3 | scheduleSend(); |
64 | } else { | |
65 | 6 | sendBuffer(); |
66 | } | |
67 | }; | |
68 | } | |
69 | ||
70 | 6 | function configure(config) { |
71 | 6 | var layout; |
72 | 6 | if (config.layout) { |
73 | 1 | layout = layouts.layout(config.layout.type, config.layout); |
74 | } | |
75 | 6 | return smtpAppender(config, layout); |
76 | } | |
77 | ||
78 | 6 | exports.name = "smtp"; |
79 | 6 | exports.appender = smtpAppender; |
80 | 6 | exports.configure = configure; |
81 |