Coder Social home page Coder Social logo

laabr's Introduction

laabr logo

well-formatted, extendable pino logger for hapi.js

Booyah! Works like a charm.

Marcus Pöhls

Travis node npm standard npm Coverage Status

  1. Introduction
  2. Installation
  3. Usage
  4. API ⇗
  5. Tokens ⇗
  6. Formats ⇗
  7. Presets ⇗
  8. Example
  9. Developing and Testing
  10. Contribution

Introduction

laabr is a well-formatted pino ⇗ logger for hapi.js ⇗ which is based on the plugin hapi-pino ⇗. It enables optionally to log in JSON for easy post-processing. It listens to various hapi.js events ⇗ and logs in a well-formatted manner. Therefor it is possible to define custom formats alike the morgan ⇗ ones or make use of available presets. Additionally it enables to define own tokens which could be used in custom formats. laabr is the Swabian translation for talking.

The modules standard and ava are used to grant a high quality implementation.

Compatibility

Major Release hapi.js version hapi-pino version node version
v6 >=18.4 @hapi/hapi >= 6.3 >=12
v5.1 >=18.3.1 @hapi/hapi >= 5.4 >=8
v5 >=18 hapi >= 5.4 >=8
v4 >=17 hapi >= 5.1 >=8
v3 >=17 hapi >= 3 >=8
v2 >=13 hapi >= 1.6 >=6

laabr vs. hapi-pino

First of all laabr extends the hapi-pino plugin. So it is possible to use laabr in an almost identical manner like hapi-pino. This plugin provides further features which probably decelerates the logging a bit, but it should be faster than the alternatives anyway. The following features are provided:

  • Easy out of the box usage
  • Context-sensitive colorization
  • Customizable identation for JSON strings
  • Wide range of preset tokens ⇗ to extract and compose data as needed
  • Preset formats ⇗ combining useful tokens for an easy start
  • Possibility to add own format presets ⇗ for an easy reuse
  • Easily customizable tokens & formats
  • Override several console logging methods
  • In despite of everything it is possible to preformat ⇗ & postformat ⇗ data, e.g. to filter sensitive data

laabr screen

Installation

For installation use the npm ⇗:

$ npm install --save laabr

or clone the repository:

$ git clone https://github.com/felixheck/laabr

Usage

Import

First you have to import the module:

const laabr = require('laabr');

Create hapi server

Afterwards create your hapi server if not already done:

const hapi = require('@hapi/hapi');
const server = hapi.server({
  port: 8888,
  host: 'localhost',
});

Registration

Finally register the plugin and set the correct options:

await server.register({
  plugin: laabr,
  options: {},
});

Example

Take a look at several more examples ⇗.

Code

const hapi = require('@hapi/hapi');
const laabr = require('laabr');

const server = hapi.server({ port: 3000 });

const options = {
  formats: { onPostStart: ':time :start :level :message' },
  tokens: { start:  () => '[start]' },
  indent: 0
};

server.route([
  {
    method: '*',
    path: '/response',
    handler() {
      return 'hello world';
    }
  },
  {
    method: 'GET',
    path: '/error',
    handler () {
      throw new Error('foobar');
    }
  }
]);

(async () => {
  try {
    await server.register({
      plugin: laabr,
      options
    });
    await server.start();
    console.log('Server started successfully');
  } catch (err) {
    console.error(err);
  }
})();

server.log('info', 'did you mean "foobar"?');

Output

// (1) `log`
$ {"message":"did you mean \"foobar\"?","timestamp":1499352305938,"level":"info"}

// (2) `onPostStart`
$ 1499352305956 [start] info server started

// (3) `response` – calling `/response`
$ 1499352307927 GET 127.0.0.1 /response 200 {} (25 ms)

// (4) `request-error` & `response` – calling `/error`
$ {"error":"foobar","timestamp":1499352320071,"level":"warn"}
$ 1499352320072 GET 127.0.0.1 /error 500 {} (3 ms)

// (5) `onPostStop` – Pressing `Ctrl + C`
$ 1499352325077 info server stopped

Developing and Testing

First you have to install all dependencies:

$ npm install

To execute all unit tests once, use:

$ npm test

