# @xen-orchestra/log

> Logging system with decoupled producers/consumer

Latest version **0.7.2** (published 2026-04-24) · ISC license · 0 weekly downloads

## Install

```sh
npm install @xen-orchestra/log
pnpm add @xen-orchestra/log
yarn add @xen-orchestra/log
bun add @xen-orchestra/log
```

## Health

**Score 65/100 (B)** — status: active.

Positive: has types; esm support; no vulnerabilities; high maintenance score; high quality score.

Warnings: low downloads; pre 1.0.

## Facts

| | |
|---|---|
| Version | 0.7.2 |
| Published | 2026-04-24 |
| First published | 2018-10-31 |
| Weekly downloads | 0 |
| License | ISC |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Node | >=8.3 |
| Dependencies | 2 |
| Unpacked size | 24.7 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| GitHub stars | 988 |
| Author | Vates SAS |
| Maintainers | mpiton, mlssfrncjrg, mathieura, florent.beauchamp, julien-f, marsaud, pdonias, benjireis, tgoettelmann, b-nollet, joris-k, elise-f, olivier.f, pierre.brunet289 |
| Keywords | async, asynchronous, capture, context |

## Links

- npm: https://www.npmjs.com/package/@xen-orchestra/log
- Repository: https://github.com/vatesfr/xen-orchestra
- Homepage: https://github.com/vatesfr/xen-orchestra/tree/master/@xen-orchestra/log
- Issues: https://github.com/vatesfr/xen-orchestra/issues
- npm.io page: https://npm.io/package/@xen-orchestra/log

## Dependencies (2)

