1 | import npmlog from 'npmlog';
|
2 | import { createLogger, format, transports } from 'winston';
|
3 | import { fs, logger } from 'appium-support';
|
4 | import _ from 'lodash';
|
5 |
|
6 |
|
7 |
|
8 | logger.patchLogger(npmlog);
|
9 | global._global_npmlog = npmlog;
|
10 |
|
11 |
|
12 | npmlog.level = 'silent';
|
13 | const levels = {
|
14 | debug: 4,
|
15 | info: 3,
|
16 | warn: 2,
|
17 | error: 1,
|
18 | };
|
19 |
|
20 | const colors = {
|
21 | info: 'cyan',
|
22 | debug: 'grey',
|
23 | warn: 'yellow',
|
24 | error: 'red',
|
25 | };
|
26 |
|
27 | const npmToWinstonLevels = {
|
28 | silly: 'debug',
|
29 | verbose: 'debug',
|
30 | debug: 'debug',
|
31 | info: 'info',
|
32 | http: 'info',
|
33 | warn: 'warn',
|
34 | error: 'error',
|
35 | };
|
36 |
|
37 | let log = null;
|
38 | let useLocalTimeZone = false;
|
39 |
|
40 |
|
41 | const timestampFormat = format.timestamp({
|
42 | format () {
|
43 | let date = new Date();
|
44 | if (useLocalTimeZone) {
|
45 | date = new Date(date.valueOf() - date.getTimezoneOffset() * 60000);
|
46 | }
|
47 |
|
48 | return date.toISOString()
|
49 | .replace(/[TZ]/g, ' ')
|
50 | .replace(/\./g, ':')
|
51 | .trim();
|
52 | },
|
53 | });
|
54 |
|
55 |
|
56 | const colorizeFormat = format.colorize({
|
57 | colors,
|
58 | });
|
59 |
|
60 |
|
61 | const stripColorFormat = format(function stripColor (info) {
|
62 | const code = /\u001b\[(\d+(;\d+)*)?m/g;
|
63 | info.message = info.message.replace(code, '');
|
64 | return info;
|
65 | })();
|
66 |
|
67 | function 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 |
|
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 |
|
93 | function 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 |
|
112 | function 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 |
|
140 | async function createTransports (args) {
|
141 | let transports = [];
|
142 | let consoleLogLevel = null;
|
143 | let fileLogLevel = null;
|
144 |
|
145 | if (args.loglevel && args.loglevel.match(':')) {
|
146 |
|
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 |
|
159 |
|
160 |
|
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 |
|
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 |
|
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 |
|
186 | async function init (args) {
|
187 |
|
188 | useLocalTimeZone = args.localTimezone;
|
189 |
|
190 |
|
191 | clear();
|
192 |
|
193 | log = createLogger({
|
194 | transports: await createTransports(args),
|
195 | levels,
|
196 | });
|
197 |
|
198 |
|
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 |
|
214 | function 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 |
|
224 | export { init, clear };
|
225 | export default init;
|