Show HN: Universal Logger for Node, Deno, Bun, Browser

(adzejs.com)

51 points by ajstacy06 3 days ago | 24 comments

Hey everyone, I had posted about my project https://adzejs.com a couple of years ago and it was met with a lot of interest, so I'm writing about the major v2 update that's just been released to see if anyone is interested.

What makes Adze interesting compared to other logging libraries like pino, bunyan, winston, etc?

Adze is universal. This means that Adze will "just work" in all of your environments. This is especially handy when working with SSR projects like sveltekit, nuxt, next, etc. You can also use Adze with Bun or Deno without any special adaptations or considerations.

Adze 2.x is also smaller (13.29kb minified and brotlied) and faster than the original. Benchmarks put it at generating 100,000 logs in ~700ms.

Version 2 also offers a cleaner API than version 1 as it no longer uses factories and instead uses static class methods.

    import adze from 'adze';

    // Generating a log
    adze.timestamp.ns('foo').log('A log with a timestamp and namespace.');

    // Making a child logger
    const logger = adze.timestamp.ns('foo').seal();
    logger.log('A log with a timestamp and namespace.');
Adze 2.x comes with support for four different types of log formats out-of-the-box. These formats include: - a human-readable pretty format - a machine-readable JSON format that is compatible with the Bunyan CLI - a format for common logs - and a format for simple stdout logging

Adze 2.x also offers better extensibility support. You can now create custom formatters and custom middleware for modifying log behavior or transporting them to another source (like a file, etc). Log listeners are also still supported.

Changing formats is easy.

    import adze, { setup } from 'adze';

    setup({
      format: 'json', // <- Change with an env var
    });

    adze.withEmoji.success('This is a pretty log!');
Adze 2.x also includes a handy new template literal logging feature for times where you are repeating logs frequently with slightly different messages (like error messages in a catch). Adze offers a new sealTag terminator that will seal your configuration into a template literal tag function to further simplify your logging. Example

    import adze from 'adze';

    // Let's create a reusable ERR tag with emoji's, timestamps, and the "my-module" namespace.
    const ERR = adze.withEmoji.timestamp.ns('my-module').sealTag();

    try {
      // do something that could fail...
    } catch (e) {
      ERR`Printing my error as an error log! ${e}`;
    }
There is much, much more to Adze than what I can present in this post, but please check it out at https://adzejs.com and let me know what you think! Try it out! Also, please give it a star to bookmark it at https://github.com/adzejs/adze if you might use it in the future!

I appreciate any feedback as well. This has been a huge labor of love for me and I hope it benefits you all as well.

Thank you!

pmdfgy 39 minutes ago | next |

Looks really nice and will give it a try on our codebase as an alternative to Winston.

Funny thing : one of the colleagues was unhappy when we proposed to define the logger as an interface, allowing us to switch implementation in one line. "We will never change the logger" he said...

    export interface Logger {
        debug(message: LoggerMessage, ...meta: unknown[]): void;
        error(err: Error): void;
        info(message: LoggerMessage, ...meta: unknown[]): void;
        trace(message: LoggerMessage, ...meta: unknown[]): void;
        warn(message: LoggerMessage, ...meta: unknown[]): void;
    }
Great job ! Emojis can actually be useful while working locally, especially to detect errors quickly.

theogravity 3 days ago | prev | next |

Neat. It looks like it's more focused on the visual side of things since the screenshot shows two different ways to visually output the results?

I wrote a universal logger that we've been using for our production systems for years that piggybacks on top of bunyan / console / etc. It's pretty much an abstraction layer that provides more structure to logs.

It allows us to easily swap between different logging libs without having to change our entire codebase. For example, we originally started with roarr then eventually migrated to pino, and it was just a few lines of re-config to do so with loglayer.

You use the existing config of your logging library and just feed the instance of it to loglayer.

https://github.com/theogravity/loglayer

It basically prevents problems like this:

  // Using `winston`:
  winston.info('my message', { some: 'data' })

  // Using `bunyan`:
  bunyan.info({ some: 'data' }, 'my message')
to:

  logLayer
    .withMetadata({ some: 'data'})
    .withError(new Error('test'))
    .info('my message')

A common use case is using loglayer + console initially, then swapping to something like pino later on.

ericyd 2 days ago | prev | next |

Looks like a cool project, and props for making it universal for all runtimes. A few pieces of feedback:

1. I don't understand the use case for emojis. If I'm using a logging library in production it's so I can pipe my structured logs into an observability tool. Do emojis provide value in this case?

2. Consider including timestamps by default and adding a config option to remove of needed. I personally can't think of a time where I wouldn't want timestamps in my logs.

Lastly a question: Just because other loggers don't claim to be universal, do they actually break on Deno/Bun?

ajstacy06 2 days ago | root | parent | next |

Also, on the topic of timestamps, some of the formatters do have them on by default. The pretty formatter which is mostly for dev does not because its not as necessary. I hope that helps explain that decision :)

