1 |
|
2 |
|
3 | var Squeeze = require('good-squeeze').Squeeze;
|
4 | var Hoek = require('hoek');
|
5 | var Moment = require('moment');
|
6 | var SafeStringify = require('json-stringify-safe');
|
7 | var Through = require('through2');
|
8 |
|
9 |
|
10 |
|
11 | var internals = {
|
12 | defaults: {
|
13 | format: 'YYMMDD/HHmmss.SSS',
|
14 | utc: true,
|
15 | color: true
|
16 | }
|
17 | };
|
18 |
|
19 | module.exports = internals.GoodConsole = function (events, config) {
|
20 |
|
21 | if (!(this instanceof internals.GoodConsole)) {
|
22 | return new internals.GoodConsole(events, config);
|
23 | }
|
24 | config = config || {};
|
25 | this._settings = Hoek.applyToDefaults(internals.defaults, config);
|
26 | this._filter = new Squeeze(events);
|
27 | };
|
28 |
|
29 | internals.GoodConsole.prototype.init = function (stream, emitter, callback) {
|
30 |
|
31 | var self = this;
|
32 |
|
33 | if (!stream._readableState.objectMode) {
|
34 | return callback(new Error('stream must be in object mode'));
|
35 | }
|
36 |
|
37 | stream.pipe(this._filter).pipe(Through.obj(function goodConsoleTransform (data, enc, next) {
|
38 |
|
39 | var eventName = data.event;
|
40 | var tags = [];
|
41 |
|
42 |
|
43 | if (Array.isArray(data.tags)) {
|
44 | tags = data.tags.concat([]);
|
45 | } else if (data.tags != null) {
|
46 | tags = [data.tags];
|
47 | }
|
48 |
|
49 |
|
50 | tags.unshift(eventName);
|
51 |
|
52 | if (eventName === 'response') {
|
53 | this.push(self._formatResponse(data, tags));
|
54 | return next();
|
55 | }
|
56 |
|
57 | if (eventName === 'wreck') {
|
58 | this.push(self._formatWreck(data, tags));
|
59 | return next();
|
60 | }
|
61 |
|
62 | var eventPrintData = {
|
63 | timestamp: data.timestamp || Date.now(),
|
64 | tags: tags,
|
65 | data: undefined
|
66 | };
|
67 |
|
68 | if (eventName === 'ops') {
|
69 | eventPrintData.data = Hoek.format('memory: %sMb, uptime (seconds): %s, load: %s',
|
70 | Math.round(data.proc.mem.rss / (1024 * 1024)),
|
71 | data.proc.uptime,
|
72 | data.os.load);
|
73 |
|
74 | this.push(self._printEvent(eventPrintData));
|
75 | return next();
|
76 | }
|
77 |
|
78 | if (eventName === 'error') {
|
79 | eventPrintData.data = 'message: ' + data.error.message + ' stack: ' + data.error.stack;
|
80 |
|
81 | this.push(self._printEvent(eventPrintData));
|
82 | return next();
|
83 | }
|
84 |
|
85 | if (eventName === 'request' || eventName === 'log') {
|
86 | eventPrintData.data = 'data: ' + (typeof data.data === 'object' ? SafeStringify(data.data) : data.data);
|
87 |
|
88 | this.push(self._printEvent(eventPrintData));
|
89 | return next();
|
90 | }
|
91 |
|
92 |
|
93 | if (data.data) {
|
94 | eventPrintData.data = 'data: ' + (typeof data.data === 'object' ? SafeStringify(data.data) : data.data);
|
95 | }
|
96 | else {
|
97 | eventPrintData.data = 'data: (none)';
|
98 | }
|
99 |
|
100 | this.push(self._printEvent(eventPrintData));
|
101 | return next();
|
102 | })).pipe(process.stdout);
|
103 |
|
104 | callback();
|
105 | };
|
106 |
|
107 | internals.GoodConsole.prototype._printEvent = function (event) {
|
108 |
|
109 | var m = Moment(parseInt(event.timestamp, 10));
|
110 | if (!this._settings.utc) { m.local(); }
|
111 |
|
112 | var timestring = m.format(this._settings.format);
|
113 | var data = event.data;
|
114 | var output = timestring + ', [' + event.tags.toString() + '], ' + data;
|
115 |
|
116 | return output + '\n';
|
117 | };
|
118 |
|
119 | internals.GoodConsole.prototype._formatResponse = function (event, tags) {
|
120 |
|
121 | var query = event.query ? JSON.stringify(event.query) : '';
|
122 | var responsePayload = '';
|
123 |
|
124 | if (typeof event.responsePayload === 'object' && event.responsePayload) {
|
125 | responsePayload = 'response payload: ' + SafeStringify(event.responsePayload);
|
126 | }
|
127 |
|
128 | var method = this._formatMethod(event.method);
|
129 | var statusCode = this._formatStatusCode(event.statusCode) || '';
|
130 |
|
131 | return this._printEvent({
|
132 | timestamp: event.timestamp,
|
133 | tags: tags,
|
134 |
|
135 | data: Hoek.format('%s: %s %s %s %s (%sms) %s', event.instance, method, event.path, query, statusCode, event.responseTime, responsePayload)
|
136 | });
|
137 | };
|
138 |
|
139 | internals.GoodConsole.prototype._formatWreck = function (event, tags) {
|
140 |
|
141 | var data, method, statusCode;
|
142 | method = this._formatMethod(event.request.method);
|
143 |
|
144 | if (event.error) {
|
145 | data = Hoek.format('%s: %s (%sms) error: %s stack: %s', method, event.request.url, event.timeSpent, event.error.message, event.error.stack);
|
146 | } else {
|
147 | statusCode = this._formatStatusCode(event.response.statusCode);
|
148 | data = Hoek.format('%s: %s %s %s (%sms)', method, event.request.url, statusCode, event.response.statusMessage, event.timeSpent);
|
149 | }
|
150 |
|
151 |
|
152 | return this._printEvent({
|
153 | timestamp: event.timestamp,
|
154 | tags: tags,
|
155 | data: data
|
156 | });
|
157 | };
|
158 |
|
159 | internals.GoodConsole.prototype._formatMethod = function (method) {
|
160 |
|
161 | var color;
|
162 | var methodColors = {
|
163 | get: 32,
|
164 | delete: 31,
|
165 | put: 36,
|
166 | post: 33
|
167 | };
|
168 | var formattedMethod = method.toLowerCase();
|
169 | if (this._settings.color) {
|
170 | color = methodColors[method.toLowerCase()] || 34;
|
171 | formattedMethod = '\x1b[1;' + color + 'm' + formattedMethod + '\x1b[0m';
|
172 | }
|
173 | return formattedMethod;
|
174 | };
|
175 |
|
176 | internals.GoodConsole.prototype._formatStatusCode = function (statusCode) {
|
177 |
|
178 | var color;
|
179 | if (statusCode && this._settings.color) {
|
180 | color = 32;
|
181 | if (statusCode >= 500) {
|
182 | color = 31;
|
183 | } else if (statusCode >= 400) {
|
184 | color = 33;
|
185 | } else if (statusCode >= 300) {
|
186 | color = 36;
|
187 | }
|
188 | return '\x1b[' + color + 'm' + statusCode + '\x1b[0m';
|
189 | }
|
190 | return statusCode;
|
191 | };
|
192 |
|
193 | internals.GoodConsole.attributes = {
|
194 | pkg: require('../package.json')
|
195 | };
|