- [lodash](https://npm.io/package/lodash.md) ^4.18.0
- [promise-toolbox](https://npm.io/package/promise-toolbox.md) ^0.21.0

## Alternatives

- [@commercetools/sync-actions](https://npm.io/package/@commercetools/sync-actions.md) — 25.1K weekly downloads
- [cwait](https://npm.io/package/cwait.md) — 21.4K weekly downloads
- [@ledgerhq/hw-app-cosmos](https://npm.io/package/@ledgerhq/hw-app-cosmos.md) — 4.2K weekly downloads
- [@financial-times/o-loading](https://npm.io/package/@financial-times/o-loading.md) — 2.8K weekly downloads
- [fa](https://npm.io/package/fa.md) — 185 weekly downloads

## Recent versions

- 0.7.2 (latest) — 2026-04-24
- 0.7.1 — 2024-11-14
- 0.7.0 — 2024-10-28
- 0.6.0 — 2023-01-27
- 0.5.0 — 2022-11-29
- 0.4.0 — 2022-10-27
- 0.3.0 — 2021-08-30
- 0.2.1 — 2021-05-26
- 0.2.0 — 2019-09-27
- 0.1.4 — 2018-10-31
- 0.1.3 — 2018-10-31
- 0.1.2 — 2018-10-31
- 0.1.1 — 2018-10-31
- 0.1.0 — 2018-10-31

## README

<!-- DO NOT EDIT MANUALLY, THIS FILE HAS BEEN GENERATED -->

# @xen-orchestra/log

[![Package Version](https://badgen.net/npm/v/@xen-orchestra/log)](https://npmjs.org/package/@xen-orchestra/log) ![License](https://badgen.net/npm/license/@xen-orchestra/log) [![PackagePhobia](https://badgen.net/bundlephobia/minzip/@xen-orchestra/log)](https://bundlephobia.com/result?p=@xen-orchestra/log) [![Node compatibility](https://badgen.net/npm/node/@xen-orchestra/log)](https://npmjs.org/package/@xen-orchestra/log)

> Logging system with decoupled producers/consumer

## Install

Installation of the [npm package](https://npmjs.org/package/@xen-orchestra/log):

```sh
npm install --save @xen-orchestra/log
```

## Usage

### Producers

Everywhere something should be logged:

```js
import { createLogger } from '@xen-orchestra/log'

const log = createLogger('my-module')

log.debug('only useful for debugging')
log.info('this information is relevant to the user')
log.warn('something went wrong but did not prevent current action')
log.error('something went wrong')
log.fatal('service/app is going down')

// you can add contextual info
log.debug('new API request', {
  method: 'foo',
  params: [ 'bar', 'baz' ]
  user: 'qux'
})

// by convention, errors go into the `error` field
log.error('could not join server', {
  error,
  server: 'example.org',
})
```

A logging method has the following signature:

```ts
interface LoggingMethod {
  (error): void

  (message: string, data?: { error?: Error; [property: string]: any }): void
}
```

### Consumer

Then, at application level, configure the logs are handled:

```js
import { createLogger } from '@xen-orchestra/log'
import { configure, catchGlobalErrors } from '@xen-orchestra/log/configure'
import transportConsole from '@xen-orchestra/log/transports/console'
import transportEmail from '@xen-orchestra/log/transports/email'

const transport = transportEmail({
  service: 'gmail',
  auth: {
    user: 'jane.smith@gmail.com',
    pass: 'H&NbECcpXF|pyXe#%ZEb',
  },
  from: 'jane.smith@gmail.com',
  to: ['jane.smith@gmail.com', 'sam.doe@yahoo.com'],
})

configure([
  {
    filter: process.env.DEBUG,

    transport: transportConsole(),
  },
  {
    // only levels >= warn
    level: 'warn',

    transport,
  },
  {
    type: 'email',

    service: 'gmail',
    auth: {
      user: 'jane.smith@gmail.com',
      pass: 'H&NbECcpXF|pyXe#%ZEb',
    },
    from: 'jane.smith@gmail.com',
    to: ['jane.smith@gmail.com', 'sam.doe@yahoo.com'],
  },
])

// send all global errors (uncaught exceptions, warnings, unhandled rejections)
// to this logger
catchGlobalErrors(createLogger('app'))
```

A transport as expected by `configure(transport)` can be:

- a function that will receive emitted logs;
- an object with a `type` property and options which will be used to create a transport (see next section);
- an object with a nested `transport` which will be used if one of the following conditions is fulfilled:
  - `filter`: [pattern](https://github.com/visionmedia/debug#wildcards) which is matched against the log namespace (can also be an array of filters);
  - `level`: the minimal level of accepted logs;
- an array of transports.

### Transports

#### Console

```js
import transportConsole from '@xen-orchestra/log/transports/console'

configure(transportConsole())
```

#### Email

Optional dependency:

```
> yarn add nodemailer pretty-format
```

Configuration:

```js
import transportEmail from '@xen-orchestra/log/transports/email'

configure(
  transportEmail({
    service: 'gmail',
    auth: {
      user: 'jane.smith@gmail.com',
      pass: 'H&NbECcpXF|pyXe#%ZEb',
    },
    from: 'jane.smith@gmail.com',
    to: ['jane.smith@gmail.com', 'sam.doe@yahoo.com'],
  })
)
```

#### Syslog

Optional dependency:

```
> yarn add split-host syslog-client
```

Configuration:

```js
import transportSyslog from '@xen-orchestra/log/transports/syslog'

// By default, log to udp://localhost:514
configure(transportSyslog())

// But TCP, a different host, or a different port can be used
configure(transportSyslog({ target: 'tcp://syslog.company.lan' }))
```

### Helpers

#### Dedupe

> Wraps a transport to limit the number of duplicate logs.

```js
import { dedupe } from '@xen-orchestra/log/dedupe'

configure(
  dedupe({
    timeout: 500e3, // default to 600e3, ie 10 minutes
    transport: console.log,
  })
)
```

Duplicate logs will be buffered for `timeout` milliseconds or until a different log is emitted, at which time a dedicated log entry is emitted which indicate the number of duplicates that occurred.

```js
const logger = createLogger('app')

// Log some duplicate messages
logger.error('Something went wrong')
logger.error('Something went wrong')
logger.error('Something went wrong')

// Log a different message
logger.info('This is a different message')
```

In this example, the first three log entries are identical and the last two will be treated as duplicates. They will be grouped together and sent to the transport as a single log with `nDuplicates: 2` data when a different log entry is emitted (or after the timeout as elapsed if there weren't one).

The output in the console would look something like:

```
app ERROR Something went wrong
app ERROR duplicates of the previous log were hidden { nDuplicates: 2 }
app INFO This is a different message
```

#### Capture

> Allow capturing all logs emitted during a call, even through asynchronous operations.

Before being able to use this feature, you need to add the transport:

```js
import { configure } from '@xen-orchestra/log/configure'
import { createCaptureTransport } from '@xen-orchestra/log/capture'
import createConsoleTransport from '@xen-orchestra/log/transports/console'

// transport that will be used globally, when not in a captured environment
const fallbackTransport = {
  filter: process.env.DEBUG,
  level: 'warn',

  transport: createConsoleTransport(),
}

// create the capture transport and pass it the fallback one
const captureTransport = createCaptureTransport(fallbackTransport)

// configure @xen-orchestra/log to use our transport
configure(captureTransport)
```

Now the `captureLogs(onLog, fn)` can be used:

```js
import { captureLogs } from '@xen-orchestra/log/capture'
import { createLogger } from '@xen-orchestra/log'

const logger = createLogger('my-logger')

await captureLogs(
  (log, fallbackTransport) => {
    // every logs emitted in the async context of `fn` will arrive here
    //
    // do not emit logs in this function or this will create a loop.

    // logs can be forwarded to the fallback transport
    fallbackTransport(log)
  },
  async () => {
    logger.debug('synchronous logs are captured')

    setTimeout(() => {
      logger.debug('logs from asynchronous callbacks too')
    }, 50)

    await new Promise(resolve => setTimeout(resolve, 50))

    logger.debug('logs in async functions or promise chains too')

    // To escape capture, run code in `captureLogs` with `undefined`
    // as the first param
    captureLogs(undefined, () => {
      logger.debug('this log will not be captured')
    })

    // Returned value and error is forwarded by `captureLogs`
    return Math.PI
  }
)
```

## Contributions

Contributions are _very_ welcomed, either on the documentation or on
the code.

You may:

- report any [issue](https://github.com/vatesfr/xen-orchestra/issues)
  you've encountered;
- fork and create a pull request.

## License

[ISC](https://spdx.org/licenses/ISC) © [Vates SAS](https://vates.fr)

---
_Source: https://npm.io/package/@xen-orchestra/log · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
