Giter Club home page Giter Club logo

ulog's Introduction

ulog v2.0.0-beta.19

The Universal Logger

npm license travis mind BLOWN

.

logo

The logger for javascript applications

ulog is the logger for Javascript applications. It's universal, meaning it runs everywhere. You can use ulog in your Express server application running on Node JS just as well as in your React single page application running in the browser. It just works.

screenshot

Features

Ulog marries the feature sets from debug and loglevel and adds some of it's own!

Feature   debug   loglevel   ulog  
Footprint 3.2 kB 1.4 kB 2.7 kB
Debug mode ✓ (1)
Levels
Configurable ✓ (2)
Dynamic config
Channels
Outputs
Custom outputs
Formatting
Preserves callstack
Configurable format
Custom formats
Colors
Alignment
Add-ons / Mods
Lazy loading
Anylogger support ✓ (3) ✓ (3)

(1) emulated with levels (2) in browser only (3) via an adapter

Try it

Have a look at the interactive tutorial. It's the fastest way to get a quick taste of ulog.

Compare it

Want to check how ulog measures up to it's competitors? Check out these loggers side-by-side:

Install

npm i -S anylogger ulog

Add to entry point

In the entry point of your application import ulog:

index.js

import "ulog"

Use

In your code, import anylogger and use it to create loggers and do logging:

import anylogger from 'anylogger'
const log = anylogger('my-app')
log('Logging is easy!')

This way, your code is decoupled from ulog and if you ever want to switch to another logging library, you will be able to do so without having to change any of that code.

Anylogger support

anylogger is a logging facade that allows code to use logging without getting coupled to a specific logging system. You can use that code with any logging system out there. ulog has anylogger support built in. For other loggers, adapters are available.

The logger for libraries

When we write a library, we install ulog as a development dependency so the library remains decoupled from ulog.

Install ulog as a dev dependency

Install anylogger as a regular dependency and ulog as a dev dependency:

npm install --save anylogger && npm install --save-dev ulog

In our tests:

test.js

import `ulog`

In our library code:

my-lib.js

import anylogger
const log = anylogger('my-lib')
log('Logging is easy')

Script tag

If you want, you can import ulog with a script tag:

<script src="https://unpkg.com/[email protected]/ulog.min.js"></script>
<!-- publishes to `self.anylogger` and `self.ulog`. -->
<!-- lazy loads ulog.lazy.min.js on demand. -->
<script src="myscript.js"></script>

myscript.js

var log = anylogger('my-module')
log('Logging is easy!')

Download

If you want the file for the browser to include in your project yourself, you can download it from here.

ulog.min.js lazy loads ulog.lazy.min.js on demand, so make sure to include both files in your download

Full bundle, no lazy loading

I recommend to use a bundler instead. Loading lots of script tags is inefficient and hard to manage. Also see the section on lazy loading with webpack

Why ulog

The two most popular logging libraries on NPM at the moment are debug and loglevel. They are both great loggers, but neither of them completely satisfied my requirements for a logging library.

debug allows for namespaced debug logging, where each logger has a name. Whether these loggers output debug logging is configurable, though not dynamic, requiring a restart before changes take effect. It's simplicity makes debug an excellent choice for debug logging (as it's name implies), but it lacks support for log levels, so if you want to log error messages for example, you end up needing another library for that. It offers nicely formatted (and even colored!) log messages, but because of that mangles the call stack, which is a huge downside in the browser imho. It's not very extensible, basically being a monolith.

loglevel also supports namespaced logging and it does offer log levels. It's configurable via localStorage but not via environment variables and just like debug requires a restart before configuration changes take effect. By default, it leaves your call stack alone, making the filename/line number entries in the browser console that much more useful. It does not offer alternative log destinations or formatters out of the box. It can be extended via plugins and there are some good plugins out there, but it's base feature set is coded as a monolith, so you cannot easily remove features. You probably won't have to though as it weighs only 1.4kB.

Both these loggers lack the ability to configure the logger from the querystring, which I found to be a very desirable feature for web development as it allows you to create a URL that has the log config embedded, which you can then send to other developers or users etc. E.g: https://example.com/page?log=debug.

What I want is a logging library that combines the best aspects of both these loggers and adds the features that I miss in both. ulog is my attempt at building this library. It's base API is compatible with that of debug and loglevel and with the console, making it a drop-in replacement for all of these in many cases. It has a configuration mechanism that is compatible with that of debug, but that is more powerful and is monitored for changes at runtime. It accepts configuration from the querystring allowing you to craft URLs with log config embedded in it. It has powerful, configurable formatting included by default and it does this without mangling the call stack, so the filename/line number entries in the browser console remain unharmed. You can specify where the log output should go and where it should drain. It's completely modular, so you can not only easily add features through 'mods', but you can actually even drop features you don't need by not loading the mods those features are in. It has native anylogger support, decoupling the client code from the logger. And it supports lazy loading so we can get all those great features without bloating our bundle.

I hope you will give ulog a try. If you have feedback on it, or found an issue, please let me know on the issue tracker.

API

ulog is very natural to use:

var log = require('anylogger')('my-module') // same as with `debug`
log('A log message')                        // same as with `debug`
log('info', 'An info message')              // not possible with `debug`
log('warn', 'A warning message')            // not possible with `debug`
log.info('Starting...')                     // same as with `loglevel` or console
log.log('Yeah!')                            // same as with console
log.error('Something went wrong', new Error('Oh no!'))
if (log.enabledFor('warn')) {
  log.warn(expensiveArguments())
}

Note that in the code above, we import anylogger and not ulog. This way the client code is decoupled from the logger.

ulog inherits it's API from anylogger. If you are able to restrict yourself to the Anylogger API, your code will be framework independent and will work with any supported logging library.

Note that any logging code written for either debug, loglevel or the console should be able to do it's logging just like it did before, but now using a ulog logger instead. This backward compatibility should make migrating from any of these to ulog very easy. And because this is just the anylogger API, you should even be able to migrate back to debug or loglevel without any changes at all, by just including the right adapter in your entry point. Of course once you get used to ulog, you will never want to go back! :p

Levels

anylogger defines 6 logging levels, which correspond with the natively available logging functions on the console. ulog creates constants for these levels on all loggers:

log.ERROR // 1
log.WARN  // 2
log.INFO  // 3
log.LOG   // 4
log.DEBUG // 5
log.TRACE // 6

In addition, ulog adds constants for pseudo-levels that enable or completely disable all logging:

log.ALL   // 7
log.NONE  // 0

log.enabledFor

anylogger defines log.enabledFor and ulog implements it by checking the logger's current log level and whether it's in debug mode. Normally, you should not have to use this method, unless you are doing some expensive calculations only to write log output. In such a case you can write:

import anylogger from 'anylogger'
const log = anylogger('my-app')
if (log.enabledFor('info')) {
  log.info(calculateResults())
}

log.level

ulog adds a property level to each logger that is a numeric representation of the current log level.

if (log.level >= log.INFO) {
	log.info('This message will be logged')
}
log.level = log.WARN
log.info('This info message will NOT be logged.')
log.warn('This warning message WILL be logged.')
log.level = log.NONE
log.error('Logging is completely disabled.')

In general, code should not set the log level directly, but instead should rely on the host environment for the log level. See the section on configuring ulog.

To check the log level, enabledFor is preferred over the level property as it is within the anylogger API.

Default log level

