UNPKG

4.57 kBJavaScriptView Raw
1/*
2 * Copyright 2018 Adobe. All rights reserved.
3 * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License. You may obtain a copy
5 * of the License at http://www.apache.org/licenses/LICENSE-2.0
6 *
7 * Unless required by applicable law or agreed to in writing, software distributed under
8 * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9 * OF ANY KIND, either express or implied. See the License for the specific language
10 * governing permissions and limitations under the License.
11 */
12const path = require('path');
13const fs = require('fs-extra');
14const chalk = require('chalk');
15const {
16 SimpleInterface,
17 rootLogger,
18 serializeMessage,
19 messageFormatJsonString,
20 messageFormatTechnical,
21 MultiLogger,
22 FileLogger,
23 ConsoleLogger,
24} = require('@adobe/helix-log');
25
26const colors = {
27 info: 'green',
28 warn: 'yellow',
29 error: 'red',
30};
31
32/**
33 * Log message filter that removes log entries produced during a progress bar
34 * (fields.progress === true), but only on a tty.
35 */
36const suppressProgress = (fields) => {
37 // eslint-disable-next-line no-underscore-dangle,no-console
38 if (fields.progress && console._stderr.isTTY) {
39 return undefined;
40 }
41 // eslint-disable-next-line no-param-reassign
42 delete fields.progress;
43 return fields;
44};
45
46/**
47 * Log message filter that removes the `progress` field so that it doesn't get logged.
48 */
49const filterProgress = (fields) => {
50 // eslint-disable-next-line no-param-reassign
51 delete fields.progress;
52 return fields;
53};
54
55/**
56 * Message format for the console that doesn't show the `info` level keyword for the `cli`
57 * category. prefixes the log entries with the category otherwise.
58 */
59const categoryAwareMessageFormatConsole = (fields) => {
60 // eslint-disable-next-line
61 const {level, timestamp, message, category = 'cli', ...rest} = fields;
62
63 const fullMsg = Object.keys(rest).length === 0 ? message : [...message, ' ', rest];
64 const ser = serializeMessage(fullMsg, { colors: false });
65
66 let lvl = level.toLowerCase();
67 if (colors[level]) {
68 lvl = chalk[colors[level]](lvl);
69 }
70 if (category === 'cli') {
71 if (level === 'info') {
72 return `${ser}`;
73 } else {
74 return `${lvl}: ${ser}`;
75 }
76 } else {
77 return chalk`{grey [${category}]} ${lvl}: ${ser}`;
78 }
79};
80
81// set the default logger, in case code uses root logger directly.
82rootLogger.loggers.get('default').formatter = categoryAwareMessageFormatConsole;
83
84// module global loggers by category
85const loggersByCategory = new Map();
86
87/**
88 * Gets the logger for the respective category or creates a new one if it does not exist yet.
89 * @param {object|string} [config='cli'] The log config or the category name.
90 * @param {string} [config.category='cli'] The log category
91 * @param {string} [config.level='cli'] The log level
92 * @param {string} [config.logsDir='logs'] The log directory.
93 * @param {Array|string} [config.logFle=['-', '${category}-server.log']] The log files(s).
94 *
95 * @returns {SimpleInterface} a helix-log simple interface.
96 */
97function getOrCreateLogger(config = 'cli') {
98 let categ;
99 if (typeof config === 'string') {
100 categ = config;
101 } else {
102 categ = (config && config.category) || 'cli';
103 }
104
105 if (loggersByCategory.has(categ)) {
106 return loggersByCategory.get(categ);
107 }
108
109 // setup helix logger
110 const level = (config && config.logLevel) || 'info';
111 const logsDir = path.normalize((config && config.logsDir) || 'logs');
112 const logFiles = config && Array.isArray(config.logFile)
113 ? config.logFile
114 : ['-', (config && config.logFile) || path.join(logsDir, `${categ}-server.log`)];
115
116 const loggers = new Map();
117 logFiles.forEach((logFile) => {
118 const name = loggers.has('default') ? logFile : 'default';
119 if (logFile === '-') {
120 loggers.set(name, new ConsoleLogger({
121 filter: categ === 'cli' ? suppressProgress : filterProgress,
122 level,
123 formatter: categoryAwareMessageFormatConsole,
124 }));
125 } else {
126 fs.ensureDirSync(path.dirname(logFile));
127 loggers.set(name, new FileLogger(logFile, {
128 level: 'debug',
129 formatter: /\.json/.test(logFile) ? messageFormatJsonString : messageFormatTechnical,
130 }));
131 }
132 });
133
134 // create simple interface
135 const log = new SimpleInterface({
136 level,
137 defaultFields: {
138 category: categ,
139 },
140 logger: new MultiLogger(loggers),
141 });
142
143 loggersByCategory.set(categ, log);
144 return log;
145}
146
147module.exports = {
148 getOrCreateLogger,
149};