@connectedcars/logutil
Version:
Simple log formatting for Node
252 lines (160 loc) • 8.87 kB
Markdown
# node-logutil
[](https://travis-ci.org/connectedcars/node-logutil)
[](https://coveralls.io/github/connectedcars/node-logutil?branch=master)
## Installation
``` javascript
npm install https://github.com/connectedcars/node-logutil#v1.1.3
```
## API
### log.debug(message[, context])
Logs the provided `message` with the optional `context` as a JSON string, if the log level is set to output debug.
The method takes most input, but the combination described above is the recommended one.
#### message
Type: `string`
Descriptive log message
#### context
Type: `object`
Object hash to provide log context.
### log.statistic(message[, context])
Logs the provided `message` with the optional `context` as a JSON string, if the log level is set to output statistic.
The method takes most input, but the combination described above is the recommended one.
#### message
Type: `string`
Descriptive log message
#### context
Type: `object`
Object hash to provide log context.
### log.info(message[, context])
Logs the provided `message` with the optional `context` as a JSON string, if the log level is set to output info.
The method takes most input, but the combination described above is the recommended one.
#### message
Type: `string`
Descriptive log message
#### context
Type: `object`
Object hash to provide log context.
### log.warn(message[, context])
Logs the provided `message` with the optional `context` as a JSON string, if the log level is set to output warnings.
The method takes most input, but the combination described above is the recommended one.
#### message
Type: `string`
Descriptive log message
#### context
Type: `object`
Object hash to provide log context.
### log.error(message[, context])
Logs the provided `message` with the optional `context` as a JSON string, if the log level is set to output errors.
The method takes most input, but the combination described above is the recommended one.
#### message
Type: `string`
Descriptive log message
#### context
Type: `object`
Object hash to provide log context.
### log.critical(message[, context])
Logs the provided `message` with the optional `context` as a JSON string, if the log level is set to output critical.
The method takes most input, but the combination described above is the recommended one.
#### message
Type: `string`
Descriptive log message
#### context
Type: `object`
Object hash to provide log context.
## log.MetricRegistry()
A singleton instance that holds the state of all metrics in the application at a given point in time. The class should be instantiated globally and kept through the process lifetime.
### log.MetricsRegistry.prototype.cumulative(name, value[, labels])
A monotically increasing number that. E.g. the number of requests since the process started.
#### name
Type: `string`
Name of the metric to write. The recommended format is `[namespace]/[metric-name]`. E.g. `ingester/queue-size`
#### value
Type: `number`
A value to write. It's worth noting that we're always using floating-point precision for these values.
#### labels
Type: `object`
An optional label map that denotes some specific properties you want to group by / filter on later. Please note that this map _must_ have hashable values. Thus, you're not allowed to nest objects.
It's also worth noting that the `MetricRegistry` will log all permutations of the label, so please only use it to store categorical types and not, say `carId`s.
Example: `{tableName: 'LatestCarPositions'}`
##### Scenarios where you'd want to use this metric type
Generally every time you're interested in monitoring:
* the relative change in a value (marginal difference)
* or the value relative to the time period
E.g.
* Inserted rows since last time
* Incoming HTTP requests
### log.MetricsRegistry.prototype.gauge(name, value[, labels, reducerFn])
_Please refer to `log.MetricsRegistry.prototype.cumulative()` for a definition of the arguments name, value and labels_
An instantaneous measurement of a varying number. Per default, we will only store the value and timestamp of the latest inserted value. So if you insert two values, e.g. `gauge('foo', 2)` at time 0 and `gauge('foo', 4)` at time 2, and the metrics are scraped at time 3, we will report `value=4,time=2` to the log. If you for some reason don't want this, you can specify an optional `reducerFn` argument:
```
// Function that takes the mean over all values in that timespan
const fn = (arr) => arr.reduce((a, b) => a+b)) / arr.length
// Sample some data
registry.gauge('foo', 10, null, fn)
registry.gauge('foo', 20, null, fn)
// logMetric reports value=30 and generates a timestamp
registry.logMetrics()
```
##### Scenarios where you'd want to use this metric type
Every time you want to capture a state at a particular time. E.g.:
* Size of a queue at a particular time
* CPU utilization
A rule of thumb: If a value can go vary (i.e. go up and down), and it should be captured at a specific time, then it's a good candidate for a gauge.
### log.MetricRegistry.prototype.logMetrics()
Captures a snapshot of the image state and adds `endTime=Date.now()` to all objects. Afterwards all metrics are dumped to stdout using `node-logutil`'s `log.statistic` function. All metrics are dumped using `LOG_LEVEL=INFO`, so make sure your application uses at least that log level.
This function is intended to be used together with [metrics-subscriber-api](https://github.com/connectedcars/metrics-subscriber-api)
### log.MetricRegistry.prototype.getMetrics()
Returns all metrics as an array of objects.
Example:
```
{
name: 'gauge-metric',
type: 'GAUGE',
value: 50,
labels: { brand: 'vw' }
}
```
### log.MetricRegistry.prototype.getPrometheusMetrics()
Returns an array of strings formatted using the [Prometheus exposition format](https://github.com/prometheus/docs/blob/master/content/docs/instrumenting/exposition_formats.md)
The format should follow their EBNF definition, but is not tested against a parser as of this moment.
This function is intended to be exposed through a small HTTP server and used in conjunction with Prometheus in the future.
## Usage
``` javascript
const log = require('logutil')
log.debug('This is a debug message')
// Outputs to stdout: {"message":"This is a debug message","severity":"DEBUG","timestamp":"2017-09-01T13:37:42Z"}
log.debug('This is a statistic value', { foo: 42, bar: 1337 })
// Outputs to stdout: {"message":"Statistics","severity":"STATISTIC","timestamp":"2017-09-01T13:37:42Z", "content":{"foo":42,"bar":1337}}
log.info('This is an info message')
// Outputs to stdout: {"message":"This is an info message","severity":"INFO","timestamp":"2017-09-01T13:37:42Z"}
log.warn('This is a warning message', { type: 'missing-item' })
// Outputs to stderr: {"message":"This is a warning message","severity":"WARNING","timestamp":"2017-09-01T13:37:42Z","context":{"type":"missing-item"}}
log.error('This is an error message', { context: { items: ['foo', 'bar'] } })
// Outputs to stderr: {"message":"This is an error message","severity":"ERROR","timestamp":"2017-09-01T13:37:42Z","context":{"items":["foo","bar"]}}
log.critical('This is a critical message', { context: { items: ['foo', 'bar'] } })
// Outputs to stderr: {"message":"This is a critical message","severity":"CRITICAL","timestamp":"2017-09-01T13:37:42Z","context":{"items":["foo","bar"]}}
log.debug(() => {
// Only runs if log level is debug
return Promise.resolve('This is a debug message')
})
// Outputs to stdout: {"message":"This is a debug message","severity":"DEBUBG","timestamp":"2017-09-01T13:37:42Z"}
log.info(() => {
// Only runs if log level is info
return new Promise(resolve => {
setTimeout(() => {
resolve('This is an info message')
}, 500)
})
})
// Outputs to stdout (after 500 ms): {"message":"This is an info message","severity":"INFO","timestamp":"2017-09-01T13:37:42Z"}
// Instantiate as a singleton
const registry = log.MetricRegistry()
// Write some data
await registry.gauge('namespace/metric-name', 20, {brand: 'vw'})
await registry.cumulative('namespace/cumulative-metric-name', 40, {brand: 'seat'})
// In case you want to do aggregations more granular than the regular dump, you can pass in a reducer function taking an array of numbers.
await registry.gauge('namespace/metric-name', 20, {brand: 'vw'}, (arr) => arr.reduce((a, b) => a+b)) / arr.length
// Dump metrics to stdout regularly
setInterval(registry.logMetrics, 30000)
```
## Configuration
Determining what to output depends on the environment variable `LOG_LEVEL`. This variable can be `DEBUG`, `STATISTIC`, `INFO`, `WARN`, `ERROR`, or `CRITICAL` but defaults to `WARN`.