Repository: klaudiosinani/signale
Branch: master
Commit: 04b262aa754b
Files: 31
Total size: 117.3 KB
Directory structure:
gitextract_wea0djjf/
├── .editorconfig
├── .gitattributes
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ └── feature_request.md
│ └── pull_request_template.md
├── .gitignore
├── .npmrc
├── .travis.yml
├── _config.yml
├── code-of-conduct.md
├── contributing.md
├── docs/
│ ├── readme.AL.md
│ ├── readme.DE.md
│ ├── readme.md
│ └── readme.zh_CN.md
├── index.js
├── license.md
├── package.json
├── readme.md
├── src/
│ ├── signale.js
│ └── types.js
├── test/
│ ├── config.ts
│ ├── custom.ts
│ ├── default.ts
│ ├── override.ts
│ ├── scoped.ts
│ ├── secrets.ts
│ ├── streams.ts
│ ├── timers.ts
│ └── tsconfig.json
└── types/
└── signale.d.ts
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
[{*.json, *.yml}]
indent_style = space
indent_size = 2
================================================
FILE: .gitattributes
================================================
* text=auto eol=lf
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior.
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Technical Info (please complete the following information)**
- OS:
- Signale Version:
- Node.js Version:
**Additional context**
Add any other context about the problem here.
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is.
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Additional context**
Add any other context or screenshots about the feature request here.
================================================
FILE: .github/pull_request_template.md
================================================
## Squashed commits
-
## Changelog in detail
-
## All changes
- [klaudiosinani/signale@`commitHash1...commitHash2`](https://github.com/klaudiosinani/signale/compare/commitHash1...commitHash2)
================================================
FILE: .gitignore
================================================
# dependencies
node_modules
yarn.lock
# logs
*.log
# OS
.DS_Store
# IDE
.vscode
.idea
*.swp
*.swo
================================================
FILE: .npmrc
================================================
package-lock=false
================================================
FILE: .travis.yml
================================================
language: node_js
node_js:
- 10
- 8
- 6
before_install:
- npm install --global npm@6.4.0
- npm --version
git:
depth: 5
cache:
directories:
- node_modules
================================================
FILE: _config.yml
================================================
remote_theme: klaudiosinani/re@v0.4.0
plugins:
- jekyll-github-metadata
- jekyll-remote-theme
- jekyll-sitemap
re:
favicon: https://raw.githubusercontent.com/klaudiosinani/klaudiosinani.github.io/refs/heads/master/public/favicon.png
simpleanalytics:
enabled: true
================================================
FILE: code-of-conduct.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at klaussinani@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/
================================================
FILE: contributing.md
================================================
# Contributing to Signale
Thank you for taking the time to contribute to Signale!
Please note that this project is released with a [Contributor Code of Conduct](code-of-conduct.md). By participating in this project you agree to abide by its terms.
## How to contribute
### Improve documentation
Typo corrections, error fixes, better explanations, more examples etc. Open an issue regarding anything that you think it could be improved! You can use the [`docs` label](https://github.com/klaudiosinani/signale/labels/docs) to find out what others have suggested!
### Improve issues
Sometimes reported issues lack information, are not reproducible, or are even plain invalid. Help us out to make them easier to resolve. Handling issues takes a lot of time that we could rather spend on fixing bugs and adding features.
### Give feedback on issues
We're always looking for more opinions on discussions in the issue tracker. It's a good opportunity to influence the future direction of the project.
The [`question` label](https://github.com/klaudiosinani/signale/labels/question) is a good place to find ongoing discussions.
### Write code
You can use issue labels to discover issues you could help us out with!
- [`feature request` issues](https://github.com/klaudiosinani/signale/labels/feature%20request) are features we are open to including
- [`bug` issues](https://github.com/klaudiosinani/signale/labels/bug) are known bugs we would like to fix
- [`future` issues](https://github.com/klaudiosinani/signale/labels/future) are those that we'd like to get to, but not anytime soon. Please check before working on these since we may not yet want to take on the burden of supporting those features
- on the [`help wanted`](https://github.com/klaudiosinani/signale/labels/help%20wanted) label you can always find something exciting going on
You may find an issue is assigned, or has the [`assigned` label](https://github.com/klaudiosinani/signale/labels/assigned). Please double-check before starting on this issue because somebody else is likely already working on it
### Translating Documentation
#### Create a Translation
- Visit the [`translations' index`](https://github.com/klaudiosinani/signale/tree/master/docs/readme.md) file to ensure that the document is not already translated in your target language.
- Add the name of the language to the document as an extension, e.g: `readme.JP.md`
- Place the translated document inside the [`docs`](https://github.com/klaudiosinani/signale/tree/master/docs) directory.
- Add your github profile and the translated document at the [`translations' index`](https://github.com/klaudiosinani/signale/tree/master/docs/readme.md) file.
- Create a Pull Request including the language in the title, e.g: `Readme: Japanese Translation`
#### Improve a Translation
- Include your github profile next to the translation you improved at the [`translations' index`](https://github.com/klaudiosinani/signale/tree/master/docs/readme.md) file.
- Create a Pull Request that delivers the improvements and include the language in the title, e.g: `Readme: Improvements for the Japanese Translation`
### Submitting an issue
- Search the issue tracker before opening an issue
- Ensure you're using the latest version of Signale
- Use a descriptive title
- Include as much information as possible;
- Steps to reproduce the issue
- Error message
- Signale version
- Operating system **etc**
### Submitting a pull request
- Non-trivial changes are often best discussed in an issue first, to prevent you from doing unnecessary work
- Try making the pull request from a [topic branch](https://github.com/dchelimsky/rspec/wiki/Topic-Branches) if it is of crucial importance
- Use a descriptive title for the pull request and commits
- You might be asked to do changes to your pull request, you can do that by just [updating the existing one](https://github.com/RichardLitt/docs/blob/master/amending-a-commit-guide.md)
================================================
FILE: docs/readme.AL.md
================================================
Signale
Librari regjistrimi shumë e konfigurueshëme
## 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.
Shikoni të gjithë regjistruesit e disponueshëm.
- `await`
- `complete`
- `error`
- `debug`
- `fatal`
- `fav`
- `info`
- `note`
- `pause`
- `pending`
- `star`
- `start`
- `success`
- `wait`
- `warn`
- `watch`
- `log`
```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)'});
```
### 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.');
```
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');
```
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');
```
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();
```
### 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);
```
### 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`');
```
### 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`. 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);
```
### 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);
```
## 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
}
}
```
Shikoni të gjitha opsionet e disponueshme në detaj.
##### `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.
### 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');
```
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();
```
## API
#### signale.`(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)
================================================
FILE: docs/readme.DE.md
================================================
Signale
Hochgradig konfigurierbares Loggingprogramm
## 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.
All verfügbaren Logger anzeigen.
- `await`
- `complete`
- `error`
- `debug`
- `fatal`
- `fav`
- `info`
- `note`
- `pause`
- `pending`
- `star`
- `start`
- `success`
- `wait`
- `warn`
- `watch`
- `log`
```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)'});
```
### 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.');
```
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');
```
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');
```
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();
```
### 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);
```
### 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`');
```
### 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`. 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);
```
### 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);
```
## 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
}
}
```
Alle verfügbaren Optionen im Detail ansehen.
##### `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.
### 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');
```
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();
```
## API
#### signale.`(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)
================================================
FILE: 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!
================================================
FILE: docs/readme.zh_CN.md
================================================
Signale
👋 可扩展的日志记录器
## 描述
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 即可开始用任意的默认记录器。
查看所有可用的默认记录器。
- `await`
- `complete`
- `error`
- `debug`
- `fatal`
- `fav`
- `info`
- `note`
- `pause`
- `pending`
- `star`
- `start`
- `success`
- `warn`
- `watch`
- `log`
```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)'});
```
### 自定义记录器
要创建自定义记录器,先定义一个 `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.');
```
下面是一个覆盖默认记录器 `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');
```
`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');
```
可以使用 `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();
```
### 交互式记录器
要初始化交互式记录器,请创建一个新的 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);
```
### 输出流
默认情况下,所有 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`');
```
### 计时器
计时器由 `time()` 和 `timeEnd()` 函数管理。 可以使用标签在初始化时唯一标识一个计时器,如果没有提供则计时器将自动分配一个。 此外,调用没有指定标签的 `timeEnd()` 函数将终止最近一个初始化时没有指定标签的计时器。
```js
const signale = require('signale');
signale.time('test');
signale.time();
signale.time();
setTimeout(() => {
signale.timeEnd();
signale.timeEnd();
signale.timeEnd('test');
}, 500);
```
## 配置
### 全局
在 `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
}
}
```
浏览所有可用选项的详细信息。
##### `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`
以大写的方式显示记录器的标签。
### 本地
要启用本地配置,请在您的 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');
```
此外,局部记录器可以拥有自己的独立配置,以覆盖父实例或继承自 `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();
```
## API
#### signale.`(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)
================================================
FILE: index.js
================================================
'use strict';
const Signale = require('./src/signale');
module.exports = Object.assign(new Signale(), {Signale});
================================================
FILE: license.md
================================================
MIT License
Copyright (c) 2018 - present Klaudio Sinani
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: package.json
================================================
{
"name": "signale",
"version": "1.4.0",
"description": "Highly configurable logging utility",
"license": "MIT",
"repository": "klaudiosinani/signale",
"author": {
"name": "Klaudio Sinani",
"email": "klaudiosinani@protonmail.ch",
"url": "https://klaudiosinani.com"
},
"maintainers": [
{
"name": "Mario Sinani",
"email": "mariosinani@protonmail.ch",
"url": "https://mariocfhq.github.io"
}
],
"engines": {
"node": ">=6"
},
"files": [
"src",
"types",
"index.js"
],
"types": "./types/signale.d.ts",
"keywords": [
"log",
"cli",
"logger",
"logging",
"hackable",
"colorful",
"console"
],
"scripts": {
"lint": "xo",
"test:ts": "tsc --noEmit -p test",
"test": "npm run lint && npm run test:ts"
},
"dependencies": {
"chalk": "^2.3.2",
"figures": "^2.0.0",
"pkg-conf": "^2.1.0"
},
"devDependencies": {
"@types/node": "^11.11.3",
"typescript": "^3.3.3333",
"xo": "*"
},
"options": {
"default": {
"displayScope": true,
"displayBadge": true,
"displayDate": false,
"displayFilename": false,
"displayLabel": true,
"displayTimestamp": false,
"underlineLabel": true,
"underlineMessage": false,
"underlinePrefix": false,
"underlineSuffix": false,
"uppercaseLabel": false
}
},
"xo": {
"space": 2
}
}
================================================
FILE: readme.md
================================================
Signale
Highly configurable logging library
## 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.
View all of the available loggers.
- `await`
- `complete`
- `error`
- `debug`
- `fatal`
- `fav`
- `info`
- `note`
- `pause`
- `pending`
- `star`
- `start`
- `success`
- `wait`
- `warn`
- `watch`
- `log`
```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)'});
```
### 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.');
```
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');
```
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');
```
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();
```
### 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);
```
### 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`');
```
### 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`. 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);
```
### 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);
```
## 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
}
}
```
View all of the available options in detail.
##### `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.
### 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');
```
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();
```
## API
#### signale.`(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)
================================================
FILE: src/signale.js
================================================
'use strict';
const util = require('util');
const path = require('path');
const readline = require('readline');
const chalk = require('chalk');
const figures = require('figures');
const pkgConf = require('pkg-conf');
const pkg = require('./../package.json');
const defaultTypes = require('./types');
const {green, grey, red, underline, yellow} = chalk;
let isPreviousLogInteractive = false;
const defaults = pkg.options.default;
const namespace = pkg.name;
class Signale {
constructor(options = {}) {
this._interactive = options.interactive || false;
this._config = Object.assign(this.packageConfiguration, options.config);
this._customTypes = Object.assign({}, options.types);
this._disabled = options.disabled || false;
this._scopeName = options.scope || '';
this._timers = options.timers || new Map();
this._types = this._mergeTypes(defaultTypes, this._customTypes);
this._stream = options.stream || process.stdout;
this._longestLabel = this._getLongestLabel();
this._secrets = options.secrets || [];
this._generalLogLevel = this._validateLogLevel(options.logLevel);
Object.keys(this._types).forEach(type => {
this[type] = this._logger.bind(this, type);
});
}
get _now() {
return Date.now();
}
get scopeName() {
return this._scopeName;
}
get currentOptions() {
return Object.assign({}, {
config: this._config,
disabled: this._disabled,
types: this._customTypes,
interactive: this._interactive,
timers: this._timers,
stream: this._stream,
secrets: this._secrets,
logLevel: this._generalLogLevel
});
}
get date() {
const _ = new Date();
return [_.getFullYear(), _.getMonth() + 1, _.getDate()].join('-');
}
get timestamp() {
const _ = new Date();
return [_.getHours(), _.getMinutes(), _.getSeconds()].join(':');
}
get filename() {
const _ = Error.prepareStackTrace;
Error.prepareStackTrace = (error, stack) => stack;
const {stack} = new Error();
Error.prepareStackTrace = _;
const callers = stack.map(x => x.getFileName());
const firstExternalFilePath = callers.find(x => {
return x !== callers[0];
});
return firstExternalFilePath ? path.basename(firstExternalFilePath) : 'anonymous';
}
get packageConfiguration() {
return pkgConf.sync(namespace, {defaults});
}
get _longestUnderlinedLabel() {
return underline(this._longestLabel);
}
get _logLevels() {
return {
info: 0,
timer: 1,
debug: 2,
warn: 3,
error: 4
};
}
set configuration(configObj) {
this._config = Object.assign(this.packageConfiguration, configObj);
}
_arrayify(x) {
return Array.isArray(x) ? x : [x];
}
_timeSpan(then) {
return (this._now - then);
}
_getLongestLabel() {
const {_types} = this;
const labels = Object.keys(_types).map(x => _types[x].label);
return labels.reduce((x, y) => x.length > y.length ? x : y);
}
_validateLogLevel(level) {
return Object.keys(this._logLevels).includes(level) ? level : 'info';
}
_mergeTypes(standard, custom) {
const types = Object.assign({}, standard);
Object.keys(custom).forEach(type => {
types[type] = Object.assign({}, types[type], custom[type]);
});
return types;
}
_filterSecrets(message) {
const {_secrets} = this;
if (_secrets.length === 0) {
return message;
}
let safeMessage = message;
_secrets.forEach(secret => {
safeMessage = safeMessage.replace(new RegExp(secret, 'g'), '[secure]');
});
return safeMessage;
}
_formatStream(stream) {
return this._arrayify(stream);
}
_formatDate() {
return `[${this.date}]`;
}
_formatFilename() {
return `[${this.filename}]`;
}
_formatScopeName() {
if (Array.isArray(this._scopeName)) {
const scopes = this._scopeName.filter(x => x.length !== 0);
return `${scopes.map(x => `[${x.trim()}]`).join(' ')}`;
}
return `[${this._scopeName}]`;
}
_formatTimestamp() {
return `[${this.timestamp}]`;
}
_formatMessage(str) {
return util.format(...this._arrayify(str));
}
_meta() {
const meta = [];
if (this._config.displayDate) {
meta.push(this._formatDate());
}
if (this._config.displayTimestamp) {
meta.push(this._formatTimestamp());
}
if (this._config.displayFilename) {
meta.push(this._formatFilename());
}
if (this._scopeName.length !== 0 && this._config.displayScope) {
meta.push(this._formatScopeName());
}
if (meta.length !== 0) {
meta.push(`${figures.pointerSmall}`);
return meta.map(item => grey(item));
}
return meta;
}
_hasAdditional({suffix, prefix}, args) {
return (suffix || prefix) ? '' : this._formatMessage(args);
}
_buildSignale(type, ...args) {
let [msg, additional] = [{}, {}];
if (args.length === 1 && typeof (args[0]) === 'object' && args[0] !== null) {
if (args[0] instanceof Error) {
[msg] = args;
} else {
const [{prefix, message, suffix}] = args;
additional = Object.assign({}, {suffix, prefix});
msg = message ? this._formatMessage(message) : this._hasAdditional(additional, args);
}
} else {
msg = this._formatMessage(args);
}
const signale = this._meta();
if (additional.prefix) {
if (this._config.underlinePrefix) {
signale.push(underline(additional.prefix));
} else {
signale.push(additional.prefix);
}
}
if (this._config.displayBadge && type.badge) {
signale.push(chalk[type.color](this._padEnd(type.badge, type.badge.length + 1)));
}
if (this._config.displayLabel && type.label) {
const label = this._config.uppercaseLabel ? type.label.toUpperCase() : type.label;
if (this._config.underlineLabel) {
signale.push(chalk[type.color](this._padEnd(underline(label), this._longestUnderlinedLabel.length + 1)));
} else {
signale.push(chalk[type.color](this._padEnd(label, this._longestLabel.length + 1)));
}
}
if (msg instanceof Error && msg.stack) {
const [name, ...rest] = msg.stack.split('\n');
if (this._config.underlineMessage) {
signale.push(underline(name));
} else {
signale.push(name);
}
signale.push(grey(rest.map(l => l.replace(/^/, '\n')).join('')));
return signale.join(' ');
}
if (this._config.underlineMessage) {
signale.push(underline(msg));
} else {
signale.push(msg);
}
if (additional.suffix) {
if (this._config.underlineSuffix) {
signale.push(underline(additional.suffix));
} else {
signale.push(additional.suffix);
}
}
return signale.join(' ');
}
_write(stream, message) {
if (this._interactive && stream.isTTY && isPreviousLogInteractive) {
readline.moveCursor(stream, 0, -1);
readline.clearLine(stream);
readline.cursorTo(stream, 0);
}
stream.write(message + '\n');
isPreviousLogInteractive = this._interactive;
}
_log(message, streams = this._stream, logLevel) {
if (this.isEnabled() && this._logLevels[logLevel] >= this._logLevels[this._generalLogLevel]) {
this._formatStream(streams).forEach(stream => {
this._write(stream, message);
});
}
}
_logger(type, ...messageObj) {
const {stream, logLevel} = this._types[type];
const message = this._buildSignale(this._types[type], ...messageObj);
this._log(this._filterSecrets(message), stream, this._validateLogLevel(logLevel));
}
_padEnd(str, targetLength) {
str = String(str);
targetLength = parseInt(targetLength, 10) || 0;
if (str.length >= targetLength) {
return str;
}
if (String.prototype.padEnd) {
return str.padEnd(targetLength);
}
targetLength -= str.length;
return str + ' '.repeat(targetLength);
}
addSecrets(secrets) {
if (!Array.isArray(secrets)) {
throw new TypeError('Argument must be an array.');
}
this._secrets.push(...secrets);
}
clearSecrets() {
this._secrets = [];
}
config(configObj) {
this.configuration = configObj;
}
disable() {
this._disabled = true;
}
enable() {
this._disabled = false;
}
isEnabled() {
return !this._disabled;
}
scope(...name) {
if (name.length === 0) {
throw new Error('No scope name was defined.');
}
return new Signale(Object.assign(this.currentOptions, {scope: name}));
}
unscope() {
this._scopeName = '';
}
time(label) {
if (!label) {
label = `timer_${this._timers.size}`;
}
this._timers.set(label, this._now);
const message = this._meta();
message.push(green(this._padEnd(this._types.start.badge, 2)));
if (this._config.underlineLabel) {
message.push(green(this._padEnd(underline(label), this._longestUnderlinedLabel.length + 1)));
} else {
message.push(green(this._padEnd(label, this._longestLabel.length + 1)));
}
message.push('Initialized timer...');
this._log(message.join(' '), this._stream, 'timer');
return label;
}
timeEnd(label) {
if (!label && this._timers.size) {
const is = x => x.includes('timer_');
label = [...this._timers.keys()].reduceRight((x, y) => {
return is(x) ? x : (is(y) ? y : null);
});
}
if (this._timers.has(label)) {
const span = this._timeSpan(this._timers.get(label));
this._timers.delete(label);
const message = this._meta();
message.push(red(this._padEnd(this._types.pause.badge, 2)));
if (this._config.underlineLabel) {
message.push(red(this._padEnd(underline(label), this._longestUnderlinedLabel.length + 1)));
} else {
message.push(red(this._padEnd(label, this._longestLabel.length + 1)));
}
message.push('Timer run for:');
message.push(yellow(span < 1000 ? span + 'ms' : (span / 1000).toFixed(2) + 's'));
this._log(message.join(' '), this._stream, 'timer');
return {label, span};
}
}
}
module.exports = Signale;
================================================
FILE: src/types.js
================================================
'use strict';
const figures = require('figures');
module.exports = {
error: {
badge: figures.cross,
color: 'red',
label: 'error',
logLevel: 'error'
},
fatal: {
badge: figures.cross,
color: 'red',
label: 'fatal',
logLevel: 'error'
},
fav: {
badge: figures('❤'),
color: 'magenta',
label: 'favorite',
logLevel: 'info'
},
info: {
badge: figures.info,
color: 'blue',
label: 'info',
logLevel: 'info'
},
star: {
badge: figures.star,
color: 'yellow',
label: 'star',
logLevel: 'info'
},
success: {
badge: figures.tick,
color: 'green',
label: 'success',
logLevel: 'info'
},
wait: {
badge: figures.ellipsis,
color: 'blue',
label: 'waiting',
logLevel: 'info'
},
warn: {
badge: figures.warning,
color: 'yellow',
label: 'warning',
logLevel: 'warn'
},
complete: {
badge: figures.checkboxOn,
color: 'cyan',
label: 'complete',
logLevel: 'info'
},
pending: {
badge: figures.checkboxOff,
color: 'magenta',
label: 'pending',
logLevel: 'info'
},
note: {
badge: figures.bullet,
color: 'blue',
label: 'note',
logLevel: 'info'
},
start: {
badge: figures.play,
color: 'green',
label: 'start',
logLevel: 'info'
},
pause: {
badge: figures.squareSmallFilled,
color: 'yellow',
label: 'pause',
logLevel: 'info'
},
debug: {
badge: figures('⬤'),
color: 'red',
label: 'debug',
logLevel: 'debug'
},
await: {
badge: figures.ellipsis,
color: 'blue',
label: 'awaiting',
logLevel: 'info'
},
watch: {
badge: figures.ellipsis,
color: 'yellow',
label: 'watching',
logLevel: 'info'
},
log: {
badge: '',
color: '',
label: '',
logLevel: 'info'
}
};
================================================
FILE: test/config.ts
================================================
import * as signale from '..';
// Overrides any existing `package.json` config
signale.config({
displayFilename: true,
displayTimestamp: true,
displayDate: false
});
signale.success('Hello from the Global scope');
function scopedConfigTest() {
// `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');
}
scopedConfigTest();
================================================
FILE: test/custom.ts
================================================
import { Signale, SignaleConstructorOptions } from '../';
type CustomLogger = 'remind' | 'santa';
const optionsCustom: SignaleConstructorOptions = {
stream: process.stdout,
scope: 'custom',
types: {
remind: {
badge: '**',
color: 'yellow',
label: 'reminder'
},
santa: {
badge: '🎅',
color: 'red',
label: 'santa'
}
}
};
const custom = new Signale(optionsCustom);
custom.remind('Improve documentation.');
custom.santa('Hoho! You have an unused variable on L45.');
custom.debug('This should still work');
================================================
FILE: test/default.ts
================================================
import * as logger1 from '..';
const { Signale } = logger1;
logger1.success('Operation successful');
logger1.debug('Hello', 'from', 'L59');
logger1.pending('Write release notes for %s', '1.2.0');
logger1.fatal(new Error('Unable to acquire lock'));
logger1.watch('Recursively watching build directory...');
logger1.complete({
prefix: '[task]',
message: 'Fix issue #59',
suffix: '(@klaussinani)'
});
const logger2 = new Signale();
logger2.success('Operation successful');
logger2.debug('Hello', 'from', 'L59');
logger2.pending('Write release notes for %s', '1.2.0');
logger2.fatal(new Error('Unable to acquire lock'));
logger2.watch('Recursively watching build directory...');
logger2.complete({
prefix: '[task]',
message: 'Fix issue #59',
suffix: '(@klaussinani)'
});
================================================
FILE: test/override.ts
================================================
import { Signale, SignaleConstructorOptions } from '../';
const optionsOverride: SignaleConstructorOptions = {
types: {
error: {
badge: '!!',
color: 'red',
label: 'fatal error'
},
success: {
badge: '++',
color: 'green',
label: 'huge success'
}
}
};
const signale = new Signale(optionsOverride);
signale.error('Default Error Log');
signale.success('Default Success Log');
const customOverride = new Signale(optionsOverride);
customOverride.error('Custom Error Log');
customOverride.success('Custom Success Log');
================================================
FILE: test/scoped.ts
================================================
import { Signale, SignaleConstructorOptions } from '../';
const optionsScope: SignaleConstructorOptions = {
scope: 'global1 scope'
};
const global1 = new Signale(optionsScope);
global1.success('Successful Operation');
const global2 = global1.scope('global2 scope');
global2.success('Hello from the global2 scope');
function scopedTest() {
const outer = global2.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);
}
scopedTest();
================================================
FILE: test/secrets.ts
================================================
import { Signale, SignaleConstructorOptions } from '..';
// In reality secrets could be securely fetched/decrypted through a dedicated API
const [USERNAME, TOKEN] = ['klaussinani', 'token'];
const opts: SignaleConstructorOptions = {
secrets: [USERNAME, TOKEN]
}
const logger1 = new Signale(opts);
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);
================================================
FILE: test/streams.ts
================================================
import { Signale, SignaleConstructorOptions } from '..';
const opts: SignaleConstructorOptions = {
stream: process.stderr, // All loggers will now write to `process.stderr`
types: {
error: {
badge: '✖',
color: 'red',
label: 'error',
// Only `error` will write to both `process.stdout` & `process.stderr`
stream: [process.stdout, process.stderr]
}
}
};
const signale = new Signale(opts);
signale.success('Message will appear on `process.stderr`');
signale.error('Message will appear on both `process.stdout` & `process.stderr`');
================================================
FILE: test/timers.ts
================================================
import * as signale from '../';
signale.time('test1');
signale.time('test2');
signale.time();
signale.time();
setTimeout(() => {
signale.timeEnd();
signale.timeEnd();
signale.timeEnd('test2');
signale.timeEnd('test1');
}, 500);
================================================
FILE: test/tsconfig.json
================================================
{
"compilerOptions": {
"module": "commonjs",
"strict": true,
"target": "ES2017"
}
}
================================================
FILE: types/signale.d.ts
================================================
/* Authors: Resi Respati
* Kingdaro
* Joydip Roy
* Klaus Sinani
*/
import { Writable as WritableStream } from 'stream';
declare namespace _signale {
export type DefaultLogger =
| 'await'
| 'complete'
| 'debug'
| 'error'
| 'fatal'
| 'fav'
| 'info'
| 'log'
| 'note'
| 'pause'
| 'pending'
| 'star'
| 'start'
| 'success'
| 'wait'
| 'warn'
| 'watch';
export type ChalkColor =
| 'black'
| 'blue'
| 'blueBright'
| 'cyan'
| 'cyanBright'
| 'gray'
| 'green'
| 'greenBright'
| 'magenta'
| 'magentaBright'
| 'red'
| 'redBright'
| 'white'
| 'whiteBright'
| 'yellow'
| 'yellowBright';
export type Secret = (string | number)[];
export type LoggerFunction = (...message: any[]) => void;
export type LogLevel = 'info' | 'timer' | 'debug' | 'warn' | 'error';
export interface LoggerConfiguration {
badge: string;
color: ChalkColor;
label: string;
logLevel?: LogLevel;
stream?: WritableStream | WritableStream[];
}
export interface InstanceConfiguration {
displayBadge?: boolean;
displayDate?: boolean;
displayFilename?: boolean;
displayLabel?: boolean;
displayScope?: boolean;
displayTimestamp?: boolean;
underlineLabel?: boolean;
underlineMessage?: boolean;
underlinePrefix?: boolean;
underlineSuffix?: boolean;
uppercaseLabel?: boolean;
}
export interface ConstructorOptions {
config?: InstanceConfiguration;
disabled?: boolean;
interactive?: boolean;
logLevel?: LogLevel;
scope?: string;
secrets?: Secret;
stream?: WritableStream | WritableStream[];
types?: Partial>;
}
export interface Constructor {
new (
options?: ConstructorOptions
): Instance;
}
interface Base {
addSecrets(secrets: Secret): void;
clearSecrets(): void;
config(configObj: InstanceConfiguration): Instance;
disable(): void;
enable(): void;
isEnabled(): boolean;
scope(...name: string[]): Instance;
time(label?: string): string;
timeEnd(label?: string): { label: string; span: number };
unscope(): void;
}
export type Instance = Base &
Record &
Record;
}
declare namespace signale {
export type Secret = _signale.Secret;
export type LogLevel = _signale.LogLevel;
export type ChalkColor = _signale.ChalkColor;
export type DefaultLogger = _signale.DefaultLogger;
export type LoggerFunction = _signale.LoggerFunction;
export interface SignaleConfiguration
extends _signale.InstanceConfiguration {}
export interface LoggerConfiguration extends _signale.LoggerConfiguration {}
export interface SignaleConstructorOptions
extends _signale.ConstructorOptions {}
}
declare const signale: _signale.Instance & {
Signale: _signale.Constructor;
};
export = signale;