0

I'm not terribly experienced in JavaScript or NodeJS, but I've written a Discord bot using discord.js that has a custom plugin framework and bunyan for logging. I pass a log object to this plugin before it is ever executed so I can keep the same logger throughout different plugins, they just have to call this.log to get it.

The problem is, when I am executing a promise and pass a callback function, there is no reference to this.log because the function is out of scope of the object that is the plugin, so I effectively have to use something global to get it into the callback during then and catch methods.

Example code that fails, as you'd expect:

var myPlugin = (function () {
    return {
        myFunction: async function(message) {
            message.guild.channels.create(category, {
                type: 'category'
            })
                .then(function(channel) {
                    channelID = channel.id;
                    this.log.debug(`Set channelID to ${channelID}`);
                })
                .catch(function(error) {
                    message.reply("Whoops, I hit an error. Please try again later.")
                    this.log.error(`Error while making category`)
                });
        }

The failure, predictably, is because this.log is out of scope for the callback:

(node:8112) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'error' of undefined
    at C:\Users\tunacado\Documents\GitHub\myproject\src\plugins\test.js:46:38
    at processTicksAndRejections (internal/process/task_queues.js:93:5)

Should I just make a global plugin set of loggers available to get around this, or is there a better way to get a Logger into a callback function other than just a second global scoped logger available to all plugins? I considered writing a defined method and just passing that in instead of an anonymous function, but I am worried because technically this.log doesn't exist until a certain initialization step in my plugin system.

Best practices appreciated. Thanks!

EDIT:

So adding a method like this to my object works for now:

        fromMessageHandlePromiseError: function(error) {
            message.reply("Whoops, I hit an error doing that. Please try again later.");
            this.log.error(`Error while making category`);
        }

I just do .catch(fromMessageHandlePromiseError) and that works as this belongs to the object itself, but not sure if this ideal or I am still doing something dumb. Thanks!

EDIT 2:

I've been asked by @MauriceNino to show how I pass the logger, sorry I didn't share that first.

It's done in my index.js like so, defined here:

// Set up logging
const { DEBUG } = require('bunyan');
var Logger = require('bunyan');
const log = new Logger({
    name: 'mybot',
    streams: [
        {
            stream: process.stdout,
            level: config.get('bot.logLevel')
        }]
    }
);

And actually passed to each plugin loaded here:

// Load plugins
var loadPlugins = require('plugin-system');
let plugins;
loadPlugins(
    {
        paths: [
            __dirname + `/src/plugins/`
        ]
    }, log)
    .then(function onSuccess(loadedPlugins) {
        log.info(`Loaded plugins: ${loadedPlugins}`);
        plugins = loadedPlugins;
        plugins.forEach(function(plugin) {
            plugin.config = config;
            plugin.log = log;
            // Allow plugins to specify their own startup methods
            // post-construction after config is passed
            if(typeof plugin.initialize === "function") {
                plugin.initialize()
            } 
        })
    })

    .catch(function onError(err) {
        log.error(`Error loading plugins: ${err}`);
    })

So I pass to each plugin a config and the log for a global config and a global logger.

robbmanes
  • 319
  • 3
  • 10
  • 1
    Can you show the part where you are passing the logger? – MauriceNino Nov 03 '20 at 14:46
  • 2
    Also have you tried using arrow functions (`async message => { ... }`) instead of functions? Functions have their own "this", arrow functions don't. – MauriceNino Nov 03 '20 at 14:48
  • @MauriceNino I have updated the question with what you were looking for, and I did not know that about arrow functions at all, I'll read more about the differences. Thank you very much; I am very new to JS coming from system programming land and just getting my feet wet. – robbmanes Nov 03 '20 at 14:51

0 Answers0