I've found that it makes sense to have different default log levels in the browser and in Node. In Node, logging is often the only UI we have available and we (the devs/admins) are the only ones that will see that logging. In the browser, we have an alternative UI (the webpage itself), so logging will be less useful for normal users.

In Node

In Node, the log level defaults to info. This allows you to use info, warn and error when informing the user of stuff that happened.

In the browser

In the browser the log level defaults to warn. This means info messages will be excluded, but for most users these messages won't be relevant anyway.

Attention! Chromium-based browsers have their own level filter and by default, debug messages are filtered away.

Footprint

When we support logging with some logging library, we add code to our application that many users don't actually need. There are techniques to remove this code from our production builds, but they sacrifice logging with it. In many scenarios we actually do want logging in the production builds. To assist support personel in diagnosing user problems for example. So it's beneficial if the minimum amount of code we need to load to support logging is small. This minimum amount of code we call the footprint. ulog keeps its footprint small by utilizing lazy loading.

Debug mode

Debug mode is a feature that ulog copied from debug and it responds to the same config option. Setting a logger to debug mode effectively means forcing it's log level to be at least debug:

DEBUG=my:app
import anylogger from 'anylogger'
const log = anylogger('my-app')
log('Hi!') // is output because logger is in debug mode

Channels

In ulog, logging is always sent to exactly one channel. By default, two channels exist: output, for messages of loggers that are in debug mode, or that are at an enabled level, and drain, for those messages that are filtered away.

By using a separate channel for the drain, we can override the default behavior of using noops for all log levels that are outside of the active levels. We could for example send all logging to a database and only later filter it, when we display it for example.

A channel has one or more configurable outputs that can optionally apply formatting to the message.

Outputs

In ulog, where messages are going is completely configurable at runtime. You can even configure where discarded messages are going.

By default all log methods on a logger are associated with one of two channels, output and drain. To configure the outputs for these channels, two config options are available:

When the logger is created, each log method is sent either to the output channel, or to the drain channel, based on the current log level for that logger and whether that logger is in debug mode.

To configure the output for a logger, we assign the name of the output to use to the relevant logger:

log_output=console

This setting can include expressions to target individual loggers, just like the debug and log settings:

log_output=console;noisy-lib:*=noop

The value part is actually a kurly format string. The same syntax can be used here as for configuring formatting. If more than one output is specified, a multiplex function will be inserted that dispatches the logging to all specified outputs.

By default, the following outputs are included:

Output console

This actually is the native console object. Using the native console directly is what allows us to leave the call stack intact in the browser developer tools.

Output noop

This is just an object with a noop log function

Custom outputs

The default outputs are not very special, but the entire machinery is in place for you to easily add any custom outputs you could ever need. You can define additional outputs by making ulog use a mod with an outputs key:

index.js

import ulog from 'ulog'
ulog.use({
  outputs: {
    custom: {
      log: function(){
        var args = [].slice.call(arguments)
        args.shift('Custom!!')
        console.log.apply(console, args)
      },
      info: function(){
        var args = [].slice.call(arguments)
        args.shift('Custom!!')
        console.info.apply(console, args)
      }
    }
  }
})

An output can either be an object with log, info, warn etc methods as shown above, or a kurly tag:

index.js

import ulog from 'ulog'
ulog.use({
  outputs: {
    custom: function(ctx){
      return function(rec) {
        rec.message.shift('Custom!!')
        console[rec.level].apply(console, rec.message)
      }
    }}
  }
})

This way you can add outputs that send log messages to memory, a file, localStorage, a database etc etc.

Formatting

Formatting is another feature that separates ulog from debug and loglevel. debug has formatting, but it is hardcoded and messes up the callstack and there is not much you can do about it. loglevel does not mess up the callstack, but it also has no formatting at all out of the box.

Ulog uses kurly to support advanced, configurable and customizable formatting, without mangling the callstack.

Configurable format

The actual format used is configurable easily via ulog's powerful configuration mechanism.

The default format string on Node is:

lvl name:20 message perf

This sacrifices the callstack for a colored and formatted message and having the perf measurements after the message i.s.o before it. The Node JS doesn't output any file name / line number information anyway.

On browsers, we want to spare the call stack, so there the default is:

lvl name perf

We don't include the message, but it will be appended as the last argument automatically. The result is nicely formatted messages with the file name / line number entries in the browser debug console intact.

To override the default format, just set config option log_format to the format you want.

Formats available out of the box include:

Format syntax

ulog uses the new options in kurly v2 to make tag open/close markers optional and enable nicer looking format strings. This means that lvl name perf and {lvl} {name} {perf} are equivalent. When using no open/close markers, any non-identifier symbols following the tag name are considered nested text. For example for the format string name:22, the name format will receive ':22' as ctx.text. This allows for parameterized formats, as ulog has done to support padding options.

Preserves callstack

ulog's formatting system has the unique (1) ability to do formatting while preserving the callstack. As long as only static kurly tags are used as formats, the call stack can remain unharmed.

(1) I do not know of any other logger out there that has this feature, but if you do know one, please let me know in the issue tracker

Included formats

Except for the message format, all included formats are static.

Format cr

Prints a 'carriage return line feed'

Format date

Prints the date the message was logged as yyyy/MM/dd.

Format lvl

Prints the level of the message as a single character:

  • 'x' for error messages
  • '!' for warning messages
  • 'i' for info messages
  • '-' for log messages
  • '>' for debug messages
  • '}' for trace messages

Format message

Prints the message, formatted and colored. Using this format breaks the callstack as it is dynamic.

Format name

Prints the logger name

Format perf

Prints the time difference between two invocations to the same logger, only if this difference is larger than 1ms. Produces output that looks like +62ms.

Format time

Prints the time the message was logged as hh:mm.

Fallback format

Any unrecognized tags are being interpreted by the fallback format. This just returns the field on the log record whose name matches. For example suppose we'd write level. This is an unrecognized tag so the wildcard formatter is used, which just returns the level field from the log record. If no field on the log record matches, it returns the original text unchanged, making 'Hello World!' a valid format string.

Padding options

All included formats support some padding options. For example, to pad out the logger names to 16 characters and align the text on the left, use name<16 or name:16. To align the text on the right, use name>16.

Custom formats

ulog's formatting system is easily extendable by adding kurly tags to a key formats under a mod:

import ulog from 'ulog'
import formats from 'ulog/mods/formats'
ulog.use({
  use: [ formats ],
  formats: {
    custom: (ctx) => (rec) => (['custom'].concat(rec.message)),
    random: (ctx, rec) => () => Math.random()
  }
})

These tags then become available in the format string.

We used two different signatures here, because there are two types of formats.

Types of formats

Formats come in two flavors:

Dynamic formats

Dynamic formats have full access to the message. But they do mess up the call stack. A dynamic format has this signature:

ulog.use({
  use: [ require('ulog/mods/formats') ],
  formats: {
    dynamicFormat: function(ctx) {
      // one-time init here
      return function(rec) {
        // rec.message contains full message
        return /* ... */
      }
    }
  }
})

Static formats

Static formats do not have access to the message. But they do not break the call stack! So prefer static formats if possible.

Static formats have this signature:

ulog.use({
  use: [ require('ulog/mods/formats') ],
  formats: {
    staticFormat: function(ctx, rec) {
      // one-time init here
      return function(){
        // rec.message is undefined
        // rec.name, rec.level etc is populated
        return /* ... */
      }
    }
  }
})

To read more about kurly and custom kurly tags, refer to the kurly documentation on creating kurly tags

Colors

