Elegant Console Logs With Consola
Tags: webdev, javascript, terminal, productivity
Published: 2023-11-16 18:30:00

Console logs are not always well structured and eye-pleasing. Unpleasant and messy console takes away from the bliss of a developer. I recently came across a package named [`consola`](https://www.npmjs.com/package/consola) which does exactly this — making consoles meaningful and elegant.

It has browser support, pausing and resuming logs, prompt support and [other useful features](https://github.com/unjs/consola#why-consola).

## Installation

You can install it by using `npm`, `yarn` or `pnpm`.

```bash
npm i consola
```

## Trying it out

Spin up a new [node/express app](https://syntackle.live/blog/how-to-create-and-deploy-an-express-js-app-to-vercel-ljgvGrsCH7ioHsAxuw3G/#creating-express-app) and start exploring.

### Basics

For a quick hands on experience, in your node application, throw an explicit error and then log it with the help of `consola`.

```js
import { consola } from "consola"

try {
    throw Error("Unexpected error")
} catch (error) {
    consola.error(error)
}

consola.error("Error...")
consola.info("Info...")
consola.warn("Warning...")
consola.log("Logged...")
```

### New Instance

You can create a new consola instance with the help of the `createConsola` method and use that instead of the default global instance.

```js
import { createConsola } from "consola"

const logger = createConsola({
    level: 0, // error/fatal logs
    fancy: true,
    formatOptions: {
        date: true,
        columns: 20
    },
})

logger.info("Info...") // this WON'T work
logger.error("Error...") // this will work
```

Set the log `level` to selectively allow only certain types of logs. [Log level](https://www.npmjs.com/package/consola#log-level) of `0` means only `FATAL` and `ERROR` logs are logged. The default log level is 3.

### Reporters

Reporters are representations of logs in the terminal. Consola (v3.2.3) provides 3 [reporters](https://github.com/unjs/consola/tree/main/src/reporters) out of the box, namely, `basic`, `fancy` and `browser`. They are configured based on log levels on the global `consola` instance.

To add a custom reporter to your newly created instance, you can use the `reporters` property which is an array of reporters.

```js
import { LogLevels, consola, createConsola } from "consola"

const infoLogger = createConsola({
    fancy: true,
    formatOptions: {
        date: true,
        columns: 20
    },
    reporters: [
        {
            log: (log) => {
                if (log.level === LogLevels.info) {
                    consola.info(JSON.stringify({
                        date: new Date().toLocaleString("default", { dateStyle: "full" }),
                        logs: new Array().concat(log.args.map((l, i) => `${l}${i < log.args.length - 1 ? `,` : ``}`)).join(" "),
                    }, null, 4))
                } else {
                    consola.error(
                        new Error("invalid log method")
                    )
                }
            }
        }
    ]
})
```

Now, when you use any logging method on this instance, it will only log for the `info` method and throw an error for other methods. You just created a custom info message logger which you can further modify however you want.

```js
// for the above instance
infoLogger.error("Won't work") ❌
infoLogger.info("Will work") ✔️
```

Multiple reporters per instance are also supported allowing you to separate logs into desired representations.

```js
reporters: [
    {
        log: (log) => {
            if (log.level === LogLevels.info) {
                consola.info(JSON.stringify({
                    date: new Date().toLocaleString("default", { dateStyle: "full" }),
                    logs: new Array().concat(log.args.map((l, i) => `${l}${i < log.args.length - 1 ? `,` : ``}`)).join(" "),
                }, null, 4))
            } else {
                consola.error(
                    new Error("invalid log method")
                )
            }
        }
    },
    {
        log: (log) => {
            createConsola({
                fancy: true,
                formatOptions: {
                    date: false
                }
            }).log(log)
        }
    }
]
```

Methods such as `addReporter`, `setReporters` and `removeReporter` are available to handle reporters for an instance.

### Wrapping native `console` method with `consola` instance

Doing so will redirect all the native `console.log` calls to the specified `consola` instance.

```js
infoLogger.wrapConsole()
// consola instance `infoLogger` will print the table
console.table(["Info", "Second Info"])
```

`restoreConsole` will restore the native functionality of `console.log` and won't redirect to consola instance.

There are several util methods present in `consola/utils` which you can use to further customize the logs.

### Prompts

Prompts are supported in consola with the help of [clack](https://github.com/natemoo-re/clack), a tool to build command-line apps. Check this out for some [prompt examples in consola](https://github.com/unjs/consola/blob/main/examples/prompt.ts).

## Conclusion

Correctly and elegantly representing console logs is an important task if you want to improve [developer productivity](https://syntackle.live/blog/5-vscode-extensions-you-must-use-JTRFImxbBtrq6btizBCJ/) and `consola` helps you with just that.