npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

canopi

v1.3.1

Published

🌴 A simple Node logging library for the cloud

Downloads

65

Readme

Canopi Codeship Status for robhaswell/canopi

🌴 A simple Node logging library for the cloud

const log = canopi('app')
log.setOutputStream(process.stdout)

log.info('something happened', { thing: value })
// {"timestamp":"...","name":"app","message":"something happened","thing":value}

const eventLog = log('events')
log.info({ kind: 'createWidget', widget: widget })
// {"timestamp":"...","name":"app:events","kind":"createWidget","widget":widget}

const widgetLog = eventLog({ widget: widget })
log.error(new Error(), { kind: 'widgetFailed' })
// {"timestamp":"...","name":"app:events","kind":"widgetFailed","widget":widget,"err":{ name, message, [code], stack }}

canopi.addErrorHandler((err) => { errors.report(err) })
// Call `errors.report` with any logged `log.<level>(err)`

Motivation

Canopi was created to support the logging needs of a microservices application deployed to Google Container Engine. In order to effectively debug a distributed application, it is required to be able to trace logs which relate to a given request, event or entity. This means that every log message needs to have some sort of context to be useful, usually supplied an object of relevant properties and values. Additionally, there is no requirement for alternative logging streams or level-based filtering, as logs are processed by log collection tooling.

Other logging solutions such as Bole or Bunyan make it difficult to log errors with some context, or are overly burdened with unnecessary functionality, so we created Canopi.

The API for Canopi is heavily inspired by Bole.

## API

canopi('name')

Create a new logger with the supplied name to be attached to each output. If you keep a logger-per module you don't need to pass loggers around, keep your concerns separated.

canopi.setOutputStream(process.stdout)

Configure the output stream.

canopi({ aProperty: 'aValue'})

Create a new logger with the fields from the supplied object included in the output.

canopi('name', { aProperty: 'aValue'})

Create a new logger with the supplied name and fields to be included in the output.

logger#debug(), logger#info(), logger#warn(), logger#error()

Loggers have 4 roughly identical log methods, one for each of the supports log-levels. Log levels are recorded on the output and can be used to determine the level of detail passed to the output.

Log methods support the following types of input:

logger.<level>('message')

Providing a message string will output the message under the property message. No substitution is performed.

logger.<level>("message", { an: 'object' })

If an object is provided with a message, the behaviour is as above but with additional properties from the object included in the output.

logger.<level>({ an: 'object' })

Providing an object will log all the fields of the object.

logger.<level>(err)

Error objects: log output will include the error name, message, complete stack and also a code where there is one.

logger.<level>(err, { an: 'object' })

The error will be logged as above, but with additional properties from the object included in the output.

logger.<level>(err, 'message')

The error will be logged as above, but with the additional message property.

Note: Any other call signature will log a CanopiUsageError at the requested log level.

logger()

The logger object returned by canopi() is also a function that accepts a name or object argument. It returns a new logger whose name is the parent logger with the new name appended after a ':' character. This is useful for splitting a logger up for grouping events. Consider the HTTP server case where you may want to group all events from a particular request together:

var log = canopi('server')

http.createServer(function (req, res) {
  req.log = log(uuid.v4()) // make a new sub-logger
  req.log.info(req)

  //...

  // log an error against this sub-logger
  req.log.error(err)
})

Formatters

Canopi supports formatters, which are convenience functions the user can defined which format unweildy objects. A formatting function takes two arguments, a key and a function. The function will be called with the value of the key if anything is logged under that key.

canopi.setFormatter('upper', (value) => value.toUpperCase())
log.info({ upper: 'make me uppercase' })
// {"timestamp":"...","upper":"MAKE ME UPPERCASE"}

canopi.setFormatter('upper', null)
log.info({ upper: 'make me uppercase' })
// {"timestamp":"...","upper":"make me uppercase"}

A default formatter for request objects is available as canopi.requestFormatter. This can also be set (along with others in the future) with canopi.defaultFormatters().

Formatters are not chainable .

Error handlers

Canopi supports a list of error handlers which are called in turn with each error logged. Errors are passed to error handlers only if they are the first argument to a log method, they are an instance of Error, and the log level is error.

canopi.addErrorHandler(function callback(err) { ... })

Call callback with a single argument of the error instance whenever an error is logged as the first argument to a log method.

Other methods

canopi.quiet()

Silence the logger output by preventing it from writing to process.stdout and removing any error handlers.