As for emoji's, they are off by default and are really only for dev with the pretty logger. You are right in that you probably would not want them in production. You would probably use a different formatter like "standard" or "json" anyway which doesn't allow them.

ericyd 2 days ago | root | parent |

That makes sense! This is obviously just my preference, but in both cases I would prefer this to be a config option, so I could control it e.g. by an environment variable. Local dev? Probably don't care about timestamps and probably would enjoy emojis. Production? Absolutely need timestamps and probably don't want emojis. I realize this is a significant API change, but just wanted to share my thoughts.

ajstacy06 2 days ago | root | parent | prev |

They may or may not break on Deno or Bun, but most will not work in the browser or if they do they are severely limited in functionality. This library was actually born out of my frustration with trying to generate logs and transport them from the browser. Plus, in my past experience, it was a bit of a pain to use older logging libraries with new SSR frameworks. They also mostly have poor, bolted-on typescript support.

SahAssar 3 days ago | prev | next |

> Adze is universal. This means that Adze will "just work" in all of your environments

I'm not sure what this means. console.log is also universal (at least for the platforms mentioned), do you mean that client side logs are shipped to the server?

ajstacy06 3 days ago | root | parent | next |

It's universal meaning it works in the web browser and the backend without any configuration. All other loggers work in one or the other, or if they work in both the functionality is severely limited. Winston and Bunyan only work server side because they use node fs streams. Pino is primarily designed to be a server side library, but it can run in the browser by using browserify and using limited funcionality.

Of course console methods are universal. This library isn't providing console methods, it's providing enterprise/production level functionality on top of the console.

If you want to use Winston in Sveltekit or the like, you have to wrap browser checks around parts of it, otherwise it will explode when bundled.

SahAssar 3 days ago | root | parent |

Right, I usually prefer to use the built in console methods with some currying when needed so I might not be the audience. This has worked well enough even for "enterprise" use-cases.

If I may give one suggestion maybe drop the dependency on 'vuepress-plugin-search-pro' (48MB) and maybe 'date-fns' (23MB), that seems like some really large dependencies that might not be required.

ajstacy06 3 days ago | root | parent |

First of all, if that's all you need then by all means, keep doing that. If you need any of the following, maybe consider a library:

- First-class TypeScript support (not bolted-on)

- Wraps and extends the entire standard API

- A convenient chainable API

- Log Listeners for capturing log data

- Middleware support for plugins and transporting logs

- Log annotations such as namespaces, labels, and other meta data

- Four formats supported out of the box:

  - Pretty - Human readable logs that are easy on the eyes
  
  - JSON - Machine readable logs that are compatible with the Bunyan CLI
  
  - Standard - Human readable stdout logs
  
  - Common - Logs that adhere to the Common Log Format
- Everything is customizable and configurable

- Tools for caching, filtering, and recalling logs

- Support for creating log threads to track data across multiple scopes

- Convenient child logger API's

If you look at the readme you'll see adze is 8kb bundled and gzipped. It's ESM so it has tree-shaking support. It's tiny. The Vue dependency is for the docs.

https://bundlephobia.com/package/adze@2.0.10

SahAssar 2 days ago | root | parent |

Install size is 38MB and was 94MB until you moved vuepress-plugin-search-pro to devDependencies (I'm guessing because of my comment?) so even though it might be treeshook to a small size install size is still quite a bit larger to install.

difosfor 3 days ago | root | parent | prev |

I guess it provides a complete console like API in all cases where some environments offer fewer console methods?

SahAssar 3 days ago | root | parent |

I'm pretty sure all the mentioned runtimes support all the standardized console methods, and it does not mention anything about compatability with more restricted environments like quickjs or embedded js.

catchmeifyoucan 3 days ago | prev | next |

Hey, cool project! Will have to try it out next time.

Some design feedback on the site:

> Universal By Nature

The contrast ratio on the headers is really hard to read. I skipped the hero section and began reading at where it said:

> which means it can be executed on the server side or the browser side without any extra considerations. Use it anywhere!

And I totally missed it's a logging library - I was expecting to see a new web framework. My preference would be to put the Simple, Chainable API section before.

vanjajaja1 2 days ago | prev | next |

cool I was looking for something like this, attractive site too

when I install it it seems to depend on vuepress-plugin-search-pro which requires yarn 2 berry. not sure if you are aware this extra hurdle

ajstacy06 2 days ago | root | parent | next |

It appears I had that mistakenly in the dependencies instead of devDependencies. I'm correcting it now and will release a patch. Thanks for the heads up!

Lord_Zero 3 days ago | prev |

What transports are supported out of the box? Like file, S3, etc?

ajstacy06 3 days ago | root | parent |

Right now daily file rotation is supported via the @adze/transport-file plugin. Plugins can be made using simple middleware hooks for any of your needs. I'm working on adding more right now for other services like Cloudwatch Logs, Google Logging, Open Telemetry, etc.