pino-to-ecs
Version:
Convert Pino logs to Elastic Common Schema
239 lines (224 loc) • 6.15 kB
JavaScript
'use strict'
const { test } = require('tap')
const Pino = require('pino')
const { Transform } = require('readable-stream')
const split = require('split2')
const toEcs = require('./index')
const { pinoToEcs } = toEcs
test('toEcs', t => {
t.test('Should parse a basic log', t => {
const incomingPost = {
random: false,
level: 30,
time: 1561122678406,
pid: 65354,
hostname: 'Skadi.local',
reqId: 1,
req: {
other: true,
method: 'POST',
url: '/body',
headers: {
host: 'localhost:3000',
'user-agent': 'curl/7.54.0',
accept: '*/*',
'content-type': 'application/json',
'content-length': '13'
},
hostname: 'localhost:3000',
remoteAddress: '127.0.0.1',
remotePort: 62061
},
msg: 'incoming request',
v: 1
}
t.deepEqual(toEcs(incomingPost), {
ecs: { version: '1.0.0' },
event: { id: 1 },
'@timestamp': 1561122678406,
message: 'incoming request',
log: { level: 30 },
host: { hostname: 'Skadi.local' },
process: { pid: 65354 },
http: {
request: {
method: 'POST',
body: { bytes: 13 },
headers: {
host: 'localhost:3000',
accept: '*/*',
'content-type': 'application/json'
}
}
},
url: {
path: '/body',
domain: 'localhost',
port: 3000
},
client: { address: '127.0.0.1', port: 62061 },
user_agent: { original: 'curl/7.54.0' },
pino: {
random: false,
req: { other: true }
}
})
t.end()
})
t.test('Should parse a basic error log', t => {
const stack = 'Error: kaboom\n at Object.fastify.get (/pino-to-ecs/examples/fastify.js:42:9)\n at preHandlerCallback (/pino-to-ecs/node_modules/fastify/lib/handleRequest.js:112:30)\n at preValidationCallback (/pino-to-ecs/node_modules/fastify/lib/handleRequest.js:101:5)\n at handler (/pino-to-ecs/node_modules/fastify/lib/handleRequest.js:70:5)\n at handleRequest (/pino-to-ecs/node_modules/fastify/lib/handleRequest.js:19:5)\n at onRunMiddlewares (/pino-to-ecs/node_modules/fastify/lib/middleware.js:22:5)\n at middlewareCallback (/pino-to-ecs/node_modules/fastify/lib/route.js:354:5)\n at next (/pino-to-ecs/node_modules/fastify/lib/hooks.js:66:7)\n at handleResolve (/pino-to-ecs/node_modules/fastify/lib/hooks.js:77:5)\n at process._tickCallback (internal/process/next_tick.js:68:7)'
const incomingError = {
level: 50,
time: 1561123426199,
pid: 70256,
hostname: 'Skadi.local',
reqId: 1,
req: {
method: 'GET',
url: '/error',
headers: {
host: 'localhost:3000',
'user-agent': 'curl/7.54.0',
accept: '*/*'
},
hostname: 'localhost:3000',
remoteAddress: '127.0.0.1',
remotePort: 62109
},
res: { statusCode: 500 },
err: {
type: 'Error',
message: 'kaboom',
stack
},
msg: 'kaboom',
v: 1
}
t.deepEqual(toEcs(incomingError), {
ecs: { version: '1.0.0' },
event: { id: 1 },
error: {
code: 'Error',
message: 'kaboom',
stack
},
'@timestamp': 1561123426199,
message: 'kaboom',
log: { level: 50 },
host: { hostname: 'Skadi.local' },
process: { pid: 70256 },
http: {
request: {
method: 'GET',
headers: {
host: 'localhost:3000',
accept: '*/*'
}
},
response: {
status_code: 500
}
},
url: {
path: '/error',
domain: 'localhost',
port: 3000
},
client: { address: '127.0.0.1', port: 62109 },
user_agent: { original: 'curl/7.54.0' }
})
t.end()
})
t.test('Response log', t => {
const outgoingResponse = {
random: false,
level: 30,
time: 1561124998358,
pid: 73235,
hostname: 'Skadi.local',
req: {
id: 1,
method: 'GET',
url: '/hello',
headers: {
host: 'localhost:3000',
'user-agent': 'curl/7.54.0',
accept: '*/*'
},
remoteAddress: '::1',
remotePort: 62385
},
res: {
statusCode: 200,
headers: {
'content-type': 'text/plain',
'content-length': 12
},
other: true
},
responseTime: 5,
msg: 'request completed',
v: 1
}
t.deepEqual(toEcs(outgoingResponse), {
ecs: { version: '1.0.0' },
event: {
id: 1,
duration: 5000000
},
'@timestamp': 1561124998358,
message: 'request completed',
log: { level: 30 },
host: { hostname: 'Skadi.local' },
process: { pid: 73235 },
http: {
request: {
method: 'GET',
headers: {
host: 'localhost:3000',
accept: '*/*'
}
},
response: {
status_code: 200,
body: { bytes: 12 },
headers: { 'content-type': 'text/plain' }
}
},
url: { path: '/hello' },
client: { address: '::1', port: 62385 },
user_agent: { original: 'curl/7.54.0' },
pino: {
random: false,
res: { other: true }
}
})
t.end()
})
t.end()
})
test('pinoToEcs', t => {
t.test('Should parse a log stream', t => {
t.plan(4)
const stdin = new Transform({
transform (chunk, encoding, callback) {
callback(null, chunk)
}
})
const stdout = split(line => JSON.parse(line))
const pino = Pino(stdin)
pinoToEcs(stdin, stdout)
stdout.once('data', data => {
t.type(data['@timestamp'], 'number')
t.type(data.host.hostname, 'string')
t.type(data.process.pid, 'number')
t.match(data, {
ecs: { version: '1.0.0' },
message: 'hello world',
log: { level: 30 }
})
})
pino.info('hello world')
})
t.end()
})