1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | const color_1 = require("@heroku-cli/color");
|
4 | const cli_ux_1 = require("cli-ux");
|
5 | exports.COLORS = [
|
6 | s => color_1.default.yellow(s),
|
7 | s => color_1.default.green(s),
|
8 | s => color_1.default.cyan(s),
|
9 | s => color_1.default.magenta(s),
|
10 | s => color_1.default.blue(s),
|
11 | s => color_1.default.bold.green(s),
|
12 | s => color_1.default.bold.cyan(s),
|
13 | s => color_1.default.bold.magenta(s),
|
14 | s => color_1.default.bold.yellow(s),
|
15 | s => color_1.default.bold.blue(s)
|
16 | ];
|
17 | const assignedColors = {};
|
18 | function getColorForIdentifier(i) {
|
19 | i = i.split('.')[0];
|
20 | if (assignedColors[i])
|
21 | return assignedColors[i];
|
22 | assignedColors[i] = exports.COLORS[Object.keys(assignedColors).length % exports.COLORS.length];
|
23 | return assignedColors[i];
|
24 | }
|
25 |
|
26 | getColorForIdentifier('run');
|
27 | getColorForIdentifier('router');
|
28 | getColorForIdentifier('web');
|
29 | getColorForIdentifier('postgres');
|
30 | getColorForIdentifier('heroku-postgres');
|
31 | let lineRegex = /^(.*?\[([\w-]+)([\d.]+)?]:)(.*)?$/;
|
32 | const red = color_1.default.red;
|
33 | const dim = i => color_1.default.dim(i);
|
34 | const other = dim;
|
35 | const path = i => color_1.default.green(i);
|
36 | const method = i => color_1.default.bold.magenta(i);
|
37 | const status = code => {
|
38 | if (code < 200)
|
39 | return code;
|
40 | if (code < 300)
|
41 | return color_1.default.green(code);
|
42 | if (code < 400)
|
43 | return color_1.default.cyan(code);
|
44 | if (code < 500)
|
45 | return color_1.default.yellow(code);
|
46 | if (code < 600)
|
47 | return color_1.default.red(code);
|
48 | return code;
|
49 | };
|
50 | const ms = (s) => {
|
51 | const ms = parseInt(s, 10);
|
52 | if (!ms)
|
53 | return s;
|
54 | if (ms < 100)
|
55 | return color_1.default.greenBright(s);
|
56 | if (ms < 500)
|
57 | return color_1.default.green(s);
|
58 | if (ms < 5000)
|
59 | return color_1.default.yellow(s);
|
60 | if (ms < 10000)
|
61 | return color_1.default.yellowBright(s);
|
62 | return color_1.default.red(s);
|
63 | };
|
64 | function colorizeRouter(body) {
|
65 | const encodeColor = ([k, v]) => {
|
66 | switch (k) {
|
67 | case 'at': return [k, v === 'error' ? red(v) : other(v)];
|
68 | case 'code': return [k, red.bold(v)];
|
69 | case 'method': return [k, method(v)];
|
70 | case 'dyno': return [k, getColorForIdentifier(v)(v)];
|
71 | case 'status': return [k, status(v)];
|
72 | case 'path': return [k, path(v)];
|
73 | case 'connect': return [k, ms(v)];
|
74 | case 'service': return [k, ms(v)];
|
75 | default: return [k, other(v)];
|
76 | }
|
77 | };
|
78 | try {
|
79 | const tokens = body.split(/\s+/).map(sub => {
|
80 | const parts = sub.split('=');
|
81 | if (parts.length === 1) {
|
82 | return parts;
|
83 | }
|
84 | else if (parts.length === 2) {
|
85 | return encodeColor(parts);
|
86 | }
|
87 | else {
|
88 | return encodeColor([parts[0], parts.splice(1).join('=')]);
|
89 | }
|
90 | });
|
91 | return tokens.map(([k, v]) => {
|
92 | if (v === undefined) {
|
93 | return other(k);
|
94 | }
|
95 | return other(k + '=') + v;
|
96 | }).join(' ');
|
97 | }
|
98 | catch (err) {
|
99 | cli_ux_1.default.warn(err);
|
100 | return body;
|
101 | }
|
102 | }
|
103 | const state = (s) => {
|
104 | switch (s) {
|
105 | case 'down': return red(s);
|
106 | case 'up': return color_1.default.greenBright(s);
|
107 | case 'starting': return color_1.default.yellowBright(s);
|
108 | case 'complete': return color_1.default.greenBright(s);
|
109 | default: return s;
|
110 | }
|
111 | };
|
112 | function colorizeRun(body) {
|
113 | try {
|
114 | if (body.match(/^Stopping all processes with SIGTERM$/))
|
115 | return color_1.default.red(body);
|
116 | let starting = body.match(/^(Starting process with command )(`.+`)(by user )?(.*)?$/);
|
117 | if (starting) {
|
118 | return [
|
119 | starting[1],
|
120 | color_1.default.cmd(starting[2]),
|
121 | starting[3] || '',
|
122 | color_1.default.green(starting[4] || '')
|
123 | ].join('');
|
124 | }
|
125 | let stateChange = body.match(/^(State changed from )(\w+)( to )(\w+)$/);
|
126 | if (stateChange) {
|
127 | return [
|
128 | stateChange[1],
|
129 | state(stateChange[2]),
|
130 | stateChange[3] || '',
|
131 | state(stateChange[4] || '')
|
132 | ].join('');
|
133 | }
|
134 | let exited = body.match(/^(Process exited with status )(\d+)$/);
|
135 | if (exited) {
|
136 | return [
|
137 | exited[1],
|
138 | exited[2] === '0' ? color_1.default.greenBright(exited[2]) : color_1.default.red(exited[2])
|
139 | ].join('');
|
140 | }
|
141 | }
|
142 | catch (err) {
|
143 | cli_ux_1.default.warn(err);
|
144 | }
|
145 | return body;
|
146 | }
|
147 | function colorizeWeb(body) {
|
148 | try {
|
149 | if (body.match(/^Unidling$/))
|
150 | return color_1.default.yellow(body);
|
151 | if (body.match(/^Restarting$/))
|
152 | return color_1.default.yellow(body);
|
153 | if (body.match(/^Stopping all processes with SIGTERM$/))
|
154 | return color_1.default.red(body);
|
155 | let starting = body.match(/^(Starting process with command )(`.+`)(by user )?(.*)?$/);
|
156 | if (starting) {
|
157 | return [
|
158 | (starting[1]),
|
159 | color_1.default.cmd(starting[2]),
|
160 | (starting[3] || ''),
|
161 | color_1.default.green(starting[4] || '')
|
162 | ].join('');
|
163 | }
|
164 | let exited = body.match(/^(Process exited with status )(\d+)$/);
|
165 | if (exited) {
|
166 | return [
|
167 | exited[1],
|
168 | exited[2] === '0' ? color_1.default.greenBright(exited[2]) : color_1.default.red(exited[2])
|
169 | ].join('');
|
170 | }
|
171 | let stateChange = body.match(/^(State changed from )(\w+)( to )(\w+)$/);
|
172 | if (stateChange) {
|
173 | return [
|
174 | stateChange[1],
|
175 | state(stateChange[2]),
|
176 | stateChange[3],
|
177 | state(stateChange[4])
|
178 | ].join('');
|
179 | }
|
180 | let apache = body.match(/^(\d+\.\d+\.\d+\.\d+ -[^-]*- \[[^\]]+] ")(\w+)( )([^ ]+)( HTTP\/\d+\.\d+" )(\d+)( .+$)/);
|
181 | if (apache) {
|
182 | const [, ...tokens] = apache;
|
183 | return [
|
184 | other(tokens[0]),
|
185 | method(tokens[1]),
|
186 | other(tokens[2]),
|
187 | path(tokens[3]),
|
188 | other(tokens[4]),
|
189 | status(tokens[5]),
|
190 | other(tokens[6])
|
191 | ].join('');
|
192 | }
|
193 | let route = body.match(/^(.* ")(\w+)(.+)(HTTP\/\d+\.\d+" .*)$/);
|
194 | if (route) {
|
195 | return [
|
196 | route[1],
|
197 | method(route[2]),
|
198 | path(route[3]),
|
199 | route[4]
|
200 | ].join('');
|
201 | }
|
202 | }
|
203 | catch (err) {
|
204 | cli_ux_1.default.warn(err);
|
205 | }
|
206 | return body;
|
207 | }
|
208 | function colorizeAPI(body) {
|
209 | if (body.match(/^Build succeeded$/))
|
210 | return color_1.default.greenBright(body);
|
211 | if (body.match(/^Build failed/))
|
212 | return color_1.default.red(body);
|
213 | const build = body.match(/^(Build started by user )(.+)$/);
|
214 | if (build) {
|
215 | return [
|
216 | build[1],
|
217 | color_1.default.green(build[2])
|
218 | ].join('');
|
219 | }
|
220 | const deploy = body.match(/^(Deploy )([\w]+)( by user )(.+)$/);
|
221 | if (deploy) {
|
222 | return [
|
223 | deploy[1],
|
224 | color_1.default.cyan(deploy[2]),
|
225 | deploy[3],
|
226 | color_1.default.green(deploy[4])
|
227 | ].join('');
|
228 | }
|
229 | const release = body.match(/^(Release )(v[\d]+)( created by user )(.+)$/);
|
230 | if (release) {
|
231 | return [
|
232 | release[1],
|
233 | color_1.default.magenta(release[2]),
|
234 | release[3],
|
235 | color_1.default.green(release[4])
|
236 | ].join('');
|
237 | }
|
238 | let starting = body.match(/^(Starting process with command )(`.+`)(by user )?(.*)?$/);
|
239 | if (starting) {
|
240 | return [
|
241 | (starting[1]),
|
242 | color_1.default.cmd(starting[2]),
|
243 | (starting[3] || ''),
|
244 | color_1.default.green(starting[4] || '')
|
245 | ].join('');
|
246 | }
|
247 | return body;
|
248 | }
|
249 | function colorizeRedis(body) {
|
250 | if (body.match(/source=\w+ sample#/)) {
|
251 | body = dim(body);
|
252 | }
|
253 | return body;
|
254 | }
|
255 | function colorizePG(body) {
|
256 | let create = body.match(/^(\[DATABASE].*)(CREATE TABLE)(.*)$/);
|
257 | if (create) {
|
258 | return [
|
259 | other(create[1]),
|
260 | color_1.default.magenta(create[2]),
|
261 | color_1.default.cyan(create[3])
|
262 | ].join('');
|
263 | }
|
264 | if (body.match(/source=\w+ sample#/)) {
|
265 | body = dim(body);
|
266 | }
|
267 | return body;
|
268 | }
|
269 | function colorize(line) {
|
270 | if (process.env.HEROKU_LOGS_COLOR === '0')
|
271 | return line;
|
272 | let parsed = line.match(lineRegex);
|
273 | if (!parsed)
|
274 | return line;
|
275 | let header = parsed[1];
|
276 | let identifier = parsed[2];
|
277 | let body = (parsed[4] || '').trim();
|
278 | switch (identifier) {
|
279 | case 'api':
|
280 | body = colorizeAPI(body);
|
281 | break;
|
282 | case 'router':
|
283 | body = colorizeRouter(body);
|
284 | break;
|
285 | case 'run':
|
286 | body = colorizeRun(body);
|
287 | break;
|
288 | case 'web':
|
289 | body = colorizeWeb(body);
|
290 | break;
|
291 | case 'heroku-redis':
|
292 | body = colorizeRedis(body);
|
293 | break;
|
294 | case 'heroku-postgres':
|
295 | case 'postgres':
|
296 | body = colorizePG(body);
|
297 | }
|
298 | return getColorForIdentifier(identifier)(header) + ' ' + body;
|
299 | }
|
300 | exports.default = colorize;
|