Who doesn't like some colors?! Apart from making things prettier, when used correctly they can actually also make our logs easier to read. Now I don't know about you, but I find reading logs hard, so I'll take all the help I can get!

If you don't want colors, you can suppress them using config option log_color.

Alignment

Browsers have great debug consoles these days. They even include stacktrace info for higher-level messages. But they did mess one thing up imho; the messages at these higher levels are indented a bit more than the other messages, making the logging harder to read. This can be clearly seen in the screenshot from ulog v2.0.0-beta-11, which did not yet have alignment:

screenshot

ulog now automatically adds some formatting that negates the extra indentation the messages at these higher levels get, so all messages are nicely aligned:

screenshot

You can control alignment with config option log_align.

Configure

ulog features a simple, yet powerful and flexible configuration mechanism. On Node JS, we can configure ulog via program arguments, environment variables or a configuration file. On browsers, we use querystring arguments or localStorage. On both platforms, the configuration is monitored and changes to it are picked up by ulog at runtime without the need to restart the application.

ulog's configuration mechanism is an extension to that of debug and is compatible with it. debug is one of the most popular logging packages in the NPM ecosystem, with tens of thousands of packages depending on it, so having ulog's configuration mechanism be compatible with it makes for a very smooth migration path. If your app or library is currently using debug, you should be able to replace it with ulog with no or only minor changes.

We configure ulog by adjusting configuration options.

  • log: The main setting to control logger's levels with
  • debug: For compatibility with debug
  • log_config: To specify the configuration file (Node JS only)
  • log_output: To configure where logging should go
  • log_drain: To configure where logs should drain
  • log_format: To configure the format for log messages
  • log_color: To enable or disable colors
  • log_align: To enable or disable alignment

Log configuration syntax

debug has a simple but powerful configuration mechanism. You set an environment variable or localStorage option named DEBUG and you assign it a value that expresses which loggers to enable. E.g.:

DEBUG=test,my:*,-my:lib

The format is basically a comma-separated list of logger names, using the asterisk as a wildcard character and optionally negating the expression by preceding it with a minus sign. So the expression above includes test and my:* loggers, except for my:lib which is excluded.

ulog extends this configuration mechanism. With debug, you can only turn loggers on and off, but ulog allows for a much more varied range of options. This is achieved by extending the configuration syntax so it also accepts the value applicable for the loggers matching the expression. Also we allow a semicolon-separated list of such expression=value pairs. For example, to set the logger test to debug and my:* loggers except for my:lib to info, we could write:

log=test=debug;my:*,-my:lib=info

If an option only contains a value, ulog implicitly adds * as the expression. So we can write:

log=info

and it's equivalent to

log=*=info

We can even combine this. So we could write:

log=info;my:*=debug

and it will set the level for all loggers to info, except for the ones starting with my:, which are set to debug.

A special case is the config option debug, which is designed to be compatible with debug so code using ulog will react to that setting in the same way.

Most of the config options support this syntax.

Via program arguments

On Node JS we can pass log configuration options as program arguments:

node ./myapp log=debug

This should be helpful when making CLI applications. These strongly rely on console messages, but are also often used in scripted setups where we would actually want to suppress that logging. Don't go and build in all kinds of custom methods to configure the logging but just use ulog and rely on it's powerful configuration mechanism instead.

Via environment variables

On Node JS we can pass log configuration options via environment variables:

log=debug node ./myapp

Via a config file

On Node JS we can place our log configuration in a file that will be read at startup and monitored for changes at runtime:

./log.config

log=debug

Via querystring parameters

In browsers, we can pass log configuration options as querystring parameters in the URL:

https://example.com/page?log=debug

Via localStorage

In browsers, we can place our log configuration in localStorage and it will be read at startup and monitored for changes at runtime:

localStorage.setItem('log', 'debug')

Dynamic config

ulog's configuration mechanism watches the configuration for changes while the program is running and reacts to them in real-time. No more restarting your application just to change some log level! On Node JS, ulog watches a configuration file. Use Config option log_format to specify the location. In browsers, ulog monitors localStorage for changes.

Config option log

Configure the levels loggers should be filtered at.

log=test=debug;my:*,-my:lib=info

Config option debug

Enables debug mode for the selected loggers.

debug=test,my:*,-my:lib

This option is compatible with that of debug.

Config option log_config

Specify the path to the log configuration file, absolute or relative to the current working directory. Node JS only. Default to ./log.config. This option does not support expressions.

log_config=./my.log.config

Config option log_output

Specify the name of the output logs should be written to. Defaults to 'console'.

log_output=my:*,-my:lib=console

Config option log_drain

Specify the name of the output logs should be drained to. Defaults to noop. When log messages are filtered out, they are sent to the drain instead of to the normal output. The default noop output is just a noop, but you could override this to send them to a separate file for example.

log_drain=my:*,-my:lib=noop

Config option log_format

Specify the format to use. Defaults to lvl name:22 message perf on Node JS and lvl name perf on browsers.

log_format=lvl name perf message;my:*=lvl name perf

This sets lvl name perf message as the default format, while assigning a different format string to all loggers starting with my:.

For more details, refer to the section on formatting

Config option log_color

Specify whether colors should be enabled. Defaults to on.

log_color=off

Config option log_align

Specify whether messages should be aligned. Defaults to on.

log_align=off

Mods

ulog is completely modular, consisting of a microscopically small core and a whole bunch of mods. The mods are small pieces of functionality, designed to be usable stand-alone or in combination with each other. All functionality that ulog adds to anylogger, it adds in the form of mods. All, except for the ability to add mods itself, in the form of method ulog.use, that is in core.

To add a mod, call ulog.use with either an individual mod

ulog.use({
  // an empty mod
})

or with an array of mods:

ulog.use([
  { /* a mod */ },
  { /* another mod */ },
])

A mod is an object that can define many things declaratively:

  • That it extends some core functionality
  • That it uses other mods
  • That it adds settings
  • That it adds properties to loggers
  • That it adds outputs / formats / other components
  • That it watches some config option

As an example, here is a mod that does most of these things:

var boolean = require('ulog/mods/props/boolean')

ulog.use({
  use: [ require('./some/mod/we/use') ],
  extend: { someProp: 'addedToUlog' },
  settings: {
    cool: {
      config: 'log_cool'
      prop: boolean(),
    }
  },
  outputs: {
    file: {
      log: function(){
        console.info.apply(console, ['file'].concat([].slice.call(arguments)))
      }
    }
  },
  formats: {
    random: function(ctx, rec){
      return function(){
        return Math.random()
      }
    }
  },
  watch: {
    'debug': function(){
      console.info('debug setting changed')
    }
  }
})

Interestingly, most of these features of mods are being added by mods. For example the ability to add settings is added by the settings mod. Studying the way ulog's featureset is built from mods by reading the source code of the included mods is the best way to learn about writing mods for now.

Lazy loading

ulog being built with mods makes it easy to use lazy loading to reduce the minimum footprint. If you use ulog from a script tag (not recommended), you can get it for free. Otherwise, you have to configure your bundler to code-split.

Lazy-loading with Webpack

Have a look at ulog.bundle.js for an example of doing lazy loading with Webpack.

Basically it only loads ulog/base and then configures a watch that loads the remaining mods on-demand. It's using Webpack-specific API require.ensure for this. Other bundlers support it in similar ways.

var ulog = require('ulog/base')

