UNPKG

5.85 kBJavaScriptView Raw
1import npmlog from 'npmlog';
2import { createLogger, format, transports } from 'winston';
3import { fs, logger } from 'appium-support';
4import _ from 'lodash';
5
6
7// set up distributed logging before everything else
8logger.patchLogger(npmlog);
9global._global_npmlog = npmlog;
10
11// npmlog is used only for emitting, we use winston for output
12npmlog.level = 'silent';
13const levels = {
14 debug: 4,
15 info: 3,
16 warn: 2,
17 error: 1,
18};
19
20const colors = {
21 info: 'cyan',
22 debug: 'grey',
23 warn: 'yellow',
24 error: 'red',
25};
26
27const npmToWinstonLevels = {
28 silly: 'debug',
29 verbose: 'debug',
30 debug: 'debug',
31 info: 'info',
32 http: 'info',
33 warn: 'warn',
34 error: 'error',
35};
36
37let log = null;
38let useLocalTimeZone = false;
39
40// add the timestamp in the correct format to the log info object
41const timestampFormat = format.timestamp({
42 format () {
43 let date = new Date();
44 if (useLocalTimeZone) {
45 date = new Date(date.valueOf() - date.getTimezoneOffset() * 60000);
46 }
47 // '2012-11-04T14:51:06.157Z' -> '2012-11-04 14:51:06:157'
48 return date.toISOString()
49 .replace(/[TZ]/g, ' ')
50 .replace(/\./g, ':')
51 .trim();
52 },
53});
54
55// set the custom colors
56const colorizeFormat = format.colorize({
57 colors,
58});
59
60// Strip the color marking within messages
61const stripColorFormat = format(function stripColor (info) {
62 const code = /\u001b\[(\d+(;\d+)*)?m/g; // eslint-disable-line no-control-regex
63 info.message = info.message.replace(code, '');
64 return info;
65})();
66
67function createConsoleTransport (args, logLvl) {
68 return new (transports.Console)({
69 name: 'console',
70 handleExceptions: true,
71 exitOnError: false,
72 json: false,
73 level: logLvl,
74 stderrLevels: ['error'],
75 format: format.combine(
76 format(function adjustDebug (info) {
77 // prepend debug marker, and shift to `info` log level
78 if (info.level === 'debug') {
79 info.level = 'info';
80 info.message = `[debug] ${info.message}`;
81 }
82 return info;
83 })(),
84 timestampFormat,
85 args.logNoColors ? stripColorFormat : colorizeFormat,
86 format.printf(function printInfo (info) {
87 return `${args.logTimestamp ? `${info.timestamp} - ` : ''}${info.message}`;
88 })
89 ),
90 });
91}
92
93function createFileTransport (args, logLvl) {
94 return new (transports.File)({
95 name: 'file',
96 filename: args.logFile,
97 maxFiles: 1,
98 handleExceptions: true,
99 exitOnError: false,
100 json: false,
101 level: logLvl,
102 format: format.combine(
103 stripColorFormat,
104 timestampFormat,
105 format.printf(function printInfo (info) {
106 return `${info.timestamp} ${info.message}`;
107 })
108 )
109 });
110}
111
112function createHttpTransport (args, logLvl) {
113 let host = '127.0.0.1';
114 let port = 9003;
115
116 if (args.webhook.match(':')) {
117 const hostAndPort = args.webhook.split(':');
118 host = hostAndPort[0];
119 port = parseInt(hostAndPort[1], 10);
120 }
121
122 return new (transports.Http)({
123 name: 'http',
124 host,
125 port,
126 path: '/',
127 handleExceptions: true,
128 exitOnError: false,
129 json: false,
130 level: logLvl,
131 format: format.combine(
132 stripColorFormat,
133 format.printf(function printInfo (info) {
134 return `${info.timestamp} ${info.message}`;
135 })
136 ),
137 });
138}
139
140async function createTransports (args) {
141 let transports = [];
142 let consoleLogLevel = null;
143 let fileLogLevel = null;
144
145 if (args.loglevel && args.loglevel.match(':')) {
146 // --log-level arg can optionally provide diff logging levels for console and file, separated by a colon
147 const lvlPair = args.loglevel.split(':');
148 consoleLogLevel = lvlPair[0] || consoleLogLevel;
149 fileLogLevel = lvlPair[1] || fileLogLevel;
150 } else {
151 consoleLogLevel = fileLogLevel = args.loglevel;
152 }
153
154 transports.push(createConsoleTransport(args, consoleLogLevel));
155
156 if (args.logFile) {
157 try {
158 // if we don't delete the log file, winston will always append and it will grow infinitely large;
159 // winston allows for limiting log file size, but as of 9.2.14 there's a serious bug when using
160 // maxFiles and maxSize together. https://github.com/flatiron/winston/issues/397
161 if (await fs.exists(args.logFile)) {
162 await fs.unlink(args.logFile);
163 }
164
165 transports.push(createFileTransport(args, fileLogLevel));
166 } catch (e) {
167 // eslint-disable-next-line no-console
168 console.log(`Tried to attach logging to file '${args.logFile}' but an error ` +
169 `occurred: ${e.message}`);
170 }
171 }
172
173 if (args.webhook) {
174 try {
175 transports.push(createHttpTransport(args, fileLogLevel));
176 } catch (e) {
177 // eslint-disable-next-line no-console
178 console.log(`Tried to attach logging to Http at ${args.webhook} but ` +
179 `an error occurred: ${e.message}`);
180 }
181 }
182
183 return transports;
184}
185
186async function init (args) {
187 // set de facto param passed to timestamp function
188 useLocalTimeZone = args.localTimezone;
189
190 // clean up in case we have initiated before since npmlog is a global object
191 clear();
192
193 log = createLogger({
194 transports: await createTransports(args),
195 levels,
196 });
197
198 // Capture logs emitted via npmlog and pass them through winston
199 npmlog.on('log', (logObj) => {
200 const winstonLevel = npmToWinstonLevels[logObj.level] || 'info';
201 let msg = logObj.message;
202 if (logObj.prefix) {
203 const prefix = `[${logObj.prefix}]`;
204 msg = `${args.logNoColors ? prefix : prefix.magenta} ${msg}`;
205 }
206 log[winstonLevel](msg);
207 if (args.logHandler && _.isFunction(args.logHandler)) {
208 args.logHandler(logObj.level, msg);
209 }
210
211 });
212}
213
214function clear () {
215 if (log) {
216 for (let transport of _.keys(log.transports)) {
217 log.remove(transport);
218 }
219 }
220 npmlog.removeAllListeners('log');
221}
222
223
224export { init, clear };
225export default init;