# Technical Documentation: klaudiosinani/signale

> ℹ️ **Provenance:** Multi-Tier Ingestion · [CodeWiki Reference](https://codewiki.google/github.com/klaudiosinani/signale)

### Overview (`readme.md`)

<h1 align="center">
  Signale
</h1>

<h4 align="center">
  Highly configurable logging library
</h4>

<div align="center">
  <img alt="Header" src="media/header.png" width="88%">
</div>

<p align="center">
  <a href="https://www.npmjs.com/package/signale">
    <img alt="NPM Downloads" src="https://img.shields.io/npm/dt/signale.svg">
  </a>
</p>

<div align="center">
  <br>
  <sup><b>Sponsored by:</b></sup>
  <br>
  <a href="https://betterstack.com">
    <div>
      <img src="https://github.com/Seldaek/monolog/assets/183678/7de58ce0-2fa2-45c0-b3e8-e60cebb3c4cf" width="200" alt="Better Stack">
    </div>
    <sup>
      Spot, Resolve, and Prevent Downtime.
    </sup>
  </a>
</div>

## Description

Hackable and configurable to the core, signale can be used for logging purposes, status reporting, as well as for handling the output rendering process of other node modules and applications.

Read this document in: [Albanian - Shqip](https://github.com/klaudiosinani/signale/blob/master/docs/readme.AL.md), [简体中文 - Simplified Chinese](https://github.com/klaudiosinani/signale/blob/master/docs/readme.zh_CN.md), [German - Deutsch](https://github.com/klaudiosinani/signale/blob/master/docs/readme.DE.md).

You can now support the development process through [GitHub Sponsors](https://github.com/sponsors/klaudiosinani).

Visit the [contributing guidelines](https://github.com/klaudiosinani/signale/blob/master/contributing.md#translating-documentation) to learn more on how to translate this document into more languages.

## Highlights

- 19 out-of-the-box loggers
- Hackable to the core
- Clean and beautiful output
- Integrated timers
- Custom pluggable loggers
- TypeScript support
- Interactive and regular modes
- Secrets & sensitive information filtering
- Filename, date and timestamp support
- Scoped loggers and timers
- Scaled logging levels mechanism
- String interpolation support
- Multiple configurable writable streams
- Simple and minimal syntax
- Globally configurable through `package.json`
- Overridable configuration per file and logger

## Contents

- [Description](#description)
- [Highlights](#highlights)
- [Install](#install)
- [Usage](#usage)
- [Configuration](#configuration)
- [API](#api)
- [Development](#development)
- [Related](#related)
- [Who's Using It?](#whos-using-it)
- [Team](#team)
- [Sponsors](#sponsors)
- [License](#license)

## Install

### Yarn

```bash
yarn add signale
```

### NPM

```bash
npm install signale
```

## Usage

### Default Loggers

Import signale and start using any of the default loggers.

<details>
<summary>View all of the available loggers.</summary>

<br/>

- `await`
- `complete`
- `error`
- `debug`
- `fatal`
- `fav`
- `info`
- `note`
- `pause`
- `pending`
- `star`
- `start`
- `success`
- `wait`
- `warn`
- `watch`
- `log`

</details>

<br/>

```js
const signale = require('signale');

signale.success('Operation successful');
signale.debug('Hello', 'from', 'L59');
signale.pending('Write release notes for %s', '1.2.0');
signale.fatal(new Error('Unable to acquire lock'));
signale.watch('Recursively watching build directory...');
signale.complete({prefix: '[task]', message: 'Fix issue #59', suffix: '(@klaudiosinani)'});
```

<div align="center">
  <img alt="Default Loggers" src="media/default-loggers.png" width="65%">
</div>

### Custom Loggers

To create a custom logger define an `options` object yielding a `types` field with the logger data and pass it as argument to a new signale instance.

```js
const {Signale} = require('signale');

const options = {
  disabled: false,
  interactive: false,
  logLevel: 'info',
  scope: 'custom',
  secrets: [],
  stream: process.stdout,
  types: {
    remind: {
      badge: '**',
      color: 'yellow',
      label: 'reminder',
      logLevel: 'info'
    },
    santa: {
      badge: '🎅',
      color: 'red',
      label: 'santa',
      logLevel: 'info'
    }
  }
};

const custom = new Signale(options);
custom.remind('Improve documentation.');
custom.santa('Hoho! You have an unused variable on L45.');
```

<div align="center">
  <img alt="Custom Loggers" src="media/custom-loggers.png" width="70%">
</div>

Here is an example where we override the default `error` and `success` loggers.

```js
const {Signale} = require('signale');

const options = {
  types: {
    error: {
      badge: '!!',
      label: 'fatal error'
    },
    success: {
      badge: '++',
      label: 'huge success'
    }
  }
};

const signale = new Signale();
signale.error('Default Error Log');
signale.success('Default Success Log');

const custom = new Signale(options);
custom.error('Custom Error Log');
custom.success('Custom Success Log');
```

<div align="center">
  <img alt="Default Loggers" src="media/override-defaults.png" width="65%">
</div>

The `options` object can hold any of the following attributes: `disabled`, `interactive`, `logLevel`, `secrets`, `stream`, `scope` and `types`. 

##### `disabled`

- Type: `Boolean`
- Default: `false`

Disables the logging functionality of all loggers belonging to the created instance.

##### `interactive`

- Type: `Boolean`
- Default: `false`

Switches all loggers belonging to the created instance into the interactive mode.

##### `logLevel`

- Type: `String`
- Default: `'info'`

Sets the general logging level of the created instance. Can be one of the following:

- `'info'` - Displays all messages from all loggers.
- `'timer'` -  Displays messages only from the `time`, `timeEnd`, `debug`, `warn`, `error` & `fatal` loggers.
- `'debug'` - Displays messages only from the `debug`, `warn`, `error` & `fatal` loggers.
- `'warn'` - Displays messages only from the `warn`, `error` & `fatal` loggers.
- `'error'` - Displays messages only from the `error` & `fatal` loggers.

##### `secrets`

- Type: `(String|Number)[]`
- Default: `[]`

An array holding secrets/sensitive-information to be removed from the body and metadata of to-be-logged messages and replaced with the default `'[secure]'` string.

##### `stream`

- Type: `stream.Writable|stream.Writable[]`
- Default: `process.stdout`

Destination to which the data is written, can be a single valid [Writable stream](https://nodejs.org/api/stream.html#stream_writable_streams) or an array holding multiple valid Writable streams.

##### `scope`

- Type: `String|String[]`

Name of the scope the logger is reporting from.

##### `types`

- Type: `Object`

Holds the configuration of the custom and default loggers.

Additionally, the configuration object of each custom/default logger type, defined in the `types` option, can hold any of the following attributes: `badge`, `label`, `color`, `logLevel` & `stream`. 

##### `badge`

- Type: `String`

The icon corresponding to the logger.

##### `label`

- Type: `String`

The label used to identify the type of the logger.

##### `color`

- Type: `String`

The color of the label, can be any of the foreground colors supported by [chalk](https://github.com/chalk/chalk#colors).

##### `logLevel`

- Type: `String`
- Default: `'info'`

The log level corresponding to the logger. Messages originating from the logger are displayed only if the log level is greater or equal to the above described general logging level `logLevel` of the `Signale` instance.

##### `stream`

- Type: `stream.Writable|stream.Writable[]`
- Default: `process.stdout`

Destination to which the data is written, can be a single valid [Writable stream](https://nodejs.org/api/stream.html#stream_writable_streams) or an array holding multiple valid Writable streams.

### Scoped Loggers

To create a scoped logger from scratch, define the `scope` field inside the `options` object and pass it as argument to a new signale instance.

```js
const {Signale} = require('signale');

const options = {
  scope: 'global scope'
};

const global = new Signale(options);
global.success('Successful Operation');
```

<div align="center">
  <img alt="Scope Vanilla" src="media/scope-vanilla.png" width="65%">
</div>

To create a scoped logger based on an already existing one, use the `scope()` function, which will return a new signale instance, inheriting all custom loggers, timers, secrets, streams, configuration, log level, interactive mode & disabled statuses from the initial one.

```js
const signale = require('signale');

const global = signale.scope('global scope');
global.success('Hello from the global scope');

function foo() {
  const outer = global.scope('outer', 'scope');
  outer.success('Hello from the outer scope');
  
  setTimeout(() => {
    const inner = outer.scope('inner', 'scope'); 
    inner.success('Hello from the inner scope');
  }, 500);
}

foo();
```

<div align="center">
  <img alt="Scope Existing" src="media/scope-existing.png" width="65%">
</div>

### Interactive Loggers

To initialize an interactive logger, create a new signale instance with the [`interactive`](#interactive) attribute set to `true`. While into the interactive mode, previously logged messages originating from an interactive logger, will be overridden only by new ones originating from the same or a different interactive logger. Note that regular messages originating from regular loggers are not overridden by the interactive ones.

```js
const {Signale} = require('signale');

const interactive = new Signale({interactive: true, scope: 'interactive'});

interactive.await('[%d/4] - Process A', 1);

setTimeout(() => {
  interactive.success('[%d/4] - Process A', 2);
  setTimeout(() => {
    interactive.await('[%d/4] - Process B', 3);
    setTimeout(() => {
      interactive.error('[%d/4] - Process B', 4);
      setTimeout(() => {}, 1000);
    }, 1000);
  }, 1000);
}, 1000);
```

<div align="center">
  <img alt="Interactive Mode" src="media/interactive-mode.gif" width="65%">
</div>

### Writable Streams

By default, all signale instances log their messages to the `process.stdout` stream. This can be modified, to match your own preference, through the [`stream`](#stream) property, where you can define a single or multiple valid Writable streams, which will be used by all logger types to log your data. Additionally, it is possible to define one or more Writable streams exclusively for a specific logger type, thus write data independently from the rest logger types.

```js
const {Signale} = require('signale');

const options = {
  stream: process.stderr, // All loggers will now write to `process.stderr`
  types: {
    error: {
      // Only `error` will write to both `process.stdout` & `process.stderr`
      stream: [process.stdout, process.stderr]
    }
  }
};

const signale = new Signale(options);
signale.success('Message will appear on `process.stderr`');
signale.error('Message will appear on both `process.stdout` & `process.stderr`');
```

<div align="center">
  <img alt="Writable Streams" src="media/writable-streams.png" width="73%">
</div>

### Secrets Filtering

By utilizing the `secrets` option, secrets and other sensitive information can be filtered out from the body as well as the metadata, i.e. scope names etc, of to-be-logged messages. The option is part of the configuration object passed to a `Signale` instance on its initialization, and is of type `Array<String|Number>`. The array can hold multiple secrets, all of which are removed, if present, from the to-be-logged messages and are replaced with the default `'[secure]'` string. Additionally, when the unary `signale.scope(name)` function is used, the returned `Signale` instance inherits all the secrets belonging to its parent. The secrets checking process is performed in a **case-sensitive** manner. Also, the unary [`signale.addSecrets()`](https://github.com/klaudiosinani/signale#signaleaddsecretssecrets) and the nullary [`signale.clearSecrets()`](https://github.com/klaudiosinani/signale#signaleclearsecrets) functions are available through the API for adding and clearing secrets respectively.

It is **critical** and **highly recommended** to **not type directly secrets in your code**, thus the following example serves **only** as a simple & easily reproducible usage demonstration.

```js
const {Signale} = require('signale');

// In reality secrets could be securely fetched/decrypted through a dedicated API 
const [USERNAME, TOKEN] = ['klaudiosinani', 'token'];

const logger1 = new Signale({
  secrets: [USERNAME, TOKEN]
});

logger1.log('$ exporting USERNAME=%s', USERNAME);
logger1.log('$ exporting TOKEN=%s', TOKEN);

// `logger2` inherits all secrets from its parent `logger1`
const logger2 = logger1.scope('parent');

logger2.log('$ exporting USERNAME=%s', USERNAME);
logger2.log('$ exporting TOKEN=%s', TOKEN);
```

<div align="center">
  <img alt="Secrets Filtering" src="media/filter-secrets.png" width="73%">
</div>

### Timers

Timer are managed by the `time()` and `timeEnd()` functions. A unique label can be used to identify a timer on initialization, though if none is provided the timer will be assigned one automatically. In addition, calling the `timeEnd()` function without a specified label will have as effect the termination of the most recently initialized timer, that was created without providing a label.

```js
const signale = require('signale');

signale.time('test');
signale.time();
signale.time();

setTimeout(() => {
  signale.timeEnd();
  signale.timeEnd();
  signale.timeEnd('test');
}, 500);
```

<div align="center">
  <img alt="Timers" src="media/timers.png" width="60%">
</div>

## Configuration

### Global

To enable global configuration define the options under the `signale` namespace in your `package.json`.

The following illustrates all the available options with their respective default values.

```json
{
  "signale": {
    "displayScope": true,
    "displayBadge": true,
    "displayDate": false,
    "displayFilename": false,
    "displayLabel": true,
    "displayTimestamp": false,
    "underlineLabel": true,
    "underlineMessage": false,
    "underlinePrefix": false,
    "underlineSuffix": false,
    "uppercaseLabel": false
  }
}
```

<details>
<summary>View all of the available options in detail.</summary>

##### `displayScope`

- Type: `Boolean`
- Default: `true`

Display the scope name of the logger. 

##### `displayBadge`

- Type: `Boolean`
- Default: `true`

Display the badge of the logger.

##### `displayDate`

- Type: `Boolean`
- Default: `false`

Display the current local date in `YYYY-MM-DD` format. 

##### `displayFilename`

- Type: `Boolean`
- Default: `false`

Display the name of the file that the logger is reporting from.

##### `displayLabel`

- Type: `Boolean`
- Default: `true`

Display the label of the logger.

##### `displayTimestamp`

- Type: `Boolean`
- Default: `false`

Display the current local time in `HH:MM:SS` format.

##### `underlineLabel`

- Type: `Boolean`
- Default: `true`

Underline the logger label.

##### `underlineMessage`

- Type: `Boolean`
- Default: `false`

Underline the logger message.

##### `underlinePrefix`

- Type: `Boolean`
- Default: `false`

Underline the logger prefix.

##### `underlineSuffix`

- Type: `Boolean`
- Default: `false`

Underline the logger suffix.

##### `uppercaseLabel`

- Type: `Boolean`
- Default: `false`

Display the label of the logger in uppercase.

</details>

### Local

To enable local configuration call the `config()` function on your signale instance. Local configurations will always override any pre-existing configuration inherited from `package.json`.

In the following example, loggers in the `foo.js` file will run under their own configuration, overriding the `package.json` one.

```js
// foo.js
const signale = require('signale');

// Overrides any existing `package.json` config
signale.config({
  displayFilename: true,
  displayTimestamp: true,
  displayDate: false
}); 

signale.success('Hello from the Global scope');
```

<div align="center">
  <img alt="Local Config" src="media/local-config.png" width="65%">
</div>

Also, scoped loggers can have their own independent configuration, overriding the one inherited by the parent instance or `package.json`. 

```js
// foo.js
const signale = require('signale');

signale.config({
  displayFilename: true,
  displayTimestamp: true,
  displayDate: false
});

signale.success('Hello from the Global scope');

function foo() {
  // `fooLogger` inherits the config of `signale`
  const fooLogger = signale.scope('foo scope');

  // Overrides both `signale` and `package.json` configs
  fooLogger.config({
    displayFilename: true,
    displayTimestamp: false,
    displayDate: true
  });

  fooLogger.success('Hello from the Local scope');
}

foo();
```

<div align="center">
  <img alt="Scope Config" src="media/scope-config.png" width="65%">
</div>

## API

#### signale.`<logger>(message[, message]|messageObj|errorObj)`

##### **`logger`**

- Type: `Function`

Can be any default or custom logger.

##### **`message`**

- Type: `String`

Can be one or more comma delimited strings.

```js
const signale = require('signale');

signale.success('Successful operation');
//=> ✔  success  Successful operation

signale.success('Successful', 'operation');
//=> ✔  success  Successful operation

signale.success('Successful %s', 'operation');
//=> ✔  success  Successful operation
```

##### **`errorObj`**

- Type: `Error Object`

Can be any error object.

```js
const signale = require('signale');

signale.error(new Error('Unsuccessful operation'));
//=> ✖  error  Error: Unsuccessful operation
//        at Module._compile (module.js:660:30)
//        at Object.Module._extensions..js (module.js:671:10)
//        ...
```

##### **`messageObj`**

- Type: `Object`

Can be an object holding the `prefix`, `message` and `suffix` attributes, with `prefix` and `suffix` always prepended and appended respectively to the logged `message`.

```js
const signale = require('signale');

signale.complete({prefix: '[task]', message: 'Fix issue #59', suffix: '(@klaudiosinani)'});
//=> [task] ☒  complete  Fix issue #59 (@klaudiosinani)

signale.complete({prefix: '[task]', message: ['Fix issue #%d', 59], suffix: '(@klaudiosinani)'});
//=> [task] ☒  complete  Fix issue #59 (@klaudiosinani)
```

#### signale.`scope(name[, name])`

Defines the scope name of the logger.

##### **`name`**

- Type: `String`

Can be one or more comma delimited strings.

```js
const signale = require('signale');

const foo = signale.scope('foo'); 
const fooBar = signale.scope('foo', 'bar');

foo.success('foo');
//=> [foo] › ✔  success  foo

fooBar.success('foo bar');
//=> [foo] [bar] › ✔  success  foo bar
```

#### signale.`unscope()`

Clears the scope name of the logger.

```js
const signale = require('signale');

const foo = signale.scope('foo'); 

foo.success('foo');
//=> [foo] › ✔  success  foo

foo.unscope();

foo.success('foo');
//=> ✔  success  foo
```

#### signale.`config(settingsObj)`

Sets the configuration of an instance overriding any existing global or local configuration.

##### **`settingsObj`**

- Type: `Object`

Can hold any of the documented [options](#global).

```js
// foo.js
const signale = require('signale');

signale.config({
  displayFilename: true,
  displayTimestamp: true,
  displayDate: true
});

signale.success('Successful operations');
//=> [2018-5-15] [11:12:38] [foo.js] › ✔  success  Successful operations
```

#### signale.`time([, label])`

- Return Type: `String`

Sets a timers and accepts an optional label. If none provided the timer will receive a unique label automatically.

Returns a string corresponding to the timer label. 

##### **`label`**

- Type: `String`

Label corresponding to the timer. Each timer must have its own unique label.

```js
const signale = require('signale');

signale.time();
//=> ▶  timer_0  Initialized timer...

signale.time();
//=> ▶  timer_1  Initialized timer...

signale.time('label');
//=> ▶  label    Initialized timer...
```

#### signale.`timeEnd([, label])`

- Return Type: `Object`

Deactivates the timer to which the given label corresponds. If no label is provided the most recent timer, that was created without providing a label, will be deactivated.

Returns an object `{label, span}` holding the timer label and the total running time.

##### **`label`**

- Type: `String`

Label corresponding to the timer, each timer has its own unique label.

```js
const signale = require('signale');

signale.time();
//=> ▶  timer_0  Initialized timer...

signale.time();
//=> ▶  timer_1  Initialized timer...

signale.time('label');
//=> ▶  label    Initialized timer...

signale.timeEnd();
//=> ◼  timer_1  Timer run for: 2ms

signale.timeEnd();
//=> ◼  timer_0  Timer run for: 2ms

signale.timeEnd('label');
//=> ◼  label    Timer run for: 2ms
```

#### signale.`disable()`

Disables the logging functionality of all loggers belonging to a specific instance.

```js
const signale = require('signale');

signale.success('foo');
//=> ✔  success  foo

signale.disable();

signale.success('foo');
//=>
```

#### signale.`enable()`

Enables the logging functionality of all loggers belonging to a specific instance.

```js
const signale = require('signale');

signale.disable();

signale.success('foo');
//=>

signale.enable();

signale.success('foo');
//=> ✔  success  foo
```

#### signale.`isEnabled()`

Checks whether the logging functionality of a specific instance is enabled.

```js
const signale = require('signale');

signale.success('foo');
//=> ✔  success  foo

signale.isEnabled();
// => true

signale.disable();

signale.success('foo');
//=>

signale.isEnabled();
// => false
```

#### signale.`addSecrets(secrets)`

Adds new secrets/sensitive-information to the targeted Signale instance.

##### **`secrets`**

- Type: `(String|Number)[]`

Array holding the secrets/sensitive-information to be filtered out.

```js
const signale = require('signale');

signale.log('$ exporting USERNAME=%s', 'klaudiosinani');
//=> $ exporting USERNAME=klaudiosinani

signale.addSecrets(['klaudiosinani']);

signale.log('$ exporting USERNAME=%s', 'klaudiosinani');
//=> $ exporting USERNAME=[secure]
```

#### signale.`clearSecrets()`

Removes all secrets/sensitive-information from the targeted Signale instance.

```js
const signale = require('signale');

signale.addSecrets(['klaudiosinani']);

signale.log('$ exporting USERNAME=%s', 'klaudiosinani');
//=> $ exporting USERNAME=[secure]

signale.clearSecrets();

signale.log('$ exporting USERNAME=%s', 'klaudiosinani');
//=> $ exporting USERNAME=klaudiosinani
```

## Development

For more info on how to contribute to the project, please read the [contributing guidelines](https://github.com/klaudiosinani/signale/blob/master/contributing.md).

- Fork the repository and clone it to your machine
- Navigate to your local fork: `cd signale`
- Install the project dependencies: `npm install` or `yarn install`
- Lint code for errors: `npm test` or `yarn test`

## Related

- [qoa](https://github.com/klaudiosinani/qoa) - Minimal interactive command-line prompts
- [taskbook](https://github.com/klaudiosinani/taskbook) - Tasks, boards & notes for the command-line habitat
- [hyperocean](https://github.com/klaudiosinani/hyperocean) - Deep oceanic blue Hyper terminal theme

## Who's Using It?

- [Boostnote](https://github.com/BoostIO/Boostnote)
- [Docz](https://github.com/pedronauck/docz)
- [Remix by Ethereum](https://github.com/ethereum/remix)
- [Semantic Release](https://github.com/semantic-release/semantic-release)
- [Shower](https://github.com/shower/shower)
- [Taskbook](https://github.com/klaudiosinani/taskbook)
- [Vant](https://github.com/youzan/vant)

View in detail all the packages and repositories that are using Signale [here](https://github.com/klaudiosinani/signale/network/dependents).

## Team

- Klaudio Sinani [(@klaudiosinani)](https://github.com/klaudiosinani)
- Mario Sinani [(@mariosinani)](https://github.com/mariosinani)

## Sponsors

A big thank you to all the people and companies supporting our Open Source work:

- [Better Stack: Spot, Resolve, and Prevent Downtime.](https://betterstack.com/)


## License

[MIT](https://github.com/klaudiosinani/signale/blob/master/license.md)

---

### Overview (`docs/readme.md`)

## Signale Translated Documentation

- [Albanian - Shqip](https://github.com/klaudiosinani/signale/blob/master/docs/readme.AL.md) by [@klaudiosinani](https://github.com/klaudiosinani)
- [简体中文 - Simplified Chinese](https://github.com/klaudiosinani/signale/blob/master/docs/readme.zh_CN.md) by [@hardo](https://github.com/hardo)
- [German - Deutsch](https://github.com/klaudiosinani/signale/blob/master/docs/readme.DE.md) by [@villabunterkunt](https://github.com/villabunterkunt)

## Contributing

Visit the [contributing guidelines](https://github.com/klaudiosinani/signale/blob/master/contributing.md#translating-documentation) to learn more on how to get involved in the translating process.

## Thanks

Tons of thank you to the amazing people that help out with the creation and maintenance of the translations, your contributions make Signale available to everyone around the world!

---

### Readme.AL (`docs/readme.AL.md`)

<h1 align="center">
  Signale
</h1>

<h4 align="center">
  Librari regjistrimi shumë e konfigurueshëme
</h4>

<div align="center">
  <img alt="Header" src="../media/header.png" width="88%">
</div>

<p align="center">
  <a href="https://www.npmjs.com/package/signale">
    <img alt="Shkarkime NPM" src="https://img.shields.io/npm/dt/signale.svg">
  </a>
</p>

<div align="center">
  <br>
  <sup><b>Sponsorizuar nga:</b></sup>
  <br>
  <a href="https://betterstack.com">
    <div>
      <img src="https://github.com/Seldaek/monolog/assets/183678/7de58ce0-2fa2-45c0-b3e8-e60cebb3c4cf" width="200" alt="Better Stack">
    </div>
    <sup>
      Zbulo, Zgjidh dhe Parandaloni Kohën e Pushimit.
    </sup>
  </a>
</div>

## Përshkrimi

Plotësisht i modifikueshëm dhe i konfigurueshëm deri në thelb, signale mund të përdoret për qëllime regjistrimi, raportim statusi, si dhe për menaxhimin e procesit të shfaqjes së output-it të moduleve dhe aplikacioneve të tjera të node.

Lexoni këtë dokument në: [English](https://github.com/klaudiosinani/signale/blob/master/readme.md), [German - Deutsch](https://github.com/klaudiosinani/signale/blob/master/docs/readme.DE.md), [简体中文 - Simplified Chinese](https://github.com/klaudiosinani/signale/blob/master/docs/readme.zh_CN.md).

Tani mund të mbështetni procesin e zhvillimit përmes [GitHub Sponsors](https://github.com/sponsors/klaudiosinani).

Vizitoni [udhëzimet e kontributit](https://github.com/klaudiosinani/signale/blob/master/contributing.md#translating-documentation) për të mësuar më shumë se si të përktheni këtë dokument në më shumë gjuhë.

## Pikat Kryesore

- 19 regjistrues të gatshëm për përdorim
- Mund të modifikohet dhe personalizohet në çdo aspekt
- Output i pastër dhe estetikisht i bukur
- Kohëmatës të integruar
- Regjistrues të personalizueshëm
- Mbështetje për TypeScript
- Modalitete interaktive dhe të rregullta
- Filtrim sekretesh dhe informacioni të ndjeshëm
- Mbështetje për emra skedarësh, data dhe kohë
- Regjistrues dhe kohëmatës të fokusuar (scoped)
- Mekanizëm i niveleve të shkallëzuara të regjistrimit
- Mbështetje për interpolim string-jesh
- Shumë flukse të regjistrueshme të ndryshueshme
- Sintaksë e thjeshtë dhe minimale
- Konfigurim global përmes `package.json`
- Konfigurim i tejkalueshëm sipas skedari apo regjistruesi

## Përmbajtja

- [Përshkrimi](#përshkrimi)
- [Pikat Kryesore](#pikat-kryesore)
- [Instalimi](#instalimi)
- [Përdorimi](#përdorimi)
- [Konfigurimi](#konfigurimi)
- [API](#api)
- [Zhvillimi](#zhvillimi)
- [Të Lidhura](#të-lidhura)
- [Kush e përdor?](#kush-e-përdor)
- [Ekipi](#ekipi)
- [Sponsorët](#sponsorët)
- [Licenca](#licenca)

## Instalimi

### Yarn

```bash
yarn add signale
```

### NPM

```bash
npm install signale
```

## Përdorimi

### Regjistruesit e Paracaktuar

Importoni signale dhe filloni të përdorni ndonjërin nga regjistruesit e paracaktuar.

<details>
<summary>Shikoni të gjithë regjistruesit e disponueshëm.</summary>

<br/>

- `await`
- `complete`
- `error`
- `debug`
- `fatal`
- `fav`
- `info`
- `note`
- `pause`
- `pending`
- `star`
- `start`
- `success`
- `wait`
- `warn`
- `watch`
- `log`

</details>

<br/>

```js
const signale = require('signale');

signale.success('Operacioni u përfundua me sukses');
signale.debug('Përshëndetje', 'nga', 'L59');
signale.pending('Shkruaj shënime për versionin %s', '1.2.0');
signale.fatal(new Error('Nuk mund të merret pranga'));
signale.watch('Po monitorohet në mënyrë rekursive direktoria e ndërtimit...');
signale.complete({prefix: '[detyrë]', message: 'Rregullo çështjen #59', suffix: '(@klaudiosinani)'});
```

<div align="center">
  <img alt="Regjistrues të Paracaktuar" src="../media/default-loggers.png" width="65%">
</div>

### Regjistrues të Personalizuar

Për të krijuar një regjistrues të personalizuar, përcaktoni një objekt `options` që përmban fushën `types` me të dhënat e regjistruesit, dhe kalojeni si argument tek një instancë e re e signale.

```js
const {Signale} = require('signale');

const options = {
  disabled: false,
  interactive: false,
  logLevel: 'info',
  scope: 'custom',
  secrets: [],
  stream: process.stdout,
  types: {
    remind: {
      badge: '**',
      color: 'yellow',
      label: 'kujtesë',
      logLevel: 'info'
    },
    santa: {
      badge: '🎅',
      color: 'red',
      label: 'santa',
      logLevel: 'info'
    }
  }
};

const custom = new Signale(options);
custom.remind('Përmirësoni dokumentacionin.');
custom.santa('Hoho! Ke një variabël të papërdorur në L45.');
```

<div align="center">
  <img alt="Regjistrues të Personalizuar" src="../media/custom-loggers.png" width="70%">
</div>

Këtu është një shembull se si të tejkaloni regjistruesit `error` dhe `success` të paracaktuar.

```js
const {Signale} = require('signale');

const options = {
  types: {
    error: {
      badge: '!!',
      label: 'gabim fatal'
    },
    success: {
      badge: '++',
      label: 'sukses i madh'
    }
  }
};

const signale = new Signale();
signale.error('Regjistrimi i gabimit të paracaktuar');
signale.success('Regjistrimi i suksesit të paracaktuar');

const custom = new Signale(options);
custom.error('Regjistrimi i gabimit të personalizuar');
custom.success('Regjistrimi i suksesit të personalizuar');
```

<div align="center">
  <img alt="Regjistrues të Paracaktuar" src="../media/override-defaults.png" width="65%">
</div>

Objekti `options` mund të përmbajë secilën nga vetitë: `disabled`, `interactive`, `logLevel`, `secrets`, `stream`, `scope` dhe `types`.

##### `disabled`

- Tipi: `Boolean`
- Parazgjedhur: `false`

Çaktivizon funksionalitetin e regjistrimit për të gjithë regjistruesit që i përkasin instancës së krijuar.

##### `interactive`

- Tipi: `Boolean`
- Parazgjedhur: `false`

Kallon të gjithë regjistruesit në modalitet interaktiv.

##### `logLevel`

- Tipi: `String`
- Parazgjedhur: `'info'`

Vendos nivelin e përgjithshëm të regjistrimit të instancës së krijuar. Mund të jetë një nga sa vijon:

- `'info'` - Shfaq të gjitha mesazhet nga të gjithë regjistruesit.
- `'timer'` - Shfaq mesazhet vetëm nga regjistruesit `time`, `timeEnd`, `debug`, `warn`, `error` & `fatal`.
- `'debug'` - Shfaq mesazhet vetëm nga regjistruesit `debug`, `warn`, `error` & `fatal`.
- `'warn'` - Shfaq mesazhet vetëm nga regjistruesit `warn`, `error` & `fatal`.
- `'error'` - Shfaq mesazhet vetëm nga regjistruesit `error` & `fatal`.

##### `secrets`

- Tipi: `(String|Number)[]`
- Parazgjedhur: `[]`

Një varg që përmban sekrete/informacion të ndjeshëm për t'u hequr nga trupi dhe metadata e mesazheve që do regjistrohen dhe do të zëvendësohet me '[secure]'.

##### `stream`

- Tipi: `stream.Writable|stream.Writable[]`
- Parazgjedhur: `process.stdout`

Destinacioni ku të dhënat shkruhen, mund të jetë një [Writable stream](https://nodejs.org/api/stream.html#stream_writable_streams) ose një varg me më shumë flukse të tilla.

##### `scope`

- Tipi: `String|String[]`

Emri i kontekstit (scope) nga ku raporton regjistruesi.

##### `types`

- Tipi: `Object`

Përmban konfigurimin e regjistruesve të personalizuar ose paracaktuar.

Gjithashtu, objekti konfigurues i çdo tipi regjistruesi të personalizuar ose paracaktuar, mund të mbajë çdo nga këto veti: `badge`, `label`, `color`, `logLevel` & `stream`.

##### `badge`

- Tipi: `String`

Ikona që i përket regjistruesit.

##### `label`

- Tipi: `String`

Etiketa që identifikon llojin e regjistruesit.

##### `color`

- Tipi: `String`

Ngjyra e etiketës, mund të jetë çdo nga ngjyrat e mbështetura nga [chalk](https://github.com/chalk/chalk#colors).

##### `logLevel`

- Tipi: `String`
- Parazgjedhur: `'info'`

Niveli që i takon regjistruesit. Mesazhet shfaqen vetëm nëse niveli i tyre është më i lartë apo i barabartë me nivelin e përgjithshëm të instancës.

##### `stream`

- Tipi: `stream.Writable|stream.Writable[]`
- Parazgjedhur: `process.stdout`

Destinacioni ku të dhënat shkruhen, mund të jetë një stream i vetëm apo një varg streams.

### Regjistrues Me Scope

Për të krijuar një regjistrues me scope nga e para, përcaktoni fushën `scope` brenda objektit `options` dhe kalojeni tek një instancë e re të signale.

```js
const {Signale} = require('signale');

const options = {
  scope: 'scope global'
};

const global = new Signale(options);
global.success('Operacioni i suksesshëm');
```

<div align="center">
  <img alt="Scope Vanilla" src="../media/scope-vanilla.png" width="65%">
</div>

Për të krijuar një regjistrues me scope të bazuar mbi një ekzistues, përdorni funksionin `scope()`, i cili do të kthejë një instancë të re signale, duke trashëguar të gjithë regjistruesit e personalizuar, kohëmatësit, sekretet, fluret, konfigurimin, nivelin e regjistrimit, mënyrën interaktive dhe statuset e çaktivuara nga ai fillestar.

```js
const signale = require('signale');

const global = signale.scope('scope global');
global.success('Përshëndetje nga scope-i global');

function foo() {
  const outer = global.scope('outer', 'scope');
  outer.success('Përshëndetje nga scope-i i jashtëm');
  
  setTimeout(() => {
    const inner = outer.scope('inner', 'scope'); 
    inner.success('Përshëndetje nga scope-i i brendshëm');
  }, 500);
}

foo();
```

<div align="center">
  <img alt="Scope Existing" src="../media/scope-existing.png" width="65%">
</div>

### Regjistrues Interaktivë

Për të inicializuar një regjistrues interaktiv, krijoni një instancë të re signale me atributin [`interactive`](#interactive) të vendosur në `true`. Kur jeni në mënyrën interaktive, mesazhet e mëparshme të regjistuar nga një regjistrues interaktiv, do të tejkalohen vetëm nga të reja që vijnë nga i njëjti ose një regjistrues tjetër interaktiv. Vini re se mesazhet e rregullta nga regjistruesit e rregullt nuk tejkalohen nga ata interaktivë.

```js
const {Signale} = require('signale');

const interactive = new Signale({interactive: true, scope: 'interactive'});

interactive.await('[%d/4] - Procesi A', 1);

setTimeout(() => {
  interactive.success('[%d/4] - Procesi A', 2);
  setTimeout(() => {
    interactive.await('[%d/4] - Procesi B', 3);
    setTimeout(() => {
      interactive.error('[%d/4] - Procesi B', 4);
      setTimeout(() => {}, 1000);
    }, 1000);
  }, 1000);
}, 1000);
```

<div align="center">
  <img alt="Modaliteti Interaktiv" src="../media/interactive-mode.gif" width="65%">
</div>

### Flukse të Shkrueshme

Në mënyrë të parazgjedhur, të gjitha instancat signale regjistrojnë mesazhet e tyre në fluksin `process.stdout`. Kjo mund të modifikohet, për t'u përshtatur me preferencën tuaj, përmes vetisë [`stream`](#stream), ku mund të përcaktoni një ose më shumë flukse të vlefshme të Shkrueshme, të cilat do të përdoren nga të gjithë llojet e regjistruesve për të regjistruar të dhënat tuaja. Gjithashtu, është e mundur të përcaktohet një ose më shumë flukse të Shkrueshme ekskluzivisht për një lloj specifik regjistruesi, duke shkruar kështu të dhëna në mënyrë të pavarur nga llojet e tjera të regjistruesve.

```js
const {Signale} = require('signale');

const options = {
  stream: process.stderr, // Të gjithë regjistruesit do të shkruajnë tek `process.stderr`
  types: {
    error: {
      // Vetëm `error` do të shkruajë tek të dy `process.stdout` & `process.stderr`
      stream: [process.stdout, process.stderr]
    }
  }
};

const signale = new Signale(options);
signale.success('Mesazhi do të shfaqet në `process.stderr`');
signale.error('Mesazhi do të shfaqet në të dy `process.stdout` & `process.stderr`');
```

<div align="center">
  <img alt="Flukse të Shkrueshme" src="../media/writable-streams.png" width="73%">
</div>

### Filtrim Sekretesh

Duke shfrytëzuar opsionin `secrets`, sekretet dhe informacione të tjera të ndjeshme mund të filtrohen nga trupi si dhe metadata, p.sh. emrat e scope-ve etj, të mesazheve për t'u regjistruar. Opsioni është pjesë e objektit të konfigurimit që kalon tek një instancë `Signale` gjatë inicializimit të saj, dhe është i tipit `Array<String|Number>`. Vargu mund të mbajë shumë sekrete, të gjitha këto hiqen, nëse janë të pranishme, nga mesazhet për t'u regjistruar dhe zëvendësohen me string-un e parazgjedhur `'[secure]'`. Gjithashtu, kur përdoret funksioni unary `signale.scope(name)`, instanca e kthyer `Signale` trashëgon të gjitha sekretet që i përkasin prindit të saj. Procesi i kontrollit të sekreteve kryhet në mënyrë **case-sensitive**. Gjithashtu, funksioni unary [`signale.addSecrets()`](https://github.com/klaudiosinani/signale/blob/master/docs/readme.AL.md#signaleaddsecretssecrets) dhe ai nullary [`signale.clearSecrets()`](https://github.com/klaudiosinani/signale/blob/master/docs/readme.AL.md#signaleclearsecrets) janë të disponueshme përmes API-së për shtim dhe pastrimin e sekreteve përkatësisht.

Është **kritike** dhe **shumë e rekomanduar** të **mos i shkruani drejtpërdrejt sekretet në kodin tuaj**, kështu që shembulli në vijim shërben **vetëm** si një demonstrim përdorimi i thjeshtë dhe lehtësisht i riprodhuar.

```js
const {Signale} = require('signale');

// Në realitet sekretet mund të merren/deshkriptohen në mënyrë të sigurt përmes API-së së dedikuar 
const [USERNAME, TOKEN] = ['klaudiosinani', 'token'];

const logger1 = new Signale({
  secrets: [USERNAME, TOKEN]
});

logger1.log('$ exporting USERNAME=%s', USERNAME);
logger1.log('$ exporting TOKEN=%s', TOKEN);

// `logger2` trashëgon të gjitha sekretet nga prindi i tij `logger1`
const logger2 = logger1.scope('parent');

logger2.log('$ exporting USERNAME=%s', USERNAME);
logger2.log('$ exporting TOKEN=%s', TOKEN);
```

<div align="center">
  <img alt="Filtrim Sekretesh" src="../media/filter-secrets.png" width="73%">
</div>

### Kohëmatës

Kohëmatësit menaxhohen nga funksionet `time()` dhe `timeEnd()`. Një etiketë unike mund të përdoret për të identifikuar një kohëmatës në inicializim, megjithëse nëse asnjë nuk jepet, kohëmatësi do të marrë një automatikisht. Përveç kësaj, thirrja e funksionit `timeEnd()` pa një etiketë të specifikuar do të ketë si efekt terminimin e kohëmatësit më të rishtëm, që u krijua pa dhënë etiketë.

```js
const signale = require('signale');

signale.time('test');
signale.time();
signale.time();

setTimeout(() => {
  signale.timeEnd();
  signale.timeEnd();
  signale.timeEnd('test');
}, 500);
```

<div align="center">
  <img alt="Kohëmatës" src="../media/timers.png" width="60%">
</div>

## Konfigurimi

### Global

Për të aktivizuar konfigurimin global përcaktoni opsionet nën namespace-in `signale` në `package.json` tuaj.

Sa në vijim ilustron të gjitha opsionet e disponueshme me vlerat e tyre përkatëse të parazgjedhura.

```json
{
  "signale": {
    "displayScope": true,
    "displayBadge": true,
    "displayDate": false,
    "displayFilename": false,
    "displayLabel": true,
    "displayTimestamp": false,
    "underlineLabel": true,
    "underlineMessage": false,
    "underlinePrefix": false,
    "underlineSuffix": false,
    "uppercaseLabel": false
  }
}
```

<details>
<summary>Shikoni të gjitha opsionet e disponueshme në detaj.</summary>

##### `displayScope`

- Tipi: `Boolean`
- Parazgjedhur: `true`

Shfaq emrin e scope-it të regjistruesit.

##### `displayBadge`

- Tipi: `Boolean`
- Parazgjedhur: `true`

Shfaq stemën e regjistruesit.

##### `displayDate`

- Tipi: `Boolean`
- Parazgjedhur: `false`

Shfaq datën aktuale lokale në formatin `YYYY-MM-DD`.

##### `displayFilename`

- Tipi: `Boolean`
- Parazgjedhur: `false`

Shfaq emrin e skedarit nga i cili raporton regjistruesi.

##### `displayLabel`

- Tipi: `Boolean`
- Parazgjedhur: `true`

Shfaq etiketën e regjistruesit.

##### `displayTimestamp`

- Tipi: `Boolean`
- Parazgjedhur: `false`

Shfaq kohën aktuale lokale në formatin `HH:MM:SS`.

##### `underlineLabel`

- Tipi: `Boolean`
- Parazgjedhur: `true`

Nënvizon etiketën e regjistruesit.

##### `underlineMessage`

- Tipi: `Boolean`
- Parazgjedhur: `false`

Nënvizon mesazhin e regjistruesit.

##### `underlinePrefix`

- Tipi: `Boolean`
- Parazgjedhur: `false`

Nënvizon parashtesën e regjistruesit.

##### `underlineSuffix`

- Tipi: `Boolean`
- Parazgjedhur: `false`

Nënvizon prapashtesën e regjistruesit.

##### `uppercaseLabel`

- Tipi: `Boolean`
- Parazgjedhur: `false`

Shfaq etiketën e regjistruesit me shkronja të mëdha.

</details>

### Lokal

Për të aktivizuar konfigurimin lokal thirrni funksionin `config()` në instancën tuaj signale. Konfigurimet lokale do të tejkalojnë gjithmonë çdo konfigurim të mëparshëm të trashëguar nga `package.json`.

Në shembullin vijues, regjistruesit në skedarin `foo.js` do të ekzekutohen nën konfigurimin e tyre, duke tejkaluar atë në `package.json`.

```js
// foo.js
const signale = require('signale');

// Tejkalon çdo konfigurim ekzistues `package.json`
signale.config({
  displayFilename: true,
  displayTimestamp: true,
  displayDate: false
}); 

signale.success('Përshëndetje nga Scope-i Global');
```

<div align="center">
  <img alt="Konfigurim Lokal" src="../media/local-config.png" width="65%">
</div>

Gjithashtu, regjistruesit me scope mund të kenë konfigurimin e tyre të pavarur, duke tejkaluar atë të trashëguar nga instanca prind ose `package.json`.

```js
// foo.js
const signale = require('signale');

signale.config({
  displayFilename: true,
  displayTimestamp: true,
  displayDate: false
});

signale.success('Përshëndetje nga Scope-i Global');

function foo() {
  // `fooLogger` trashëgon konfigurimin e `signale`
  const fooLogger = signale.scope('foo scope');

  // Tejkalon të dy konfigurimet `signale` dhe `package.json`
  fooLogger.config({
    displayFilename: true,
    displayTimestamp: false,
    displayDate: true
  });

  fooLogger.success('Përshëndetje nga Scope-i Lokal');
}

foo();
```

<div align="center">
  <img alt="Konfigurim Scope" src="../media/scope-config.png" width="65%">
</div>

## API

#### signale.`<logger>(message[, message]|messageObj|errorObj)`

##### **`logger`**

- Tipi: `Function`

Mund të jetë çdo regjistrues i paracaktuar ose i personalizuar.

##### **`message`**

- Tipi: `String`

Mund të jetë një ose më shumë string-e të ndarë me presje.

```js
const signale = require('signale');

signale.success('Operacion i suksesshëm');
//=> ✔  success  Operacion i suksesshëm

signale.success('Operacion', 'i suksesshëm');
//=> ✔  success  Operacion i suksesshëm

signale.success('Operacion i %s', 'suksesshëm');
//=> ✔  success  Operacion i suksesshëm
```

##### **`errorObj`**

- Tipi: `Error Object`

Mund të jetë çdo objekt gabimi.

```js
const signale = require('signale');

signale.error(new Error('Operacion i pasuksesshëm'));
//=> ✖  error  Error: Operacion i pasuksesshëm
//        at Module._compile (module.js:660:30)
//        at Object.Module._extensions..js (module.js:671:10)
//        ...
```

##### **`messageObj`**

- Tipi: `Object`

Mund të jetë një objekt që përmban atributet `prefix`, `message` dhe `suffix`, me `prefix` dhe `suffix` gjithmonë të shtuar përpara dhe pas `message`-it përkatësisht.

```js
const signale = require('signale');

signale.complete({prefix: '[detyrë]', message: 'Rregullo çështjen #59', suffix: '(@klaudiosinani)'});
//=> [detyrë] ☒  complete  Rregullo çështjen #59 (@klaudiosinani)

signale.complete({prefix: '[detyrë]', message: ['Rregullo çështjen #%d', 59], suffix: '(@klaudiosinani)'});
//=> [detyrë] ☒  complete  Rregullo çështjen #59 (@klaudiosinani)
```

#### signale.`scope(name[, name])`

Përcakton emrin e scope-it të regjistruesit.

##### **`name`**

- Tipi: `String`

Mund të jetë një ose më shumë string-e të ndarë me presje.

```js
const signale = require('signale');

const foo = signale.scope('foo'); 
const fooBar = signale.scope('foo', 'bar');

foo.success('foo');
//=> [foo] › ✔  success  foo

fooBar.success('foo bar');
//=> [foo] [bar] › ✔  success  foo bar
```

#### signale.`unscope()`

Pastron emrin e scope-it të regjistruesit.

```js
const signale = require('signale');

const foo = signale.scope('foo'); 

foo.success('foo');
//=> [foo] › ✔  success  foo

foo.unscope();

foo.success('foo');
//=> ✔  success  foo
```

#### signale.`config(settingsObj)`

Vendos konfigurimin e një instance duke tejkaluar çdo konfigurim global ose lokal ekzistues.

##### **`settingsObj`**

- Tipi: `Object`

Mund të mbajë çdo nga [opsionet](#global) e dokumentuara.

```js
// foo.js
const signale = require('signale');

signale.config({
  displayFilename: true,
  displayTimestamp: true,
  displayDate: true
});

signale.success('Operacione të suksesshme');
//=> [2018-5-15] [11:12:38] [foo.js] › ✔  success  Operacione të suksesshme
```

#### signale.`time([, label])`

- Tipi i Kthimit: `String`

Vendos një kohëmatës dhe pranon një etiketë opsionale. Nëse asnjë nuk jepet, kohëmatësi do të marrë një etiketë unike automatikisht.

Kthen një string që korrespondon me etiketën e kohëmatësit.

##### **`label`**

- Tipi: `String`

Etiketa që korrespondon me kohëmatësin. Çdo kohëmatës duhet të ketë etiketën e tij unike.

```js
const signale = require('signale');

signale.time();
//=> ▶  timer_0  Inicializua kohëmatësi...

signale.time();
//=> ▶  timer_1  Inicializua kohëmatësi...

signale.time('etiketa');
//=> ▶  etiketa    Inicializua kohëmatësi...
```

#### signale.`timeEnd([, label])`

- Tipi i Kthimit: `Object`

Çaktivizon kohëmatësin të cilit i korrespondon etiketa e dhënë. Nëse nuk jepet etiketë, kohëmatësi më i fundit, që u krijua pa dhënë etiketë, do të çaktivizohet.

Kthen një objekt `{label, span}` që përmban etiketën e kohëmatësit dhe kohën totale të ekzekutimit.

##### **`label`**

- Tipi: `String`

Etiketa që korrespondon me kohëmatësin, çdo kohëmatës ka etiketën e tij unike.

```js
const signale = require('signale');

signale.time();
//=> ▶  timer_0  Inicializua kohëmatësi...

signale.time();
//=> ▶  timer_1  Inicializua kohëmatësi...

signale.time('etiketa');
//=> ▶  etiketa    Inicializua kohëmatësi...

signale.timeEnd();
//=> ◼  timer_1  Kohëmatësi funksionoi për: 2ms

signale.timeEnd();
//=> ◼  timer_0  Kohëmatësi funksionoi për: 2ms

signale.timeEnd('etiketa');
//=> ◼  etiketa    Kohëmatësi funksionoi për: 2ms
```

#### signale.`disable()`

Çaktivizon funksionalitetin e regjistrimit të të gjithë regjistruesve që i përkasin një instance specifike.

```js
const signale = require('signale');

signale.success('foo');
//=> ✔  success  foo

signale.disable();

signale.success('foo');
//=>
```

#### signale.`enable()`

Aktivizon funksionalitetin e regjistrimit të të gjithë regjistruesve që i përkasin një instance specifike.

```js
const signale = require('signale');

signale.disable();

signale.success('foo');
//=>

signale.enable();

signale.success('foo');
//=> ✔  success  foo
```

#### signale.`isEnabled()`

Kontrollon nëse funksionaliteti i regjistrimit të një instance specifike është i aktivizuar.

```js
const signale = require('signale');

signale.success('foo');
//=> ✔  success  foo

signale.isEnabled();
// => true

signale.disable();

signale.success('foo');
//=>

signale.isEnabled();
// => false
```

#### signale.`addSecrets(secrets)`

Shton sekrete/informacione të ndjeshme të reja në instancën e synuar Signale.

##### **`secrets`**

- Tipi: `(String|Number)[]`

Varg që përmban sekretet/informacionin e ndjeshëm për t'u filtruar.

```js
const signale = require('signale');

signale.log('$ exporting USERNAME=%s', 'klaudiosinani');
//=> $ exporting USERNAME=klaudiosinani

signale.addSecrets(['klaudiosinani']);

signale.log('$ exporting USERNAME=%s', 'klaudiosinani');
//=> $ exporting USERNAME=[secure]
```

#### signale.`clearSecrets()`

Heq të gjitha sekretet/informacionin e ndjeshëm nga instanca e synuar Signale.

```js
const signale = require('signale');

signale.addSecrets(['klaudiosinani']);

signale.log('$ exporting USERNAME=%s', 'klaudiosinani');
//=> $ exporting USERNAME=[secure]

signale.clearSecrets();

signale.log('$ exporting USERNAME=%s', 'klaudiosinani');
//=> $ exporting USERNAME=klaudiosinani
```

## Zhvillimi

Për më shumë informacion se si të kontribuoni në projekt, ju lutemi lexoni [udhëzimet e kontributit](https://github.com/klaudiosinani/signale/blob/master/contributing.md).

- Bëni fork të repository dhe klonojeni në makinën tuaj
- Navigoni në fork-un tuaj lokal: `cd signale`
- Instaloni varësitë e projektit: `npm install` ose `yarn install`
- Kontrolloni kodin për gabime: `npm test` ose `yarn test`

## Të Lidhura

- [qoa](https://github.com/klaudiosinani/qoa) - Porosi minimale interaktive të linjës së komandës
- [taskbook](https://github.com/klaudiosinani/taskbook) - Detyra, panele dhe shënime për mjedisin e linjës së komandës
- [hyperocean](https://github.com/klaudiosinani/hyperocean) - Temë e thellë blu oqeanike për terminalin Hyper

## Kush e përdor?

- [Boostnote](https://github.com/BoostIO/Boostnote)
- [Docz](https://github.com/pedronauck/docz)
- [Remix by Ethereum](https://github.com/ethereum/remix)
- [Semantic Release](https://github.com/semantic-release/semantic-release)
- [Shower](https://github.com/shower/shower)
- [Taskbook](https://github.com/klaudiosinani/taskbook)
- [Vant](https://github.com/youzan/vant)

Shikoni në detaj të gjitha paketat dhe repository që përdorin Signale [këtu](https://github.com/klaudiosinani/signale/network/dependents).

## Ekipi

- Klaudio Sinani [(@klaudiosinani)](https://github.com/klaudiosinani)
- Mario Sinani [(@mariosinani)](https://github.com/mariosinani)

## Sponsorët

Falëminderit të madh të gjithë njerëzve dhe kompanive që mbështesin punën tonë të Kodit të Hapur:

- [Better Stack: Zbulo, Zgjidh dhe Parandaloni Kohën e Pushimit.](https://betterstack.com/)

## Licenca

[MIT](https://github.com/klaudiosinani/signale/blob/master/license.md)

---

### Readme.DE (`docs/readme.DE.md`)

<h1 align="center">
  Signale
</h1>

<h4 align="center">
  Hochgradig konfigurierbares Loggingprogramm
</h4>

<div align="center">
  <img alt="Header" src="../media/header.png" width="88%">
</div>

<p align="center">
  <a href="https://travis-ci.com/klaudiosinani/signale">
    <img alt="Build Status" src="https://travis-ci.com/klaudiosinani/signale.svg?branch=master">
  </a>
  <a href="https://www.npmjs.com/package/signale">
    <img alt="NPM Downloads" src="https://img.shields.io/npm/dt/signale.svg">
  </a>
</p>

## Beschreibung

Signale ist bis ins letzte hack- und konfigurierbar. Es kann für Logging und Statusreports als auch für das Handling des Output Rendering Process von anderen Node Modulen und Anwendungen verwendet werden.

Diese Dokument in [Albanian - Shqip](https://github.com/klaudiosinani/signale/blob/master/docs/readme.AL.md), [English](https://github.com/klaudiosinani/signale/blob/master/readme.md), [简体中文 - Simplified Chinese](https://github.com/klaudiosinani/signale/blob/master/docs/readme.zh_CN.md) lesen.

Lies die [contributing guidelines](https://github.com/klaudiosinani/signale/blob/master/contributing.md#translating-documentation) um zu lernen, wie du dieses Dokument in mehr Sprachen übersetzen kannst.

## Höhepunkte

- 19 out-of-the-box loggers
- durch und durch hackbar
- sauberer, schöner Output
- intergrierte Timer
- benutzerdefinierte Logger
- TypeScript Unterstützung
- Interaktive und reguläre Modi
- Geheimnisse & sensitives Filtern von Informationen
- Unterstützung von Filename, Datum und timestamp 
- gezielte Logger und Timer
- skaliert Loglevel
- Unterstützugn von String interpolation
- mehrere konfigurierbare beschreibbare Streams
- einfache, minimalistische Syntax
- Global konfigurierbar durch `package.json`
- Konfiguration kann per File und Logger überschrieben werden

## Inhalt

- [Beschreibung](#beschreibung)
- [Höhepunkte](#höhepunkte)
- [Installation](#installation)
- [Verwendung](#verwendung)
- [Konfiguration](#konfiguration)
- [API](#api)
- [Development](#development)
- [Related](#related)
- [Wer verwendet es?](#wer-verwendet-es)
- [Team](#team)
- [License](#license)

## Installation

### Yarn

```bash
yarn add signale
```

### NPM

```bash
npm install signale
```

## Verwendung

### Default Loggers

Signale importieren und einen der default Logger verwenden.


<details>
<summary>All verfügbaren Logger anzeigen.</summary>

<br/>

- `await`
- `complete`
- `error`
- `debug`
- `fatal`
- `fav`
- `info`
- `note`
- `pause`
- `pending`
- `star`
- `start`
- `success`
- `wait`
- `warn`
- `watch`
- `log`

</details>

<br/>

```js
const signale = require('signale');

signale.success('Operation successful');
signale.debug('Hello', 'from', 'L59');
signale.pending('Write release notes for %s', '1.2.0');
signale.fatal(new Error('Unable to acquire lock'));
signale.watch('Recursively watching build directory...');
signale.complete({prefix: '[task]', message: 'Fix issue #59', suffix: '(@klaudiosinani)'});
```

<div align="center">
  <img alt="Default Loggers" src="../media/default-loggers.png" width="65%">
</div>

### Benutzerdefinierte Logger

Um einen benutzerdefinierten Logger zu erzeugen, wird ein `options` Objekt mit einem `types` field mit dem Logger definiert. Als argument wird eine neue Signale Instanz übergeben.


```js
const {Signale} = require('signale');

const options = {
  disabled: false,
  interactive: false,
  logLevel: 'info',
  scope: 'custom',
  secrets: [],
  stream: process.stdout,
  types: {
    remind: {
      badge: '**',
      color: 'yellow',
      label: 'reminder',
      logLevel: 'info'
    },
    santa: {
      badge: '🎅',
      color: 'red',
      label: 'santa',
      logLevel: 'info'
    }
  }
};

const custom = new Signale(options);
custom.remind('Improve documentation.');
custom.santa('Hoho! You have an unused variable on L45.');
```

<div align="center">
  <img alt="Custom Loggers" src="../media/custom-loggers.png" width="70%">
</div>
Ein Beispiel, wo die voreingestellten `error` und `success` Logger überschrieben werden.


```js
const {Signale} = require('signale');

const options = {
  types: {
    error: {
      badge: '!!',
      label: 'fatal error'
    },
    success: {
      badge: '++',
      label: 'huge success'
    }
  }
};

const signale = new Signale();
signale.error('Default Error Log');
signale.success('Default Success Log');

const custom = new Signale(options);
custom.error('Custom Error Log');
custom.success('Custom Success Log');
```

<div align="center">
  <img alt="Default Loggers" src="../media/override-defaults.png" width="65%">
</div>

Das `options` Objekt kann jedes der folgenden Attribute erhalten: `disabled`, `interactive`, `logLevel`, `secrets`, `stream`, `scope` und `types`. 

##### `disabled`

- Type: `Boolean`
- Default: `false`

Verhindert das Logging aller Logger, die zur der Instanz gehören.

##### `interactive`

- Type: `Boolean`
- Default: `false`

Schaltet alle Logger, die zu der Instanz gehören, in den interaktiven Modus.

##### `logLevel`

- Type: `String`
- Default: `'info'`

Setzte den Loglevel der erstellten Instanz. Es kann einer der folgenden Level gewählt werden:

- `'info'` - Zeigt alle Benachrichtigungen von allen Loggern.
- `'timer'` -  Zeigt Benachrichtigungen von den `time`, `timeEnd`, `debug`, `warn`, `error` & `fatal` Loggern.
- `'debug'` - Zeigt Benachrichtigungen von den `debug`, `warn`, `error` & `fatal` Loggern.
- `'warn'` - Zeigt Benachrichtigungen von den `warn`, `error` & `fatal` Loggern.
- `'error'` - Zeigt Benachrichtigungen von den `error` & `fatal` Loggern.

##### `secrets`

- Type: `(String|Number)[]`
- Default: `[]`

Ein Array, in dem geheime/sensitive Informationen angegeben werden können. Diese werden vom Body und den Metadaten der Benachtichtigungen entfernt und mit dem voreingestellten `'[secure]'` String ersetzt.

##### `stream`

- Type: `stream.Writable|stream.Writable[]`
- Default: `process.stdout`

Die Daten werden in dieses Ziel geschrieben. Es kann ein einzelner, gültiger [Writable stream](https://nodejs.org/api/stream.html#stream_writable_streams) oder ein Array mit mehreren Writable streams sein.

##### `scope`

- Type: `String|String[]`

Name des Bereichs für den geloggt wird.

##### `types`

- Type: `Object`

Enthält die Konfiguration der benutzerdefinierten und voreingestellten Logger.

Zusätzlich kann das configuration object von jedem Logger-Type, das in der `types` Option definiert wird, folgende Attibute erhalten: `badge`, `label`, `color`, `logLevel` & `stream`. 

##### `badge`

- Type: `String`

Icon, der zum Logger gehört.


##### `label`

- Type: `String`

Label, das verwendet wird, um den Typ des Loggers zu erkennen.

##### `color`

- Type: `String`

Farbe des Labels. Kann eine der von [chalk](https://github.com/chalk/chalk#colors) unterstützten Farben sein.


##### `logLevel`

- Type: `String`
- Default: `'info'`

Das passende Loglevel für den Logger. Nachrichten vom Logger werden angezeigt, wenn der Loglevel größer oder gleich zum oben beschriebenen generellen Loglevel `logLevel` der `Signale` Instanz ist.

### Zielgerichtete Logger

Um einen neuen Logger für einen bestimmten Bereich zu erstellen, wird das `scope` Feld im `options` Objekt definiert und als Argument an die neue Signale Instanz übergeben.

```js
const {Signale} = require('signale');

const options = {
  scope: 'global scope'
};

const global = new Signale(options);
global.success('Successful Operation');
```

<div align="center">
  <img alt="Scope Vanilla" src="../media/scope-vanilla.png" width="65%">
</div>

Um einen Logger für einen besteimmten Bereich, basierend auf einem bereits existierenden Logger, zu erstellen wird die `scope()` Funktion verwendet. Sie retourniert eine neue signale Instanz, die alle benutzerdefinierten Logger, Timer, Geheimnisse, Streams, Konfiguation, Loglevel, Interaktiven Modi und deaktivierten Stati erbt.

```js
const signale = require('signale');

const global = signale.scope('global scope');
global.success('Hello from the global scope');

function foo() {
  const outer = global.scope('outer', 'scope');
  outer.success('Hello from the outer scope');
  
  setTimeout(() => {
    const inner = outer.scope('inner', 'scope'); 
    inner.success('Hello from the inner scope');
  }, 500);
}

foo();
```

<div align="center">
  <img alt="Scope Existing" src="../media/scope-existing.png" width="65%">
</div>

### Interaktive Loggers

Um einen intaktiven Logger zu initialisieren, wird eine neue signale Instanz mit dem Wert `true` im [`interactive`](#interactive) Attribute erzeugt. Während man sich im interaktiven Modus befindet, werden zuvor geloggte Nachrichten von einem anderen interaktiven Logger von den neuen, die von dem gleichen oder einem neuen interaktiven Logger kommen, überschrieben. Reguläre Nachrichten von regulären Loggern werden von interaktiven nicht überschrieben.

```js
const {Signale} = require('signale');

const interactive = new Signale({interactive: true, scope: 'interactive'});

interactive.await('[%d/4] - Process A', 1);

setTimeout(() => {
  interactive.success('[%d/4] - Process A', 2);
  setTimeout(() => {
    interactive.await('[%d/4] - Process B', 3);
    setTimeout(() => {
      interactive.error('[%d/4] - Process B', 4);
      setTimeout(() => {}, 1000);
    }, 1000);
  }, 1000);
}, 1000);
```

<div align="center">
  <img alt="Interactive Mode" src="../media/interactive-mode.gif" width="65%">
</div>

### Writable Streams

Standardmäßig loggen alle Signale Instanzen die Nachrichten in den `process.stdout` stream. Der Ziel-Stream kann durch die [`stream`](#stream) property angepasst werden. Hier kann ein einzelner oder mehrere gütlige Writable streams angegeben werden, welche dann von allen Loggertypen verwendet werden. Zusätzlich ist es möglich einen oder mehrere Wirtable Streams exklusiv für einen spezifischen Loggertypen zu definieren - also Daten unabhängig von den restlichen Loggertypen zu schreiben.

```js
const {Signale} = require('signale');

const options = {
  stream: process.stderr, // All loggers will now write to `process.stderr`
  types: {
    error: {
      // Only `error` will write to both `process.stdout` & `process.stderr`
      stream: [process.stdout, process.stderr]
    }
  }
};

const signale = new Signale(options);
signale.success('Message will appear on `process.stderr`');
signale.error('Message will appear on both `process.stdout` & `process.stderr`');
```

<div align="center">
  <img alt="Writable Streams" src="../media/writable-streams.png" width="73%">
</div>

### Secrets Filtering

Durch das Verwenden der `secrets` Option werden geheime/sensitive Informationen, wie Scopenamen,  aus den Nachrichten (Body und Metadaten) herausgefiltert. Diese Option ist Teil des configuration-Objects, dass einer `Signale`-Instanz oder Initialisierung übergeben wird und hat den Typ `Array<String|Number>`. Das Array kann mehrere Geheimnisse enthalten. Alle werden, wenn sie vorkommen, entfernt und durch den standardmäßigen `'[secure]'` string ersetzt. Wenn die unäre `signale.scope(name)` Funktion verwendet wird, dann erbt die erzeugte `Signale` Instanz alle Geheimnisse des Parents. Die Geheimnisse werden **case-sensitive** überprüft. Die unäre [`signale.addSecrets()`](https://github.com/klaudiosinani/signale#signaleaddsecretssecrets) und die nullstellige [`signale.clearSecrets()`](https://github.com/klaudiosinani/signale#signaleclearsecrets) Funktion sind über die API verfügbar um die Geheimnisse hinzuzufügen/zu löschen.

Es ist **entscheidende** und **stark befürwortete** Praxis, sensitive Informationen **nicht direkt im Quellcode zu speichern**. Das Beispiel ist **alleinig** zur die Demonstration gedacht:

```js
const {Signale} = require('signale');

// In reality secrets could be securely fetched/decrypted through a dedicated API 
const [USERNAME, TOKEN] = ['klaudiosinani', 'token'];

const logger1 = new Signale({
  secrets: [USERNAME, TOKEN]
});

logger1.log('$ exporting USERNAME=%s', USERNAME);
logger1.log('$ exporting TOKEN=%s', TOKEN);

// `logger2` inherits all secrets from its parent `logger1`
const logger2 = logger1.scope('parent');

logger2.log('$ exporting USERNAME=%s', USERNAME);
logger2.log('$ exporting TOKEN=%s', TOKEN);
```

<div align="center">
  <img alt="Secrets Filtering" src="../media/filter-secrets.png" width="73%">
</div>

### Timers

Timer werden von den `time()` und `timeEnd()`  Funktionen verwaltet. Zur Identifikation eines Timers oder einer Initialisierung kann ein einzigartiges Label vewrendet werden. Wenn keines bereitgestellt wird, dann wird eines automatisch generiert. Wenn die `timeEnd()` Funktion ohne ein Label aufgerufen wird, dann wird der als letztes initialisierte Timer terminiert, der ohne Label erstellt wurde.

```js
const signale = require('signale');

signale.time('test');
signale.time();
signale.time();

setTimeout(() => {
  signale.timeEnd();
  signale.timeEnd();
  signale.timeEnd('test');
}, 500);
```

<div align="center">
  <img alt="Timers" src="../media/timers.png" width="60%">
</div>

## Konfiguration

### Global

Um die gloabel Konfiguration zu ermöglichen, müssen die Optionen unter dem`signale` namespace im `package.json` defniert werden.

Das folgende Beispiel zeigt alle verfügbaren Optionen mit den zugehörigen voreingestellten Werten.

```json
{
  "signale": {
    "displayScope": true,
    "displayBadge": true,
    "displayDate": false,
    "displayFilename": false,
    "displayLabel": true,
    "displayTimestamp": false,
    "underlineLabel": true,
    "underlineMessage": false,
    "underlinePrefix": false,
    "underlineSuffix": false,
    "uppercaseLabel": false
  }
}
```

<details>
<summary>Alle verfügbaren Optionen im Detail ansehen.</summary>

##### `displayScope`

- Type: `Boolean`
- Default: `true`

Zeigt den Bereichsnamen des Loggers.

##### `displayBadge`

- Type: `Boolean`
- Default: `true`

Zeigt den Badge des Loggers.

##### `displayDate`

- Type: `Boolean`
- Default: `false`

Zeugt das aktuelle lokale Datum im `YYYY-MM-DD` Format. 

##### `displayFilename`

- Type: `Boolean`
- Default: `false`

Zeigt den Dateinamen vom File, für das der Logger loggt.

##### `displayLabel`

- Type: `Boolean`
- Default: `true`

Zeigt das Label des Loggers.

##### `displayTimestamp`

- Type: `Boolean`
- Default: `false`

Zeigt die lokale Zeit im `HH:MM:SS` Format.

##### `underlineLabel`

- Type: `Boolean`
- Default: `true`

Unterstreicht das Logger-Label.

##### `underlineMessage`

- Type: `Boolean`
- Default: `false`

Unterstreicht die Logger Nachricht.

##### `underlinePrefix`

- Type: `Boolean`
- Default: `false`

Unterstreicht die Logger Prefix.

##### `underlineSuffix`

- Type: `Boolean`
- Default: `false`

Unterstreicht die Logger Suffix.

##### `uppercaseLabel`

- Type: `Boolean`
- Default: `false`

Zeigt das Label des Loggers in Großbuchstaben.

</details>

### Local

Um die lokale Konfiguration zu ermöglichen muss die `config()` Funktion der Signale Instanz aufgerufen werden. Lokale Konfigurationen überschreiben immer die geerbte Konfiguration von `package.json`.

Das folgende Beispiel zeigt wie Logger in der `foo.js`-Datei unter einer eigenen Konfiguation laufen, die die `package.json`-Konfiguration überschreibt.

```js
// foo.js
const signale = require('signale');

// Overrides any existing `package.json` config
signale.config({
  displayFilename: true,
  displayTimestamp: true,
  displayDate: false
}); 

signale.success('Hello from the Global scope');
```

<div align="center">
  <img alt="Local Config" src="../media/local-config.png" width="65%">
</div>

Logger für bestimmte Bereiche können eine unabhängige, eigene Konfiguration erhalten, die die vom Parent oder vom `package.json` vererbte Konfiguation überschreibt.

```js
// foo.js
const signale = require('signale');

signale.config({
  displayFilename: true,
  displayTimestamp: true,
  displayDate: false
});

signale.success('Hello from the Global scope');

function foo() {
  // `fooLogger` inherits the config of `signale`
  const fooLogger = signale.scope('foo scope');

  // Overrides both `signale` and `package.json` configs
  fooLogger.config({
    displayFilename: true,
    displayTimestamp: false,
    displayDate: true
  });

  fooLogger.success('Hello from the Local scope');
}

foo();
```

<div align="center">
  <img alt="Scope Config" src="../media/scope-config.png" width="65%">
</div>

## API

#### signale.`<logger>(message[, message]|messageObj|errorObj)`

##### **`logger`**

- Type: `Function`

Kann jeder vereingestellte oder benutzerdefinierte Logger sein.

##### **`message`**

- Type: `String`

Kann ein oder mehrere durch Kommas getrennte Strings sein.

```js
const signale = require('signale');

signale.success('Successful operation');
//=> ✔  success  Successful operation

signale.success('Successful', 'operation');
//=> ✔  success  Successful operation

signale.success('Successful %s', 'operation');
//=> ✔  success  Successful operation
```

##### **`errorObj`**

- Type: `Error Object`

Kann jedes Error Objekt sein.

```js
const signale = require('signale');

signale.error(new Error('Unsuccessful operation'));
//=> ✖  error  Error: Unsuccessful operation
//        at Module._compile (module.js:660:30)
//        at Object.Module._extensions..js (module.js:671:10)
//        ...
```

##### **`messageObj`**

- Type: `Object`

Kann jedes Objet sein, dass die Attribute `prefix`, `message` und `suffix` enthält, wobei `prefix` und `suffix` immer vor bzw. nach der `message` eingefügt werden.

```js
const signale = require('signale');

signale.complete({prefix: '[task]', message: 'Fix issue #59', suffix: '(@klaudiosinani)'});
//=> [task] ☒  complete  Fix issue #59 (@klaudiosinani)

signale.complete({prefix: '[task]', message: ['Fix issue #%d', 59], suffix: '(@klaudiosinani)'});
//=> [task] ☒  complete  Fix issue #59 (@klaudiosinani)
```

#### signale.`scope(name[, name])`

Definiert den Namen des Loggers.

##### **`name`**

- Type: `String`

Kann ein oder mehrere durch Kommas getrennte Strings sein.

```js
const signale = require('signale');

const foo = signale.scope('foo'); 
const fooBar = signale.scope('foo', 'bar');

foo.success('foo');
//=> [foo] › ✔  success  foo

fooBar.success('foo bar');
//=> [foo] [bar] › ✔  success  foo bar
```

#### signale.`unscope()`

Löscht den Bereichsnamen des Loggers.

```js
const signale = require('signale');

const foo = signale.scope('foo'); 

foo.success('foo');
//=> [foo] › ✔  success  foo

foo.unscope();

foo.success('foo');
//=> ✔  success  foo
```

#### signale.`config(settingsObj)`

Die Konfiguration der Instanz wird gesestzt und überschreibt die existierende lokale oder globale Konfiguration.

##### **`settingsObj`**

- Type: `Object`

Kann jede der dokumentierten [options](#global) enthalten.

```js
// foo.js
const signale = require('signale');

signale.config({
  displayFilename: true,
  displayTimestamp: true,
  displayDate: true
});

signale.success('Successful operations');
//=> [2018-5-15] [11:12:38] [foo.js] › ✔  success  Successful operations
```

#### signale.`time([, label])`

- Return Type: `String`

Setzt einen Timer und aktzeptiert ein optionales Label. Wenn kein Label übergeben wird, dann erhält der Timer automatisch eines.

Retourniert einen String, der das Timerlabel repräsentiert.

##### **`label`**

- Type: `String`

Label, dass zum Timer gehört. Jeder Timer braucht ein einzigartiges Label.

```js
const signale = require('signale');

signale.time();
//=> ▶  timer_0  Initialized timer...

signale.time();
//=> ▶  timer_1  Initialized timer...

signale.time('label');
//=> ▶  label    Initialized timer...
```

#### signale.`timeEnd([, label])`

- Return Type: `Object`

Deaktiviert den Timer, der zu dem Label gehört. Wenn kein Label angegeben wird, dann wird der letzte Timer, der ohne Label erzeugt wurde, deaktiviert.

Retourniert ein Objekt `{label, span}`, welches das Label und die Laufzeit enthält.

##### **`label`**

- Type: `String`

Label, dass zu dem Timer gehört. Jeder Timer hat ein einzigartiges label.

```js
const signale = require('signale');

signale.time();
//=> ▶  timer_0  Initialized timer...

signale.time();
//=> ▶  timer_1  Initialized timer...

signale.time('label');
//=> ▶  label    Initialized timer...

signale.timeEnd();
//=> ◼  timer_1  Timer run for: 2ms

signale.timeEnd();
//=> ◼  timer_0  Timer run for: 2ms

signale.timeEnd('label');
//=> ◼  label    Timer run for: 2ms
```

#### signale.`disable()`

Deaktiviert das Logging für alle Logger die zu einer spezifischen Instanz gehören.

```js
const signale = require('signale');

signale.success('foo');
//=> ✔  success  foo

signale.disable();

signale.success('foo');
//=>
```

#### signale.`enable()`

Aktiviert das Logging für alle Logger die zu einer spezifischen Instanz gehören.
```js
const signale = require('signale');

signale.disable();

signale.success('foo');
//=>

signale.enable();

signale.success('foo');
//=> ✔  success  foo
```

#### signale.`isEnabled()`

Überprüft, ob Logging für die spezifische Instanz aktiviert ist.

```js
const signale = require('signale');

signale.success('foo');
//=> ✔  success  foo

signale.isEnabled();
// => true

signale.disable();

signale.success('foo');
//=>

signale.isEnabled();
// => false
```

#### signale.`addSecrets(secrets)`

Fügt neue geheime/sensitive Informatioen hinzu.

##### **`secrets`**

- Type: `(String|Number)[]`

Array, dass die geheime/sensitive Information enthält, die herausgefiltert werden soll.

```js
const signale = require('signale');

signale.log('$ exporting USERNAME=%s', 'klaudiosinani');
//=> $ exporting USERNAME=klaudiosinani

signale.addSecrets(['klaudiosinani']);

signale.log('$ exporting USERNAME=%s', 'klaudiosinani');
//=> $ exporting USERNAME=[secure]
```

#### signale.`clearSecrets()`

Entfernt alle geheimen/sensitiven Informationen der spezifischen Signale Instanz.

```js
const signale = require('signale');

signale.addSecrets(['klaudiosinani']);

signale.log('$ exporting USERNAME=%s', 'klaudiosinani');
//=> $ exporting USERNAME=[secure]

signale.clearSecrets();

signale.log('$ exporting USERNAME=%s', 'klaudiosinani');
//=> $ exporting USERNAME=klaudiosinani
```

## Development

Mehr Informationen im Bezug auf Beiträge zum Projekt finden sich in den [contributing guidelines](https://github.com/klaudiosinani/signale/blob/master/contributing.md).

- Das Repository forken und auf deine Maschine klonen
- Zum lokalen Fork navigieren: `cd signale`
- Die dependencies installieren: `npm install` oder `yarn install`
- Den Code für Fehler linten: `npm test` or `yarn test`
## Related

- [qoa](https://github.com/klaudiosinani/qoa) - Minimal interactive command-line prompts
- [taskbook](https://github.com/klaudiosinani/taskbook) - Tasks, boards & notes for the command-line habitat
- [hyperocean](https://github.com/klaudiosinani/hyperocean) - Deep oceanic blue Hyper terminal theme

## Who's Using It?

- [Boostnote](https://github.com/BoostIO/Boostnote)
- [Docz](https://github.com/pedronauck/docz)
- [Remix by Ethereum](https://github.com/ethereum/remix)
- [Semantic Release](https://github.com/semantic-release/semantic-release)
- [Shower](https://github.com/shower/shower)
- [Taskbook](https://github.com/klaudiosinani/taskbook)
- [Vant](https://github.com/youzan/vant)

Repositories, die Signale verwenden im Detail ansehen: [hier](https://github.com/klaudiosinani/signale/network/dependents).

## Team

- Klaudio Sinani [(@klaudiosinani)](https://github.com/klaudiosinani)
- Mario Sinani [(@mariosinani)](https://github.com/mariosinani)

## License

[MIT](https://github.com/klaudiosinani/signale/blob/master/license.md)

---

### Readme.Zh CN (`docs/readme.zh_CN.md`)

<h1 align="center">
  Signale
</h1>

<h4 align="center">
  👋 可扩展的日志记录器
</h4>

<div align="center">
  <img alt="Header" src="../media/header.png" width="88%">
</div>

<p align="center">
  <a href="https://travis-ci.org/klaudiosinani/signale">
    <img alt="Build Status" src="https://travis-ci.com/klaudiosinani/signale.svg?branch=master">
  </a>
</p>

## 描述

Signale 的核心是可扩展和可配置的，可将其用于日志记录、状态报告以及处理其他 Node 模块和应用的输出渲染方式。

您可以使用以下语言阅读本文档 [Albanian - Shqip](https://github.com/klaudiosinani/signale/blob/master/docs/readme.AL.md), [English](https://github.com/klaudiosinani/signale/blob/master/readme.md), [German - Deutsch](https://github.com/klaudiosinani/signale/blob/master/docs/readme.DE.md).

浏览 [contributing guidelines](https://github.com/klaudiosinani/signale/blob/master/contributing.md#translating-documentation) 以了解如何将该文档翻译成其他语言。

## 亮点

- 16个开箱即用的记录器
- 可扩展的核心
- 简洁漂亮的输出
- 集成了计时器
- 自定义可插拔记录器
- 交互模式和常规模式
- 文件名，日期和时间戳支持
- 局部记录器和计时器
- 字符串插值支持
- 多个可配置的输出流
- 简单且简洁的语法
- 可通过 `package.json` 进行全局配置
- 可覆盖每个文件和记录器的配置

## 目录

- [描述](#描述)
- [亮点](#亮点)
- [安装](#安装)
- [使用](#使用)
- [配置](#配置)
- [API](#api)
- [开发](#开发)
- [相关项目](#相关项目)
- [团队](#团队)
- [许可](#许可)

## 安装

```bash
npm install signale
```

## 使用

### 默认记录器

导入 signale 即可开始用任意的默认记录器。

<details>
<summary>查看所有可用的默认记录器。</summary>

<br/>

- `await`
- `complete`
- `error`
- `debug`
- `fatal`
- `fav`
- `info`
- `note`
- `pause`
- `pending`
- `star`
- `start`
- `success`
- `warn`
- `watch`
- `log`

</details>

<br/>

```js
const signale = require('signale');

signale.success('Operation successful');
signale.debug('Hello', 'from', 'L59');
signale.pending('Write release notes for %s', '1.2.0');
signale.fatal(new Error('Unable to acquire lock'));
signale.watch('Recursively watching build directory...');
signale.complete({prefix: '[task]', message: 'Fix issue #59', suffix: '(@klaudiosinani)'});
```

<div align="center">
  <img alt="Default Loggers" src="../media/default-loggers.png" width="65%">
</div>

### 自定义记录器 

要创建自定义记录器，先定义一个 `options` 对象，在其 `types` 属性中填入记录器相关数据，然后将该对象作为参数传递给新的 signale 实例。

```js
const {Signale} = require('signale');

const options = {
  disabled: false,
  interactive: false,
  stream: process.stdout,
  scope: 'custom',
  types: {
    remind: {
      badge: '**',
      color: 'yellow',
      label: 'reminder'
    },
    santa: {
      badge: '🎅',
      color: 'red',
      label: 'santa'
    }
  }
};

const custom = new Signale(options);
custom.remind('Improve documentation.');
custom.santa('Hoho! You have an unused variable on L45.');
```

<div align="center">
  <img alt="Custom Loggers" src="../media/custom-loggers.png" width="70%">
</div>

下面是一个覆盖默认记录器 `error` 和 `success` 的例子

```js
const {Signale} = require('signale');

const options = {
  types: {
    error: {
      badge: '!!',
      label: 'fatal error'
    },
    success: {
      badge: '++',
      label: 'huge success'
    }
  }
};

const signale = new Signale();
signale.error('Default Error Log');
signale.success('Default Success Log');

const custom = new Signale(options);
custom.error('Custom Error Log');
custom.success('Custom Success Log');
```

<div align="center">
  <img alt="Default Loggers" src="../media/override-defaults.png" width="65%">
</div>

`options` 对象可以包含以下任何属性： `disabled`, `interactive`, `stream`, `scope` and `types`. 

##### `disabled`

- 类型: `Boolean`
- 默认值: `false`

禁用所创建实例的所有日志记录功能。

##### `interactive`

- 类型: `Boolean`
- 默认值: `false`

将所创建实例的所有记录器切换到交互模式

##### `stream`

- 类型: `Writable stream` (输出流) 或 `Array of Writable streams` (包含输出流的数组)
- 默认: `process.stdout`

写入数据的目标可以是单个有效的 [输出流(Writable stream)](https://nodejs.org/api/stream.html#stream_writable_streams) 或包含多个有效输出流的数组。

##### `scope`

- 类型: `String` 或 `Array of Strings`

记录器的作用域名称。

##### `types`

- 类型: `Object`

持有自定义记录器和默认记录器的配置。

##### `badge`

- 类型: `String`

与记录器对应的徽章图标。

##### `label`

- 类型: `String`

用于标识记录器类型的标签。

##### `color`

- 类型: `String`

标签的颜色，可以是 [chalk](https://github.com/chalk/chalk#colors) 支持的任何前景色。

### 局部记录器

要从头创建局部记录器，需在 `options` 对象的 `scope` 属性中定义作用域名，然后将其作为一个参数传递给新的 signale 实例。

```js
const {Signale} = require('signale');

const options = {
  scope: 'global scope'
};

const global = new Signale(options);
global.success('Successful Operation');
```

<div align="center">
  <img alt="Scope Vanilla" src="../media/scope-vanilla.png" width="65%">
</div>

可以使用 `scope()` 函数基于现有的记录器创建局部记录器，该函数将返回新的signale实例，该实例继承已有实例的所有自定义记录器、计时器、流、配置、禁用状态和交互模式信息。

```js
const signale = require('signale');

const global = signale.scope('global scope');
global.success('Hello from the global scope');

function foo() {
  const outer = global.scope('outer', 'scope');
  outer.success('Hello from the outer scope');
  
  setTimeout(() => {
    const inner = outer.scope('inner', 'scope'); 
    inner.success('Hello from the inner scope');
  }, 500);
}

foo();
```

<div align="center">
  <img alt="Scope Existing" src="../media/scope-existing.png" width="65%">
</div>

### 交互式记录器 

要初始化交互式记录器，请创建一个新的 signale 实例，并将 [`interactive`](#interactive) 属性设置为 `true`。 进入交互模式时，之前来自交互式记录器的消息，会被后面来自相同实例中相同或不同的记录器的消息所覆盖。 请注意来自常规记录器的常规消息不会被交互式记录器覆盖。

```js
const {Signale} = require('signale');

const interactive = new Signale({interactive: true, scope: 'interactive'});

interactive.await('[%d/4] - Process A', 1);

setTimeout(() => {
  interactive.success('[%d/4] - Process A', 2);
  setTimeout(() => {
    interactive.await('[%d/4] - Process B', 3);
    setTimeout(() => {
      interactive.error('[%d/4] - Process B', 4);
      setTimeout(() => {}, 1000);
    }, 1000);
  }, 1000);
}, 1000);
```

<div align="center">
  <img alt="Interactive Mode" src="../media/interactive-mode.gif" width="65%">
</div>


### 输出流

默认情况下，所有 signale 实例都将其消息记录到 `process.stdout` 输出流。 可以通过 stream 属性进行修改以匹配您自己的选项，你可以在其中定义单个或多个有效的输出流，所有类型的记录器都将使用这些流来记录您的数据。 此外，可以专门为特定记录器类型定义一个或多个可写流，从而独立于其余记录器类型写入数据。

```js
const {Signale} = require('signale');

const options = {
  stream: process.stderr, // 所有的记录器现在都会将数据写入 `process.stderr`
  types: {
    error: {
      // 只有 `error` 记录器会将数据同时写入 `process.stdout` 和 `process.stderr`
      stream: [process.stdout, process.stderr]
    }
  }
};

const signale = new Signale(options);
signale.success('Message will appear on `process.stderr`');
signale.error('Message will appear on both `process.stdout` & `process.stderr`');
```

<div align="center">
  <img alt="Writable Streams" src="../media/writable-streams.png" width="73%">
</div>

### 计时器

计时器由 `time()` 和 `timeEnd()` 函数管理。 可以使用标签在初始化时唯一标识一个计时器，如果没有提供则计时器将自动分配一个。 此外，调用没有指定标签的 `timeEnd()` 函数将终止最近一个初始化时没有指定标签的计时器。

```js
const signale = require('signale');

signale.time('test');
signale.time();
signale.time();

setTimeout(() => {
  signale.timeEnd();
  signale.timeEnd();
  signale.timeEnd('test');
}, 500);
```

<div align="center">
  <img alt="Timers" src="../media/timers.png" width="60%">
</div>

## 配置

### 全局

在 `package.json` 中的 `signale` 命名空间下定义选项以启用全局配置。

以下说明了所有可用选项及其各自的默认值。

```json
{
  "signale": {
    "coloredInterpolation": false,
    "displayScope": true,
    "displayBadge": true,
    "displayDate": false,
    "displayFilename": false,
    "displayLabel": true,
    "displayTimestamp": false,
    "underlineLabel": true,
    "underlineMessage": false,
    "underlinePrefix": false,
    "underlineSuffix": false,
    "uppercaseLabel": false
  }
}
```

<details>
<summary>浏览所有可用选项的详细信息。</summary>

##### `coloredInterpolation`

- 类型: `Boolean`
- 默认值: `false`

以彩色的方式显示用于替换字符串插值上的占位符标记参数。

##### `displayScope`

- 类型: `Boolean`
- 默认值: `true`

显示记录器的作用域名称。

##### `displayBadge`

- 类型: `Boolean`
- 默认值: `true`

显示记录器的徽章图标。

##### `displayDate`

- 类型: `Boolean`
- 默认值: `false`

以 `YYYY-MM-DD` 的格式显示当前本地日期。

##### `displayFilename`

- 类型: `Boolean`
- 默认值: `false`

显示记录器消息来源的文件名。

##### `displayLabel`

- 类型: `Boolean`
- 默认值: `true`

显示记录器的标签。

##### `displayTimestamp`

- 类型: `Boolean`
- 默认值: `false`

以 `HH:MM:SS` 的格式显示当前本地时间。

##### `underlineLabel`

- 类型: `Boolean`
- 默认值: `true`

给记录器的标签添加下划线。

##### `underlineMessage`

- 类型: `Boolean`
- 默认值: `false`

给记录器的消息内容添加下划线。

##### `underlinePrefix`

- 类型: `Boolean`
- 默认值: `false`

给记录器的前缀添加下划线。

##### `underlineSuffix`

- 类型: `Boolean`
- 默认值: `false`

给记录器的后缀添加下划线。

##### `uppercaseLabel`

- 类型: `Boolean`
- 默认值: `false`

以大写的方式显示记录器的标签。

</details>

### 本地

要启用本地配置，请在您的 signale 实例上调用 `config()` 函数。本地配置将始终覆盖从 `package.json` 继承的任何预先存在的配置。

在以下示例中， `foo.js` 文件中的记录器将在其自己的配置下运行，从而覆盖 `package.json` 文件中的配置。

```js
// foo.js
const signale = require('signale');

// 覆盖任何存在于 `package.json` 的配置
signale.config({
  displayFilename: true,
  displayTimestamp: true,
  displayDate: false
}); 

signale.success('Hello from the Global scope');
```

<div align="center">
  <img alt="Local Config" src="../media/local-config.png" width="65%">
</div>

此外，局部记录器可以拥有自己的独立配置，以覆盖父实例或继承自 `package.json` 的配置。

```js
// foo.js
const signale = require('signale');

signale.config({
  displayFilename: true,
  displayTimestamp: true,
  displayDate: false
});

signale.success('Hello from the Global scope');

function foo() {
  // `fooLogger` 继承了 `signale` 的配置
  const fooLogger = signale.scope('foo scope');

  // 同时覆盖 `signale` 和 `package.json` 的配置
  fooLogger.config({
    displayFilename: true,
    displayTimestamp: false,
    displayDate: true
  });

  fooLogger.success('Hello from the Local scope');
}

foo();
```

<div align="center">
  <img alt="Scope Config" src="../media/scope-config.png" width="65%">
</div>

## API

#### signale.`<logger>(message[, message]|messageObj|errorObj)`

##### **`logger`**

- 类型: `Function`

可以是任何默认或自定义记录器。

##### **`message`**

- 类型: `String`

可以是一个或多个逗号分隔的字符串。

```js
const signale = require('signale');

signale.success('Successful operation');
//=> ✔  success  Successful operation

signale.success('Successful', 'operation');
//=> ✔  success  Successful operation

signale.success('Successful %s', 'operation');
//=> ✔  success  Successful operation
```

##### **`errorObj`**

- 类型: `Error Object`

可以是任意错误 (error) 对象。

```js
const signale = require('signale');

signale.error(new Error('Unsuccessful operation'));
//=> ✖  error  Error: Unsuccessful operation
//        at Module._compile (module.js:660:30)
//        at Object.Module._extensions..js (module.js:671:10)
//        ...
```

##### **`messageObj`**

- 类型: `Object`

可以是包含 `prefix` 、 `message` 和 `suffix` 属性的对象，`prefix` (前缀)和 `suffix` (后缀) 始终预先添加并附加到记录的 `message` (消息)里。

```js
const signale = require('signale');

signale.complete({prefix: '[task]', message: 'Fix issue #59', suffix: '(@klaudiosinani)'});
//=> [task] ☒  complete  Fix issue #59 (@klaudiosinani)

signale.complete({prefix: '[task]', message: ['Fix issue #%d', 59], suffix: '(@klaudiosinani)'});
//=> [task] ☒  complete  Fix issue #59 (@klaudiosinani)
```

#### signale.`scope(name[, name])`

定义记录器的作用域名称。

##### **`name`**

- 类型: `String`

可以是一个或多个用逗号分隔的字符串。

```js
const signale = require('signale');

const foo = signale.scope('foo'); 
const fooBar = signale.scope('foo', 'bar');

foo.success('foo');
//=> [foo] › ✔  success  foo

fooBar.success('foo bar');
//=> [foo] [bar] › ✔  success  foo bar
```

#### signale.`unscope()`

清除记录器的作用域名称。

```js
const signale = require('signale');

const foo = signale.scope('foo'); 

foo.success('foo');
//=> [foo] › ✔  success  foo

foo.unscope();

foo.success('foo');
//=> ✔  success  foo
```

#### signale.`config(settingsObj)`

设置实例的配置项以覆盖任意已存在的全局或本地配置。

##### **`settingsObj`**

- 类型: `Object`

可以持有任意[配置选项](#全局)。

```js
// foo.js
const signale = require('signale');

signale.config({
  displayFilename: true,
  displayTimestamp: true,
  displayDate: true
});

signale.success('Successful operations');
//=> [2018-5-15] [11:12:38] [foo.js] › ✔  success  Successful operations
```

#### signale.`time([, label])`

- 返回类型: `String`

激活一个计时器并接受一个可选的标签。如果没有提供参数，计时器将自动生成一个唯一的标签。

返回与计时器标签相对应的字符串。

##### **`label`**

- 类型: `String`

与计时器对应的标签。每个计时器必须有自己独有的标签。

```js
const signale = require('signale');

signale.time();
//=> ▶  timer_0  Initialized timer...

signale.time();
//=> ▶  timer_1  Initialized timer...

signale.time('label');
//=> ▶  label    Initialized timer...
```

#### signale.`timeEnd([, label])`

- 返回类型: `Object`

取消激活给定标签对应的计时器。如果未提供标签，则将取消激活在未提供标签的情况下创建的最新的计时器。

返回一个 `{label, span}` 对象，该对象持有计时器的标签与总共运行时间。

##### **`label`**

- 类型: `String`

与计时器对应的标签。每个计时器必须有自己独有的标签。

```js
const signale = require('signale');

signale.time();
//=> ▶  timer_0  Initialized timer...

signale.time();
//=> ▶  timer_1  Initialized timer...

signale.time('label');
//=> ▶  label    Initialized timer...

signale.timeEnd();
//=> ◼  timer_1  Timer run for: 2ms

signale.timeEnd();
//=> ◼  timer_0  Timer run for: 2ms

signale.timeEnd('label');
//=> ◼  label    Timer run for: 2ms
```

#### signale.`disable()`

禁用特定实例包含的所有记录器的记录功能。

```js
const signale = require('signale');

signale.success('foo');
//=> ✔  success  foo

signale.disable();

signale.success('foo');
//=>
```

#### signale.`enable()`

启用特定实例包含的所有记录器的记录功能。

```js
const signale = require('signale');

signale.disable();

signale.success('foo');
//=>

signale.enable();

signale.success('foo');
//=> ✔  success  foo
```

## 开发

想知道如何参与到该项目的更多信息, 请阅读 [contributing guidelines](https://github.com/klaudiosinani/signale/blob/master/contributing.md) 。

- Fork 该仓库并 clone 到你的本地机器上
- 进入你的本地仓库: `cd signale`
- 安装项目依赖: `npm install` or `yarn install`
- 测试代码: `npm test` or `yarn test`

## 相关项目

- [chalk](https://github.com/chalk/chalk) - Terminal string styling done right
- [figures](https://github.com/sindresorhus/figures) - Unicode symbols

## 团队

- Klaudio Sinani [(@klaudiosinani)](https://github.com/klaudiosinani)

## 许可

[MIT](https://github.com/klaudiosinani/signale/blob/master/license.md)

---

--- METRICS ---
- Files Extracted: 5
- Estimated Token Budget: ~21956 tokens
- Canonical Reference: https://codewiki.google/github.com/klaudiosinani/signale
