Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

The Logger

The Logger is a simple but important tool that gives you clean, colored, timestamped output in your terminal. Instead of raw console.log() calls scattered everywhere, the Logger gives every message a consistent format so you always know what happened, when it happened, and where it came from.

Creating a logger

const { Logger } = require('highrise.bot');

const log = new Logger("MyBot");

The "MyBot" is what shows up in square brackets at the start of every line. Change it to whatever makes sense for your bot. If you have a bot called BananaBot, use "BananaBot". If you are running multiple bots, give each one a different prefix so you can tell them apart in the terminal.

The four log levels

The Logger has four methods, one for each type of message:

log.info()

For general information. Things that are working normally and you want to know about:

log.info('Bot', `Online in ${metadata.room.room_name}`);
log.info('Bot', `Running as ${metadata.bot_id}`);

Output looks like:

[MyBot] │ 12:00:01 │ [INFO] │ Bot │ Online in My Room

log.warn()

For things that are not errors but worth paying attention to. Unexpected but non-critical situations:

log.warn('Chat', `${failed}/${total} message parts failed to send`);
log.warn('Bot', 'Could not move to starting position');

Output looks like:

[MyBot] │ 12:00:05 │ [WARN] │ Chat │ 1/3 message parts failed to send

log.error()

For things that went wrong. Failed requests, unexpected states, anything that needs attention:

log.error('Moderation', `Failed to kick ${userId}: ${result.error}`);
log.error('Wallet', 'Could not fetch wallet balance');

Output looks like:

[MyBot] │ 12:00:10 │ [ERROR] │ Moderation │ Failed to kick user

log.debug()

For detailed information useful while you are building and testing. Debug logs also show the file and line number where they were called, which is helpful for tracing exactly where something happened:

log.debug('Chat', `Command received: ${message.command()}`);
log.debug('Room', `User count: ${count}`);

Output looks like:

[MyBot] │ 12:00:15 │ [DEBUG] │ Chat │ Command received: !ping
↳ /home/user/my-bot/index.js:25:9

The file path shown below debug messages tells you exactly which line of code produced that log. This is very useful when you have a large bot and need to find where something is coming from.

The format

Every log line follows the same format:

[prefix] │ HH:MM:SS │ [LEVEL] │ category │ message

The category is the second argument you pass to each method. Use it to describe which part of your bot the message is coming from. Good categories are short and descriptive: 'Bot', 'Chat', 'Moderation', 'Tip', 'Room'.

Logging multiple values

You can pass multiple values after the category and they will be joined with a · separator:

log.info('Close', `code: ${code}`, `reason: ${reason}`, `attempts: ${attempts}`);

Output:

[MyBot] │ 12:00:20 │ [INFO] │ Close │ code: 1006 · reason: null · attempts: 2

Objects are automatically converted to JSON strings so you can log them directly without calling JSON.stringify() yourself:

log.debug('Data', { userId: 'abc123', username: 'yahya' });
// logs the object as JSON automatically

Using the logger in your bot

Here is a realistic example of how to use all four levels throughout a bot:

const { Highrise, Logger } = require('highrise.bot');
require('dotenv').config();

const log = new Logger("MyBot");
const bot = new Highrise();

bot.once('Ready', async (metadata) => {
    // info: normal startup message
    log.info('Bot', `Online in ${metadata.room.room_name}`);
    log.info('Bot', `Bot ID: ${metadata.bot_id}`);
});

bot.on('Chat', async (user, message) => {
    // debug: useful while building, remove in production
    log.debug('Chat', `${user.username}: ${message.content}`);

    if (message.command() === '!kick') {
        const target = message.args(0);

        if (!target) {
            await bot.message.send('Usage: !kick <username>');
            return;
        }

        const found = await bot.room.users.find(target);
        if (!found) {
            await bot.message.send(`${target} is not in the room.`);
            return;
        }

        const result = await bot.room.moderation.kick(found.user.id);

        if (!result.ok) {
            // error: something went wrong
            log.error('Kick', `Failed to kick ${target}: ${result.error}`);
            await bot.message.send('Could not kick that user.');
            return;
        }

        // info: action completed successfully
        log.info('Kick', `${user.username} kicked ${target}`);
        await bot.message.send(`${target} was kicked.`);
    }
});

bot.on('UserJoined', async (user, position) => {
    log.debug('Join', `${user.username} joined the room`);
    await bot.message.send(`Welcome, ${user.username}!`);
});

bot.login(process.env.BOT_TOKEN, process.env.ROOM_ID);

Using the right log level for each situation makes your terminal output much easier to read. Info tells you what is happening normally. Warn flags things that need attention. Error marks real problems. Debug gives you the detail you need when actively working on something.