Create Logger Driver


#1

Can someone point me in the direction of how to properly create a Logger driver for use in Adonis? I have some existing Winston drivers (Airbrake, Greylog) that I want to use from a pre-existing project (Express app) but I am looking for an example of how to properly set them up in Adonis.

Going through the source it looks like the Drivers are loaded from /node_modules/@adonisjs/framework/src/Logger/Drivers/index.js so it is unclear to me how I can add my own custom Drivers that have new transports when I can not modify the above file.

Any guidance would be appreciated.


#2

Looks like I want to bind a new driver and transport via a hook. I have created start/hooks.js with the following code:

const { ioc } = require('@adonisjs/fold');
const { hooks } = require('@adonisjs/ignitor');
const Helpers = use('Helpers');
const Airbrake = require(`${Helpers.appRoot()}/lib/logging/drivers/Airbrake`);

hooks.before.providersRegistered(() => {
  ioc.extend('Adonis/Src/Logger', 'airbrake', function () { return Airbrake} );
});

However when I dump the _callHooks (lifecycle, event) method in Ignitor/index.js it does not show my hook at all.

Anyone spot what I am missing here?


Override Logger to log in Rollbar
#3

I am an idiot. I was attempting to use the IOC to load Helpers here when I should not be.

My hooks file ended up looking like this:

const { ioc } = require('@adonisjs/fold');
const { hooks } = require('@adonisjs/ignitor');
const Airbrake = require('../lib/logging/drivers/Airbrake');
hooks.before.providersRegistered(() => {
  ioc.extend('Adonis/Src/Logger', 'airbrake', function () { return Airbrake} );
});

In case anyone else see this and is curious what my driver file looks like, here it is before I clean it up (and probably wrap the Airbrake transport in a service provider or something). I know this code is ugly.

'use strict'
const _ = require('lodash');
const Winston = require('winston');
const airbrake = require('airbrake');

class AirbrakeTransport extends Winston.Transport {
  constructor(opts) {
    super(opts);

    const Env = use('Env');
    this.Airbrake = airbrake.createClient(Env.get('AIRBRAKE_PROJECT_ID'), Env.get('AIRBRAKE_API_KEY'));
    this.Airbrake.handleExceptions();
  }

  log(level = '', msg = '', meta = {}, callback) {
    let error = null;
    if (meta instanceof Error) {
      error = meta;
    } else {
      error = new Error(`${msg} | ${Object.keys(meta)
        .map((k) => `${k}=${JSON.stringify(meta[k])}`)
        .join(' | ')}`);
    }
    this.Airbrake.notify(error, (err) => {
      if (err) {
        return callback(err);
      }
      return callback();
    });
  }
};



/**
 * Winston Airbrake driver that will output to both Airbrake and the console
 * @class WinstonAirbrake
 * @constructor
 */
class WinstonAirbrake {
  setConfig(config) {
    /**
     * Merging user config with defaults.
     */
    this.config = Object.assign({}, {
      name: 'x-report-services',
      level: 'info',
      colorize: 'all',
      timestamp: new Date().toLocaleTimeString()
    }, config)

    /**
     * Creating new instance of winston with file transport
     */
    this.logger = new Winston.Logger({
      transports: [
        new AirbrakeTransport(this.config),
        new Winston.transports.Console({ ...this.config, name: 'console' })
      ]
    });

    /**
     * Updating winston levels with syslog standard levels.
     */
    this.logger.setLevels(this.levels)
  }

  /**
   * A list of available log levels
   *
   * @attribute levels
   *
   * @return {Object}
   */
  get levels() {
    return {
      emerg: 0,
      alert: 1,
      crit: 2,
      error: 3,
      warning: 4,
      notice: 5,
      info: 6,
      debug: 7
    }
  }

  /**
   * Returns the current level for the driver
   *
   * @attribute level
   *
   * @return {String}
   */
  get level() {
    return this.logger.transports[this.config.name].level
  }

  /**
   * Update driver log level at runtime
   *
   * @param  {String} level
   *
   * @return {void}
   */
  set level(level) {
    this.logger.transports[this.config.name].level = level
  }

  /**
   * Log message for a given level.
   *
   * @method log
   *
   * @param  {Number}    level
   * @param  {String}    msg
   * @param  {...Spread} meta
   *
   * @return {void}
   */
  log(level, msg, ...meta) {
    const levelName = _.findKey(this.levels, (num) => {
      return num === level
    });
    this.logger.log(levelName, msg, ...meta)
  }
}

module.exports = WinstonAirbrake;

All is well now.


#4

Hi @thsteinmetz,

Do you mind sharing your implementation for this driver? I’ll making a log driver too.

Thanks!


#5

@carlsonorozco the above code is the implementation I currently have. What exactly are you looking for?


#6

Hi @thsteinmetz did you implement it using a package/service provider?


#7

Here you are


#8

Thanks @keeross


#9

SumoLogic Logger Driver https://twitter.com/carlsonorozco/status/977181466131013632