UNPKG

7.73 kBJavaScriptView Raw
1const chalk = require('chalk');
2const path = require('path');
3const fs = require('fs');
4
5// A map used to define message levels
6// and set their display styles
7const MESSAGE_CATEGORY_MAPPING = {
8 TRACE: {
9 level: 10,
10 display: {}
11 },
12 DEBUG: {
13 level: 20,
14 display: {
15 color: 'gray',
16 bold: false,
17 prefix: '[Debug]: ',
18 method: 'warn',
19 }
20 },
21 INFO: {
22 level: 30,
23 display: {
24 color: null,
25 bold: false,
26 prefix: '',
27 method: 'log'
28 }
29 },
30 WARN: {
31 level: 40,
32 display: {
33 color: 'yellow',
34 bold: true,
35 prefix: '[Warn]: ',
36 method: 'warn'
37 }
38 },
39 ERROR: {
40 level: 50,
41 display: {
42 color: 'red',
43 bold: true,
44 prefix: '[Error]: ',
45 method: 'error'
46 }
47 },
48 FATAL: {
49 level: 60,
50 display: {
51 color: 'rgb(128, 0, 0)',
52 bold: true,
53 prefix: '[Fatal]: ',
54 method: 'error'
55 }
56 }
57};
58
59let instance = null;
60
61class Messenger {
62 // Make this class as a singleton
63 constructor({ doDebug }) {
64 if (!instance) {
65 instance = this;
66 }
67 // TODO: change buffer data type to avoid overflow
68 // Used to record all CLI std behaviors
69 this._buffer = [];
70 // This flag is for debug use in order to
71 // determine whether commands is working on debug mode
72 this.doDebug = doDebug || false;
73 return instance;
74 }
75
76 static getInstance() {
77 return instance || new Messenger({});
78 }
79
80 // If the commander works under debug mode, write all debug level messages to local file
81 // when the messanger instance get disposed and set to null
82 dispose() {
83 if (this.doDebug) {
84 this.writeLogToFile();
85 }
86 instance = null;
87 }
88
89 // Used to track http request behaviors
90 // Record information to buffer
91 trace(data) {
92 const operation = 'TRACE';
93 this._buffer.push({
94 time: Messenger.getTime(),
95 operation,
96 level: MESSAGE_CATEGORY_MAPPING[operation].level,
97 msg: data
98 });
99 }
100
101 // Used to track CLI internal debug messages
102 // Record CLI behaviors to buffer and
103 // expose debug messages immediately when user have --debug option set
104 debug(data) {
105 const operation = 'DEBUG';
106 this._buffer.push({
107 time: Messenger.getTime(),
108 operation,
109 level: MESSAGE_CATEGORY_MAPPING[operation].level,
110 msg: data
111 });
112 if (this.doDebug) {
113 Messenger.displayMessage(operation, data);
114 }
115 }
116
117 // Used to display the information during the CLI command execution
118 info(data) {
119 const operation = 'INFO';
120 this._buffer.push({
121 time: Messenger.getTime(),
122 operation,
123 level: MESSAGE_CATEGORY_MAPPING[operation].level,
124 msg: data
125 });
126 Messenger.displayMessage(operation, data);
127 }
128
129 // record warn message to buffer and
130 // print to console
131 warn(data) {
132 const operation = 'WARN';
133 this._buffer.push({
134 time: Messenger.getTime(),
135 operation,
136 level: MESSAGE_CATEGORY_MAPPING[operation].level,
137 msg: data
138 });
139 Messenger.displayMessage(operation, data);
140 }
141
142 // record error message to buffer and
143 // print to console
144 error(data) {
145 const msg = data.constructor.name === 'Error' ? data.message : data;
146
147 const operation = 'ERROR';
148 this._buffer.push({
149 time: Messenger.getTime(),
150 operation,
151 level: MESSAGE_CATEGORY_MAPPING[operation].level,
152 msg,
153 });
154 Messenger.displayMessage(operation, msg);
155 }
156
157 // record fatal message to buffer and
158 // print to console
159 fatal(data) {
160 const msg = data.constructor.name === 'Error' ? data.stack.substring(7) : data;
161
162 const operation = 'FATAL';
163 this._buffer.push({
164 time: Messenger.getTime(),
165 operation,
166 level: MESSAGE_CATEGORY_MAPPING[operation].level,
167 msg,
168 });
169 Messenger.displayMessage(operation, msg);
170 }
171
172 /**
173 * A view wrapper, print std in different styles according to different message category
174 * @param {string} operation The message category
175 * @param {string} data The text content to be displayed
176 */
177 static displayMessage(operation, data) {
178 const { color, method, bold, prefix } = MESSAGE_CATEGORY_MAPPING[operation].display;
179 const msg = prefix + data;
180 Messenger.displayWithStyle(color, method, bold, msg);
181 }
182
183 /**
184 * The print function to display message in different styles
185 * @param {string} color set the color of the text
186 * @param {string} method set the console method, could be log, warn and error
187 * @param {boolean} bold a boolean value decide whether the font should be bold
188 * @param {string} msg The text content to be displayed
189 */
190 static displayWithStyle(color, method, bold, msg) {
191 if (color) {
192 if (bold) {
193 console[method](chalk`{bold.${color} ${msg}}`);
194 } else {
195 console[method](chalk`{${color} ${msg}}`);
196 }
197 } else {
198 if (bold) {
199 console[method](chalk`{bold ${msg}}`);
200 } else {
201 console[method](msg);
202 }
203 }
204 }
205
206 // Read Trace level info from buffer,
207 // modify the structure to make it easy to read,
208 // write restructured info to a log file
209 writeLogToFile() {
210 const time = Messenger.getTime();
211 const filePath = path.join(process.cwd(), `ask-cli-${time}.log`);
212 const content = [];
213 for (const item of this._buffer) {
214 if (item.operation === 'TRACE') {
215 content.push(`\n[${item.time}] - ${item.operation} - ${item.msg.activity.toUpperCase()}`);
216 if (item.msg['request-id']) {
217 content.push(`\nrequest-id: ${item.msg['request-id']}`);
218 }
219 content.push(`\n${item.msg.request.method} ${item.msg.request.url}`);
220 content.push(`\nstatus code: ${item.msg.response.statusCode} ${item.msg.response.statusMessage}`);
221 if (item.msg.error) {
222 content.push(`\nerror: ${item.msg.error}\n`);
223 }
224 content.push(`\nRequest headers: ${JSON.stringify(item.msg.request.headers)}`);
225 if (item.msg.request.body) {
226 content.push(`\nRequest body: ${item.msg.request.body}`);
227 }
228 content.push(`\nResponse headers: ${JSON.stringify(item.msg.response.headers)}`);
229 if (item.msg.body) {
230 content.push(`\nResponse body: ${JSON.stringify(item.msg.body)}`);
231 }
232 content.push('\n----------------------------------------');
233 }
234 }
235 if (content.length >= 1) {
236 fs.writeFileSync(filePath, content);
237 console.log(`\nDetail log has been recorded at ${filePath}`);
238 }
239 }
240
241 // Used to get system time and format the time into the pattern of "week-month-day-year-hour:minute:second-time zone".
242 // eg: Mon-Apr-01-2019-11:31:21-GMT-0700-(PDT)
243 static getTime() {
244 return new Date().toString().replace(/ /g, '-');
245 }
246}
247
248module.exports = Messenger;