Aurelia-logging - multiple loggers

So as usual I have some questions about something I am trying to do with Aurelia, in this case the LogManager.

Here is what I want to do…

  1. setup an appender that goes to the console
  2. setup an appender that goes to Rollbar
  3. setup an appender that goes to my custom error api endpoint

Okay, so that is all easy enough to do with a few simple calls:

Use the aurelia.use.developmentLogging() for console, or just use ConsoleAppender()
Use Jujens RollbarAppender for Rollbar.js (Rollbar is awesome btw) (https://www.jujens.eu/category/aurelia.html)
Use a customLogAppender for the error api

Now that all works fine, if you want the logger to call all 3 appenders for the configured setLevel (info, warn, …)

But, ha! You knew there had to be one… I want to have some calls, go to the console and Rollbar, but others go to the console and the customLog.

Here is my reasoning, Rollbar captures browser telemetry over and above the thrown error message that is extremely handy for debugging client issues. But its a paid service, and I would like to keep the hit count down. I want to be able to sent more verbose logging to the app server when needed for caught errors and debugging.

It doesn’t appear that aurelia-logger allows me to do that. Its odd in that there is a ‘name/id’ that is required when you instantiate the logger, but you cannot change what appenders are going to be called. And since its a singleton, you cannot create multiple loggers for the various combinations I want to use at least in all the examples I have been able to find

Or I guess I should say, ‘I’ can’t see how to do that in the current logger. I also don’t understand the use case for current way the name/id is used. Why have multiple names for the same configuration of the logger? It seems more something that should be set in a config at app level versus for each instantiated logger for the same set of appenders. Maybe I am missing something (as usual) on that and someone can set me straight.

So here is an example of my current code.

main.js sets up the appenders…

import { Aurelia, LogManager } from 'aurelia-framework'
.
.
.
    if (environment.debug)
    {
        //defaults to debug and console
        aurelia.use.developmentLogging();
    }
    else
    {
	    LogManager.addAppender(new m.ConsoleAppender());
        LogManager.addAppender(new RollbarAppender);
        //LogManager.addAppender(new customLogAppender); // not implemented yet

        LogManager.setLevel(LogManager.logLevel.error);
    }

I have created a service to use in each VM that I want to use the logger:

import { LogManager } from "aurelia-framework";

export var logger = LogManager.getLogger('MyApp');

Then in each VM I use the following import:

import { logger } from 'services/logger';

logger.error("Error: Missing state data", this.state);

What I would like to do is configure in main.ts something like the following (not checked, just thoughts typed out):

    if (environment.debug)
    {
        //defaults to debug and console
        aurelia.use.developmentLogging();
    }
    else
    {
        LogManager.setName("MyApp");

		//Setup Console and Rollbar for Errors
		let loggerName = "LogConsoleRollbar";
		LogManager.addLogger(loggerName);
	    LogManager.getLogger(loggerName).addAppender(new m.ConsoleAppender());
        LogManager.getLogger(loggerName).addAppender(new RollbarAppender);

        LogManager.getLogger(loggerName).setLevel(LogManager.logLevel.error);

		//Setup Console and Custom for Info
		loggerName = "LogConsoleCustom";
		LogManager.addLogger(loggerName);
	    LogManager.getLogger(loggerName).addAppender(new m.ConsoleAppender());
        LogManager.getLogger(loggerName).addAppender(new customLogAppender);

        LogManager.getLogger(loggerName).setLevel(LogManager.logLevel.debug);
    }

Then to use the logger the service would be something like this:

import { LogManager } from "aurelia-framework";

export var rollbar = LogManager.getLogger('LogConsoleRollbar');
export var customLog = LogManager.getLogger('LogConsoleCustom');

Then in each VM is use the following import:

import { rollbar } from 'services/logger';

and/or

import { customLog } from 'services/logger';

So, am I off base and aurelia-logging can something similar to this? If not should it?
Is this reasonable, a code smell, unique case and can not be generalized, something else?

Open to learning a better way of thinking about this, and other ideas on how to manage this.

1 Like

We can for a given logger set its level but we can’t set it for the log appender. So your proposition should work but you’ll sometimes have to forward some messages to multiple loggers and you can’t create dedicated logger for part of the app.

My suggestion would be to create a LogAppenderForwarder that looks like this:

class LogAppenderForwarder implements Appender {
    constructor(private appender: Appender, level: string) {
        this.level = logLevel[level];  // this is https://github.com/aurelia/logging/blob/master/src/index.js#L35 to map the string to a number.
    }

   debug(logger, ...rest) {
       if (this.level < logger.level) {
            return;
       }

      this.appender.debug(logger, ...rest);
   }

  // Implement info/error/warn
}

and then create it like:

Logger.addAppender(new LogAppenderForwarder(new RollbarAppender(), 'error'));

This way, you can use loggers as usual in your code.

Inspiration taken from https://github.com/aurelia/logging/blob/master/src/index.js#L74 (you can also use it to make the dispatch more dynamic and reduce the code in the class.

1 Like