or to run tests based on file watcher, use:

$ npm start

To get information about the test coverage, use:

$ npm run coverage

Contribution

Fork this repository and push in your ideas.

Do not forget to add corresponding tests to keep up 100% test coverage.
For further information read the contributing guideline.

laabr's People

Contributors

adrivanhoudt avatar dependabot-preview[bot] avatar felixheck avatar jedrus2000 avatar tmbarkve avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

laabr's Issues

Response header fields not logged

First, thanks for this great project !

Hapi: 18.4.0
laabr: 5.1.2

Only - you see as output on any response header field. I.e.: :res[cache-control]

After debugging I've found that it is because data.res.header is undefined.
Hapi has headers object in response, so using similar code as in req, solves issue.

I will do PR with this.

How to apply custom format with color?

I have custom format option like this:

formats: {
    log: ':time[iso], :level: :message'
}

how i can apply color with custom format? like, how to change time color from grey to red?

Unable to specify pino options

Thanks for this great project. When using v6.0.2 of laabr, I am able to specify level option for pino.

{
  plugin: require( "laabr" ),
  options: {
    pino: {
      level: "debug"
    }
  }
}

but when using v6.1.0 I get a validation error

Error [ValidationError]: "pino.level" is not allowed

Is this a regression or intended to be more restrictive? or am I missing something? Here is the commit that seems to have removed the functionality. d3e9ae4

We need to be able to specify the log level so that we can silence the logging that occurs when running our integration tests.

Correlation Id Token

I am unable to get the correlation id token to display in my logs. Instead it is just outputting: -

"laabr": "^2.2.2",

My Config:

    laabr.format('response', ':cid - :req[x-request-id] - :time[utc], :tags :req[host] :method :url :status (:responseTime)');

    server.register({
      register: laabr.plugin,
      options: {
        colored: true,
        correlator: {
          enabled: true,
          header: 'x-request-id'
        },
        pino: {
          level: 'trace'
        },
        hapiPino: {
          logPayload: true,
          mergeHapiLogData: true
        }
      }
    });

Logger Output:
- - ThisIsTheRequestId - Tue, 18 Jul 2017 17:29:53 GMT, - localhost:22001 GET /408 200 (618)

URL no longer appearing in log output in version 6+

The Problem:

Whenever I hit the api from any route it logs out like this:

Screen Shot 2020-10-22 at 10 47 10 AM

The dash is in the place the url should be, this was working in version 5 so something must have caused this issue in version 6+

Is it possible to limit the payload tag?

Hi.
I'm curious if is possible to limit the size of logged payload? I typically wanna log the payload but when I get these chonkers I wanna limit the payload to some arbitrary number. Is that possible?

how to use laabr directly on another file?

hi, when using pino, i can use it directly in another file as below:

import pino from 'pino';
export const logger = pino({level: 'info'});

how can i do the same with laabr?

Problems when logging on AWS CloudWatch

Hello,
based on laabr and hapi-pino options I came up with this config:

export const laabrOptions = {
  formats: {
    log: ':time[iso], :level, :message',
    response: ':time[iso], :level, :method, :url, :status, :payload, bm-request-token: :req[request-token], (:responseTime ms)',
    'request-error': ':time[iso], :level, :method, :url, :payload, :error[output.statusCode], :error, :error[stack]',
    onPostStart: ':time[iso], :level, :message',
    onPostStop: ':time[iso], :level, :message',
  },
  indent: 0,
  colored: true,
  pino: {
    level: 'debug',
  },
  hapiPino: {
    prettyPrint: process.env.NODE_ENV !== 'production',
    mergeHapiLogData: true,
    ignorePaths: ['/healthcheck', '/favicon.ico'],
    ignoreFunc: (options, request) => request.path.startsWith('/assets'),
    redact: ['req.headers.authorization'],
    logPayload: true,
  },
};

Then I register it in hapi
await server.register({ plugin: laabr, options: laabrOptions });
and well -> locally it looks great
2020-12-11T10:22:33.467Z, info, GET, /healthcheck, 200, {}, request-token: xaxaxa, (5 ms)

when we run this on aws cloudwatch what is see is this:

