UNPKG

9.68 kBJavaScriptView Raw
1/**
2* Copyright (c) Microsoft. All rights reserved.
3*
4* Licensed under the Apache License, Version 2.0 (the "License");
5* you may not use this file except in compliance with the License.
6* You may obtain a copy of the License at
7* http://www.apache.org/licenses/LICENSE-2.0
8*
9* Unless required by applicable law or agreed to in writing, software
10* distributed under the License is distributed on an "AS IS" BASIS,
11* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12* See the License for the specific language governing permissions and
13* limitations under the License.
14*/
15
16'use strict';
17
18//
19// Logging infrastructure for the xplat CLI
20//
21
22var log = require('winston');
23var eyes = require('eyes');
24var os = require('os');
25var Table = require('easy-table');
26var util = require('util');
27var wrap = require('wordwrap');
28var path = require('path');
29
30var utilsCore = require('./utilsCore');
31
32// use cli output settings by default
33log.cli();
34
35log.add(log.transports.File, {
36 name: 'silly',
37 filename: path.join(utilsCore.azureDir(), 'azure.details.log'),
38 level: 'silly',
39 json: false,
40 colorize:false,
41 padlevel: false,
42 timestamp: false,
43 stripColors: true,
44 silent: process.env.AZURE_CLI_DISABLE_LOG_CAPTURE,
45 options: { flags: 'w' },
46 formatter: function (options) {
47 var code = /\u001b\[(\d+(;\d+)*)?m/g;
48 var msg = (undefined !== options.message ? options.message.replace(code, '') : '');
49 return options.level + ': '+ msg +
50 (options.meta && Object.keys(options.meta).length ? '\n' + JSON.stringify(options.meta, null, 2) : '');
51 }
52});
53
54var jsonMode;
55
56log.format = function (options) {
57 var consoleTransport = log['default'].transports['console'];
58 var testTransport = log['default'].transports['memory'];
59 if (consoleTransport && testTransport) {
60 throw new Error('Internal error: both console and memory transports are specified.');
61 }
62 var defaultTransport = consoleTransport || testTransport;
63 if (!defaultTransport) {
64 throw new Error('Internal error: missing winston transport.');
65 }
66 defaultTransport.colorize = process.stdout.isTTY;
67 if (arguments.length === 0) {
68 return {
69 json: jsonMode,
70 level: defaultTransport.level,
71 };
72 }
73
74 if (options.json) {
75 log.padLevels = false;
76 log.stripColors = true;
77 jsonMode = true;
78 defaultTransport.formatter = function (options) {
79 if (options.meta && Object.keys(options.meta).length || !options.message) {
80 return options.meta ? JSON.stringify(options.meta, null, 2) : '';
81 } else if (options.message) {
82 return options.level + ': ' + options.message.replace(/\x1b\[[0-9]+m/g, '');
83 }
84 };
85 //do not set 'json' flags on transport, as winston makes unfavorable assumption
86 //such as deep clone of the "meta" field which complicates the json object.
87 } else {
88 log.padLevels = true;
89 log.stripColors = false;
90 jsonMode = false;
91 defaultTransport.formatter = null;
92 }
93 if (options.level) {
94 defaultTransport.level = options.level;
95 }
96};
97
98log.json = function (level, data) {
99 if (arguments.length == 1) {
100 data = level;
101 level = 'data';
102 }
103
104 if (log.format().json) {
105 if (!data) {
106 log.log(level, '');
107 } else {
108 log.log(level, '', data);
109 }
110 } else {
111 var lines = eyes.inspect(data, level, { stream: false });
112 lines.split('\n').forEach(function (line) {
113 // eyes all is "cyan" by default, so this property accessor will
114 // fix the entry/exit color codes of the line. it's needed because we're
115 // splitting the eyes formatting and inserting winston formatting where it
116 // wasn't before.
117 log.log(level, line[eyes.defaults.styles.all]);
118 });
119 }
120};
121
122log.table = function (level, data, transform) {
123 if (arguments.length == 2) {
124 transform = data;
125 data = level;
126 level = 'data';
127 }
128
129 if (log.format().json) {
130 log.log(level, 'table', data);
131 } else {
132 var table = new Table();
133 table.LeftPadder = Table.LeftPadder;
134 table.padLeft = Table.padLeft;
135 table.RightPadder = Table.RightPadder;
136 table.padRight = Table.padRight;
137
138 if (data && data.forEach) {
139 data.forEach(function (item) { transform(table, item); table.newLine(); });
140 } else if (data) {
141 for (var item in data) {
142 transform(table, item);
143 table.newLine();
144 }
145 }
146
147 for (var i in table.lines) {
148 for (var column in table.columns) {
149 if (!table.lines[i].hasOwnProperty(column)) {
150 table.lines[i][column] = '';
151 }
152 }
153 }
154
155 var lines = table.toString();
156 lines.substring(0, lines.length - 1).split('\n').forEach(function (line) {
157 log.log(level, line);
158 });
159 }
160};
161
162log.nameValue = function (name, value, indent, displayNullValue) {
163 var delimiter = ': ';
164 if (!displayNullValue) displayNullValue = false;
165 if (!indent) indent = 0;
166 var key = spaces(indent) + name;
167 key += spaces(32 - key.length);
168
169 if (value !== undefined && value !== null) {
170 log.data(key + delimiter + value);
171 } else if (displayNullValue) {
172 log.data(key + delimiter + '""');
173 }
174};
175
176log.header = function (header, indent, newLine) {
177 var delimiter = ':';
178 if (newLine) {
179 log.data('');
180 }
181 log.data(spaces(indent) + header + delimiter);
182};
183
184log.list = function (items, indent, newLine) {
185 items.forEach(function (item) {
186 log.listItem(item, indent, newLine);
187 });
188};
189
190log.listItem = function (item, indent, newLine) {
191 if (newLine) {
192 log.data('');
193 }
194 log.data(spaces(indent) + item);
195};
196
197/**
198 * This overrides original winston warn function to work properly within --json option
199 */
200var originalWarn = log.warn;
201log.warn = function() {
202 if (!log.format().json) {
203 originalWarn.apply(this, arguments);
204 }
205};
206
207function getProperty(value, propertyName) {
208 if (typeof value === 'undefined' || value === null) {
209 return '';
210 }
211
212 if (!propertyName) {
213 return value;
214 }
215
216 var first = propertyName.split('.')[0];
217 var rest = propertyName.slice(first.length + 1);
218 return getProperty(value[first], rest);
219}
220
221function spaces(num) {
222 if (num > 0) {
223 return new Array(num + 1).join(' ');
224 }
225 return '';
226}
227
228function toWidth(string, width) {
229 var pad = width - string.length;
230 return string + spaces(pad);
231}
232
233function defaultFormat(data) {
234 if (typeof data === 'undefined' || data === null) {
235 return '';
236 }
237 if (data instanceof Array) {
238 if (data.length === 0) {
239 return '[]';
240 }
241 return data.join(', ');
242 }
243
244 return data.toString();
245}
246
247function doReport(indentation, reportFormat, data, outfn) {
248 if (reportFormat.length === 0) {
249 return;
250 }
251
252 var maxWidth = 80;
253 if (process.stdout.isTTY) {
254 maxWidth = process.stdout.columns;
255 }
256
257 var headerWidth = Math.max.apply(null,
258 reportFormat.map(function (item) { return item[0].length; })
259 ) + 2;
260
261 reportFormat.forEach(function (item) {
262 var title = item[0] + ':';
263 var field = item[1];
264 var formatter = item[2] || defaultFormat;
265
266 var value = getProperty(data, field);
267 if (formatter instanceof Array) {
268 outfn(spaces(indentation) + toWidth(title, headerWidth));
269 doReport(indentation + headerWidth, formatter, value, outfn);
270 } else {
271 var leftIndentation = 'verbose: '.length + indentation + headerWidth;
272 var formatted = wrap.hard(leftIndentation, maxWidth)(formatter(value));
273 formatted = spaces(indentation) + toWidth(title, headerWidth) +
274 formatted.slice(leftIndentation);
275 outfn(formatted);
276 }
277 });
278}
279
280log.report = function (reportFormat, data) {
281 if (log.format().json) {
282 log.log('data', 'table', data);
283 } else {
284 doReport(0, reportFormat, data, log.data.bind(log));
285 }
286};
287
288log.report.allProperties = function (data) {
289 if (typeof data === 'undefined' || data === null || data === '') {
290 return '[]';
291 }
292 var subreport = Object.keys(data).map(function (key) {
293 return [key, key];
294 });
295 var result = [];
296 doReport(0, subreport, data, function (o) { result.push(o); });
297 result.push('');
298 return result.join(os.EOL);
299};
300
301log.report.asDate = function (data) {
302 return new Date(Date.parse(data)).toString();
303};
304
305log.report.inspect = function (data) {
306 return util.inspect(data, {depth: null});
307};
308
309log.createLogFilter = function () {
310 return function handle(resource, next, callback) {
311 log.silly('requestOptions');
312 try {
313 var loggedResource = JSON.parse(JSON.stringify(resource));
314 if (loggedResource.headers && loggedResource.headers.Authorization) {
315 delete loggedResource.headers.Authorization;
316 }
317 if (loggedResource.body &&
318 (loggedResource.body.length > 2 * 1024 * 1024 || loggedResource.body.type === 'Buffer')) { // if the body is large, log only that the body was too large to log and was more than likely file content
319 loggedResource.body = 'REQUEST BODY DATA OMMITTED FROM LOG DUE TO SIZE';
320 }
321 log.json('silly', loggedResource);
322 } catch (error) {
323 return callback (error);
324 }
325 return next(resource, function (err, response, body) {
326 log.silly('returnObject');
327 if (response) {
328 var bodyToLog = body;
329 if (body && (body.length > 2 * 1024 * 1024 || body.type === 'Buffer')) { // if the body is large, log only that the body was too large to log and was more than likely file content
330 bodyToLog = 'RESPONSE BODY DATA OMMITTED FROM LOG DUE TO SIZE';
331 }
332 log.json('silly', {
333 statusCode: response.statusCode,
334 header: response.headers,
335 body: bodyToLog
336 });
337 }
338
339 callback(err, response, body);
340 });
341 };
342};
343log.spaces = spaces;
344
345module.exports = log;
\No newline at end of file