UNPKG

13 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3const appRoot = require("app-root-path");
4const cluster = require("cluster");
5const fs = require("fs-extra");
6const _ = require("lodash");
7const os = require("os");
8const p = require("path");
9const serializeError = require("serialize-error");
10const trace = require("stack-trace");
11const winston = require("winston");
12const context = require("../internal/util/cls");
13const redact_1 = require("./redact");
14const winstonLogger = winston.createLogger({
15 level: "debug",
16 exitOnError: false,
17 silent: true,
18});
19/**
20 * Global logger instance
21 * By default all logging to this logger will be suppressed.
22 * Call configureLogging to set up console and file transports.
23 */
24exports.logger = winstonLogger;
25/* tslint:disable:no-console */
26// Save console log methods
27const GlobalConsoleMethods = {
28 error: console.error,
29 warn: console.warn,
30 info: console.info,
31 debug: console.debug,
32 log: console.log,
33 trace: console.trace,
34};
35/* tslint:enable:no-console */
36/**
37 * Constants for the logging format
38 */
39var LoggingFormat;
40(function (LoggingFormat) {
41 /**
42 * Print only the message provided to the logger methods
43 */
44 LoggingFormat[LoggingFormat["None"] = 0] = "None";
45 /**
46 * Print level and message
47 */
48 LoggingFormat[LoggingFormat["Simple"] = 1] = "Simple";
49 /**
50 * Print the full client related metadata
51 */
52 LoggingFormat[LoggingFormat["Full"] = 2] = "Full";
53})(LoggingFormat = exports.LoggingFormat || (exports.LoggingFormat = {}));
54/**
55 * Logging configuration suppress all logger logging
56 */
57exports.NoLogging = {
58 console: {
59 enabled: false,
60 },
61 file: {
62 enabled: false,
63 },
64 redact: true,
65 color: true,
66};
67/**
68 * Logging configuration to simply pass through log message to the console
69 */
70exports.PlainLogging = {
71 console: {
72 enabled: true,
73 level: "info",
74 format: LoggingFormat.None,
75 redirect: false,
76 },
77 file: {
78 enabled: false,
79 },
80 redact: true,
81 color: true,
82};
83/**
84 * CLI-style logging configuration
85 */
86exports.MinimalLogging = {
87 console: {
88 enabled: true,
89 level: "info",
90 format: LoggingFormat.Simple,
91 redirect: false,
92 },
93 file: {
94 enabled: false,
95 },
96 redact: true,
97 color: true,
98};
99/**
100 * Default logging configuration for running automation clients
101 */
102exports.ClientLogging = {
103 console: {
104 enabled: true,
105 level: process.env.ATOMIST_CONFIG_LOGGING_LEVEL ? process.env.ATOMIST_CONFIG_LOGGING_LEVEL : "info",
106 format: LoggingFormat.Full,
107 redirect: true,
108 },
109 file: {
110 enabled: false,
111 },
112 redact: true,
113 color: true,
114};
115/**
116 * Configure the logging sub-system with the provided LoggingConfiguration
117 * It is safe to call this method several times to re-configure the logger.
118 * @param config
119 */
120function configureLogging(config) {
121 try {
122 winstonLogger.silent = true;
123 winstonLogger.clear();
124 // Set up console logging
125 if (config.console.enabled === true) {
126 const ct = new winston.transports.Console({
127 level: validateLevel(config.console.level || "info"),
128 format: getFormat(config.console.format, config.redact, config.callsites, config.color),
129 });
130 if (config.console.redirect === true) {
131 redirectConsoleLogging();
132 }
133 else {
134 unRedirectConsoleLogging();
135 }
136 winstonLogger.add(ct);
137 winstonLogger.silent = false;
138 }
139 // Set up file logging
140 if (config.file.enabled) {
141 let filename = config.file.filename;
142 if (!filename) {
143 const appDir = __dirname.split(p.join("node_modules", "@atomist", "automation-client"))[0];
144 let pj;
145 try {
146 pj = require(p.join(appDir, "package.json"));
147 }
148 catch (e) {
149 pj = { name: "atm-client" };
150 }
151 filename = p.join(os.homedir(), ".atomist", "log", `${pj.name.replace(/@/g, "").replace(/\//g, "_")}_local.log`);
152 }
153 const path = p.resolve(filename);
154 fs.mkdirsSync(p.dirname(path));
155 const ft = new winston.transports.File({
156 filename: p.basename(path),
157 dirname: p.dirname(path),
158 level: validateLevel(config.file.level || config.console.level),
159 maxsize: 10 * 1024 * 1024,
160 maxFiles: 10,
161 tailable: true,
162 // zippedArchive: true, // see https://github.com/winstonjs/winston/issues/1128
163 format: winston.format.combine(getFormat(config.file.format, config.redact, config.callsites, config.color), winston.format.uncolorize()),
164 });
165 winstonLogger.add(ft);
166 winstonLogger.silent = false;
167 }
168 // Set up custom transports
169 if (config.custom && config.custom.length > 0) {
170 config.custom.forEach(t => winstonLogger.add(t));
171 winstonLogger.silent = false;
172 }
173 }
174 catch (e) {
175 // If we catch an error during logging initialization, we have to play it
176 // safe and write straight to stderr as logging might be silentest.
177 process.stderr.write(`Error occurred during logging initialization: ${e.stack}`);
178 throw e;
179 }
180}
181exports.configureLogging = configureLogging;
182/**
183 * Configure the logging sub-system based on the provided Configuration object
184 * @param configuration
185 */
186function clientLoggingConfiguration(configuration) {
187 const lc = Object.assign({}, exports.ClientLogging);
188 if (configuration.logging) {
189 if (configuration.logging.level) {
190 lc.console = {
191 enabled: true,
192 level: configuration.logging.level,
193 format: LoggingFormat.Full,
194 };
195 }
196 if (_.get(configuration, "logging.file.enabled") === true) {
197 let filename = p.join(".", "log", `${configuration.name.replace(/@/g, "").replace(/\//g, "_")}.log`);
198 if (configuration.logging.file.name) {
199 filename = configuration.logging.file.name;
200 }
201 lc.file = {
202 enabled: true,
203 level: configuration.logging.file.level || configuration.logging.level,
204 filename,
205 format: LoggingFormat.Full,
206 };
207 }
208 lc.custom = _.get(configuration, "logging.custom.transports");
209 lc.callsites = _.get(configuration, "logging.callsite");
210 lc.color = _.get(configuration, "logging.color");
211 }
212 lc.redact = configuration.redact.log;
213 return lc;
214}
215exports.clientLoggingConfiguration = clientLoggingConfiguration;
216function validateLevel(level) {
217 const levels = ["silly", "debug", "verbose", "info", "warn", "error"];
218 if (!levels.includes(level)) {
219 throw new Error(`Log level '${level}' is invalid. Only levels '${levels.join(", ")}' are allowed`);
220 }
221 return level;
222}
223function callsite(logInfo) {
224 const oldLimit = Error.stackTraceLimit;
225 try {
226 Error.stackTraceLimit = Infinity;
227 throw new Error();
228 }
229 catch (e) {
230 const root = appRoot.path;
231 const callsites = trace.parse(e).map(l => ({
232 fileName: l.getFileName(),
233 lineNumber: l.getLineNumber(),
234 })).filter(cs => !!cs.fileName).reverse();
235 const callsite = callsites[callsites.findIndex(cs => cs.fileName.includes("node_modules/winston")) - 1];
236 if (!!callsite) {
237 return Object.assign(Object.assign({}, logInfo), { callsite: Object.assign(Object.assign({}, callsite), { fileName: callsite.fileName.split(root)[1].slice(1) }) });
238 }
239 }
240 finally {
241 Error.stackTraceLimit = oldLimit;
242 }
243 return logInfo;
244}
245exports.callsite = callsite;
246/* tslint:disable:cyclomatic-complexity */
247const clientFormat = info => {
248 const c = winston.format.colorize();
249 const executionContext = context.get();
250 let ctx;
251 if (cluster.isMaster) {
252 ctx = c.colorize(info.level, "m");
253 }
254 else {
255 ctx = c.colorize(info.level, "w");
256 }
257 ctx += ":" + c.colorize(info.level, process.pid.toString());
258 if (executionContext) {
259 if (executionContext.invocationId) {
260 ctx += ":" + c.colorize(info.level, executionContext.invocationId);
261 }
262 if (executionContext.workspaceName) {
263 ctx += ":" + c.colorize(info.level, executionContext.workspaceName);
264 }
265 else if (executionContext.workspaceId) {
266 ctx += ":" + c.colorize(info.level, executionContext.workspaceId);
267 }
268 if (executionContext.operation) {
269 ctx += ":" + c.colorize(info.level, executionContext.operation);
270 }
271 if (executionContext.ts) {
272 const duration = _.padStart((Date.now() - executionContext.ts).toString(), 3, "0");
273 ctx += ":" + c.colorize(info.level, duration);
274 }
275 }
276 let callsite;
277 if (!!info.callsite) {
278 callsite = ` [${c.colorize(info.level, info.callsite.fileName)}:${c.colorize(info.level, info.callsite.lineNumber || -1)}]`;
279 }
280 const level = c.colorize(info.level, _.padEnd(info.level, 5));
281 let formatted = info.timestamp + (!!callsite ? callsite : "") + (!!ctx ? " [" + ctx + "]" : "")
282 + " [" + level + "] " + (info.message ? info.message : "");
283 if (info.meta) {
284 const meta = info.meta;
285 if (meta instanceof Error) {
286 const err = meta;
287 if (err.stack && err.stack.includes(err.message)) {
288 formatted = `${formatted}${formatted.endsWith(":") ? " " : ": "}
289${err.stack}`;
290 }
291 else if (err.stack) {
292 formatted = `${formatted}${formatted.endsWith(":") ? " " : ": "}${err.message}
293${err.stack}`;
294 }
295 else {
296 formatted = `${formatted}${formatted.endsWith(":") ? " " : ": "}${err.message}`;
297 }
298 }
299 else if (meta.stack) {
300 if (meta.stack && meta.stack.includes(meta.message)) {
301 formatted = `${formatted}${formatted.endsWith(":") ? " " : ": "}
302${meta.stack}`;
303 }
304 else {
305 formatted = `${formatted}${formatted.endsWith(":") ? " " : ": "}${meta.message ? meta.message : ""}
306${meta.stack}`;
307 }
308 }
309 else if (!(Array.isArray(meta) && meta.length === 0)) {
310 formatted = `${formatted}${formatted.endsWith(":") ? " " : ": "}${JSON.stringify(serializeError(info.meta))}`;
311 }
312 }
313 return formatted;
314};
315/* tslint:enable:cyclomatic-complexity */
316/* tslint:disable:no-console */
317function unRedirectConsoleLogging() {
318 console.error = GlobalConsoleMethods.error;
319 console.info = GlobalConsoleMethods.info;
320 console.log = GlobalConsoleMethods.log;
321 console.trace = GlobalConsoleMethods.trace;
322 console.warn = GlobalConsoleMethods.warn;
323}
324function redirectConsoleLogging() {
325 console.error = (message, ...optionalParams) => {
326 winstonLogger.error(message, ...optionalParams);
327 };
328 console.info = (message, ...optionalParams) => {
329 winstonLogger.info(message, ...optionalParams);
330 };
331 console.log = (message, ...optionalParams) => {
332 winstonLogger.info(message, ...optionalParams);
333 };
334 console.trace = (message, ...optionalParams) => {
335 winstonLogger.debug(message, ...optionalParams);
336 };
337 console.warn = (message, ...optionalParams) => {
338 winstonLogger.warn(message, ...optionalParams);
339 };
340}
341/* tslint:enable:no-console */
342function getFormat(format, redact, callsites, color) {
343 switch (format) {
344 case LoggingFormat.Full:
345 return winston.format.combine(...(redact ? [winston.format(redact_1.redactLog)()] : []), winston.format.timestamp(), ...(callsites ? [winston.format(callsite)()] : []), winston.format.splat(), winston.format.printf(clientFormat), ...(color ? [] : [winston.format.uncolorize()]));
346 case LoggingFormat.Simple:
347 return winston.format.combine(...(redact ? [winston.format(redact_1.redactLog)()] : []), winston.format.colorize(), winston.format.splat(), ...(callsites ? [winston.format(callsite)()] : []), winston.format.simple(), ...(color ? [] : [winston.format.uncolorize()]));
348 case LoggingFormat.None:
349 default:
350 return winston.format.combine(...(redact ? [winston.format(redact_1.redactLog)()] : []), winston.format.splat(), ...(callsites ? [winston.format(callsite)()] : []), winston.format.printf(info => info.message), ...(color ? [] : [winston.format.uncolorize()]));
351 }
352}
353process.on("uncaughtException", err => {
354 exports.logger.error(serializeError(err));
355});
356//# sourceMappingURL=logger.js.map
\No newline at end of file