[6a3a178] | 1 | const dateFormat = require('date-format');
|
---|
| 2 | const os = require('os');
|
---|
| 3 | const util = require('util');
|
---|
| 4 | const path = require('path');
|
---|
| 5 |
|
---|
| 6 | const styles = {
|
---|
| 7 | // styles
|
---|
| 8 | bold: [1, 22],
|
---|
| 9 | italic: [3, 23],
|
---|
| 10 | underline: [4, 24],
|
---|
| 11 | inverse: [7, 27],
|
---|
| 12 | // grayscale
|
---|
| 13 | white: [37, 39],
|
---|
| 14 | grey: [90, 39],
|
---|
| 15 | black: [90, 39],
|
---|
| 16 | // colors
|
---|
| 17 | blue: [34, 39],
|
---|
| 18 | cyan: [36, 39],
|
---|
| 19 | green: [32, 39],
|
---|
| 20 | magenta: [35, 39],
|
---|
| 21 | red: [91, 39],
|
---|
| 22 | yellow: [33, 39]
|
---|
| 23 | };
|
---|
| 24 |
|
---|
| 25 | function colorizeStart(style) {
|
---|
| 26 | return style ? `\x1B[${styles[style][0]}m` : '';
|
---|
| 27 | }
|
---|
| 28 |
|
---|
| 29 | function colorizeEnd(style) {
|
---|
| 30 | return style ? `\x1B[${styles[style][1]}m` : '';
|
---|
| 31 | }
|
---|
| 32 |
|
---|
| 33 | /**
|
---|
| 34 | * Taken from masylum's fork (https://github.com/masylum/log4js-node)
|
---|
| 35 | */
|
---|
| 36 | function colorize(str, style) {
|
---|
| 37 | return colorizeStart(style) + str + colorizeEnd(style);
|
---|
| 38 | }
|
---|
| 39 |
|
---|
| 40 | function timestampLevelAndCategory(loggingEvent, colour) {
|
---|
| 41 | return colorize(
|
---|
| 42 | util.format(
|
---|
| 43 | '[%s] [%s] %s - ',
|
---|
| 44 | dateFormat.asString(loggingEvent.startTime),
|
---|
| 45 | loggingEvent.level.toString(),
|
---|
| 46 | loggingEvent.categoryName
|
---|
| 47 | ),
|
---|
| 48 | colour
|
---|
| 49 | );
|
---|
| 50 | }
|
---|
| 51 |
|
---|
| 52 | /**
|
---|
| 53 | * BasicLayout is a simple layout for storing the logs. The logs are stored
|
---|
| 54 | * in following format:
|
---|
| 55 | * <pre>
|
---|
| 56 | * [startTime] [logLevel] categoryName - message\n
|
---|
| 57 | * </pre>
|
---|
| 58 | *
|
---|
| 59 | * @author Stephan Strittmatter
|
---|
| 60 | */
|
---|
| 61 | function basicLayout(loggingEvent) {
|
---|
| 62 | return timestampLevelAndCategory(loggingEvent) + util.format(...loggingEvent.data);
|
---|
| 63 | }
|
---|
| 64 |
|
---|
| 65 | /**
|
---|
| 66 | * colouredLayout - taken from masylum's fork.
|
---|
| 67 | * same as basicLayout, but with colours.
|
---|
| 68 | */
|
---|
| 69 | function colouredLayout(loggingEvent) {
|
---|
| 70 | return timestampLevelAndCategory(loggingEvent, loggingEvent.level.colour) + util.format(...loggingEvent.data);
|
---|
| 71 | }
|
---|
| 72 |
|
---|
| 73 | function messagePassThroughLayout(loggingEvent) {
|
---|
| 74 | return util.format(...loggingEvent.data);
|
---|
| 75 | }
|
---|
| 76 |
|
---|
| 77 | function dummyLayout(loggingEvent) {
|
---|
| 78 | return loggingEvent.data[0];
|
---|
| 79 | }
|
---|
| 80 |
|
---|
| 81 | /**
|
---|
| 82 | * PatternLayout
|
---|
| 83 | * Format for specifiers is %[padding].[truncation][field]{[format]}
|
---|
| 84 | * e.g. %5.10p - left pad the log level by 5 characters, up to a max of 10
|
---|
| 85 | * both padding and truncation can be negative.
|
---|
| 86 | * Negative truncation = trunc from end of string
|
---|
| 87 | * Positive truncation = trunc from start of string
|
---|
| 88 | * Negative padding = pad right
|
---|
| 89 | * Positive padding = pad left
|
---|
| 90 | *
|
---|
| 91 | * Fields can be any of:
|
---|
| 92 | * - %r time in toLocaleTimeString format
|
---|
| 93 | * - %p log level
|
---|
| 94 | * - %c log category
|
---|
| 95 | * - %h hostname
|
---|
| 96 | * - %m log data
|
---|
| 97 | * - %d date in constious formats
|
---|
| 98 | * - %% %
|
---|
| 99 | * - %n newline
|
---|
| 100 | * - %z pid
|
---|
| 101 | * - %f filename
|
---|
| 102 | * - %l line number
|
---|
| 103 | * - %o column postion
|
---|
| 104 | * - %s call stack
|
---|
| 105 | * - %x{<tokenname>} add dynamic tokens to your log. Tokens are specified in the tokens parameter
|
---|
| 106 | * - %X{<tokenname>} add dynamic tokens to your log. Tokens are specified in logger context
|
---|
| 107 | * You can use %[ and %] to define a colored block.
|
---|
| 108 | *
|
---|
| 109 | * Tokens are specified as simple key:value objects.
|
---|
| 110 | * The key represents the token name whereas the value can be a string or function
|
---|
| 111 | * which is called to extract the value to put in the log message. If token is not
|
---|
| 112 | * found, it doesn't replace the field.
|
---|
| 113 | *
|
---|
| 114 | * A sample token would be: { 'pid' : function() { return process.pid; } }
|
---|
| 115 | *
|
---|
| 116 | * Takes a pattern string, array of tokens and returns a layout function.
|
---|
| 117 | * @return {Function}
|
---|
| 118 | * @param pattern
|
---|
| 119 | * @param tokens
|
---|
| 120 | * @param timezoneOffset
|
---|
| 121 | *
|
---|
| 122 | * @authors ['Stephan Strittmatter', 'Jan Schmidle']
|
---|
| 123 | */
|
---|
| 124 | function patternLayout(pattern, tokens) {
|
---|
| 125 | const TTCC_CONVERSION_PATTERN = '%r %p %c - %m%n';
|
---|
| 126 | const regex = /%(-?[0-9]+)?(\.?-?[0-9]+)?([[\]cdhmnprzxXyflos%])(\{([^}]+)\})?|([^%]+)/;
|
---|
| 127 |
|
---|
| 128 | pattern = pattern || TTCC_CONVERSION_PATTERN;
|
---|
| 129 |
|
---|
| 130 | function categoryName(loggingEvent, specifier) {
|
---|
| 131 | let loggerName = loggingEvent.categoryName;
|
---|
| 132 | if (specifier) {
|
---|
| 133 | const precision = parseInt(specifier, 10);
|
---|
| 134 | const loggerNameBits = loggerName.split('.');
|
---|
| 135 | if (precision < loggerNameBits.length) {
|
---|
| 136 | loggerName = loggerNameBits.slice(loggerNameBits.length - precision).join('.');
|
---|
| 137 | }
|
---|
| 138 | }
|
---|
| 139 | return loggerName;
|
---|
| 140 | }
|
---|
| 141 |
|
---|
| 142 | function formatAsDate(loggingEvent, specifier) {
|
---|
| 143 | let format = dateFormat.ISO8601_FORMAT;
|
---|
| 144 | if (specifier) {
|
---|
| 145 | format = specifier;
|
---|
| 146 | // Pick up special cases
|
---|
| 147 | if (format === 'ISO8601') {
|
---|
| 148 | format = dateFormat.ISO8601_FORMAT;
|
---|
| 149 | } else if (format === 'ISO8601_WITH_TZ_OFFSET') {
|
---|
| 150 | format = dateFormat.ISO8601_WITH_TZ_OFFSET_FORMAT;
|
---|
| 151 | } else if (format === 'ABSOLUTE') {
|
---|
| 152 | format = dateFormat.ABSOLUTETIME_FORMAT;
|
---|
| 153 | } else if (format === 'DATE') {
|
---|
| 154 | format = dateFormat.DATETIME_FORMAT;
|
---|
| 155 | }
|
---|
| 156 | }
|
---|
| 157 | // Format the date
|
---|
| 158 | return dateFormat.asString(format, loggingEvent.startTime);
|
---|
| 159 | }
|
---|
| 160 |
|
---|
| 161 | function hostname() {
|
---|
| 162 | return os.hostname().toString();
|
---|
| 163 | }
|
---|
| 164 |
|
---|
| 165 | function formatMessage(loggingEvent) {
|
---|
| 166 | return util.format(...loggingEvent.data);
|
---|
| 167 | }
|
---|
| 168 |
|
---|
| 169 | function endOfLine() {
|
---|
| 170 | return os.EOL;
|
---|
| 171 | }
|
---|
| 172 |
|
---|
| 173 | function logLevel(loggingEvent) {
|
---|
| 174 | return loggingEvent.level.toString();
|
---|
| 175 | }
|
---|
| 176 |
|
---|
| 177 | function startTime(loggingEvent) {
|
---|
| 178 | return dateFormat.asString('hh:mm:ss', loggingEvent.startTime);
|
---|
| 179 | }
|
---|
| 180 |
|
---|
| 181 | function startColour(loggingEvent) {
|
---|
| 182 | return colorizeStart(loggingEvent.level.colour);
|
---|
| 183 | }
|
---|
| 184 |
|
---|
| 185 | function endColour(loggingEvent) {
|
---|
| 186 | return colorizeEnd(loggingEvent.level.colour);
|
---|
| 187 | }
|
---|
| 188 |
|
---|
| 189 | function percent() {
|
---|
| 190 | return '%';
|
---|
| 191 | }
|
---|
| 192 |
|
---|
| 193 | function pid(loggingEvent) {
|
---|
| 194 | return loggingEvent && loggingEvent.pid ? loggingEvent.pid.toString() : process.pid.toString();
|
---|
| 195 | }
|
---|
| 196 |
|
---|
| 197 | function clusterInfo() {
|
---|
| 198 | // this used to try to return the master and worker pids,
|
---|
| 199 | // but it would never have worked because master pid is not available to workers
|
---|
| 200 | // leaving this here to maintain compatibility for patterns
|
---|
| 201 | return pid();
|
---|
| 202 | }
|
---|
| 203 |
|
---|
| 204 | function userDefined(loggingEvent, specifier) {
|
---|
| 205 | if (typeof tokens[specifier] !== 'undefined') {
|
---|
| 206 | return typeof tokens[specifier] === 'function' ? tokens[specifier](loggingEvent) : tokens[specifier];
|
---|
| 207 | }
|
---|
| 208 |
|
---|
| 209 | return null;
|
---|
| 210 | }
|
---|
| 211 |
|
---|
| 212 | function contextDefined(loggingEvent, specifier) {
|
---|
| 213 | const resolver = loggingEvent.context[specifier];
|
---|
| 214 |
|
---|
| 215 | if (typeof resolver !== 'undefined') {
|
---|
| 216 | return typeof resolver === 'function' ? resolver(loggingEvent) : resolver;
|
---|
| 217 | }
|
---|
| 218 |
|
---|
| 219 | return null;
|
---|
| 220 | }
|
---|
| 221 |
|
---|
| 222 | function fileName(loggingEvent, specifier) {
|
---|
| 223 | let filename = loggingEvent.fileName || '';
|
---|
| 224 | if (specifier) {
|
---|
| 225 | const fileDepth = parseInt(specifier, 10);
|
---|
| 226 | const fileList = filename.split(path.sep);
|
---|
| 227 | if (fileList.length > fileDepth) {
|
---|
| 228 | filename = fileList.slice(-fileDepth).join(path.sep);
|
---|
| 229 | }
|
---|
| 230 | }
|
---|
| 231 |
|
---|
| 232 | return filename;
|
---|
| 233 | }
|
---|
| 234 |
|
---|
| 235 | function lineNumber(loggingEvent) {
|
---|
| 236 | return loggingEvent.lineNumber ? `${loggingEvent.lineNumber}` : '';
|
---|
| 237 | }
|
---|
| 238 |
|
---|
| 239 | function columnNumber(loggingEvent) {
|
---|
| 240 | return loggingEvent.columnNumber ? `${loggingEvent.columnNumber}` : '';
|
---|
| 241 | }
|
---|
| 242 |
|
---|
| 243 | function callStack(loggingEvent) {
|
---|
| 244 | return loggingEvent.callStack || '';
|
---|
| 245 | }
|
---|
| 246 |
|
---|
| 247 | /* eslint quote-props:0 */
|
---|
| 248 | const replacers = {
|
---|
| 249 | c: categoryName,
|
---|
| 250 | d: formatAsDate,
|
---|
| 251 | h: hostname,
|
---|
| 252 | m: formatMessage,
|
---|
| 253 | n: endOfLine,
|
---|
| 254 | p: logLevel,
|
---|
| 255 | r: startTime,
|
---|
| 256 | '[': startColour,
|
---|
| 257 | ']': endColour,
|
---|
| 258 | y: clusterInfo,
|
---|
| 259 | z: pid,
|
---|
| 260 | '%': percent,
|
---|
| 261 | x: userDefined,
|
---|
| 262 | X: contextDefined,
|
---|
| 263 | f: fileName,
|
---|
| 264 | l: lineNumber,
|
---|
| 265 | o: columnNumber,
|
---|
| 266 | s: callStack
|
---|
| 267 | };
|
---|
| 268 |
|
---|
| 269 | function replaceToken(conversionCharacter, loggingEvent, specifier) {
|
---|
| 270 | return replacers[conversionCharacter](loggingEvent, specifier);
|
---|
| 271 | }
|
---|
| 272 |
|
---|
| 273 | function truncate(truncation, toTruncate) {
|
---|
| 274 | let len;
|
---|
| 275 | if (truncation) {
|
---|
| 276 | len = parseInt(truncation.substr(1), 10);
|
---|
| 277 | // negative truncate length means truncate from end of string
|
---|
| 278 | return len > 0 ? toTruncate.slice(0, len) : toTruncate.slice(len);
|
---|
| 279 | }
|
---|
| 280 |
|
---|
| 281 | return toTruncate;
|
---|
| 282 | }
|
---|
| 283 |
|
---|
| 284 | function pad(padding, toPad) {
|
---|
| 285 | let len;
|
---|
| 286 | if (padding) {
|
---|
| 287 | if (padding.charAt(0) === '-') {
|
---|
| 288 | len = parseInt(padding.substr(1), 10);
|
---|
| 289 | // Right pad with spaces
|
---|
| 290 | while (toPad.length < len) {
|
---|
| 291 | toPad += ' ';
|
---|
| 292 | }
|
---|
| 293 | } else {
|
---|
| 294 | len = parseInt(padding, 10);
|
---|
| 295 | // Left pad with spaces
|
---|
| 296 | while (toPad.length < len) {
|
---|
| 297 | toPad = ` ${toPad}`;
|
---|
| 298 | }
|
---|
| 299 | }
|
---|
| 300 | }
|
---|
| 301 | return toPad;
|
---|
| 302 | }
|
---|
| 303 |
|
---|
| 304 | function truncateAndPad(toTruncAndPad, truncation, padding) {
|
---|
| 305 | let replacement = toTruncAndPad;
|
---|
| 306 | replacement = truncate(truncation, replacement);
|
---|
| 307 | replacement = pad(padding, replacement);
|
---|
| 308 | return replacement;
|
---|
| 309 | }
|
---|
| 310 |
|
---|
| 311 | return function (loggingEvent) {
|
---|
| 312 | let formattedString = '';
|
---|
| 313 | let result;
|
---|
| 314 | let searchString = pattern;
|
---|
| 315 |
|
---|
| 316 | /* eslint no-cond-assign:0 */
|
---|
| 317 | while ((result = regex.exec(searchString)) !== null) {
|
---|
| 318 | // const matchedString = result[0];
|
---|
| 319 | const padding = result[1];
|
---|
| 320 | const truncation = result[2];
|
---|
| 321 | const conversionCharacter = result[3];
|
---|
| 322 | const specifier = result[5];
|
---|
| 323 | const text = result[6];
|
---|
| 324 |
|
---|
| 325 | // Check if the pattern matched was just normal text
|
---|
| 326 | if (text) {
|
---|
| 327 | formattedString += text.toString();
|
---|
| 328 | } else {
|
---|
| 329 | // Create a raw replacement string based on the conversion
|
---|
| 330 | // character and specifier
|
---|
| 331 | const replacement = replaceToken(conversionCharacter, loggingEvent, specifier);
|
---|
| 332 | formattedString += truncateAndPad(replacement, truncation, padding);
|
---|
| 333 | }
|
---|
| 334 | searchString = searchString.substr(result.index + result[0].length);
|
---|
| 335 | }
|
---|
| 336 | return formattedString;
|
---|
| 337 | };
|
---|
| 338 | }
|
---|
| 339 |
|
---|
| 340 | const layoutMakers = {
|
---|
| 341 | messagePassThrough () {
|
---|
| 342 | return messagePassThroughLayout;
|
---|
| 343 | },
|
---|
| 344 | basic () {
|
---|
| 345 | return basicLayout;
|
---|
| 346 | },
|
---|
| 347 | colored () {
|
---|
| 348 | return colouredLayout;
|
---|
| 349 | },
|
---|
| 350 | coloured () {
|
---|
| 351 | return colouredLayout;
|
---|
| 352 | },
|
---|
| 353 | pattern (config) {
|
---|
| 354 | return patternLayout(config && config.pattern, config && config.tokens);
|
---|
| 355 | },
|
---|
| 356 | dummy () {
|
---|
| 357 | return dummyLayout;
|
---|
| 358 | }
|
---|
| 359 | };
|
---|
| 360 |
|
---|
| 361 | module.exports = {
|
---|
| 362 | basicLayout,
|
---|
| 363 | messagePassThroughLayout,
|
---|
| 364 | patternLayout,
|
---|
| 365 | colouredLayout,
|
---|
| 366 | coloredLayout: colouredLayout,
|
---|
| 367 | dummyLayout,
|
---|
| 368 | addLayout (name, serializerGenerator) {
|
---|
| 369 | layoutMakers[name] = serializerGenerator;
|
---|
| 370 | },
|
---|
| 371 | layout (name, config) {
|
---|
| 372 | return layoutMakers[name] && layoutMakers[name](config);
|
---|
| 373 | }
|
---|
| 374 | };
|
---|