Skip to content

Commit

Permalink
feat: initial works for v2.0.0
Browse files Browse the repository at this point in the history
BREAKING CHANGE: lot's of internals had been changed.
  • Loading branch information
pi0 committed Oct 8, 2018
1 parent fca58e8 commit 455b6f9
Show file tree
Hide file tree
Showing 15 changed files with 347 additions and 238 deletions.
119 changes: 68 additions & 51 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
# Consola
# 🎮 Consola

Elegant Console Logger
[![Standard JS][standard-js-src]][standard-js-href]
[![npm version][npm-version-src]][npm-version-href]
[![npm downloads][npm-downloads-src]][npm-downloads-href]

> Elegant Console Logger
## Why Consola?

- Easy to use
- Fancy output with Fallback for Continuous Integration (CI) environments
- Fancy output with fallback for continuous integration (CI) environments
- Global mockable stdout/stderr wrapper
- Pluggable reporters
- Consistent Command Line Interface (CLI) experience
- Scoped Loggers
- Consistent command line interface (CLI) experience
- Tag support

## Installation

Expand All @@ -25,7 +29,7 @@ Using npm:
npm i consola
```

## Getting started
## Getting Started

```js
const consola = require('consola')
Expand All @@ -39,33 +43,43 @@ consola.error(new Error('Foo'))

<div align="center">
<br>
<img src="./assets/screen1.png" width="600px">
<img src="./assets/fancy.png" width="600px">
<p>Fancy Reporter</p>
<br>
</div>

<pre>
[2:17:17 PM] Starting build
[2:17:17 PM] [TEST] Log from test scope
[2:17:18 PM] Built!
[2:17:18 PM] Some info
[2:17:18 PM] Error: Foo
[18:40:19] [DEBUG] A message with consola.debug()
[18:40:19] [ERROR] A message with consola.error()
[18:40:19] [FATAL] A message with consola.fatal()
[18:40:19] [INFO] A message with consola.info()
[18:40:19] [LOG] A message with consola.log()
[18:40:19] [READY] A message with consola.ready()
[18:40:19] [START] A message with consola.start()
[18:40:19] [SUCCESS] A message with consola.success()
[18:40:19] [TRACE] A message with consola.trace()
[18:40:19] [WARN] A message with consola.warn()
[18:40:19] [INFO] Consola can format JSON values too
{
"name": "Cat",
"color": "#454545"
}
[18:40:19] [ERROR] Something bad happened!
> Object.<anonymous> (/Users/pooya/Code/nuxt/consola/demo.js:30:17)
> Module._compile (module.js:652:30)
> Object.Module._extensions..js (module.js:663:10)
> Module.load (module.js:565:32)
> tryModuleLoad (module.js:505:12)
> Function.Module._load (module.js:497:3)
> Function.Module.runMain (module.js:693:10)
> startup (bootstrap_node.js:191:16)
> bootstrap_node.js:612:3
</pre>
<div align="center">
<p>Minimal Reporter (CI)</p>
<br>
<p>Minimal Reporter (CI)</p>
<br>
</div>

## Scoped Loggers

Group logs using an scope:

```js
const logger = consola.withScope('test')

logger.info('Log from test scope') // [Test] Log from test scope
```

## Reporters

Choose between one of the built-in reporters or bring own reporter.
Expand All @@ -79,8 +93,6 @@ Available reporters:
- [JSONReporter](./src/reporters/json.js)
- [WinstonReporter](./src/reporters/winston.js)

Please see [Examples](./examples) for usage info.

### Creating your own reporter

A reporter (Class or Object) exposes `log(logObj)` method.
Expand Down Expand Up @@ -113,44 +125,37 @@ consola.add(new BasicReporter)
## Methods

- `consola.<type>(logObj)`
- `consola.<type>(message, logObj)`
- `consola.<type>(message...)`

Log to all reporters. Arguments can be either of type `String` (message) or a `logObj`.
- `consola.<type>(args...)`

`logObj` is compatible with `Error` objects. So it is safe to pass an error instead of `logObj`.
Log to all reporters.