// add a mod to ulog to load logging on-demand
ulog.use({
  watch: {
    // watch for changes in these config keys
    'debug,log':
    // when changes happen, load the other mods if needed
    function(){
      // webpack specific API to lazy load modules
      require.ensure(
        // ensure these modules are loaded
        [ 'ulog/mods/lazy' ],
        // then execute this function, notice require being overridden
        function(require){
          // use the overridden require to lazy load the modules
          if (ulog.use(require('ulog/mods/lazy'))) {
            // re-initialize the loggers if mods were added
            ulog.ext()
          }
        },
        'ulog.lazy' // chunkname webpack will use: ulog.lazy.min.js
      )
    },
  }
})

If you have successfully lazy-loaded ulog with other bundlers, please drop a comment in the issue tracker, or create a PR to add a section about it here in the README.

Decoupling code from debug

As I said before, debug is excellent for debug logging and there are tens of thousands of packages using it. The downside of this is that you will more or less get debug shoved through your throat if you include any of these libraries because they are tighly coupled to it. But we can uncouple them with the magic of bundlers like Webpack.

To replace debug with anylogger everywhere, you can use this Webpack alias to make all modules that use debug switch to anylogger instead:

{
    resolve: {
        alias: {
            'debug': 'anylogger'
        }
    }
}

This works because the anylogger API is backwards compatible with that of debug. And because ulog has native anylogger support, once you import ulog in your entry point, all those libraries that were using debug start to use ulog automagically!

Performance considerations

By default, the logging methods on the log object that correspond to a log level which is higher than the currently set level, are replaced by no-op methods. As such, you generally don't have to worry about the performance overhead of leaving the log statements in the production code. There is one exception to this rule though. If preparing the message itself is a costly operation, you may want to surround the log code with an if (log.enabledFor(level)) statement:

if (log.enabledFor('info')) {
	var message = doLotsOfWorkToGenerateLogMessage();
	log.info(message);
}

Issues

Add an issue in the issue tracker to let me know of any problems you find, or questions you may have.

Credits

Credits go to:

  • Felix Geisendörfer from debuggable.com for kindly giving up the ulog namespace on NPM. Thanks Felix!
  • TJ Holowaychuk for creating debug, which was a great inspiration for ulog.
  • Tim Perry for creating loglevel, which was another great inspiration for ulog.
  • Community members Jakub Jirutka and Alex Kesling for making code contributions.

Copyright

Copyright 2021 by Stijn de Witt.

License

Licensed under the MIT Open Source license.

ulog's People

Contributors

akesling avatar download avatar greenkeeper[bot] avatar greenkeeperio-bot avatar jirutka avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ulog's Issues

v2: Change license to MIT

For v2, switch to the MIT license.
CC-BY is currently recommending against using it for software projects.
See #28

Disable log_color programatically

  • ulog version: 2.0.0-beta.18

I cannot disable log_color programmatically for all loggers. I have tried

const ulog = require("ulog");
ulog.set("log_color", "off");
// or
ulog.set("log_color", false);

But if I create a logger afterwards const log = ulog("my"), it logs with colors. (Same with log_align).

Note: disabling or excluding whole colors mod would be also fine.

Function is printed instead of logger name

After a Chrome update ulog suddenly prints the complete formatting function instead of the logger name.

image

The function that is printed (function() {return fmt(rec)} function() {return fmt(rec)} function() {return fmt(rec)}) appears to be this one: https://github.com/Download/ulog/blob/master/mods/formats/formatter.js#L19

Looks like a function call is missing somewhere? Is this a known problem? I have upgraded to the latest beta version (2.0.0-beta.18) and the problem is the same.

EDIT:

Some more information: The problem only occurs with Chrome Version 92.0.4515.107. With the version I had installed before it worked correctly. Also with Firefox everything works as expected.

Strip ulog completely from production codes with Webpack/Babel

Just wondering what is the recommended approach to strip the ulog dependency and related logging codes completely from production codes? Like for instance, with Babel there's the babel-plugin-transform-remove-console to remove all console.* codes, or with Webpack the strip-loader to do so.

Ulog doesn't work in Safari

In Safari, webpage using ulog crash with SyntaxError: Invalid regular expression: invalid group specifier name error in console.

For example, in my case, the webapp will not render at all (web page is blank). It looks that it because ulog uses regex lookbehind not supported in Safari at all (even latest 14). It must be a long standing bug. I tried 1.5 year old versions with the same result.

I'm not able to tell you more. Because it makes our web app completely unusable for Mac users, we have to replace ulog with alternative. However, here are screenshots just before replacing ulog.

image

CleanShot 2021-04-16 at 10 35 38@2x

Q: Ulog for mac command line?

Have been using ulog in the browser and it works well.

I am trying to a script like this "node test.js" in the command line. This JS file includes a library that includes ulog.

webpack:////usr/local/lib/node_modules/ulog/browser.js?:3
var qs = location.search.substring(1),
^
ReferenceError: location is not defined

Is there any solution to this? I am writing a test harness for a library, and would like to test some functions without invoking the browser.

Logging via format string no longer works, advice please

@Download in version one, I could write a positional string formatter.

log.debug('setting return-url: %s', uri);

Now, it seems the default is to use template strings (presumably kurly);

log.debug(`setting return-url: ${uri}`);

With my old format, I now get

image

I can't quite grok how to swap back to the original format. Is is possible?

Cheers

ps I have been sitting on this change for quite a while and only now trying to address it.

Sort out typescript support

I would like ulog to have typescript support out of the box.

This has been requested multiple times:

Now is the time to do it as the library design for v2 has mostly stabilized now and the public signature for ulog is mostly clear.
There are some challenges to overcome though.

We have the complex situation that methods and properties are being added to ulog and all loggers dynamically at runtime through mods. E.g. in normal situations (using the default mods), two methods, get() and set(), will be added to ulog by the settings mod. In fact any mod that has an extends key will end up extending the ulog object itself. Looking at the source of the settings mod for example we see this:

module.exports = {
  extend: {
    settings: {},
    get: function() { /* ... */  },
    set: function() { /* ... */ },
  }
}

So this mod adds a settings property and methods get and set to ulog.

I have no idea how to model this with Typescript... Maybe mods could include their own typescript definitions? But I think we will find that it's not actually possible because Typescript AFAIK is a compile-time tool so cannot rely on run-time behavior.

So for now my idea is this:

We start simple and first make the base typescript definition that extends the one from anylogger and only adds the stuff that is added in core:

ulog.mods = []
ulog.use = function(mod) { /* ... */ }

Let's call this core.d.ts

We then make typescript definitions for all the standard entries that ulog offers:

  • ulog: The basic config you get when you require('ulog')
  • full: The config with all mods included you get when you require('ulog/full')
  • debug: A config designed to closely resemble debug
  • ... (not sure yet)

So we would get:

  • ulog.d.ts extends core.d.ts
  • full.d.ts extends ulog.d.ts
  • debug.d.ts extends ulog.d.ts
  • ...

I would love for someone that is experienced with Typescript to prepare a PR for this. This time, it will get merged, I promise you!

Here is a list of of people that have expressed an interest in adding typescript to ulog in the past:

I am hoping one of you finds the time to help out with this. I know you wanted this in the past, but the project (read: I) wasn't ready for it yet. Now, we are ready to add this, so grab your chance and get listed on the credits! 👍

cannot add custom formats

Hi, this libraray looks like exactly what I need.
I am trying to add a custom format, but it fails with "Cannot read property 'push' of undefined"

It is with the latest best, I have put a codesandbox here:
https://codesandbox.io/s/731lrno381

