UNPKG

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