- `add(reporter)`
- `addReporter(reporter)`

Register a custom reporter instance.

- `remove(reporter)`
- `removeReporter(reporter?)`

Remove a registered reporter.

- `clear()`

Remove all current reporters (Useful for writing tests).
If no arguments are passed all reporters will be removed.

- `defaults(defaults)`
- `create(options)`

Shortcut to `create({ defaults })`.

- `scope(scope)`

Shortcut to `defaults({ scope })`.
Create a new `Consola` instance and inherit all parent options for defaults.

## Fields

- `reporters`

An array of active reporters
An array of active reporters.

- `level`

The level to display logs. Any logs at or above this level will be displayed.
List of available levels [here](./src/types.js)

You can set log level using `CONSOLA_LEVEL` environment variable.

## logObject

logObject is a free-to-extend object which will be passed to reporters.
Expand All @@ -159,25 +164,29 @@ Here are standard possible fields:

Common fields:

- `message`
- `args`
- `date`
- `scope`
- `message`
- `tag`

Extended fields:

- `clear`
- `badge`
- `clear`
- `icon`

Depricated fields:

- `additional`
- `stack`
- `additionalStyle` (By default: `grey`)
- `icon` (Default depends on log type)
- `additionalStyle`
- `scope`

## Integrations

### With jest