�[90m2020-12-11T10:11:44.144Z�[39m, �[32minfo�[39m, �[32mGET�[39m, /healthcheck, �[32m200�[39m, 
{}
, bm-request-token: -, (1 ms)

Note that id does log healthcheck path, would you have any idea what am I doing wrong here?

TypeError: Converting circular structure to JSON

Issue when using laabr with hapipal/hecks because of JSON.stringify(data.payload). I am trying to implement bull-arena or bull-board in hapi since I am using bull as a worker. When I attempt to retry a failing job, I get this circular structure error. This is completely crashing my worker.

➜  laabr-circ-ref node index.js
1571340150312 info server started at: http://iesous:3000
(node:36683) UnhandledPromiseRejectionWarning: TypeError: Converting circular structure to JSON
    at JSON.stringify (<anonymous>)
    at Object.data [as payload] (/usr/app/issues/laabr-circ-ref/node_modules/laabr/src/tokens.js:124:8)
    at eval (eval at format.replace (/usr/app/issues/laabr-circ-ref/node_modules/laabr/src/logger.js:48:23), <anonymous>:1:72)
    at format.replace (/usr/app/issues/laabr-circ-ref/node_modules/laabr/src/logger.js:48:23)
    at String.replace (<anonymous>)
    at compile (/usr/app/issues/laabr-circ-ref/node_modules/laabr/src/logger.js:39:21)
    at /usr/app/issues/laabr-circ-ref/node_modules/laabr/src/logger.js:84:25
    at Object.write (/usr/app/issues/laabr-circ-ref/node_modules/pino/lib/tools.js:237:25)
    at Pino.write (/usr/app/issues/laabr-circ-ref/node_modules/pino/lib/proto.js:153:15)
    at Pino.LOG (/usr/app/issues/laabr-circ-ref/node_modules/pino/lib/tools.js:36:21)
(node:36683) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 4)
(node:36683) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
1571340153978 GET 127.0.0.1 http://localhost:3000/queues/?test=failed 200 {} (17 ms)
1571340156102 GET 127.0.0.1 http://localhost:3000/queues/?test=failed 200 {} (7 ms)

Steps to reproduce:

mkdir laabrtest
cd laabrtest
npm i @hapi/hapi bull bull-board hecks laabr
touch index.js

Then, in index.js paste in this:

const bull = require('bull');
const hapi = require('@hapi/hapi');
const laabr = require('laabr');
const hecks = require('hecks');
const express = require('express');
const { setQueues, UI } = require('bull-board');

const server = hapi.Server({ port: 3000 });

const queue = new bull('test');

// queue always throws error so we can retry
queue.process(() => {

    throw Error('there was an error');
});

const app = express();
app.use('/', UI);

(async () => {

    setQueues([queue]);

    await server.register([
        laabr,
        hecks
    ]);

    server.route({
        method: '*',
        path: '/{expressPath*}',
        handler: {
            express: app
        }
    });

    await server.start();

    queue.add({ thisthing: 'should break' });
})();

Start your server and go to http://localhost:3000
Go to your failed jobs and retry.
You should get the error I paste above.

Log call without tags shows -

Log with tags:

2018-07-23T12:12:41.407Z [info] ["INIT"] Server running at: http://localhost:3000

Log without tags:

2018-07-23T12:12:41.403Z [info] - server started

Would look cleaner to not show anything in case of no tags or an empty array

Redact option is not usable

Hello thanks for this library. It is really useful.

After working a while with it, I wanted to use the redact feature of pino to remove some sensitive information from the loglines. Unfortunately I found an unwanted behavior.

In this sandbox I've added a minimal example where I both defined the redact property in the pino and hapiPino property of the laabr options. The property in the hapiPino setting is completely ignored, as the Pino instance is created inside the logger.js without passing the options to Pino. When hapi-pino is initialized, it reuses the pino instance, but is unable to inject the redact settings, as they are used at the construction of Pino.

When the property in the pino setting is enabled, an exception is thrown. This results from the redact function which returns a string, this is then passed to the prettifier function in laabr. At the validation step for the preformatter an error is thrown, as it expects an Object.

I hope this helps to find a solution for this bug.

Adding pre from request object

Hapijs has this feature called preHandler that allows us to add some handler before router handlers (like middlewares as plugin from express) but data from this preHandler are not in req object in laabr context.

Could it be possible to add it so it can be used inside preFormatter or tokens definition?

Missing output of console.error when using override: true

Maybe it is me, not laabr but I've such issue - with laabr option override: true, when i.e. TypeError occurs and is caught, then reported with console.error(error), I see only :

image

My stack is :

Node v10 with Docker (node:10) : https://hub.docker.com/_/node/
hapi: 18.3.1,
laabr: 5.1.6
nodemon: 1.19.4

It started to annoy me more and more, and if error was happening, I had to start server with override set to false.
I looked into source code how console is implemented and found this :

https://github.com/whatwg/console/blob/f276b49ad99c815a17a86b6c6cbc8ca842eb69d3/reference-implementation/Logger.js#L29

so I went to my ❤️ laabr and this

server.log(level, data.length === 1 ? data[0] : data)

I've changed into :

server.log(level, util.format(data))

( with const util = require('util') at source file beginning. )

So after this output looks that :
image

My laabr options are :

      options: {
        override: true, // Override several console logging methods
        formats: {
          log: ':time[iso], :level, :message',
          request: ':time[iso], :level, :message',
          response: ':time[iso], :level, :method, :url, :status, :payload, (:responseTime ms)',
          'request-error': ':time[iso], :level, :method, :url, :error[output.statusCode], :error, :error[stack]',
          onPostStart: ':time[iso], :level, :message',
          onPostStop: ':time[iso], :level, :message',
          uncaught: ':time[iso], :level, :error, :error[stack]'
        },
        pino: { level: 'info' },
        indent: 0,
        colored: true
    }

I must say that I cannot exactly reproduce it at my local Linux (using i.e. Node 14, latest laabr). So with my laabr options, using simple.js I've got this :

image

With my modification in laabr/src/utils.js it looks like this :
image

I think is more readable. @felixheck What do you think ?

logQueryParams is not allowed

version 6.1.3
The option logQueryParams is disabled in the joi validation, and this makes it impossible to access the query parameters in the logger. (For my specific use case this makes the library unusable). It is easy enough to allow it, I think. Is there any specific reason why this has been disabled?

no query params, no message

When I use curl to make request to hapi I am getting this information:

curl -X DELETE -H "Authorization: Bearer $TOKEN" "http://localhost:9000/api/foo?blub=hufnefal"
{"statusCode":400,"error":"Bad Request","message":"failed to delete"}% 

The information I am after in the logs are

  1. 400
  2. blub=hufnefal
  3. failed to delete

But I am somehow not getting that information with

        {
          plugin: Laabr,
          options: {
            colored: true,
            formats: {
              response: ':time :method :url :status :payload (:responseTime ms)'
            }
          }
        }

and

  "dependencies": {
    ...
    "@hapi/hapi": "21.3.2",
    "joi": "17.11.0",
    "laabr": "6.1.3",
    ...
  },

I tried specifying message and I also tried specifying logQueryParams as suggested in #104
I would have thought this should do the trick:

        {
          plugin: Laabr,
          options: {
            colored: true,
            hapiPino: { logQueryParams: true },
            formats: {
              response: ':time :method :url :status :payload (:responseTime ms) :message'
            }
          }
        }

What am I missing?

Register using Glue

I can't seem to make it work when I compose my server using Glue from a config json.

I get Error: Invalid plugin options and "register" must be a Function if I try to register using "plugin": "laabr" and Cannot find module 'laabr.plugin' if I try with "plugin": "laabr.plugin"

If I add module.exports.register = plugin to your plugin it works as expected. I guess Glue expects a register function.

not able to disable payload

I'm using this package in hapi js and i wanted to disable the payload log when it's a huge payload and log the error when there is any error but there is no option for this

joi as a peer dependency

Any chance to have joi as a peer dependency?

I already had a (more recent) version of joi in my package and suddenly I started getting this error:
Error: Cannot mix different versions of joi schemas

Removing laabr fixed the issue but I think it would be good to have joi as a peer dependency to avoid this errors and let the developer to pick the version he wants to use.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.