cabinjs/cabin

Any way to return "msg" (in prod) as JSON?

armenr opened this issue · 6 comments

Hey @niftylettuce!

We're using DataDog, and it's friendly to "natural JSON" a la pino.

I've read the documentation and understand that whatever is returned @ the msg property has to be a string, and that it complies with the apache-friendly log RFCs, because you're a man of taste and experience.

BUT, is there any way to get JSON back? Or maybe have the choice between apache-friendly and JSON?

Thanks for all your work, man!
_Armen

Can you give me an example / code snippet of what you're trying to do and the expected vs. actual output? I would definitely accept a PR for sure man or fix this myself for you. Just need to know specifically what you mean

Thanks dude!

Example prod log entry sent to DataDog:

{
   "level":30,
   "time":1573656121209,
   "pid":12011,
   "hostname":"ip-10-0-12-50",
   "msg":"0.0.0.0 - - 13/Nov/2019:14:42:01 +0000 \"GET /static/favicon.ico HTTP/1.1\" 200 - {\"id\":\"5dcc163992279d2eeb055564\",\"timestamp\":\"2019-11-13T14:42:01.000Z\",\"request\":{\"method\":\"GET\",\"query\":{},\"headers\":{\"accept\":\"image/webp,image/apng,image/*,*/*;q=0.8\",\"accept-encoding\":\"gzip, deflate\",\"accept-language\":\"en-US,en;q=0.9\",\"cache-control\":\"no-cache\",\"connection\":\"keep-alive\",\"cookie\":\"dtCookie=v_4_srv_1_sn_83AA82EA4B50C9EE45C851779D892DC8_perc_100000_ol_0_mul_1\",\"host\":\"1.1.1.1:3000\",\"pragma\":\"no-cache\",\"referer\":\"http://1.1.1.1:3000/\",\"user-agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36\",\"x-dynatrace-application\":\"1%3B1%3Bea7c4b59f27d43eb\",\"x-dynatrace-origin-url\":\"http://1.1.1.1:3000/static/favicon.ico\",\"username\":\"NoAuth\"},\"cookies\":{\"dtCookie\":\"v_4_srv_1_sn_83AA82EA4B50C9EE45C851779D892DC8_perc_100000_ol_0_mul_1\"},\"url\":\"/static/favicon.ico\",\"timestamp\":\"2019-11-13T14:42:01.205Z\",\"id\":\"5d5ac094-9cfb-4789-abb6-cfc8178f85ad\",\"http_version\":\"1.1\"},\"user\":{\"ip_address\":\"0.0.0.0\"},\"response\":{\"headers\":{\"x-powered-by\":\"Good Vibes 1.0\",\"access-control-allow-origin\":\"*\",\"surrogate-control\":\"no-store\",\"cache-control\":\"no-store, no-cache, must-revalidate, proxy-revalidate\",\"pragma\":\"no-cache\",\"expires\":\"0\",\"x-content-type-options\":\"nosniff\",\"x-frame-options\":\"SAMEORIGIN\",\"x-xss-protection\":\"1; mode=block\",\"x-request-id\":\"5d5ac094-9cfb-4789-abb6-cfc8178f85ad\",\"x-request-username\":\"NoAuth\",\"accept-ranges\":\"bytes\",\"last-modified\":\"Mon, 11 Nov 2019 21:07:42 GMT\",\"etag\":\"W/\\\"1a47-16e5c4b3130\\\"\",\"content-type\":\"image/x-icon\",\"x-response-time\":\"1.256ms\",\"vary\":\"Accept-Encoding\",\"content-encoding\":\"gzip\",\"date\":\"Wed, 13 Nov 2019 14:42:01 GMT\",\"connection\":\"keep-alive\",\"transfer-encoding\":\"chunked\"},\"http_version\":\"1.1\",\"status_code\":200,\"reason_phrase\":\"OK\",\"timestamp\":\"2019-11-13T14:42:01.000Z\",\"duration\":1.256},\"duration\":0.534569,\"app\":{\"node\":\"v12.13.0\",\"environment\":\"production\",\"hostname\":\"ip-10-0-12-50\",\"pid\":12011}}",
   "v":1
}

Pino + CabinJS thangz:


const pinoProd = pino({
  customLevels: {
    log: 30,
  },
});

const pinoDev = pino({
  prettyPrint: {
    colorize: true,
  },
});

const cabin = new Cabin({
  capture: false,
  axe: {
    logger: NODE_ENV === 'production' ? pinoProd : pinoDev,
  },
});

Desired behavior:

In the log output, you'll notice that everything at the "msg" value is stringified JSON.

I'm wondering if there's a way to return all of this - in prod - as pure and total JSON, since tools like DataDog and GrayLog are friendly to JSON, and would automatically detect and extract the key/value pairs as labels/fields.

I know it's absolutely ANTITHETICAL to the concept of the apache log standards, but...I'm shamelessly asking :)

Have you tried specifying a custom message function? Not sure if that would fix it for you - let me know otherwise.

https://github.com/cabinjs/cabin#options
https://github.com/cabinjs/cabin/blob/master/src/message.js

I've read through that - not to be a nag or a baby - but I'm just trying to figure out (maybe with an example) what a custom function would look like?

I'm actually more confused now, since the code I see is this:

function apacheCommonLogFormat(options) {
  const { req, res, ctx } = options;

  const creds = auth(req);

  const startTime = getStartTime(req);

  return `${ctx ? ctx.ip : req.ip} - ${creds ? creds.name : '-'} ${clfDate(
    startTime
  )} "${req.method} ${req.url} HTTP/${req.httpVersionMajor}.${
    req.httpVersionMinor
  }" ${res.statusCode} ${res.getHeader('content-length') || '-'}`;
}

but it looks like I'm getting WAY more than that in my logs to DataDog...sorry for sounding illiterate.

That code returns a string as the message. If you want an object, you could do something like:

function message(options) {
  const { req, res, ctx } = options;
  const creds = auth(req);
  const startTime = getStartTime(req);

  return {
    ip: ctx ? ctx.ip : req.ip,
    creds: creds ? creds.name : '-',
    clf_date: clfDate(startTime),
    method: req.method,
    url: req.url,
    http_version: req.httpVersionMajor + '.' + req.httpVersionMinor,
    status_code: res.statusCode,
    content_length: res.getHeader('content-length') || '-'
  };
}

// ...

const cabin = new Cabin({
  capture: false,
  axe: {
    logger: NODE_ENV === 'production' ? pinoProd : pinoDev,
  },
  message // <--
});

Pino itself might be stringifying the message, I am unsure.