```js
consola.clear().add({
consola.removeReporter().addReporter({
log: jest.fn()
})
```
Expand All @@ -192,4 +201,12 @@ consola.clear().add({

## License

MIT
MIT - Made with 💖 By Nuxt.js team!

<!-- Refs -->
[standard-js-src]: https://flat.badgen.net/badge/code%20style/standard/green
[standard-js-href]: https://standardjs.com
[npm-version-src]: https://flat.badgen.net/npm/v/consola/latest
[npm-version-href]: https://npmjs.com/package/consola
[npm-downloads-src]: https://flat.badgen.net/npm/dt/consola
[npm-downloads-href]: https://npmjs.com/package/consola
Binary file added assets/fancy.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
31 changes: 31 additions & 0 deletions demo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#!/usr/bin/env node

const Consola = require('./src/cjs')

const reporters = [
'FancyReporter',
'BasicReporter',
'JSONReporter',
'WinstonReporter'
]

Consola.level = 5

for (const reporter of reporters) {
Consola.log(reporter, '-----------------------')

const consola = Consola.create({
reporters: [new Consola[reporter]()]
})

for (let type of Object.keys(consola.types).sort()) {
consola[type](`A message with consola.${type}()`)
}

consola.info('Consola can format JSON values too', {
name: 'Cat',
color: '#454545'
})

consola.error(new Error('Something bad happened!'))
}
15 changes: 0 additions & 15 deletions examples/basic.js

This file was deleted.

3 changes: 0 additions & 3 deletions examples/error.js

This file was deleted.

7 changes: 0 additions & 7 deletions examples/json.js

This file was deleted.

16 changes: 0 additions & 16 deletions examples/winston.js

This file was deleted.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
"winston"
],
"contributes": [
"Pooya Parsa <pooya@pi0.ir>"
"Pooya Parsa <pooya@pi0.ir>",
"Clark Du (@clarkdo)"
],
"dependencies": {
"chalk": "^2.4.1",
Expand Down

6 comments on commit 455b6f9

@pi0
Copy link
Member Author

@pi0 pi0 commented on 455b6f9 Oct 8, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@clarkdo @pimlie Your comments on it would be so useful.

@pimlie
Copy link
Contributor

@pimlie pimlie commented on 455b6f9 Oct 8, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, am I correct its mostly interface changes and improvements but not so much new functionality or did I overlook something?

Here are my thoughts:

  • could we provide absolute (level = 3) and relative (level = +1) level change support with protection against under/over setting (not lower then the lowest or higher then the highest level). Eg I use that for automatically in/de-creasing the log level when you use -v or -q on the command-line.
  • small typo, its deprecated not depricated (in dutch depri is actually short for depressing, hope you dont feel that way! 😄 )
  • it would be nice if type names could be automatically and/or manually padded to the same length, so everything fully aligns. Maybe just a feature for fancy?
  • Also for fancy, should scope/tag maybe be the same colour as the level?
    Eg I currently use a type 'worker' and then set the workerId as scope (with a green color, so on an error you have the red text error and a green number of the worker). The reasoning behind this was that I dont wanted to print 10.000 times 'success', the colour green is more than enough. Maybe we could otherwise add a label property to logObj which overwrites the type name on printing?
  • instead of setting a log method for every type, we could maybe also use a Proxy:
class _Consola {
  constructor() {
    this.types = ['info', 'log', 'warn', 'error']
  }

  create(options) {
    return new Proxy(new Console(/*merge options*/), _ConsolaHandler)
  }

  hasType(type) {
    return this.types.indexOf(type) > -1
  }
  
  __log(type, msg) {
    console.info(type.toUpperCase() + ': ' + msg)
  }
  
  getStreamByType(type) {
    if (this.types[type] && this.types[type].stream) {
      return this.types[type].stream
    } else {
      return process.stdout
    }
  }
}

const _ConsolaHandler = {
  get(target, property, receiver) {
    if (property in target) {
      return target[property]
    } else {
      const key = `_${property}`
      if (key in target) {
        return target[key]
      } else {
        const hasType = target.hasType(property)
        if (hasType) {
          target[key] = (...args) => {
            target.__log(property, ...args)
          }
          target[key].write = (...args) => {
            target.getStreamByType(property).write(...args)
          }
          return target[key]
        }
      }
    }
  }
}

const consola = new Proxy(new _Consola(), _ConsolaHandler)

consola.log('log')
consola.log.write('log\n') // direct acccess to the stream
consola.info('info')
consola.warn('warning')
consola.error('error')
console.log('has trace?', consola.hasType('trace'), consola.trace) // => false, undefined
try {
  consola.trace('trace')
} catch(e) {
  console.log(e.message) // => not a function
}
  • Lastly, the one thing I think we should really support is different streams for different log levels. I think its better to support behaviour which people expect. Eg when using consola it should still be possible to redirect errors easily to a different file like nuxt-generate >/dev/null 2>nuxt.errors.log.

@pi0
Copy link
Member Author

@pi0 pi0 commented on 455b6f9 Oct 8, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pimlie Really cool ideas. Will try to implement most of them before release. About using Proxy, I have some doubt as I was planning to add browser support. But not really against it if gives us too many advantages.

@pimlie
Copy link
Contributor

@pimlie pimlie commented on 455b6f9 Oct 8, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pi0 You probably know this already, but there is a polyfill for Proxy which supports the traps used here.
Although in this case its perfectly fine, I was mostly triggered by log.bind(this). To me it looked like this is exactly a case for which Proxy was meant to be used. But I understand why you would want to keep using bind (eg implementing your own Consola instance would be a bit more difficult with Proxy)

(btw, prefixing the log functions with a _ in my example above is of course not necessary)

@pi0
Copy link
Member Author

@pi0 pi0 commented on 455b6f9 Oct 8, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pimlie True about polyfill but it adds some overhead :( The binds will ensure that users won't call log functions with the context of another class and only will happen once creating consola instance (or custom ones with create)

PS: please check your mailbox :D

@pimlie
Copy link
Contributor

@pimlie pimlie commented on 455b6f9 Oct 9, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My concerns with bind are mostly stemming from this SO question, but as said in this case its probably fine as no one in their right mind will create hundreds of log types.

Btw, should we also create helper methods to create a new global consola instance? Something like the following. Using replace-with should keep the initial reference so you wouldnt need to clear the module cache and then explictly use require instead of es6 imports.

import replaceWith from 'replace-with'

const createGlobalConsola = (options) => {
  setGlobalConsola(new Consola(options))
}

const setGlobalConsola = (newGlobalConsola) => {
  if (!(consolaOrOptions instanceof Consola)) {
    throw new Error('Should be a Consola instance')
  }
  
  replaceWith(global.consola, newGlobalConsola)
}

Please sign in to comment.