If you uncomment the line:
// appLog.formats.push(myFormat);
it will show the exception.

Also not really important for me, but I noticed that TRACE level wasnt showing up when.

Let me know if you have some idea about the formatter, I just want to be able to do exactly what your example shows - prefix the message with the module-name i.e. main-app or component-XYZ.

Configurable polling timeout in watch.browser

Hi

Nice project. Love the modular architecture. 👍

One thing i was missing was an configurable timeout. Would be nice in production mode to have bigger polling timeout than in development since it get then changed less often. 😉

So in watch.browser.js the hard coded value should be replaced by a variable, maybe a function parameter.

Then it could be implemented as a setting parameter:

var settings = grab(this, 'settings', {})
name = settings[name] && settings[name].config || name

BTW: There seems to be a bug 🐛 in the code. The interval get not stopped if someone call ulog.set("log_config", {});

set: function(name) {
if (name === 'log_config') config.update(this)

Maybe a destructor callback would do the trick. At the end, watch.browser.js would look something like that:

module.exports = function(ulog, pollingTimeout) {
  const intervalId = window.setInterval(watchFnc, pollingTimeout);
  return () => {
      window.clearInterval(intervalId);
  }
}

An in-range update of cross-env is breaking the build 🚨

Version 3.2.0 of cross-env just got published.

Branch Build failing 🚨
Dependency cross-env
Current Version 3.1.4
Type devDependency

This version is covered by your current version range and after updating it in your project the build failed.

As cross-env is “only” a devDependency of this project it might not break production or downstream projects, but “only” your build or test tools – preventing new deploys or publishes.

I recommend you give this issue a high priority. I’m sure you can resolve this 💪


Status Details
  • continuous-integration/travis-ci/push The Travis CI build could not complete due to an error Details
Release Notes v3.2.0

<a name"3.2.0">

3.2.0 (2017-03-04)

Features

  • revamp: revamp the entire lib (backward compatible) (#63) (dad00c46)
Commits

The new version differs by 4 commits .

  • dad00c4 feat(revamp): revamp the entire lib (backward compatible) (#63)
  • e33a85c docs(README): Add doc for cross-var. (#58)
  • 5e590ec docs(README): added how to use cross-env to run npm sub-scripts (#53)
  • afdb2de docs(README): mention Bash on Windows (#49)

See the full diff.

Not sure how things should work exactly?

There is a collection of frequently asked questions and of course you may always ask my humans.


Your Greenkeeper Bot 🌴

tests contains inadvertent config properties

@Download This error seems to be a problem in the test case. The config of ulog contains {log: 'pass'} so the log level become NaN which is true 😁

The question now: Why are the configs log and test either 'ok' or 'pass'?

I added some console outputs in mods/config/index.js:

update: function(ulog) {
  ...
  var changed = update(ulog.config, newCfg)
  console.log("changed: ", changed);
  ...
},

...

after: function (logger) {
  console.log("after [logger.level]: ", logger.level);
  config.update(this);
  console.log("after [logger.level]: ", logger.level);
}

Console output:

after [logger.level]:  3
changed:  [
  { name: 'test', old: 'ok', new: 'pass' },
  { name: 'log', new: 'pass' },
  { name: 'log_output', new: 'console;test=drain' }
]
after [logger.level]:  NaN

Originally posted by @Messj1 in #58 (comment)

Worker support

It should be possible to use ulog in (service) workers. And it should be possible to control the logging config of the workers. That last thing is a bit tricky as workers don't have access to localStorage for example.

We need to implement a publish / subscribe messaging system using postMessage so the ulog in the worker can get it's config from the ulog running on the main thread

[DOC] update channel and output part

Hi

I used ulog in a AMD environment with an high order channel which override the output channel if config highorder_log level get reached.
ulog is a really nice project for such extending stuff. 💪

Would be nice to have some hints in the documentation. So i rewrote the channel an output part cause the channel configuration is part of channel and not the output. Output doesn't get configured at all 😉

Hope this helps.

Channels

In ulog, logging is always "sent" to exactly one channel. The channel get selected by the configuration. It can also be selected by extending ulog with a mod. (see Custom channel selection )
A channel has one or more configurable outputs that can optionally apply formatting to the message.
By default, two channels exist:

  • output, for messages of loggers that are in debug mode, or that are at an enabled level
  • drain, for those messages that are filtered away.

Channel selection

By having a separate channel for the drain, there is the opportunity to use another output than noop(link). So you can configure where discarded messages are going.
For example: send all logging records to a database and filter it later, when it get display for example (see output)

By default all log methods on a logger are associated with one of two channels, output and drain. When the logger is created, each log method is sent either to the output channel, or to the drain channel, based on the current log level for that logger and whether that logger is in debug mode.
To configure the drain / output channel selection the following log config can be used:

log := log_setting [; log_setting] ...
log_setting:= [ logger_name [, logger_name] ... = ] log_level
log_level:= [all | error | warn | info | debug | trace | none]
logger_name:= [ * | [-]{regular expression} ]

Custom channel selection

If the default behavior is not enough the channel selection in ulog can easily be extended like followed:

ulog.use({
    after: function (logger) {
        for (var level in this.levels) {
            var channel = logger.enabledFor(level) ? 'output': 'drain';
            // calculate new channel
            logger[level] = logger.channels[channel].fns[level];
        }
    },
});

Output configuration

The channel output, where messages are going, is completely configurable at runtime.
To configure the outputs for a channel, use the log_ config option. The default channels config options are:

To configure the output for a channel, we assign the name of the output to use to the relevant loggers:

log_output=console

This setting can include expressions to target individual loggers, just like the debug and log settings:

log_output=console;noisy-lib:*=noop

The value part is actually a kurly format string. The same syntax can be used here as for configuring formatting.
If more than one output is specified, a multiplex function will be inserted that dispatches the logging to all specified outputs.

log_output=console custom_output

Syntax:

log_channel := channel_setting [; channel_setting] ...
channel_setting:= [ logger_name [, logger_name] ... = ] output_name [ output_name] ...
logger_name:= [ * | [-]{regular expression} ]
output_name:= defined outputs in ulog

Hint: The first match get used. Except the default logger_name (*)

Outputs

Outputs are used in channels to peform the actual task and "log" the data.
By default, the following outputs are included:

Output console

...

Output noop

...

Fix formatting

Either don't apply formatting, or do, but the current hybrid design is confusing.

If the inheritable formats property is empty, don't apply any formatting and leave line numbers intact
If there are formats configured, make sure all log methods use these formats.

Can't get the `drain` functionality to work in the browser

I'm trying to test out the drain functionality in the browser but I'm not sure how to set it up. This is what I tried

import ulog from 'ulog'

ulog.use({
  outputs: {
    custom: {
      log: function (...args) {
        console.log(args)
      },
    },
  },
})

ulog.set('output', '*=custom')

This didn't seem to work, and neither did doing

localStorage.setItem('log_drain', '*=custom')

I'm expecting the custom.log function to be called but it's not being called. How would I get this to work in the browser?

Can't remove padding for name

I've tried the below settings in Node

log_format="lvl name message"
log_format="lvl name:0 message"

Neither remove the padding for name. I looked through the source and it appears to be a conflict with the default padding setting, and a Number check you have in formatter.

If I edit the source for name and remove the default padding, things behave.

As an alternative solution I looked through the docs in regards to dynamicFormats and staticFormats and it's unclear how to use those formats once created.

Use ESModules instead of module.exports

Using ulog in Angular triggers this warning:

Warning: .\src\app\app.module.ts depends on 'ulog'. CommonJS or AMD dependencies can cause optimization bailouts.
For more info see: https://angular.io/guide/build#configuring-commonjs-dependencies

'debug' seems to be adding ESModules to v5

debug-js/debug#786

core ext javascript blocking time

Ah yes. This is not pure, but there is an issue with the notify sometimes taking too long, so I tried to split up the work. Chrome complains sometimes about setTimeout handler taking too long to complete. This happens when it hovers around the 50ms mark.

Originally posted by @Download in #52 (comment)

Remove dependency on anylogger

From README.md https://github.com/Download/ulog#use:

npm install --save anylogger && npm install --save-dev ulog

However, when I want to use ulog without anylogger:

 npm install --save ulog

then the anylogger is installed anyway because it's in the packages.json as dependencies. The logger should be independent to logging facade. (IMO logging facade is great for lib development, but I do not find it useful for the app development.)

License motivations - CC-BY

Why use such license? I'm wondering about your motivations. (it's not very well suited for software)

Why not MIT or similar?

image

Debug levels and scoped to modules unreliably works

I wondering if it is just me. I want to change (and also have set) log levels through local storage (in Chrome). It is described here.

On occasion that I have managed to get levels and scoping to work, I have to set BOTH the log level and the scoping. This isn't documented.

For example, I have a module 'Loader' that I want to exclude:

eg I set these values in Chrome > Dev tools > Application > Local Storage < [site]:
key | value
log | debug
debug | *,-Loader

Does this resonate with anyone? Cheers in advance

ps I load ulog via anylogger

log no longer maps to the console object? log.dir broken?

In 2.0.0 beta 13, a call to log.dir() used to map to console.dir() in Chrome.

In beta 14 thru 18 this functionality is broken, and I see an error in Chrome's console that says log.dir() is not a valid function. Will this functionality be restored? Or is there a better way to move forward?

Rollup & ulog not working (no log output)

I can't seem to get rollup & ulog to work. My expectation is that I should install anylogger as a dependency and ulog as a devDependency, then import "ulog" followed by import anylogger from "anylogger" and create a log object that can be used to log strings. Unfortunately, there is no log message output.

I've written a repro so it can be easier to debug/give guidance:

https://github.com/canadaduane/ulog-test

Thanks.

Support 'structured logging'

Many logging frameworks support 'structured logging'.
Client code passes as first parameter an object to the log method, and the fields of this object are copied to the log record.

E.g.:

var log = anylogger('my:logger')
log.info({ some: 'field'}, 'Some message'}

In combination with a JSON output, this should give output something like this:

{ "level": "info", "name": "my:logger", "some": "field", "message": ["Some message"]}

No logging message appear (ulog doesn't look to be loaded)

The documentation for running anylogger assumes anylogger as bundled package where it is directly loaded in the browser and globally available. The problem looks to be that ulog is not actually bundled because the only reference to ulog as the implementation is an import. Bundlers will treeshake the package.

Expected

Add logging to a project and logging messages will be written to the console in the browser

Actual

Nothing appears inn the browser console.

Steps to reproduce

Created a brand new Vue project and add logging

  1. vue create logger-demo
  2. yarn install anylogger ulog
  3. add the logging code into main.js
  4. yarn serve
  5. load up the browser, dev tools and look at messages

Based on the sample README, only a reference to anylogger is required.

import anylogger from 'anylogger';

const log = anylogger('Logging');
log.debug('I will not log unless log level is reduced');
log.error('I should log even in default settings');

// result nothing logs

Add ulog import, still doesn't work (under my build system-let's assume "tree shaking" broadly)

import anylogger from 'anylogger';
import ulog from 'ulog';

const log = anylogger('Logging');
log.debug('I will not log unless log level is reduced');
log.error('I should log even in default settings');

// result still nothing logs (as ulog still isn't there)

Of course, the logging library is actually required to implement the interface. Without typings, it all gets hard and picked something random to log that ensures the library is bundled.

import anylogger from 'anylogger';
import ulog from 'ulog';

const log = anylogger('Logging');
// need a reference to include library and ensure no linting errors
log.debug(ulog.levels);  // ah, need typings so added `declare module 'ulog'` (or could use `ulog.d.ts`)
log.debug('I will not log unless log level is reduced');
log.error('I should log even in default settings');

// now I log

ReferenceError: util is not defined

[0] /home/neo/projects/dm-iwish/node_modules/ulog/node.js:12
[0] logger = function(){stream.write(util.format.apply(this, arguments) + '\n')}
[0] ^
[0]
[0] ReferenceError: util is not defined
[0] at Object.logger (/home/neo/projects/dm-iwish/node_modules/ulog/node.js:12:37)
[0] at Object. (/home/neo/projects/dm-iwish/node_modules/just-wait/bin/just-wait.js:64:6)
[0] at Module._compile (module.js:556:32)
[0] at Object.Module._extensions..js (module.js:565:10)
[0] at Module.load (module.js:473:32)
[0] at tryModuleLoad (module.js:432:12)
[0] at Function.Module._load (module.js:424:3)
[0] at Module.require (module.js:483:17)
[0] at require (internal/module.js:20:19)
[0] at Object. (/home/neo/projects/dm-iwish/node_modules/just-wait/index.js:1:63)
[0] npm run watch-server-start exited with code 1

Benchmark

Hi

The benchmark of the diary project seem to be a little unusually since it reconfigure ulog every time.
https://github.com/maraisr/diary/blob/0170945748168a5fa4b9b2927371f5d7d20bb3b5/bench/index.ts#L70-L86

ulog() {
    let events: any[] = [];
    ulog.use([
	    {
		    outputs: {
			    custom: {
				    log(...args) {
					    events.push(args);
				    },
			    },
		    },
	    },
    ]);
    const suite = ulog('standard');
    suite.output = 'custom';
    suite.info('info message');
    return events;
}

I think you should make an issue that every log framework owner should write the test by himself.

Benchmark
diary x 840,954 ops/sec ±1.18% (89 runs sampled)
ulog x 25,409 ops/sec ±40.24% (10 runs sampled)

As far as i know is the console log call directly mapped on the logger. So it can't be faster than a direct call. I think a good benchmark would be to see

  • the memory usage of an logger object and the ulog.
  • the time usage to initialize
  • the performance to configure an new logger

localStorage variables not working - maybe it's me

I'm new to React, so it might-be/probably-is me. But, I can't get the localStorage variables to work in my app like they do in the tutorial. The URL variable does work.

I set the Application -> Local Storage -> {local url}
key | value
log | warn; app:re*=all

My loggers are named app:render, app:discover, app:updateFile.

I only want the app:render logs to show everything.

It also does not see the log_format localStorage entry either. So, I'm wondering if this feature just hasn't been completed yet. Or do I have something set wrong?

I can get to the localStorage.getItem( 'log' ) from inside my app just fine - it returns whatever is in there.

Hopefully, it's something simple I'm missing.

v2: Add `anylogger` support

Ulog has a microscopically small footprint of just over 1kB. But we can do better!

anylogger currently has a footprint of just over 500 bytes. We can make ulog depend on anylogger and only add the missing features. This will make ulog the first library with native anylogger support.

Libraries can depend on anylogger instead of on ulog and only add a ~500 bytes footprint shared with all other libraries depending on anylogger. App developers can then plug in whatever logging framework they like. But with ulog depending on anylogger, we can probably beat most other loggers out there in terms of total dependency size. We should be able to stay below 1.5kB total at the app level and finally have a way for all libraries to participate in logging without incurring expensive overhead.

Config polling interval does not stop on resetting config

Split off from #52 where @Messj1 wrote:

BTW: There seems to be a bug 🐛 in the code. The interval get not stopped if someone call ulog.set("log_config", {});

set: function(name) {
if (name === 'log_config') config.update(this)

Maybe a destructor callback would do the trick. At the end, watch.browser.js would look something like that:

module.exports = function(ulog, pollingTimeout) {
  const intervalId = window.setInterval(watchFnc, pollingTimeout);
  return () => {
      window.clearInterval(intervalId);
  }
}

Is log_config being honored?

I am having trouble getting log_config to be honored. When I specify log_config like this:

    "watch:serve": "export log_config=./local.config.prod && ts-node-dev --respawn ./src/server.ts",

ulog does not pick up the filename from the log_config env var.

I am likely using this improperly and the example in your documentation does not provide a practical example (it shows log_config=./my.log.config). It is possible I am missing something in the docs though.

I have also tried this:

ulog.set('log_config', './log.config.dev');

Is there something I would need to do w/r to ulog to get it to reload configs from file?

Any help would be greatly appreciated.

Usage with Next.js - Tons of logs (circular config?)

Getting started with ulog, doesn't seem to work fine.

  • I get unrelated logs that print the actual ulog object 🤔
  • I get a circular name, because both pages A and B load the logger.ts file

PR/branch: https://github.com/UnlyEd/next-right-now/pull/50/files


utils/app/logger.ts

import ulog from 'ulog';

// Formats aren't applied when doing logger.info()
// See https://github.com/Download/ulog#formatting
const myFormat = (logger, method, args): void => {
  // add the logger name to the call
  args.unshift(`[${logger.name}] `);
};

/**
 * Create a new
 *
 * @param name
 * @param options
 */
export const createLogger = (name: string, options?: object): any => {
  ulog.level = process.env.APP_STAGE === 'production' ? ulog.ERROR : ulog.DEBUG;
  ulog.formats.push(myFormat);

  return ulog(name);
};

export default createLogger;

Usage:

import { createLogger } from '../../utils/app/logger';

const fileLabel = 'components/appBootstrap/MultiversalAppBootstrap';
const logger = createLogger(fileLabel);

... in react component
logger(props);

Log output:

[Function: components/appBootstrap/MultiversalAppBootstrap] {
  NONE: 0,
  ulog: { version: '2.0.0-beta.6' },
  ERROR: 1,
  WARN: 2,
  INFO: 3,
  LOG: 4,
  DEBUG: 5,
  TRACE: 6,
  error: [Function: bound ],
  warn: [Function: bound ],
  info: [Function: bound ],
  log: [Function: bound ],
  verbose: [Function: bound ],
  debug: [Function: bound ],
  trace: [Function: nop],
  silly: [Function: nop],
  dir: [Function: bound bound consoleCall],
  table: [Function: bound bound consoleCall],
  time: [Function: bound bound consoleCall],
  timeEnd: [Function: bound bound consoleCall],
  assert: [Function: bound bound consoleCall]
} debug [
  '[components/appBootstrap/MultiversalAppBootstrap] ',
  '[components/appBootstrap/MultiversalAppBootstrap] ',
  '[components/appBootstrap/MultiversalAppBootstrap] ',
  {
    Component: [Function: WithApollo] { displayName: 'withApollo(ExamplesPage)' },
    router: ServerRouter {
      route: '/[locale]/examples',
      pathname: '/[locale]/examples',
      query: [Object],
      asPath: '/fr/examples',
      isFallback: false,
      basePath: ''
    },
    pageProps: {
      apolloState: [Object],
      bestCountryCodes: [Array],
      customer: [Object],
      customerRef: 'customer1',
      i18nTranslations: [Object],
      gcmsLocales: 'FR, EN',
      hasLocaleFromUrl: true,
      isReadyToRender: true,
      isStaticRendering: true,
      lang: 'fr',
      locale: 'fr',
      products: [Array]
    },
    __N_SSG: true
  }
]
[Function: components/appBootstrap/MultiversalAppBootstrap] {
  NONE: 0,
  ulog: { version: '2.0.0-beta.6' },
  ERROR: 1,
  WARN: 2,
  INFO: 3,
  LOG: 4,
  DEBUG: 5,
  TRACE: 6,
  error: [Function: bound ],
  warn: [Function: bound ],
  info: [Function: bound ],
  log: [Function: bound ],
  verbose: [Function: bound ],
  debug: [Function: bound ],
  trace: [Function: nop],
  silly: [Function: nop],
  dir: [Function: bound bound consoleCall],
  table: [Function: bound bound consoleCall],
  time: [Function: bound bound consoleCall],
  timeEnd: [Function: bound bound consoleCall],
  assert: [Function: bound bound consoleCall]
} debug [
  '[components/appBootstrap/MultiversalAppBootstrap] ',
  '[components/appBootstrap/MultiversalAppBootstrap] ',
  '[components/appBootstrap/MultiversalAppBootstrap] ',
  '[components/appBootstrap/MultiversalAppBootstrap] ',
  {
    Component: [Function: WithApollo] { displayName: 'withApollo(ExamplesPage)' },
    router: ServerRouter {
      route: '/[locale]/examples',
      pathname: '/[locale]/examples',
      query: [Object],
      asPath: '/fr/examples',
      isFallback: false,
      basePath: ''
    },
    pageProps: {
      apolloState: [Object],
      bestCountryCodes: [Array],
      customer: [Object],
      customerRef: 'customer1',
      i18nTranslations: [Object],
      gcmsLocales: 'FR, EN',
      hasLocaleFromUrl: true,
      isReadyToRender: true,
      isStaticRendering: true,
      lang: 'fr',
      locale: 'fr',
      products: [Array]
    },
    __N_SSG: true
  }
]
[Function: components/appBootstrap/MultiversalAppBootstrap] {
  NONE: 0,
  ulog: { version: '2.0.0-beta.6' },
  ERROR: 1,
  WARN: 2,
  INFO: 3,
  LOG: 4,
  DEBUG: 5,
  TRACE: 6,
  error: [Function: bound ],
  warn: [Function: bound ],
  info: [Function: bound ],
  log: [Function: bound ],
  verbose: [Function: bound ],
  debug: [Function: bound ],
  trace: [Function: nop],
  silly: [Function: nop],
  dir: [Function: bound bound consoleCall],
  table: [Function: bound bound consoleCall],
  time: [Function: bound bound consoleCall],
  timeEnd: [Function: bound bound consoleCall],
  assert: [Function: bound bound consoleCall]
} debug [
  '[components/appBootstrap/MultiversalAppBootstrap] ',
  '[components/appBootstrap/MultiversalAppBootstrap] ',
  '[components/appBootstrap/MultiversalAppBootstrap] ',
  '[components/appBootstrap/MultiversalAppBootstrap] ',
  '[components/appBootstrap/MultiversalAppBootstrap] ',
  {
    Component: [Function: WithApollo] { displayName: 'withApollo(ExamplesPage)' },
    router: ServerRouter {
      route: '/[locale]/examples',
      pathname: '/[locale]/examples',
      query: [Object],
      asPath: '/fr/examples',
      isFallback: false,
      basePath: ''
    },
    pageProps: {
      apolloState: [Object],
      bestCountryCodes: [Array],
      customer: [Object],
      customerRef: 'customer1',
      i18nTranslations: [Object],
      gcmsLocales: 'FR, EN',
      hasLocaleFromUrl: true,
      isReadyToRender: true,
      isStaticRendering: true,
      lang: 'fr',
      locale: 'fr',
      products: [Array]
    },
    __N_SSG: true
  }
]
[Function: components/appBootstrap/MultiversalAppBootstrap] {
  NONE: 0,
  ulog: { version: '2.0.0-beta.6' },
  ERROR: 1,
  WARN: 2,
  INFO: 3,
  LOG: 4,
  DEBUG: 5,
  TRACE: 6,
  error: [Function: bound ],
  warn: [Function: bound ],
  info: [Function: bound ],
  log: [Function: bound ],
  verbose: [Function: bound ],
  debug: [Function: bound ],
  trace: [Function: nop],
  silly: [Function: nop],
  dir: [Function: bound bound consoleCall],
  table: [Function: bound bound consoleCall],
  time: [Function: bound bound consoleCall],
  timeEnd: [Function: bound bound consoleCall],
  assert: [Function: bound bound consoleCall]
} debug [
  '[components/appBootstrap/MultiversalAppBootstrap] ',
  '[components/appBootstrap/MultiversalAppBootstrap] ',
  '[components/appBootstrap/MultiversalAppBootstrap] ',
  '[components/appBootstrap/MultiversalAppBootstrap] ',
  '[components/appBootstrap/MultiversalAppBootstrap] ',
  '[components/appBootstrap/MultiversalAppBootstrap] ',
  {
    Component: [Function: WithApollo] { displayName: 'withApollo(ExamplesPage)' },
    router: ServerRouter {
      route: '/[locale]/examples',
      pathname: '/[locale]/examples',
      query: [Object],
      asPath: '/fr/examples',
      isFallback: false,
      basePath: ''
    },
    pageProps: {
      apolloState: [Object],
      bestCountryCodes: [Array],
      customer: [Object],
      customerRef: 'customer1',
      i18nTranslations: [Object],
      gcmsLocales: 'FR, EN',
      hasLocaleFromUrl: true,
      isReadyToRender: true,
      isStaticRendering: true,
      lang: 'fr',
      locale: 'fr',
      products: [Array]
    },
    __N_SSG: true
  }
]
[components/appBootstrap/MultiversalAppBootstrap]  [components/appBootstrap/MultiversalAppBootstrap]  [components/appBootstrap/MultiversalAppBootstrap]  [components/appBootstrap/MultiversalAppBootstrap]  [components/appBootstrap/MultiversalAppBootstrap]  [components/appBootstrap/MultiversalAppBootstrap]  [components/appBootstrap/MultiversalAppBootstrap]  [components/appBootstrap/MultiversalAppBootstrap]  [components/appBootstrap/MultiversalAppBootstrap]  [components/appBootstrap/MultiversalAppBootstrap]  [components/appBootstrap/MultiversalAppBootstrap]  [components/appBootstrap/MultiversalAppBootstrap]  [components/appBootstrap/MultiversalAppBootstrap]  [components/appBootstrap/MultiversalAppBootstrap]  [components/appBootstrap/MultiversalAppBootstrap]  [components/appBootstrap/MultiversalAppBootstrap]  [components/appBootstrap/MultiversalAppBootstrap]  [components/appBootstrap/MultiversalAppBootstrap]  [components/appBootstrap/MultiversalAppBootstrap]  [components/appBootstrap/MultiversalAppBootstrap]  [components/appBootstrap/MultiversalAppBootstrap]  [components/appBootstrap/MultiversalAppBootstrap]  [components/appBootstrap/MultiversalAppBootstrap]  [components/appBootstrap/MultiversalAppBootstrap]  {
  Component: [Function: WithApollo] { displayName: 'withApollo(ExamplesPage)' },
  router: ServerRouter {
    route: '/[locale]/examples',
    pathname: '/[locale]/examples',
    query: { locale: 'fr' },
    asPath: '/fr/examples',
    isFallback: false,
    basePath: ''
  },
  pageProps: {
    apolloState: {
      'Customer:ck73s1dqm2ue30b98uepy9is3': [Object],
      '$Customer:ck73s1dqm2ue30b98uepy9is3.theme': [Object],
      'Asset:ck8mzb256padd0b84v3446rik': [Object],
      ROOT_QUERY: [Object]
    },
    bestCountryCodes: [ 'fr', 'en' ],
    customer: {
      id: 'ck73s1dqm2ue30b98uepy9is3',
      label: 'Mon client adoréasd4',
      theme: [Object],
      __typename: 'Customer'
    },
    customerRef: 'customer1',
    i18nTranslations: { fr: [Object] },
    gcmsLocales: 'FR, EN',
    hasLocaleFromUrl: true,
    isReadyToRender: true,
    isStaticRendering: true,
    lang: 'fr',
    locale: 'fr',
    products: [ [Object] ]
  },
  __N_SSG: true
}

How to configure ulog with different levels?

Thanks for the great library, it's exactly what I was looking for.

I had some problems setting the levels of the logger. It would be great to document how to do this when instantiating through anylogger! I couldn't find any examples here, or in anylogger's docs that would allow you to set a default level for all logs on startup. I have tried the debug style with setting DEBUG from env vars, but that doesn't seem to register.

I understand the default level is info, which seems to work, but how can I change this at runtime?

Here's a minimal 3-file sample where I've tried to log with ulog.
The log.info statement will print, but the log.debug will not.

// ----------------------------------------------------------------------------------
// config.ts

import dotenv from 'dotenv'

export function parseEnv() {
    const conf = dotenv.config()
    if (conf.error) throw conf.error
    return Object.freeze(conf.parsed)
}

// ----------------------------------------------------------------------------------
// .env
log=debug

// ----------------------------------------------------------------------------------
// main.ts
import "ulog"
import anylogger from 'anylogger'
const log = anylogger('main')

parseEnv()

log.info('Logging is easy!')
log.debug('Logging is hard!')

The order to calling ulog.enable(...) and anylogger(...) does matter

I find the order to calling ulog.enable(...) and anylogger(...) does matter. A logger should be defined first with anylogger('test') and then call ulog.enable('test'), otherwise it would not work. It is counter-intuitive for me. Maybe document should mention this.

Best regards,
Rice Yeh

Fail to build on windows

npm install

> [email protected] prepare C:\D\repos\ulog
> npm run test -S && npm run clean -S && npm run build -S


> [email protected] test C:\D\repos\ulog
> nyc tape test.js 2>&1 | tap-nirvana


    'sh' is not recognized as an internal or external command,
    operable program or batch file.
    ----------|---------|----------|---------|---------|-------------------
    File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
    ----------|---------|----------|---------|---------|-------------------
    All files |       0 |        0 |       0 |       0 |
    ----------|---------|----------|---------|---------|-------------------

Can't quite figure out how the drain functionality works

I'm doing this

import ulog from 'ulog'

export const log = ulog('sample-app')

log.level = log.WARN

ulog.use({
  outputs: {
    drain: {
      log: function (...args) {
        console.log('drain...', args)
      },
    },
  },
})

ulog.set('drain', 'drain')

log('foo...')
log.warn('warn...')

In the browser, the call log('foo') gets sent to drain. But the call log.warn('warn...') doesn't and I don't understand why. How can I set this up so that all log levels get sent to drain?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.