Merge pull request #1174 from CartoDB/dgaubert/ch78384/maps-api-replace-log4js-logger-by-pino-bis-bis
Create log-collector utililityremotes/origin/dgaubert/ch78384/maps-api-replace-log4js-logger-by-pino
commit
568e428a58
@ -0,0 +1,15 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function tag ({ tags }) {
|
||||
if (!Array.isArray(tags) || !tags.every((tag) => typeof tag === 'string')) {
|
||||
throw new Error('Required "tags" option must be a valid Array: [string, string, ...]');
|
||||
}
|
||||
|
||||
return function tagMiddleware (req, res, next) {
|
||||
const { logger } = res.locals;
|
||||
res.locals.tags = tags;
|
||||
res.on('finish', () => logger.info({ tags: res.locals.tags }));
|
||||
|
||||
next();
|
||||
};
|
||||
};
|
@ -0,0 +1,11 @@
|
||||
'use strict';
|
||||
|
||||
const split = require('split2');
|
||||
const logCollector = require('./log-collector');
|
||||
const metricsCollector = require('./metrics-collector');
|
||||
|
||||
process.stdin
|
||||
.pipe(split())
|
||||
.pipe(logCollector())
|
||||
.pipe(metricsCollector())
|
||||
.pipe(process.stdout);
|
@ -0,0 +1,78 @@
|
||||
'use strict'
|
||||
|
||||
const split = require('split2');
|
||||
const assingDeep = require('assign-deep');
|
||||
const { Transform } = require('stream');
|
||||
const DEV_ENVS = ['test', 'development'];
|
||||
const logs = new Map();
|
||||
|
||||
const LEVELS = {
|
||||
10: 'trace',
|
||||
20: 'debug',
|
||||
30: 'info',
|
||||
40: 'warn',
|
||||
50: 'error',
|
||||
60: 'fatal'
|
||||
}
|
||||
|
||||
module.exports = function logCollector () {
|
||||
return new Transform({
|
||||
transform (chunk, enc, callback) {
|
||||
let entry;
|
||||
|
||||
try {
|
||||
entry = JSON.parse(chunk);
|
||||
const { level, time } = entry;
|
||||
|
||||
if (level === undefined && time === undefined) {
|
||||
throw new Error('Entry log is not valid');
|
||||
}
|
||||
} catch (e) {
|
||||
if (DEV_ENVS.includes(process.env.NODE_ENV)) {
|
||||
this.push(chunk + '\n');
|
||||
}
|
||||
return callback();
|
||||
}
|
||||
|
||||
const { id, end } = entry;
|
||||
|
||||
if (id === undefined) {
|
||||
entry.level = LEVELS[entry.level];
|
||||
this.push(`${JSON.stringify(entry)}\n`);
|
||||
return callback();
|
||||
}
|
||||
|
||||
if (end === true) {
|
||||
const accEntry = logs.get(id);
|
||||
accEntry.level = LEVELS[accEntry.level];
|
||||
accEntry.time = entry.time;
|
||||
this.push(`${JSON.stringify(accEntry)}\n`);
|
||||
logs.delete(id);
|
||||
return callback();
|
||||
}
|
||||
|
||||
if (logs.has(id)) {
|
||||
const accEntry = logs.get(id);
|
||||
|
||||
if (accEntry.level > entry.level) {
|
||||
delete entry.level
|
||||
}
|
||||
|
||||
let error;
|
||||
if (hasProperty(accEntry, 'error') && hasProperty(entry, 'error')) {
|
||||
logs.set(id, assingDeep({}, accEntry, entry, { error: accEntry.error.concat(entry.error) }));
|
||||
} else {
|
||||
logs.set(id, assingDeep({}, accEntry, entry));
|
||||
}
|
||||
} else {
|
||||
logs.set(id, entry);
|
||||
}
|
||||
|
||||
callback();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function hasProperty(obj, prop) {
|
||||
return Object.prototype.hasOwnProperty.call(obj, prop)
|
||||
}
|
@ -0,0 +1,119 @@
|
||||
'use strict'
|
||||
|
||||
const http = require('http');
|
||||
const { Counter, Histogram, register } = require('prom-client');
|
||||
const split = require('split2');
|
||||
const { Transform } = require('stream');
|
||||
const DEV_ENVS = ['test', 'development'];
|
||||
|
||||
const requestCounter = new Counter({
|
||||
name: 'maps_api_requests_total',
|
||||
help: 'MAPS API requests total'
|
||||
});
|
||||
|
||||
const requestOkCounter = new Counter({
|
||||
name: 'maps_api_requests_ok_total',
|
||||
help: 'MAPS API requests ok total'
|
||||
});
|
||||
|
||||
const requestErrorCounter = new Counter({
|
||||
name: 'maps_api_requests_errors_total',
|
||||
help: 'MAPS API requests errors total'
|
||||
});
|
||||
|
||||
const responseTimeHistogram = new Histogram({
|
||||
name: 'maps_api_response_time_total',
|
||||
help: 'MAPS API response time total'
|
||||
});
|
||||
|
||||
const userRequestCounter = new Counter({
|
||||
name: 'maps_api_requests',
|
||||
help: 'MAPS API requests per user',
|
||||
labelNames: ['user', 'http_code']
|
||||
});
|
||||
|
||||
const userRequestOkCounter = new Counter({
|
||||
name: 'maps_api_requests_ok',
|
||||
help: 'MAPS API requests per user with success HTTP code',
|
||||
labelNames: ['user', 'http_code']
|
||||
});
|
||||
|
||||
const userRequestErrorCounter = new Counter({
|
||||
name: 'maps_api_requests_errors',
|
||||
help: 'MAPS API requests per user with error HTTP code',
|
||||
labelNames: ['user', 'http_code']
|
||||
});
|
||||
|
||||
const userResponseTimeHistogram = new Histogram({
|
||||
name: 'maps_api_response_time',
|
||||
help: 'MAPS API response time total',
|
||||
labelNames: ['user']
|
||||
});
|
||||
|
||||
module.exports = function metricsCollector () {
|
||||
return new Transform({
|
||||
transform (chunk, enc, callback) {
|
||||
let entry;
|
||||
|
||||
try {
|
||||
entry = JSON.parse(chunk);
|
||||
const { level, time } = entry;
|
||||
|
||||
if (level === undefined && time === undefined) {
|
||||
throw new Error('Entry log is not valid');
|
||||
}
|
||||
} catch (e) {
|
||||
if (DEV_ENVS.includes(process.env.NODE_ENV)) {
|
||||
this.push(chunk);
|
||||
}
|
||||
return callback();
|
||||
}
|
||||
|
||||
const { request, response, stats } = entry;
|
||||
|
||||
if (request === undefined || response === undefined || stats === undefined) {
|
||||
this.push(chunk);
|
||||
return callback();
|
||||
}
|
||||
|
||||
const { statusCode, headers } = response;
|
||||
const { 'carto-user': user } = headers;
|
||||
|
||||
if (statusCode === undefined || headers === undefined || user === undefined) {
|
||||
this.push(chunk);
|
||||
return callback();
|
||||
}
|
||||
|
||||
requestCounter.inc();
|
||||
userRequestCounter.labels(user, `${statusCode}`).inc();
|
||||
|
||||
if (statusCode >= 200 && statusCode < 400) {
|
||||
requestOkCounter.inc();
|
||||
userRequestOkCounter.labels(user, `${statusCode}`).inc();
|
||||
} else {
|
||||
requestErrorCounter.inc();
|
||||
userRequestErrorCounter.labels(user, `${statusCode}`).inc();
|
||||
}
|
||||
|
||||
const { response: responseTime } = stats;
|
||||
|
||||
if (Number.isFinite(responseTime)) {
|
||||
responseTimeHistogram.observe(responseTime);
|
||||
userResponseTimeHistogram.labels(user).observe(responseTime);
|
||||
}
|
||||
|
||||
this.push(chunk);
|
||||
callback();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const port = process.env.PORT || 9145;
|
||||
|
||||
http
|
||||
.createServer((req, res) => {
|
||||
res.writeHead(200, { 'Content-Type': register.contentType });
|
||||
res.end(register.metrics());
|
||||
})
|
||||
.listen(port)
|
||||
.unref();
|
Loading…
Reference in new issue