Merge pull request #1271 from ZachHaber/prettier

Run prettier and require it
This commit is contained in:
Lam Wei Li 2022-06-23 01:14:04 +08:00 committed by GitHub
commit cb5e44e303
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
133 changed files with 5306 additions and 4602 deletions

10
.gitattributes vendored
View File

@ -8,18 +8,20 @@
# for example, after the build script is run)
.* text eol=lf
*.css text eol=lf
*.scss text eol=lf
*.html text eol=lf
*.js text eol=lf
*.cjs text eol=lf
*.mjs text eol=lf
*.cjs text eol=lf
*.mjs text eol=lf
*.ts text eol=lf
*.cts text eol=lf
*.mts text eol=lf
*.cts text eol=lf
*.mts text eol=lf
*.json text eol=lf
*.md text eol=lf
*.sh text eol=lf
*.txt text eol=lf
*.xml text eol=lf
*.yml text eol=lf
# Exclude the `.htaccess` file from GitHub's language statistics
# https://github.com/github/linguist#using-gitattributes

3
.prettierignore Normal file
View File

@ -0,0 +1,3 @@
coverage
.github
.nyc_output

View File

@ -76,7 +76,7 @@
## 6.4.4
- [fix: set logger.level on runtime will no longer wrongly reset useCallStack](https://github.com/log4js-node/log4js-node/pull/1217) - thanks [@peteriman](https://github.com/peteriman)
- [fix: set logger.level on runtime will no longer wrongly reset useCallStack](https://github.com/log4js-node/log4js-node/pull/1217) - thanks [@peteriman](https://github.com/peteriman)
- [docs: updated docs for broken links and inaccessible pages](https://github.com/log4js-node/log4js-node/pull/1219) - thanks [@peteriman](https://github.com/peteriman)
- [docs: broken link to gelf appender](https://github.com/log4js-node/log4js-node/pull/1218) - thanks [@mattalexx](https://github.com/mattalexx)
- [docs: updated docs for appenders module loading](https://github.com/log4js-node/log4js-node/pull/985) - thanks [@leonimurilo](https://github.com/leonimurilo)
@ -89,7 +89,6 @@
## 6.4.3
- [fix: added filename validation](https://github.com/log4js-node/log4js-node/pull/1201) - thanks [@peteriman](https://github.com/peteriman)
- [refactor: do not initialise default appenders as it will be done again by configure()](https://github.com/log4js-node/log4js-node/pull/1210) - thanks [@peteriman](https://github.com/peteriman)
- [refactor: defensive coding for cluster=null if require('cluster') fails in try-catch ](https://github.com/log4js-node/log4js-node/pull/1199) - thanks [@peteriman](https://github.com/peteriman)
@ -153,6 +152,7 @@
- [chore(deps-dev): bump typescript from 4.5.4 to 4.5.5](https://github.com/log4js-node/log4js-node/pull/1166) - thanks [@peteriman](https://github.com/peteriman)
## 6.4.0 - BREAKING CHANGE 💥
New default file permissions may cause external applications unable to read logs.
A [manual code/configuration change](https://github.com/log4js-node/log4js-node/pull/1141#issuecomment-1076224470) is required.
@ -199,7 +199,7 @@ A [manual code/configuration change](https://github.com/log4js-node/log4js-node/
- chore(deps-dev): bump eslint-plugin-prettier from 3.4.1 to 4.0.0
- chore(deps-dev): bump husky from 3.1.0 to 7.0.4
- chore(deps-dev): bump prettier from 1.19.0 to 2.5.1
- chore(deps-dev): bump typescript from 3.9.10 to 4.5.4
- chore(deps-dev): bump typescript from 3.9.10 to 4.5.4
- [chore(deps-dev): bump eslint-config-prettier from 6.15.0 to 8.3.0](https://github.com/log4js-node/log4js-node/pull/1129) - thanks [@peteriman](https://github.com/peteriman)
- [chore(deps): updated dependencies](https://github.com/log4js-node/log4js-node/pull/1121) - thanks [@peteriman](https://github.com/peteriman)
- chore(deps-dev): bump codecov from 3.6.1 to 3.8.3

View File

@ -1,5 +1,4 @@
log4js-node [![CodeQL](https://github.com/log4js-node/log4js-node/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/log4js-node/log4js-node/actions/workflows/codeql-analysis.yml) [![Node.js CI](https://github.com/log4js-node/log4js-node/actions/workflows/node.js.yml/badge.svg)](https://github.com/log4js-node/log4js-node/actions/workflows/node.js.yml)
===========
# log4js-node [![CodeQL](https://github.com/log4js-node/log4js-node/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/log4js-node/log4js-node/actions/workflows/codeql-analysis.yml) [![Node.js CI](https://github.com/log4js-node/log4js-node/actions/workflows/node.js.yml/badge.svg)](https://github.com/log4js-node/log4js-node/actions/workflows/node.js.yml)
[![NPM](https://nodei.co/npm/log4js.png?downloads=true&downloadRank=true&stars=true)](https://nodei.co/npm/log4js/)
@ -67,7 +66,7 @@ See example.js for a full example, but here's a snippet (also in `examples/fromr
const log4js = require("log4js");
log4js.configure({
appenders: { cheese: { type: "file", filename: "cheese.log" } },
categories: { default: { appenders: ["cheese"], level: "error" } }
categories: { default: { appenders: ["cheese"], level: "error" } },
});
const logger = log4js.getLogger("cheese");
@ -102,7 +101,7 @@ There's also [an example application](https://github.com/log4js-node/log4js-exam
import log4js from "log4js";
log4js.configure({
appenders: { cheese: { type: "file", filename: "cheese.log" } },
categories: { default: { appenders: ["cheese"], level: "error" } }
categories: { default: { appenders: ["cheese"], level: "error" } },
});
const logger = log4js.getLogger();

View File

@ -2,7 +2,7 @@
## Supported Versions
We're aiming to only support the latest major version of log4js. Older than that is usually *very* old.
We're aiming to only support the latest major version of log4js. Older than that is usually _very_ old.
| Version | Supported |
| ------- | ------------------ |
@ -13,7 +13,7 @@ We're aiming to only support the latest major version of log4js. Older than that
Report vulnerabilities via email to:
* Gareth Jones <gareth.nomiddlename@gmail.com>
* Lam Wei Li <lam_wei_li@hotmail.com>
- Gareth Jones <gareth.nomiddlename@gmail.com>
- Lam Wei Li <lam_wei_li@hotmail.com>
Please put "[log4js:security]" in the subject line. We will aim to respond within a day or two.

View File

@ -1,14 +1,20 @@
<!doctype html>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="chrome=1">
<title>{{ site.title | default: site.github.repository_name }} by {{ site.github.owner_name }}</title>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="chrome=1" />
<title>
{{ site.title | default: site.github.repository_name }} by {{
site.github.owner_name }}
</title>
<link rel="stylesheet" href="{{ '/assets/css/style.css?v=' | append: site.github.build_revision | relative_url }}">
<meta name="viewport" content="width=device-width">
<link
rel="stylesheet"
href="{{ '/assets/css/style.css?v=' | append: site.github.build_revision | relative_url }}"
/>
<meta name="viewport" content="width=device-width" />
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>
<body>
@ -18,7 +24,11 @@
<p>{{ site.description | default: site.github.project_tagline }}</p>
{% if site.github.is_project_page %}
<p class="view"><a href="{{ site.github.repository_url }}">View the Project on GitHub <small>{{ github_name }}</small></a></p>
<p class="view">
<a href="{{ site.github.repository_url }}"
>View the Project on GitHub <small>{{ github_name }}</small></a
>
</p>
{% endif %}
<ul>
@ -34,42 +44,72 @@
</ul>
{% if site.github.is_user_page %}
<p class="view"><a href="{{ site.github.owner_url }}">View My GitHub Profile</a></p>
{% endif %}
{% if site.show_downloads %}
<ul>
<li><a href="{{ site.github.zip_url }}">Download <strong>ZIP File</strong></a></li>
<li><a href="{{ site.github.tar_url }}">Download <strong>TAR Ball</strong></a></li>
<li><a href="{{ site.github.repository_url }}">View On <strong>GitHub</strong></a></li>
</ul>
<p class="view">
<a href="{{ site.github.owner_url }}">View My GitHub Profile</a>
</p>
{% endif %} {% if site.show_downloads %}
<ul>
<li>
<a href="{{ site.github.zip_url }}"
>Download <strong>ZIP File</strong></a
>
</li>
<li>
<a href="{{ site.github.tar_url }}"
>Download <strong>TAR Ball</strong></a
>
</li>
<li>
<a href="{{ site.github.repository_url }}"
>View On <strong>GitHub</strong></a
>
</li>
</ul>
{% endif %}
</header>
<section>
{{ content }}
</section>
<section>{{ content }}</section>
<footer>
{% if site.github.is_project_page %}
<p>This project is maintained by <a href="{{ site.github.owner_url }}">{{ site.github.owner_name }}</a></p>
<p>
This project is maintained by
<a href="{{ site.github.owner_url }}">{{ site.github.owner_name }}</a>
</p>
{% endif %}
<p><small>Hosted on GitHub Pages &mdash; Theme by <a href="https://github.com/orderedlist">orderedlist</a></small></p>
<p>
<small
>Hosted on GitHub Pages &mdash; Theme by
<a href="https://github.com/orderedlist">orderedlist</a></small
>
</p>
</footer>
</div>
<script src="{{ '/assets/js/scale.fix.js' | relative_url }}"></script>
{% if site.google_analytics %}
{% if site.google_analytics %}
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
(function (i, s, o, g, r, a, m) {
i["GoogleAnalyticsObject"] = r;
(i[r] =
i[r] ||
function () {
(i[r].q = i[r].q || []).push(arguments);
}),
(i[r].l = 1 * new Date());
(a = s.createElement(o)), (m = s.getElementsByTagName(o)[0]);
a.async = 1;
a.src = g;
m.parentNode.insertBefore(a, m);
})(
window,
document,
"script",
"https://www.google-analytics.com/analytics.js",
"ga"
);
ga('create', '{{ site.google_analytics }}', 'auto');
ga('send', 'pageview');
ga("create", "{{ site.google_analytics }}", "auto");
ga("send", "pageview");
</script>
{% endif %}
{% endif %}
</body>
</html>

View File

@ -13,34 +13,39 @@ Configuration objects must define at least one appender, and a default category.
`configure` method call returns the configured log4js object.
### Configuration Object
Properties:
* `levels` (optional, object) - used for defining custom log levels, or redefining existing ones; this is a map with the level name as the key (string, case insensitive), and an object as the value. The object should have two properties: the level value (integer) as the value, and the colour. Log levels are used to assign importance to log messages, with the integer value being used to sort them. If you do not specify anything in your configuration, the default values are used (ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < MARK < OFF - note that OFF is intended to be used to turn off logging, not as a level for actual logging, i.e. you would never call `logger.off('some log message')`). Levels defined here are used in addition to the default levels, with the integer value being used to determine their relation to the default levels. If you define a level with the same name as a default level, then the integer value in the config takes precedence. Level names must begin with a letter, and can only contain letters, numbers and underscores.
* `appenders` (object) - a map of named appenders (string) to appender definitions (object); appender definitions must have a property `type` (string) - other properties depend on the appender type.
* `categories` (object) - a map of named categories (string) to category definitions (object). You must define the `default` category which is used for all log events that do not match a specific category. Category definitions have two properties:
* `appenders` (array of strings) - the list of appender names to be used for this category. A category must have at least one appender.
* `level` (string, case insensitive) - the minimum log level that this category will send to the appenders. For example, if set to 'error' then the appenders will only receive log events of level 'error', 'fatal', 'mark' - log events of 'info', 'warn', 'debug', or 'trace' will be ignored.
* `enableCallStack` (boolean, optional, defaults to `false`) - setting this to `true` will make log events for this category use the call stack to generate line numbers and file names in the event. See [pattern layout](layouts.md) for how to output these values in your appenders.
* `pm2` (boolean) (optional) - set this to true if you're running your app using [pm2](http://pm2.keymetrics.io), otherwise logs will not work (you'll also need to install pm2-intercom as pm2 module: `pm2 install pm2-intercom`)
* `pm2InstanceVar` (string) (optional, defaults to 'NODE_APP_INSTANCE') - set this if you're using pm2 and have changed the default name of the NODE_APP_INSTANCE variable.
* `disableClustering` (boolean) (optional) - set this to true if you liked the way log4js used to just ignore clustered environments, or you're having trouble with PM2 logging. Each worker process will do its own logging. Be careful with this if you're logging to files, weirdness can occur.
- `levels` (optional, object) - used for defining custom log levels, or redefining existing ones; this is a map with the level name as the key (string, case insensitive), and an object as the value. The object should have two properties: the level value (integer) as the value, and the colour. Log levels are used to assign importance to log messages, with the integer value being used to sort them. If you do not specify anything in your configuration, the default values are used (ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < MARK < OFF - note that OFF is intended to be used to turn off logging, not as a level for actual logging, i.e. you would never call `logger.off('some log message')`). Levels defined here are used in addition to the default levels, with the integer value being used to determine their relation to the default levels. If you define a level with the same name as a default level, then the integer value in the config takes precedence. Level names must begin with a letter, and can only contain letters, numbers and underscores.
- `appenders` (object) - a map of named appenders (string) to appender definitions (object); appender definitions must have a property `type` (string) - other properties depend on the appender type.
- `categories` (object) - a map of named categories (string) to category definitions (object). You must define the `default` category which is used for all log events that do not match a specific category. Category definitions have two properties:
- `appenders` (array of strings) - the list of appender names to be used for this category. A category must have at least one appender.
- `level` (string, case insensitive) - the minimum log level that this category will send to the appenders. For example, if set to 'error' then the appenders will only receive log events of level 'error', 'fatal', 'mark' - log events of 'info', 'warn', 'debug', or 'trace' will be ignored.
- `enableCallStack` (boolean, optional, defaults to `false`) - setting this to `true` will make log events for this category use the call stack to generate line numbers and file names in the event. See [pattern layout](layouts.md) for how to output these values in your appenders.
- `pm2` (boolean) (optional) - set this to true if you're running your app using [pm2](http://pm2.keymetrics.io), otherwise logs will not work (you'll also need to install pm2-intercom as pm2 module: `pm2 install pm2-intercom`)
- `pm2InstanceVar` (string) (optional, defaults to 'NODE_APP_INSTANCE') - set this if you're using pm2 and have changed the default name of the NODE_APP_INSTANCE variable.
- `disableClustering` (boolean) (optional) - set this to true if you liked the way log4js used to just ignore clustered environments, or you're having trouble with PM2 logging. Each worker process will do its own logging. Be careful with this if you're logging to files, weirdness can occur.
## Loggers - `log4js.getLogger([category])`
This function takes a single optional string argument to denote the category to be used for log events on this logger. If no category is specified, the events will be routed to the appender for the `default` category. The function returns a `Logger` object which has its level set to the level specified for that category in the config and implements the following functions:
* `<level>(args...)` - where `<level>` can be any of the lower case names of the levels (including any custom levels defined). For example: `logger.info('some info')` will dispatch a log event with a level of info. If you're using the basic, coloured or message pass-through [layouts](layouts.md), the logged string will have its formatting (placeholders like `%s`, `%d`, etc) delegated to [util.format](https://nodejs.org/api/util.html#util_util_format_format_args).
* `is<level>Enabled()` - returns true if a log event of level <level> (camel case) would be dispatched to the appender defined for the logger's category. For example: `logger.isInfoEnabled()` will return true if the level for the logger is INFO or lower.
* `addContext(<key>,<value>)` - where `<key>` is a string, `<value>` can be anything. This stores a key-value pair that is added to all log events generated by the logger. Uses would be to add ids for tracking a user through your application. Currently only the `logFaces` appenders make use of the context values.
* `removeContext(<key>)` - removes a previously defined key-value pair from the context.
* `clearContext()` - removes all context pairs from the logger.
* `setParseCallStackFunction(function)` - Allow to override the default way to parse the callstack data for the layout pattern, a generic javascript Error object is passed to the function. Must return an object with properties : `functionName` / `fileName` / `lineNumber` / `columnNumber` / `callStack`. Can for example be used if all of your log call are made from one "debug" class and you would to "erase" this class from the callstack to only show the function which called your "debug" class.
- `<level>(args...)` - where `<level>` can be any of the lower case names of the levels (including any custom levels defined). For example: `logger.info('some info')` will dispatch a log event with a level of info. If you're using the basic, coloured or message pass-through [layouts](layouts.md), the logged string will have its formatting (placeholders like `%s`, `%d`, etc) delegated to [util.format](https://nodejs.org/api/util.html#util_util_format_format_args).
- `is<level>Enabled()` - returns true if a log event of level <level> (camel case) would be dispatched to the appender defined for the logger's category. For example: `logger.isInfoEnabled()` will return true if the level for the logger is INFO or lower.
- `addContext(<key>,<value>)` - where `<key>` is a string, `<value>` can be anything. This stores a key-value pair that is added to all log events generated by the logger. Uses would be to add ids for tracking a user through your application. Currently only the `logFaces` appenders make use of the context values.
- `removeContext(<key>)` - removes a previously defined key-value pair from the context.
- `clearContext()` - removes all context pairs from the logger.
- `setParseCallStackFunction(function)` - Allow to override the default way to parse the callstack data for the layout pattern, a generic javascript Error object is passed to the function. Must return an object with properties : `functionName` / `fileName` / `lineNumber` / `columnNumber` / `callStack`. Can for example be used if all of your log call are made from one "debug" class and you would to "erase" this class from the callstack to only show the function which called your "debug" class.
The `Logger` object has the following properties:
* `level` - where `level` is a log4js level or a string that matches a level (e.g. 'info', 'INFO', etc). This allows overriding the configured level for this logger. Changing this value applies to all loggers of the same category.
* `useCallStack` - where `useCallStack` is a boolean to indicate if log events for this category use the call stack to generate line numbers and file names in the event. This allows overriding the configured useCallStack for this logger. Changing this value applies to all loggers of the same category.
- `level` - where `level` is a log4js level or a string that matches a level (e.g. 'info', 'INFO', etc). This allows overriding the configured level for this logger. Changing this value applies to all loggers of the same category.
- `useCallStack` - where `useCallStack` is a boolean to indicate if log events for this category use the call stack to generate line numbers and file names in the event. This allows overriding the configured useCallStack for this logger. Changing this value applies to all loggers of the same category.
## Shutdown - `log4js.shutdown(cb)`
`shutdown` accepts a callback that will be called when log4js has closed all appenders and finished writing log events. Use this when your programme exits to make sure all your logs are written to files, sockets are closed, etc.
## Custom Layouts - `log4js.addLayout(type, fn)`
This function is used to add user-defined layout functions. See [layouts](layouts.md) for more details and an example.

View File

@ -1,73 +1,79 @@
# Log4js - Appenders
Appenders serialise log events to some form of output. They can write to files, send emails, send data over the network. All appenders have a `type` which determines which appender gets used. For example:
```javascript
const log4js = require('log4js');
const log4js = require("log4js");
log4js.configure({
appenders: {
out: { type: 'stdout' },
app: { type: 'file', filename: 'application.log' }
out: { type: "stdout" },
app: { type: "file", filename: "application.log" },
},
categories: {
default: { appenders: [ 'out', 'app' ], level: 'debug' }
}
default: { appenders: ["out", "app"], level: "debug" },
},
});
```
This defines two appenders named 'out' and 'app'. 'out' uses the [stdout](stdout.md) appender which writes to standard out. 'app' uses the [file](file.md) appender, configured to write to 'application.log'.
## Core Appenders
The following appenders are included with log4js. Some require extra dependencies that are not included as part of log4js (the [smtp](https://github.com/log4js-node/smtp) appender needs [nodemailer](https://www.npmjs.com/package/nodemailer) for example), and these will be noted in the docs for that appender. If you don't use those appenders, then you don't need the extra dependencies.
* [categoryFilter](categoryFilter.md)
* [console](console.md)
* [dateFile](dateFile.md)
* [file](file.md)
* [fileSync](fileSync.md)
* [logLevelFilter](logLevelFilter.md)
* [multiFile](multiFile.md)
* [multiprocess](multiprocess.md)
* [noLogFilter](noLogFilter.md)
* [recording](recording.md)
* [stderr](stderr.md)
* [stdout](stdout.md)
* [tcp](tcp.md)
* [tcp-server](tcp-server.md)
- [categoryFilter](categoryFilter.md)
- [console](console.md)
- [dateFile](dateFile.md)
- [file](file.md)
- [fileSync](fileSync.md)
- [logLevelFilter](logLevelFilter.md)
- [multiFile](multiFile.md)
- [multiprocess](multiprocess.md)
- [noLogFilter](noLogFilter.md)
- [recording](recording.md)
- [stderr](stderr.md)
- [stdout](stdout.md)
- [tcp](tcp.md)
- [tcp-server](tcp-server.md)
## Optional Appenders
The following appenders are supported by log4js, but are no longer distributed with log4js core from version 3 onwards.
* [gelf](https://github.com/log4js-node/gelf)
* [hipchat](https://github.com/log4js-node/hipchat)
* [logFaces-HTTP](https://github.com/log4js-node/logFaces-HTTP)
* [logFaces-UDP](https://github.com/log4js-node/logFaces-UDP)
* [loggly](https://github.com/log4js-node/loggly)
* [logstashHTTP](https://github.com/log4js-node/logstashHTTP)
* [logstashUDP](https://github.com/log4js-node/logstashUDP)
* [mailgun](https://github.com/log4js-node/mailgun)
* [rabbitmq](https://github.com/log4js-node/rabbitmq)
* [redis](https://github.com/log4js-node/redis)
* [slack](https://github.com/log4js-node/slack)
* [smtp](https://github.com/log4js-node/smtp)
- [gelf](https://github.com/log4js-node/gelf)
- [hipchat](https://github.com/log4js-node/hipchat)
- [logFaces-HTTP](https://github.com/log4js-node/logFaces-HTTP)
- [logFaces-UDP](https://github.com/log4js-node/logFaces-UDP)
- [loggly](https://github.com/log4js-node/loggly)
- [logstashHTTP](https://github.com/log4js-node/logstashHTTP)
- [logstashUDP](https://github.com/log4js-node/logstashUDP)
- [mailgun](https://github.com/log4js-node/mailgun)
- [rabbitmq](https://github.com/log4js-node/rabbitmq)
- [redis](https://github.com/log4js-node/redis)
- [slack](https://github.com/log4js-node/slack)
- [smtp](https://github.com/log4js-node/smtp)
For example, if you were previously using the gelf appender (`type: 'gelf'`) then you should add `@log4js-node/gelf` to your dependencies and change the type to `type: '@log4js-node/gelf'`.
## Other Appenders
These appenders are maintained by its own authors and may be useful for you:
* [udp](https://github.com/iassasin/log4js-udp-appender)
- [udp](https://github.com/iassasin/log4js-udp-appender)
## Custom Appenders
Log4js can load appenders from outside the core appenders. The `type` config value is used as a require path if no matching appender can be found. For example, the following configuration will attempt to load an appender from the module 'cheese/appender', passing the rest of the config for the appender to that module:
```javascript
log4js.configure({
appenders: { gouda: { type: 'cheese/appender', flavour: 'tasty' } },
categories: { default: { appenders: ['gouda'], level: 'debug' }}
appenders: { gouda: { type: "cheese/appender", flavour: "tasty" } },
categories: { default: { appenders: ["gouda"], level: "debug" } },
});
```
Log4js checks the following places (in this order) for appenders based on the type value:
1. The core appenders: `require('./appenders/' + type)`
2. node_modules: `require(type)`
3. relative to the main file of your application: `require(path.dirname(require.main.filename) + '/' + type)`
@ -76,14 +82,18 @@ Log4js checks the following places (in this order) for appenders based on the ty
If you want to write your own appender, read the [documentation](writing-appenders.md) first.
## Advanced configuration
If you've got a custom appender of your own, or are using webpack (or some other bundler), you may find it easier to pass
in the appender module in the config instead of loading from the node.js require path. Here's an example:
```javascript
const myAppenderModule = {
configure: (config, layouts, findAppender, levels) => { /* ...your appender config... */ }
configure: (config, layouts, findAppender, levels) => {
/* ...your appender config... */
},
};
log4js.configure({
appenders: { custom: { type: myAppenderModule } },
categories: { default: { appenders: ['custom'], level: 'debug' } }
categories: { default: { appenders: ["custom"], level: "debug" } },
});
```

View File

@ -8,7 +8,7 @@ header ul {
height: auto;
padding: 0;
list-style: disc;
clear:both;
clear: both;
margin-left: 20px;
}

View File

@ -1,55 +1,64 @@
# Categories
Categories are groups of log events. The category for log events is defined when you get a _Logger_ from log4js (`log4js.getLogger('somecategory')`). Log events with the same _category_ will go to the same _appenders_.
## Default configuration
When defining your appenders through a configuration, at least one category must be defined.
```javascript
const log4js = require('log4js');
const log4js = require("log4js");
log4js.configure({
appenders: {
out: { type: 'stdout' },
app: { type: 'file', filename: 'application.log' }
out: { type: "stdout" },
app: { type: "file", filename: "application.log" },
},
categories: {
default: { appenders: [ 'out' ], level: 'trace' },
app: { appenders: ['app'], level: 'trace' }
}
default: { appenders: ["out"], level: "trace" },
app: { appenders: ["app"], level: "trace" },
},
});
const logger = log4js.getLogger();
logger.trace('This will use the default category and go to stdout');
const logToFile = log4js.getLogger('app');
logToFile.trace('This will go to a file');
logger.trace("This will use the default category and go to stdout");
const logToFile = log4js.getLogger("app");
logToFile.trace("This will go to a file");
```
## Categories inheritance
Log4js supports a hierarchy for categories, using dots to separate layers - for example, log events in the category 'myapp.submodule' will use the level for 'myapp' if none is defined for 'myapp.submodule', and also any appenders defined for 'myapp'.
This behaviour can be disabled by setting inherit=false on the sub-category.
Log4js supports a hierarchy for categories, using dots to separate layers - for example, log events in the category 'myapp.submodule' will use the level for 'myapp' if none is defined for 'myapp.submodule', and also any appenders defined for 'myapp'.
This behaviour can be disabled by setting inherit=false on the sub-category.
```javascript
const log4js = require('log4js');
const log4js = require("log4js");
log4js.configure({
appenders: {
console: { type: 'console' },
app: { type: 'file', filename: 'application.log' }
console: { type: "console" },
app: { type: "file", filename: "application.log" },
},
categories: {
default: { appenders: [ 'console' ], level: 'trace' },
catA: { appenders: ['console'], level: 'error' },
'catA.catB': { appenders: ['app'], level: 'trace' },
}
default: { appenders: ["console"], level: "trace" },
catA: { appenders: ["console"], level: "error" },
"catA.catB": { appenders: ["app"], level: "trace" },
},
});
const loggerA = log4js.getLogger('catA');
loggerA.error('This will be written to console with log level ERROR');
loggerA.trace('This will not be written');
const loggerAB = log4js.getLogger('catA.catB');
loggerAB.error('This will be written with log level ERROR to console and to a file');
loggerAB.trace('This will be written with log level TRACE to console and to a file');
const loggerA = log4js.getLogger("catA");
loggerA.error("This will be written to console with log level ERROR");
loggerA.trace("This will not be written");
const loggerAB = log4js.getLogger("catA.catB");
loggerAB.error(
"This will be written with log level ERROR to console and to a file"
);
loggerAB.trace(
"This will be written with log level TRACE to console and to a file"
);
```
Two categories are defined:
- Log events with category 'catA' will go to appender 'console' only.
- Log events with category 'catA.catB' will go to appenders 'console' and 'app'.
- Log events with category 'catA.catB' will go to appenders 'console' and 'app'.
Appenders will see and log an event only if the category level is less than or equal to the event's level.

View File

@ -4,44 +4,50 @@ This is not strictly an appender - it wraps around another appender and stops lo
## Configuration
* `type` - `"categoryFilter"`
* `exclude` - `string | Array<string>` - the category (or categories if you provide an array of values) that will be excluded from the appender.
* `appender` - `string` - the name of the appender to filter.
- `type` - `"categoryFilter"`
- `exclude` - `string | Array<string>` - the category (or categories if you provide an array of values) that will be excluded from the appender.
- `appender` - `string` - the name of the appender to filter.
## Example
```javascript
log4js.configure({
appenders: {
everything: { type: 'file', filename: 'all-the-logs.log' },
'no-noise': { type: 'categoryFilter', exclude: 'noisy.component', appender: 'everything' }
everything: { type: "file", filename: "all-the-logs.log" },
"no-noise": {
type: "categoryFilter",
exclude: "noisy.component",
appender: "everything",
},
},
categories: {
default: { appenders: [ 'no-noise' ], level: 'debug' }
}
default: { appenders: ["no-noise"], level: "debug" },
},
});
const logger = log4js.getLogger();
const noisyLogger = log4js.getLogger('noisy.component');
logger.debug('I will be logged in all-the-logs.log');
noisyLogger.debug('I will not be logged.');
const noisyLogger = log4js.getLogger("noisy.component");
logger.debug("I will be logged in all-the-logs.log");
noisyLogger.debug("I will not be logged.");
```
Note that you can achieve the same outcome without using the category filter, like this:
```javascript
log4js.configure({
appenders: {
everything: { type: 'file', filename: 'all-the-logs.log' }
everything: { type: "file", filename: "all-the-logs.log" },
},
categories: {
default: { appenders: [ 'everything' ], level: 'debug' },
'noisy.component': { appenders: ['everything'], level: 'off' }
}
default: { appenders: ["everything"], level: "debug" },
"noisy.component": { appenders: ["everything"], level: "off" },
},
});
const logger = log4js.getLogger();
const noisyLogger = log4js.getLogger('noisy.component');
logger.debug('I will be logged in all-the-logs.log');
noisyLogger.debug('I will not be logged.');
const noisyLogger = log4js.getLogger("noisy.component");
logger.debug("I will be logged in all-the-logs.log");
noisyLogger.debug("I will not be logged.");
```
Category filter becomes useful when you have many categories you want to exclude, passing them as an array.

View File

@ -5,17 +5,21 @@ If you're running log4js in an application that uses [node's core cluster](https
This can cause problems in some rare circumstances, if you're experiencing weird logging problems, then use the `disableClustering: true` option in your log4js configuration to have every process behave as if it were the master process. Be careful if you're logging to files.
## I'm using PM2, but I'm not getting any logs!
To get log4js working with [PM2](http://pm2.keymetrics.io), you'll need to install the [pm2-intercom](https://www.npmjs.com/package/pm2-intercom) module.
```bash
pm2 install pm2-intercom
```
Then add the value `pm2: true` to your log4js configuration. If you're also using `node-config`, then you'll probably have renamed your `NODE_APP_INSTANCE` environment variable. If so, you'll also need to add `pm2InstanceVar: '<NEW_APP_INSTANCE_ID>'` where `<NEW_APP_INSTANCE_ID>` should be replaced with the new name you gave the instance environment variable.
```javascript
log4js.configure({
appenders: { out: { type: 'stdout'}},
categories: { default: { appenders: ['out'], level: 'info'}},
appenders: { out: { type: "stdout" } },
categories: { default: { appenders: ["out"], level: "info" } },
pm2: true,
pm2InstanceVar: 'INSTANCE_ID'
pm2InstanceVar: "INSTANCE_ID",
});
```

View File

@ -3,30 +3,31 @@
The connect/express logger was added to log4js by [danbell](https://github.com/danbell). This allows connect/express servers to log using log4js. See `example-connect-logger.js`.
```javascript
var log4js = require('log4js');
var express = require('express');
var log4js = require("log4js");
var express = require("express");
log4js.configure({
appenders: {
console: { type: 'console' },
file: { type: 'file', filename: 'cheese.log' }
},
categories: {
cheese: { appenders: ['file'], level: 'info' },
default: { appenders: ['console'], level: 'info' }
}
appenders: {
console: { type: "console" },
file: { type: "file", filename: "cheese.log" },
},
categories: {
cheese: { appenders: ["file"], level: "info" },
default: { appenders: ["console"], level: "info" },
},
});
var logger = log4js.getLogger('cheese');
var logger = log4js.getLogger("cheese");
var app = express();
app.use(log4js.connectLogger(logger, { level: 'info' }));
app.get('/', function(req,res) {
res.send('hello world');
app.use(log4js.connectLogger(logger, { level: "info" }));
app.get("/", function (req, res) {
res.send("hello world");
});
app.listen(5000);
```
The log4js.connectLogger supports the passing of an options object that can be used to set the following:
- log level
- log format string or function (the same as the connect/express logger)
- nolog expressions (represented as a string, regexp, or array)
@ -35,54 +36,77 @@ The log4js.connectLogger supports the passing of an options object that can be u
For example:
```javascript
app.use(log4js.connectLogger(logger, { level: log4js.levels.INFO, format: ':method :url' }));
app.use(
log4js.connectLogger(logger, {
level: log4js.levels.INFO,
format: ":method :url",
})
);
```
or:
```javascript
app.use(log4js.connectLogger(logger, {
level: 'auto',
// include the Express request ID in the logs
format: (req, res, format) => format(`:remote-addr - ${req.id} - ":method :url HTTP/:http-version" :status :content-length ":referrer" ":user-agent"`)
}));
app.use(
log4js.connectLogger(logger, {
level: "auto",
// include the Express request ID in the logs
format: (req, res, format) =>
format(
`:remote-addr - ${req.id} - ":method :url HTTP/:http-version" :status :content-length ":referrer" ":user-agent"`
),
})
);
```
When you request of POST, you want to log the request body parameter like JSON.
The log format function is very useful.
Please use log format function instead "tokens" property for use express's request or response.
```javascript
app.use(log4js.connectLogger(logger, {
level: 'info',
format: (req, res, format) => format(`:remote-addr :method :url ${JSON.stringify(req.body)}`)
}));
app.use(
log4js.connectLogger(logger, {
level: "info",
format: (req, res, format) =>
format(`:remote-addr :method :url ${JSON.stringify(req.body)}`),
})
);
```
Added automatic level detection to connect-logger, depends on http status response, compatible with express 3.x and 4.x.
* http responses 3xx, level = WARN
* http responses 4xx & 5xx, level = ERROR
* else, level = INFO
- http responses 3xx, level = WARN
- http responses 4xx & 5xx, level = ERROR
- else, level = INFO
```javascript
app.use(log4js.connectLogger(logger, { level: 'auto' }));
app.use(log4js.connectLogger(logger, { level: "auto" }));
```
The levels of returned status codes can be configured via status code rulesets.
```javascript
app.use(log4js.connectLogger(logger, { level: 'auto', statusRules: [
{ from: 200, to: 299, level: 'debug' },
{ codes: [303, 304], level: 'info' }
]}));
app.use(
log4js.connectLogger(logger, {
level: "auto",
statusRules: [
{ from: 200, to: 299, level: "debug" },
{ codes: [303, 304], level: "info" },
],
})
);
```
The log4js.connectLogger also supports a nolog option where you can specify a string, regexp, or array to omit certain log messages. Example of 1.2 below.
```javascript
app.use(log4js.connectLogger(logger, { level: 'auto', format: ':method :url', nolog: '\\.gif|\\.jpg$' }));
app.use(
log4js.connectLogger(logger, {
level: "auto",
format: ":method :url",
nolog: "\\.gif|\\.jpg$",
})
);
```
The log4js.connectLogger can add a response of express to context if `context` flag is set to `true`.
@ -97,20 +121,23 @@ app.use(log4js.connectLogger(logger, { context: true }));
In layout:
```javascript
log4js.addLayout('customLayout', () => {
log4js.addLayout("customLayout", () => {
return (loggingEvent) => {
const res = loggingEvent.context.res;
return util.format(...loggingEvent.data, res ? `status: ${res.statusCode}` : '');
return util.format(
...loggingEvent.data,
res ? `status: ${res.statusCode}` : ""
);
};
});
```
## Example nolog values
| nolog value | Will Not Log | Will Log |
|-------------|--------------|----------|
| `"\\.gif"` | http://example.com/hoge.gif http://example.com/hoge.gif?fuga | http://example.com/hoge.agif |
| `"\\.gif\|\\.jpg$"` | http://example.com/hoge.gif http://example.com/hoge.gif?fuga http://example.com/hoge.jpg?fuga | http://example.com/hoge.agif http://example.com/hoge.ajpg http://example.com/hoge.jpg?hoge |
| `"\\.(gif\|jpe?g\|png)$"` | http://example.com/hoge.gif http://example.com/hoge.jpeg | http://example.com/hoge.gif?uid=2 http://example.com/hoge.jpg?pid=3 |
| `/\.(gif\|jpe?g\|png)$/` | as above | as above |
| `["\\.jpg$", "\\.png", "\\.gif"]` | same as `"\\.jpg\|\\.png\|\\.gif"` | same as `"\\.jpg\|\\.png\|\\.gif"` |
| nolog value | Will Not Log | Will Log |
| --------------------------------- | --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| `"\\.gif"` | http://example.com/hoge.gif http://example.com/hoge.gif?fuga | http://example.com/hoge.agif |
| `"\\.gif\|\\.jpg$"` | http://example.com/hoge.gif http://example.com/hoge.gif?fuga http://example.com/hoge.jpg?fuga | http://example.com/hoge.agif http://example.com/hoge.ajpg http://example.com/hoge.jpg?hoge |
| `"\\.(gif\|jpe?g\|png)$"` | http://example.com/hoge.gif http://example.com/hoge.jpeg | http://example.com/hoge.gif?uid=2 http://example.com/hoge.jpg?pid=3 |
| `/\.(gif\|jpe?g\|png)$/` | as above | as above |
| `["\\.jpg$", "\\.png", "\\.gif"]` | same as `"\\.jpg\|\\.png\|\\.gif"` | same as `"\\.jpg\|\\.png\|\\.gif"` |

View File

@ -4,15 +4,16 @@ This appender uses node's console object to write log events. It can also be use
# Configuration
* `type` - `console`
* `layout` - `object` (optional, defaults to colouredLayout) - see [layouts](layouts.md)
- `type` - `console`
- `layout` - `object` (optional, defaults to colouredLayout) - see [layouts](layouts.md)
Note that all log events are output using `console.log` regardless of the event's level (so `ERROR` events will not be logged using `console.error`).
# Example
```javascript
log4js.configure({
appenders: { console: { type: 'console' } },
categories: { default: { appenders: [ 'console' ], level: 'info' } }
appenders: { console: { type: "console" } },
categories: { default: { appenders: ["console"], level: "info" } },
});
```

View File

@ -2,7 +2,7 @@
I love pull requests, and I need all the help I can get. However, there are a few rules to follow if you want a better chance of having your pull request merged:
* Fork the repo, make a feature branch just for your changes
* On the branch, only commit changes for the feature you're adding. Each pull request should concentrate on a single change - don't mix multiple features.
* Your feature should be covered by tests. Run the tests with npm test. This is very important - without tests, your feature may be broken by subsequent changes and I may never know. Plus it's always nice to know that your changes work :-)
* Don't bump the npm version - yours may not be the only feature that makes it into a version, and you don't know when your pull request may get merged (the version may have changed by then).
- Fork the repo, make a feature branch just for your changes
- On the branch, only commit changes for the feature you're adding. Each pull request should concentrate on a single change - don't mix multiple features.
- Your feature should be covered by tests. Run the tests with npm test. This is very important - without tests, your feature may be broken by subsequent changes and I may never know. Plus it's always nice to know that your changes work :-)
- Don't bump the npm version - yours may not be the only feature that makes it into a version, and you don't know when your pull request may get merged (the version may have changed by then).

View File

@ -1,6 +1,6 @@
# Contributors
Many people have helped make log4js what it is today. Here's a list of everyone who has contributed to the code. There are lots of people who've helped by submitting bug reports or pull requests that I haven't merged, but I have used their ideas to implement a different way. Thanks to you all. This library also owes a huge amount to the [original log4js project](https://github.com/stritti/log4js). If you'd like to help out, take a look at the [contributor guidelines](contrib-guidelines.md).
Many people have helped make log4js what it is today. Here's a list of everyone who has contributed to the code. There are lots of people who've helped by submitting bug reports or pull requests that I haven't merged, but I have used their ideas to implement a different way. Thanks to you all. This library also owes a huge amount to the [original log4js project](https://github.com/stritti/log4js). If you'd like to help out, take a look at the [contributor guidelines](contrib-guidelines.md).
<ul>
{% for contributor in site.github.contributors %}

View File

@ -4,67 +4,75 @@ This is a file appender that rolls log files based on a configurable time, rathe
## Configuration
* `type` - `"dateFile"`
* `filename` - `string` - the path of the file where you want your logs written.
* `pattern` - `string` (optional, defaults to `yyyy-MM-dd`) - the pattern to use to determine when to roll the logs.
* `layout` - (optional, defaults to basic layout) - see [layouts](layouts.md)
- `type` - `"dateFile"`
- `filename` - `string` - the path of the file where you want your logs written.
- `pattern` - `string` (optional, defaults to `yyyy-MM-dd`) - the pattern to use to determine when to roll the logs.
- `layout` - (optional, defaults to basic layout) - see [layouts](layouts.md)
Any other configuration parameters will be passed to the underlying [streamroller](https://github.com/nomiddlename/streamroller) implementation (see also node.js core file streams):
* `encoding` - `string` (default "utf-8")
* `mode` - `integer` (default 0o600 - [node.js file modes](https://nodejs.org/dist/latest-v12.x/docs/api/fs.html#fs_file_modes))
* `flags` - `string` (default 'a' - [node.js file flags](https://nodejs.org/dist/latest-v12.x/docs/api/fs.html#fs_file_system_flags))
* `compress` - `boolean` (default false) - compress the backup files using gzip (backup files will have `.gz` extension)
* `keepFileExt` - `boolean` (default false) - preserve the file extension when rotating log files (`file.log` becomes `file.2017-05-30.log` instead of `file.log.2017-05-30`).
* `fileNameSep` - `string` (default '.') - the filename separator when rolling. e.g.: abc.log`.`2013-08-30 or abc`.`2013-08-30.log (keepFileExt)
* `alwaysIncludePattern` - `boolean` (default false) - include the pattern in the name of the current log file.
* `numBackups` - `integer` (default 1) - the number of old files that matches the pattern to keep (excluding the hot file).
- `encoding` - `string` (default "utf-8")
- `mode` - `integer` (default 0o600 - [node.js file modes](https://nodejs.org/dist/latest-v12.x/docs/api/fs.html#fs_file_modes))
- `flags` - `string` (default 'a' - [node.js file flags](https://nodejs.org/dist/latest-v12.x/docs/api/fs.html#fs_file_system_flags))
- `compress` - `boolean` (default false) - compress the backup files using gzip (backup files will have `.gz` extension)
- `keepFileExt` - `boolean` (default false) - preserve the file extension when rotating log files (`file.log` becomes `file.2017-05-30.log` instead of `file.log.2017-05-30`).
- `fileNameSep` - `string` (default '.') - the filename separator when rolling. e.g.: abc.log`.`2013-08-30 or abc`.`2013-08-30.log (keepFileExt)
- `alwaysIncludePattern` - `boolean` (default false) - include the pattern in the name of the current log file.
- `numBackups` - `integer` (default 1) - the number of old files that matches the pattern to keep (excluding the hot file).
The `pattern` is used to determine when the current log file should be renamed and a new log file created. For example, with a filename of 'cheese.log', and the default pattern of `.yyyy-MM-dd` - on startup this will result in a file called `cheese.log` being created and written to until the next write after midnight. When this happens, `cheese.log` will be renamed to `cheese.log.2017-04-30` and a new `cheese.log` file created. The appender uses the [date-format](https://github.com/nomiddlename/date-format) library to parse the `pattern`, and any of the valid formats can be used. Also note that there is no timer controlling the log rolling - changes in the pattern are determined on every log write. If no writes occur, then no log rolling will happen. If your application logs infrequently this could result in no log file being written for a particular time period.
Note that, from version 4.x of log4js onwards, the file appender can take any of the options for the [file appender](file.md) as well. So you could roll files by both date and size.
## Example (default daily log rolling)
```javascript
log4js.configure({
appenders: {
everything: { type: 'dateFile', filename: 'all-the-logs.log' }
everything: { type: "dateFile", filename: "all-the-logs.log" },
},
categories: {
default: { appenders: [ 'everything' ], level: 'debug' }
}
default: { appenders: ["everything"], level: "debug" },
},
});
```
This example will result in files being rolled every day. The initial file will be `all-the-logs.log`, with the daily backups being `all-the-logs.log.2017-04-30`, etc.
## Example with hourly log rolling (and compressed backups)
```javascript
log4js.configure({
appenders: {
everything: { type: 'dateFile', filename: 'all-the-logs.log', pattern: 'yyyy-MM-dd-hh', compress: true }
everything: {
type: "dateFile",
filename: "all-the-logs.log",
pattern: "yyyy-MM-dd-hh",
compress: true,
},
},
categories: {
default: { appenders: [ 'everything' ], level: 'debug'}
}
default: { appenders: ["everything"], level: "debug" },
},
});
```
This will result in one current log file (`all-the-logs.log`). Every hour this file will be compressed and renamed to `all-the-logs.log.2017-04-30-08.gz` (for example) and a new `all-the-logs.log` created.
## Memory usage
If your application logs a large volume of messages, and find memory usage increasing due to buffering log messages before being written to a file, then you can listen for "log4js:pause" events emitted by the file appenders. Your application should stop logging when it receives one of these events with a value of `true` and resume when it receives an event with a value of `false`.
```javascript
log4js.configure({
appenders: {
output: { type: 'dateFile', filename: 'out.log' }
output: { type: "dateFile", filename: "out.log" },
},
categories: { default: { appenders: ['output'], level: 'debug'}}
categories: { default: { appenders: ["output"], level: "debug" } },
});
let paused = false;
process.on("log4js:pause", (value) => paused = value);
process.on("log4js:pause", (value) => (paused = value));
const logger = log4js.getLogger();
while (!paused) {

View File

@ -3,23 +3,27 @@
## I want errors to go to a special file, but still want everything written to another file - how do I do that?
You'll need to use the [logLevelFilter](logLevelFilter.md). Here's an example configuration:
```javascript
log4js.configure({
appenders: {
everything: { type: 'file', filename: 'all-the-logs.log' },
emergencies: { type: 'file', filename: 'oh-no-not-again.log' },
'just-errors': { type: 'logLevelFilter', appender: 'emergencies', level: 'error' }
everything: { type: "file", filename: "all-the-logs.log" },
emergencies: { type: "file", filename: "oh-no-not-again.log" },
"just-errors": {
type: "logLevelFilter",
appender: "emergencies",
level: "error",
},
},
categories: {
default: { appenders: ['just-errors', 'everything'], level: 'debug' }
}
default: { appenders: ["just-errors", "everything"], level: "debug" },
},
});
const logger = log4js.getLogger();
logger.debug('This goes to all-the-logs.log');
logger.info('As does this.');
logger.error('This goes to all-the-logs.log and oh-no-not-again.log');
logger.debug("This goes to all-the-logs.log");
logger.info("As does this.");
logger.error("This goes to all-the-logs.log and oh-no-not-again.log");
```
## I want to reload the configuration when I change my config file - how do I do that?
@ -29,6 +33,7 @@ Previous versions of log4js used to watch for changes in the configuration file
## What happened to `replaceConsole` - it doesn't work any more?
I removed `replaceConsole` - it caused a few weird errors, and I wasn't entirely comfortable with messing around with a core part of node. If you still want to do this, then code like this should do the trick:
```javascript
log4js.configure(...); // set up your categories and appenders
const logger = log4js.getLogger('console');
@ -46,25 +51,29 @@ Nodemailer version 4.0.1 (the not-deprecated version) requires a node version >=
## I want line numbers in my logs!
You need to enable call stack for the category, and use pattern layout to output the values. e.g.
```javascript
const log4js = require('log4js');
const log4js = require("log4js");
log4js.configure({
appenders: {
out: {
type: 'stdout',
type: "stdout",
layout: {
type: 'pattern', pattern: '%d %p %c %f:%l %m%n'
}
}
type: "pattern",
pattern: "%d %p %c %f:%l %m%n",
},
},
},
categories: {
default: { appenders: ['out'], level: 'info', enableCallStack: true }
}
default: { appenders: ["out"], level: "info", enableCallStack: true },
},
});
const logger = log4js.getLogger('thing');
logger.info('this should give me a line number now');
const logger = log4js.getLogger("thing");
logger.info("this should give me a line number now");
```
Would output something like this:
```bash
2019-05-22T08:41:07.312 INFO thing index.js:16 this should give me a line number now
```

View File

@ -4,20 +4,21 @@ The file appender writes log events to a file. It supports an optional maximum f
## Configuration
* `type` - `"file"`
* `filename` - `string` - the path of the file where you want your logs written.
* `maxLogSize` - `integer` (optional, defaults to undefined) - the maximum size (in bytes) for the log file. If not specified or 0, then no log rolling will happen.
`maxLogSize` can also accept `string` with the size suffixes: ***K***, ***M***, ***G*** such as `1K`, `1M`, `1G`.
* `backups` - `integer` (optional, defaults to 5) - the number of old log files to keep during log rolling (excluding the hot file).
* `layout` - (optional, defaults to basic layout) - see [layouts](layouts.md)
- `type` - `"file"`
- `filename` - `string` - the path of the file where you want your logs written.
- `maxLogSize` - `integer` (optional, defaults to undefined) - the maximum size (in bytes) for the log file. If not specified or 0, then no log rolling will happen.
`maxLogSize` can also accept `string` with the size suffixes: **_K_**, **_M_**, **_G_** such as `1K`, `1M`, `1G`.
- `backups` - `integer` (optional, defaults to 5) - the number of old log files to keep during log rolling (excluding the hot file).
- `layout` - (optional, defaults to basic layout) - see [layouts](layouts.md)
Any other configuration parameters will be passed to the underlying [streamroller](https://github.com/nomiddlename/streamroller) implementation (see also node.js core file streams):
* `encoding` - `string` (default "utf-8")
* `mode` - `integer` (default 0o600 - [node.js file modes](https://nodejs.org/dist/latest-v12.x/docs/api/fs.html#fs_file_modes))
* `flags` - `string` (default 'a' - [node.js file flags](https://nodejs.org/dist/latest-v12.x/docs/api/fs.html#fs_file_system_flags))
* `compress` - `boolean` (default false) - compress the backup files using gzip (backup files will have `.gz` extension)
* `keepFileExt` - `boolean` (default false) - preserve the file extension when rotating log files (`file.log` becomes `file.1.log` instead of `file.log.1`).
* `fileNameSep` - `string` (default '.') - the filename separator when rolling. e.g.: abc.log`.`1 or abc`.`1.log (keepFileExt)
- `encoding` - `string` (default "utf-8")
- `mode` - `integer` (default 0o600 - [node.js file modes](https://nodejs.org/dist/latest-v12.x/docs/api/fs.html#fs_file_modes))
- `flags` - `string` (default 'a' - [node.js file flags](https://nodejs.org/dist/latest-v12.x/docs/api/fs.html#fs_file_system_flags))
- `compress` - `boolean` (default false) - compress the backup files using gzip (backup files will have `.gz` extension)
- `keepFileExt` - `boolean` (default false) - preserve the file extension when rotating log files (`file.log` becomes `file.1.log` instead of `file.log.1`).
- `fileNameSep` - `string` (default '.') - the filename separator when rolling. e.g.: abc.log`.`1 or abc`.`1.log (keepFileExt)
Note that, from version 4.x of log4js onwards, the file appender can take any of the options for the [dateFile appender](dateFile.md) as well. So you could roll files by both date and size.
@ -26,45 +27,54 @@ Note that, from version 4.x of log4js onwards, the file appender can take any of
```javascript
log4js.configure({
appenders: {
everything: { type: 'file', filename: 'all-the-logs.log' }
everything: { type: "file", filename: "all-the-logs.log" },
},
categories: {
default: { appenders: [ 'everything' ], level: 'debug' }
}
default: { appenders: ["everything"], level: "debug" },
},
});
const logger = log4js.getLogger();
logger.debug('I will be logged in all-the-logs.log');
logger.debug("I will be logged in all-the-logs.log");
```
This example will result in a single log file (`all-the-logs.log`) containing the log messages.
## Example with log rolling (and compressed backups)
```javascript
log4js.configure({
appenders: {
everything: { type: 'file', filename: 'all-the-logs.log', maxLogSize: 10485760, backups: 3, compress: true }
everything: {
type: "file",
filename: "all-the-logs.log",
maxLogSize: 10485760,
backups: 3,
compress: true,
},
},
categories: {
default: { appenders: [ 'everything' ], level: 'debug'}
}
default: { appenders: ["everything"], level: "debug" },
},
});
```
This will result in one current log file (`all-the-logs.log`). When that reaches 10Mb in size, it will be renamed and compressed to `all-the-logs.log.1.gz` and a new file opened called `all-the-logs.log`. When `all-the-logs.log` reaches 10Mb again, then `all-the-logs.log.1.gz` will be renamed to `all-the-logs.log.2.gz`, and so on.
## Memory usage
If your application logs a large volume of messages, and find memory usage increasing due to buffering log messages before being written to a file, then you can listen for "log4js:pause" events emitted by the file appenders. Your application should stop logging when it receives one of these events with a value of `true` and resume when it receives an event with a value of `false`.
If your application logs a large volume of messages, and find memory usage increasing due to buffering log messages before being written to a file, then you can listen for "log4js:pause" events emitted by the file appenders. Your application should stop logging when it receives one of these events with a value of `true` and resume when it receives an event with a value of `false`.
```javascript
log4js.configure({
appenders: {
output: { type: 'file', filename: 'out.log' }
output: { type: "file", filename: "out.log" },
},
categories: { default: { appenders: ['output'], level: 'debug'}}
categories: { default: { appenders: ["output"], level: "debug" } },
});
let paused = false;
process.on("log4js:pause", (value) => paused = value);
process.on("log4js:pause", (value) => (paused = value));
const logger = log4js.getLogger();
while (!paused) {

View File

@ -4,45 +4,53 @@ The sync file appender writes log events to a file, the only difference to the n
## Configuration
* `type` - `"fileSync"`
* `filename` - `string` - the path of the file where you want your logs written.
* `maxLogSize` - `integer` (optional, defaults to undefined) - the maximum size (in bytes) for the log file. If not specified or 0, then no log rolling will happen.
`maxLogSize` can also accept `string` with the size suffixes: ***K***, ***M***, ***G*** such as `1K`, `1M`, `1G`.
* `backups` - `integer` (optional, defaults to 5) - the number of old log files to keep during log rolling (excluding the hot file).
* `layout` - (optional, defaults to basic layout) - see [layouts](layouts.md)
- `type` - `"fileSync"`
- `filename` - `string` - the path of the file where you want your logs written.
- `maxLogSize` - `integer` (optional, defaults to undefined) - the maximum size (in bytes) for the log file. If not specified or 0, then no log rolling will happen.
`maxLogSize` can also accept `string` with the size suffixes: **_K_**, **_M_**, **_G_** such as `1K`, `1M`, `1G`.
- `backups` - `integer` (optional, defaults to 5) - the number of old log files to keep during log rolling (excluding the hot file).
- `layout` - (optional, defaults to basic layout) - see [layouts](layouts.md)
Any other configuration parameters will be passed to the underlying node.js core stream implementation:
* `encoding` - `string` (default "utf-8")
* `mode` - `integer` (default 0o600 - [node.js file modes](https://nodejs.org/dist/latest-v12.x/docs/api/fs.html#fs_file_modes))
* `flags` - `string` (default 'a')
- `encoding` - `string` (default "utf-8")
- `mode` - `integer` (default 0o600 - [node.js file modes](https://nodejs.org/dist/latest-v12.x/docs/api/fs.html#fs_file_modes))
- `flags` - `string` (default 'a')
## Example
```javascript
log4js.configure({
appenders: {
everything: { type: 'fileSync', filename: 'all-the-logs.log' }
everything: { type: "fileSync", filename: "all-the-logs.log" },
},
categories: {
default: { appenders: [ 'everything' ], level: 'debug' }
}
default: { appenders: ["everything"], level: "debug" },
},
});
const logger = log4js.getLogger();
logger.debug('I will be logged in all-the-logs.log');
logger.debug("I will be logged in all-the-logs.log");
```
This example will result in a single log file (`all-the-logs.log`) containing the log messages.
## Example with log rolling
```javascript
log4js.configure({
appenders: {
everything: { type: 'file', filename: 'all-the-logs.log', maxLogSize: 10458760, backups: 3 }
everything: {
type: "file",
filename: "all-the-logs.log",
maxLogSize: 10458760,
backups: 3,
},
},
categories: {
default: { appenders: [ 'everything' ], level: 'debug'}
}
default: { appenders: ["everything"], level: "debug" },
},
});
```
This will result in one current log file (`all-the-logs.log`). When that reaches 10Mb in size, it will be renamed and compressed to `all-the-logs.log.1.gz` and a new file opened called `all-the-logs.log`. When `all-the-logs.log` reaches 10Mb again, then `all-the-logs.log.1.gz` will be renamed to `all-the-logs.log.2.gz`, and so on.

View File

@ -9,171 +9,203 @@ For most use cases you will not need to configure layouts - there are some appen
Most appender configuration will take a field called `layout`, which is an object - typically with a single field `type` which is the name of a layout defined below. Some layouts require extra configuration options, which should be included in the same object.
## Example
```javascript
log4js.configure({
appenders: { out: { type: 'stdout', layout: { type: 'basic' } } },
categories: { default: { appenders: ['out'], level: 'info' } }
appenders: { out: { type: "stdout", layout: { type: "basic" } } },
categories: { default: { appenders: ["out"], level: "info" } },
});
```
This configuration replaces the [stdout](stdout.md) appender's default `coloured` layout with `basic` layout.
# Built-in Layouts
## Basic
* `type` - `basic`
- `type` - `basic`
Basic layout will output the timestamp, level, category, followed by the formatted log event data.
## Example
```javascript
log4js.configure({
appenders: { 'out': { type: 'stdout', layout: { type: 'basic' } } },
categories: { default: { appenders: ['out'], level: 'info' } }
appenders: { out: { type: "stdout", layout: { type: "basic" } } },
categories: { default: { appenders: ["out"], level: "info" } },
});
const logger = log4js.getLogger('cheese');
logger.error('Cheese is too ripe!');
const logger = log4js.getLogger("cheese");
logger.error("Cheese is too ripe!");
```
This will output:
```
[2017-03-30 07:57:00.113] [ERROR] cheese - Cheese is too ripe!
```
## Coloured
* `type` - `coloured` (or `colored`)
- `type` - `coloured` (or `colored`)
This layout is the same as `basic`, except that the timestamp, level and category will be coloured according to the log event's level (if your terminal/file supports it - if you see some weird characters in your output and no colour then you should probably switch to `basic`). The colours used are:
* `TRACE` - 'blue'
* `DEBUG` - 'cyan'
* `INFO` - 'green'
* `WARN` - 'yellow'
* `ERROR` - 'red'
* `FATAL` - 'magenta'
- `TRACE` - 'blue'
- `DEBUG` - 'cyan'
- `INFO` - 'green'
- `WARN` - 'yellow'
- `ERROR` - 'red'
- `FATAL` - 'magenta'
## Message Pass-Through
* `type` - `messagePassThrough`
- `type` - `messagePassThrough`
This layout just formats the log event data, and does not output a timestamp, level or category. It is typically used in appenders that serialise the events using a specific format (e.g. [gelf](https://github.com/log4js-node/gelf)).
## Example
```javascript
log4js.configure({
appenders: { out: { type: 'stdout', layout: { type: 'messagePassThrough' } } },
categories: { default: { appenders: [ 'out' ], level: 'info' } }
appenders: {
out: { type: "stdout", layout: { type: "messagePassThrough" } },
},
categories: { default: { appenders: ["out"], level: "info" } },
});
const logger = log4js.getLogger('cheese');
const cheeseName = 'gouda';
logger.error('Cheese is too ripe! Cheese was: ', cheeseName);
const logger = log4js.getLogger("cheese");
const cheeseName = "gouda";
logger.error("Cheese is too ripe! Cheese was: ", cheeseName);
```
This will output:
```
Cheese is too ripe! Cheese was: gouda
```
## Dummy
* `type` - `dummy`
- `type` - `dummy`
This layout only outputs the first value in the log event's data. It was added for the [logstashUDP](https://github.com/log4js-node/logstashUDP) appender, and I'm not sure there's much use for it outside that.
## Example
```javascript
log4js.configure({
appenders: { out: { type: 'stdout', layout: { type: 'dummy' } } },
categories: { default: { appenders: [ 'out' ], level: 'info' } }
appenders: { out: { type: "stdout", layout: { type: "dummy" } } },
categories: { default: { appenders: ["out"], level: "info" } },
});
const logger = log4js.getLogger('cheese');
const cheeseName = 'gouda';
logger.error('Cheese is too ripe! Cheese was: ', cheeseName);
const logger = log4js.getLogger("cheese");
const cheeseName = "gouda";
logger.error("Cheese is too ripe! Cheese was: ", cheeseName);
```
This will output:
```
Cheese is too ripe! Cheese was:
```
## Pattern
* `type` - `pattern`
* `pattern` - `string` - specifier for the output format, using placeholders as described below
* `tokens` - `object` (optional) - user-defined tokens to be used in the pattern
- `type` - `pattern`
- `pattern` - `string` - specifier for the output format, using placeholders as described below
- `tokens` - `object` (optional) - user-defined tokens to be used in the pattern
## Pattern format
The pattern string can contain any characters, but sequences beginning with `%` will be replaced with values taken from the log event, and other environmental values.
Format for specifiers is `%[padding].[truncation][field]{[format]}` - padding and truncation are optional, and format only applies to a few tokens (notably, date). Both padding and truncation values can be negative.
* Positive truncation - truncate the string starting from the beginning
* Negative truncation - truncate the string starting from the end of the string
* Positive padding - left pad the string to make it this length, if the string is longer than the padding value then nothing happens
* Negative padding - right pad the string to make it this length, if the string is longer than the padding value then nothing happens
To make fixed-width columns in your log output, set padding and truncation to the same size (they don't have to have the same sign though, you could have right truncated, left padded columns that are always 10 characters wide with a pattern like "%10.-10m").
- Positive truncation - truncate the string starting from the beginning
- Negative truncation - truncate the string starting from the end of the string
- Positive padding - left pad the string to make it this length, if the string is longer than the padding value then nothing happens
- Negative padding - right pad the string to make it this length, if the string is longer than the padding value then nothing happens
To make fixed-width columns in your log output, set padding and truncation to the same size (they don't have to have the same sign though, you could have right truncated, left padded columns that are always 10 characters wide with a pattern like "%10.-10m").
e.g. %5.10p - left pad the log level by up to 5 characters, keep the whole string to a max length of 10.
So, for a log level of INFO the output would be " INFO", for DEBUG it would be "DEBUG" and for a (custom) log level of CATASTROPHIC it would be "CATASTROPH".
Fields can be any of:
* `%r` time in toLocaleTimeString format
* `%p` log level
* `%c` log category
* `%h` hostname
* `%m` log data
* `%d` date, formatted - default is `ISO8601`, format options are: `ISO8601`, `ISO8601_WITH_TZ_OFFSET`, `ABSOLUTETIME`, `DATETIME`, or any string compatible with the [date-format](https://www.npmjs.com/package/date-format) library. e.g. `%d{DATETIME}`, `%d{yyyy/MM/dd-hh.mm.ss}`
* `%%` % - for when you want a literal `%` in your output
* `%n` newline
* `%z` process id (from `process.pid`)
* `%f` full path of filename (requires `enableCallStack: true` on the category, see [configuration object](api.md))
* `%f{depth}` path's depth let you chose to have only filename (`%f{1}`) or a chosen number of directories
* `%l` line number (requires `enableCallStack: true` on the category, see [configuration object](api.md))
* `%o` column postion (requires `enableCallStack: true` on the category, see [configuration object](api.md))
* `%s` call stack (requires `enableCallStack: true` on the category, see [configuration object](api.md))
* `%x{<tokenname>}` add dynamic tokens to your log. Tokens are specified in the tokens parameter.
* `%X{<tokenname>}` add values from the Logger context. Tokens are keys into the context values.
* `%[` start a coloured block (colour will be taken from the log level, similar to `colouredLayout`)
* `%]` end a coloured block
- `%r` time in toLocaleTimeString format
- `%p` log level
- `%c` log category
- `%h` hostname
- `%m` log data
- `%d` date, formatted - default is `ISO8601`, format options are: `ISO8601`, `ISO8601_WITH_TZ_OFFSET`, `ABSOLUTETIME`, `DATETIME`, or any string compatible with the [date-format](https://www.npmjs.com/package/date-format) library. e.g. `%d{DATETIME}`, `%d{yyyy/MM/dd-hh.mm.ss}`
- `%%` % - for when you want a literal `%` in your output
- `%n` newline
- `%z` process id (from `process.pid`)
- `%f` full path of filename (requires `enableCallStack: true` on the category, see [configuration object](api.md))
- `%f{depth}` path's depth let you chose to have only filename (`%f{1}`) or a chosen number of directories
- `%l` line number (requires `enableCallStack: true` on the category, see [configuration object](api.md))
- `%o` column postion (requires `enableCallStack: true` on the category, see [configuration object](api.md))
- `%s` call stack (requires `enableCallStack: true` on the category, see [configuration object](api.md))
- `%x{<tokenname>}` add dynamic tokens to your log. Tokens are specified in the tokens parameter.
- `%X{<tokenname>}` add values from the Logger context. Tokens are keys into the context values.
- `%[` start a coloured block (colour will be taken from the log level, similar to `colouredLayout`)
- `%]` end a coloured block
## Tokens
User-defined tokens can be either a string or a function. Functions will be passed the log event, and should return a string. For example, you could define a custom token that outputs the log event's context value for 'user' like so:
```javascript
log4js.configure({
appenders: {
out: { type: 'stdout', layout: {
type: 'pattern',
pattern: '%d %p %c %x{user} %m%n',
tokens: {
user: function(logEvent) {
return AuthLibrary.currentUser();
}
}
}}
out: {
type: "stdout",
layout: {
type: "pattern",
pattern: "%d %p %c %x{user} %m%n",
tokens: {
user: function (logEvent) {
return AuthLibrary.currentUser();
},
},
},
},
},
categories: { default: { appenders: ['out'], level: 'info' } }
categories: { default: { appenders: ["out"], level: "info" } },
});
const logger = log4js.getLogger();
logger.info('doing something.');
logger.info("doing something.");
```
This would output:
```
2017-06-01 08:32:56.283 INFO default charlie doing something.
```
You can also use the Logger context to store tokens (sometimes called Nested Diagnostic Context, or Mapped Diagnostic Context) and use them in your layouts.
```javascript
log4js.configure({
appenders: {
out: { type: 'stdout', layout: {
type: 'pattern',
pattern: '%d %p %c %X{user} %m%n'
}}
out: {
type: "stdout",
layout: {
type: "pattern",
pattern: "%d %p %c %X{user} %m%n",
},
},
},
categories: { default: { appenders: ['out'], level: 'info' } }
categories: { default: { appenders: ["out"], level: "info" } },
});
const logger = log4js.getLogger();
logger.addContext('user', 'charlie');
logger.info('doing something.');
logger.addContext("user", "charlie");
logger.info("doing something.");
```
This would output:
```
2017-06-01 08:32:56.283 INFO default charlie doing something.
```
Note that you can also add functions to the Logger Context, and they will be passed the logEvent as well.
# Adding your own layouts
@ -181,30 +213,36 @@ Note that you can also add functions to the Logger Context, and they will be pas
You can add your own layouts by calling `log4js.addLayout(type, fn)` before calling `log4js.configure`. `type` is the label you want to use to refer to your layout in appender configuration. `fn` is a function that takes a single object argument, which will contain the configuration for the layout instance, and returns a layout function. A layout function takes a log event argument and returns a string (usually, although you could return anything as long as the appender knows what to do with it).
## Custom Layout Example
This example can also be found in examples/custom-layout.js.
```javascript
const log4js = require('log4js');
log4js.addLayout('json', function(config) {
return function(logEvent) { return JSON.stringify(logEvent) + config.separator; }
This example can also be found in examples/custom-layout.js.
```javascript
const log4js = require("log4js");
log4js.addLayout("json", function (config) {
return function (logEvent) {
return JSON.stringify(logEvent) + config.separator;
};
});
log4js.configure({
appenders: {
out: { type: 'stdout', layout: { type: 'json', separator: ',' } }
out: { type: "stdout", layout: { type: "json", separator: "," } },
},
categories: {
default: { appenders: ['out'], level: 'info' }
}
default: { appenders: ["out"], level: "info" },
},
});
const logger = log4js.getLogger('json-test');
logger.info('this is just a test');
logger.error('of a custom appender');
logger.warn('that outputs json');
const logger = log4js.getLogger("json-test");
logger.info("this is just a test");
logger.error("of a custom appender");
logger.warn("that outputs json");
log4js.shutdown(() => {});
```
This example outputs the following:
```javascript
{"startTime":"2017-06-05T22:23:08.479Z","categoryName":"json-test","data":["this is just a test"],"level":{"level":20000,"levelStr":"INFO"},"context":{}},
{"startTime":"2017-06-05T22:23:08.483Z","categoryName":"json-test","data":["of a custom appender"],"level":{"level":40000,"levelStr":"ERROR"},"context":{}},

View File

@ -4,10 +4,10 @@ The log level filter allows you to restrict the log events that an appender will
## Configuration
* `type` - `logLevelFilter`
* `appender` - `string` - the name of an appender, defined in the same configuration, that you want to filter
* `level` - `string` - the minimum level of event to allow through the filter
* `maxLevel` - `string` (optional, defaults to `FATAL`) - the maximum level of event to allow through the filter
- `type` - `logLevelFilter`
- `appender` - `string` - the name of an appender, defined in the same configuration, that you want to filter
- `level` - `string` - the minimum level of event to allow through the filter
- `maxLevel` - `string` (optional, defaults to `FATAL`) - the maximum level of event to allow through the filter
If an event's level is greater than or equal to `level` and less than or equal to `maxLevel` then it will be sent to the appender.
@ -25,4 +25,5 @@ log4js.configure({
}
});
```
Log events of `debug`, `info`, `error`, and `fatal` will go to `all-the-logs.log`. Events of `error` and `fatal` will also go to `panic-now.log`.

View File

@ -1,20 +1,26 @@
# Migrating from log4js versions older than 2.x
## Configuration
If you try to use your v1 configuration with v2 code, you'll most likely get an error that says something like 'must have property "appenders" of type object'. The format of the configuration object has changed (see the [api](api.md) docs for details). The main changes are a need for you to name your appenders, and you also have to define the default category. For example, if your v1 config looked like this:
```javascript
{ appenders: [
{ type: 'console' },
{
type: 'dateFile',
filename: 'logs/task',
pattern:"-dd.log",
alwaysIncludePattern: true,
category: 'task'
}
] }
{
appenders: [
{ type: "console" },
{
type: "dateFile",
filename: "logs/task",
pattern: "-dd.log",
alwaysIncludePattern: true,
category: "task",
},
];
}
```
Then your v2 config should be something like this:
```javascript
{
appenders: {
@ -36,7 +42,9 @@ Then your v2 config should be something like this:
The functions to define the configuration programmatically have been remove (`addAppender`, `loadAppender`, etc). All configuration should now be done through the single `configure` function, passing in a filename or object.
## Console replacement
V1 used to allow you to replace the node.js console functions with versions that would log to a log4js appender. This used to cause some weird errors, so I decided it was better to remove it from the log4js core functionality. If you still want to do this, you can replicate the behaviour with code similar to this:
```javascript
log4js.configure(...); // set up your categories and appenders
const logger = log4js.getLogger('console'); // any category will work
@ -44,9 +52,11 @@ console.log = logger.info.bind(logger); // do the same for others - console.debu
```
## Config Reloading
Previous versions of log4js used to watch for changes in the configuration file and reload when it changed. It didn't always work well, sometimes leaving file handles or sockets open. This feature was removed in version 2.x. As a replacement, I'd suggest using a library like [watchr](https://www.npmjs.com/package/watchr) to notify you of file changes. Then you can call `log4js.shutdown` followed by `log4js.configure` again.
## Appenders
If you have written your own custom appenders, they will not work without modification in v2. See the guide to [writing appenders](writing-appenders.md) for details on how appenders work in 2.x. Note that if you want to write your appender to work with both 1.x and 2.x, then you can tell what version you're running in by examining the number of arguments passed to the `configure` function of your appender: 2 arguments means v1, 4 arguments means v2.
All the core appenders have been upgraded to work with v2, except for the clustered appender which has been removed. The core log4js code handles cluster mode transparently.
@ -54,10 +64,12 @@ All the core appenders have been upgraded to work with v2, except for the cluste
The `logFaces` appender was split into two versions to make testing easier and the code simpler; one has HTTP support, the other UDP.
## Exit listeners
Some appenders used to define their own `exit` listeners, and it was never clear whose responsibility it was to clean up resources. Now log4js does not define any `exit` listeners. Instead your application should register an `exit` listener, and call `log4js.shutdown` to be sure that all log messages get written before your application terminates.
## New Features
* MDC contexts - you can now add key-value pairs to a logger (for grouping all log messages from a particular user, for example). Support for these values exists in the [pattern layout](layouts.md), the logFaces ([UDP](https://github.com/log4js-node/logFaces-UDP) and [HTTP](https://github.com/log4js-node/logFaces-HTTP)) appender, and the [multi-file appender](multiFile.md).
* Automatic cluster support - log4js now handles clusters transparently
* Custom levels - you can define your own log levels in the configuration object, including the colours
* Improved performance - several changes have been made to improve performance, especially for the file appenders.
- MDC contexts - you can now add key-value pairs to a logger (for grouping all log messages from a particular user, for example). Support for these values exists in the [pattern layout](layouts.md), the logFaces ([UDP](https://github.com/log4js-node/logFaces-UDP) and [HTTP](https://github.com/log4js-node/logFaces-HTTP)) appender, and the [multi-file appender](multiFile.md).
- Automatic cluster support - log4js now handles clusters transparently
- Custom levels - you can define your own log levels in the configuration object, including the colours
- Improved performance - several changes have been made to improve performance, especially for the file appenders.

View File

@ -4,11 +4,11 @@ The multiFile appender can be used to dynamically write logs to multiple files,
## Configuration
* `type` - `"multiFile"`
* `base` - `string` - the base part of the generated log filename
* `property` - `string` - the value to use to split files (see below).
* `extension` - `string` - the suffix for the generated log filename.
* `timeout` - `integer` - optional activity timeout in ms after which the file will be closed.
- `type` - `"multiFile"`
- `base` - `string` - the base part of the generated log filename
- `property` - `string` - the value to use to split files (see below).
- `extension` - `string` - the suffix for the generated log filename.
- `timeout` - `integer` - optional activity timeout in ms after which the file will be closed.
All other properties will be passed to the created [file](file.md) appenders. For the property value, `categoryName` is probably the most useful - although you could use `pid` or `level`. If the property is not found then the appender will look for the value in the context map. If that fails, then the logger will not output the logging event, without an error. This is to allow for dynamic properties which may not exist for all log messages.
@ -17,37 +17,49 @@ All other properties will be passed to the created [file](file.md) appenders. Fo
```javascript
log4js.configure({
appenders: {
multi: { type: 'multiFile', base: 'logs/', property: 'categoryName', extension: '.log' }
multi: {
type: "multiFile",
base: "logs/",
property: "categoryName",
extension: ".log",
},
},
categories: {
default: { appenders: [ 'multi' ], level: 'debug' }
}
default: { appenders: ["multi"], level: "debug" },
},
});
const logger = log4js.getLogger();
logger.debug('I will be logged in logs/default.log');
const otherLogger = log4js.getLogger('cheese');
otherLogger.info('Cheese is cheddar - this will be logged in logs/cheese.log');
logger.debug("I will be logged in logs/default.log");
const otherLogger = log4js.getLogger("cheese");
otherLogger.info("Cheese is cheddar - this will be logged in logs/cheese.log");
```
This example will result in two log files (`logs/default.log` and `logs/cheese.log`) containing the log messages.
## Example with log rolling (and compressed backups)
```javascript
log4js.configure({
appenders: {
everything: {
type: 'multiFile', base: 'logs/', property: 'userID', extension: '.log',
maxLogSize: 10485760, backups: 3, compress: true
}
type: "multiFile",
base: "logs/",
property: "userID",
extension: ".log",
maxLogSize: 10485760,
backups: 3,
compress: true,
},
},
categories: {
default: { appenders: [ 'everything' ], level: 'debug'}
}
default: { appenders: ["everything"], level: "debug" },
},
});
const userLogger = log4js.getLogger('user');
userLogger.addContext('userID', user.getID());
userLogger.info('this user just logged in');
const userLogger = log4js.getLogger("user");
userLogger.addContext("userID", user.getID());
userLogger.info("this user just logged in");
```
This will result in one log file (`logs/u12345.log`), capped at 10Mb in size, with three backups kept when rolling the file. If more users were logged, each user would get their own files, and their own backups.

View File

@ -1,43 +1,51 @@
# Multiprocess Appender
*You probably want to use the [tcp server](tcp-server.md) or [tcp appender](tcp.md) instead of this - they are more flexible*
_You probably want to use the [tcp server](tcp-server.md) or [tcp appender](tcp.md) instead of this - they are more flexible_
*Note that if you're just using node core's `cluster` module then you don't need to use this appender - log4js will handle logging within the cluster transparently.*
_Note that if you're just using node core's `cluster` module then you don't need to use this appender - log4js will handle logging within the cluster transparently._
The multiprocess appender sends log events to a master server over TCP sockets. It can be used as a simple way to centralise logging when you have multiple servers or processes. It uses the node.js core networking modules, and so does not require any extra dependencies. Remember to call `log4js.shutdown` when your application terminates, so that the sockets get closed cleanly.
## Configuration
* `type` - `multiprocess`
* `mode` - `master|worker` - controls whether the appender listens for log events sent over the network, or is responsible for serialising events and sending them to a server.
* `appender` - `string` (only needed if `mode` == `master`)- the name of the appender to send the log events to
* `loggerPort` - `integer` (optional, defaults to `5000`) - the port to listen on, or send to
* `loggerHost` - `string` (optional, defaults to `localhost`) - the host/IP address to listen on, or send to
- `type` - `multiprocess`
- `mode` - `master|worker` - controls whether the appender listens for log events sent over the network, or is responsible for serialising events and sending them to a server.
- `appender` - `string` (only needed if `mode` == `master`)- the name of the appender to send the log events to
- `loggerPort` - `integer` (optional, defaults to `5000`) - the port to listen on, or send to
- `loggerHost` - `string` (optional, defaults to `localhost`) - the host/IP address to listen on, or send to
## Example (master)
```javascript
log4js.configure({
appenders: {
file: { type: 'file', filename: 'all-the-logs.log' },
server: { type: 'multiprocess', mode: 'master', appender: 'file', loggerHost: '0.0.0.0' }
file: { type: "file", filename: "all-the-logs.log" },
server: {
type: "multiprocess",
mode: "master",
appender: "file",
loggerHost: "0.0.0.0",
},
},
categories: {
default: { appenders: ['file'], level: 'info' }
}
default: { appenders: ["file"], level: "info" },
},
});
```
This creates a log server listening on port 5000, on all IP addresses the host has assigned to it. Note that the appender is not included in the appenders listed for the categories. Also note that the multiprocess master appender will send every event it receives to the underlying appender, regardless of level settings.
## Example (worker)
```javascript
log4js.configure({
appenders: {
network: { type: 'multiprocess', mode: 'worker', loggerHost: 'log.server' }
network: { type: "multiprocess", mode: "worker", loggerHost: "log.server" },
},
categories: {
default: { appenders: ['network'], level: 'error' }
}
default: { appenders: ["network"], level: "error" },
},
});
```
This will send all error messages to `log.server:5000`.

View File

@ -1,58 +1,61 @@
# Category Filter
The no log filter allows you to exclude the log events that an appender will record.
The no log filter allows you to exclude the log events that an appender will record.
The log events will be excluded depending on the regular expressions provided in the configuration.
This can be useful when you debug your application and you want to exclude some noisily logs that are irrelevant to your investigation.
You can stop to log them through a regular expression.
This can be useful when you debug your application and you want to exclude some noisily logs that are irrelevant to your investigation.
You can stop to log them through a regular expression.
## Configuration
* `type` - `"noLogFilter"`
* `exclude` - `string | Array<string>` - the regular expression (or the regular expressions if you provide an array of values) will be used for evaluating the events to pass to the appender. The events, which will match the regular expression, will be excluded and so not logged.
* `appender` - `string` - the name of an appender, defined in the same configuration, that you want to filter.
- `type` - `"noLogFilter"`
- `exclude` - `string | Array<string>` - the regular expression (or the regular expressions if you provide an array of values) will be used for evaluating the events to pass to the appender. The events, which will match the regular expression, will be excluded and so not logged.
- `appender` - `string` - the name of an appender, defined in the same configuration, that you want to filter.
## Example
```javascript
log4js.configure({
appenders: {
everything: { type: 'file', filename: 'all-the-logs.log' },
filtered: {
type: 'noLogFilter',
exclude: 'not',
appender: 'everything' }
everything: { type: "file", filename: "all-the-logs.log" },
filtered: {
type: "noLogFilter",
exclude: "not",
appender: "everything",
},
},
categories: {
default: { appenders: [ 'filtered' ], level: 'debug' }
}
default: { appenders: ["filtered"], level: "debug" },
},
});
const logger = log4js.getLogger();
logger.debug('I will be logged in all-the-logs.log');
logger.debug('I will be not logged in all-the-logs.log');
logger.debug("I will be logged in all-the-logs.log");
logger.debug("I will be not logged in all-the-logs.log");
```
Note that:
* an array of strings can be specified in the configuration
* a case insensitive match will be done
* empty strings will be not considered and so removed from the array of values
- an array of strings can be specified in the configuration
- a case insensitive match will be done
- empty strings will be not considered and so removed from the array of values
```javascript
log4js.configure({
appenders: {
everything: { type: 'file', filename: 'all-the-logs.log' },
filtered: {
type: 'noLogFilter',
exclude: ['NOT', '\\d', ''],
appender: 'everything' }
everything: { type: "file", filename: "all-the-logs.log" },
filtered: {
type: "noLogFilter",
exclude: ["NOT", "\\d", ""],
appender: "everything",
},
},
categories: {
default: { appenders: [ 'filtered' ], level: 'debug' }
}
default: { appenders: ["filtered"], level: "debug" },
},
});
const logger = log4js.getLogger();
logger.debug('I will be logged in all-the-logs.log');
logger.debug('I will be not logged in all-the-logs.log');
logger.debug('A 2nd message that will be excluded in all-the-logs.log');
```
logger.debug("I will be logged in all-the-logs.log");
logger.debug("I will be not logged in all-the-logs.log");
logger.debug("A 2nd message that will be excluded in all-the-logs.log");
```

View File

@ -4,7 +4,7 @@ This appender stores the log events in memory. It is mainly useful for testing (
## Configuration
* `type` - `recording`
- `type` - `recording`
There is no other configuration for this appender.
@ -12,19 +12,19 @@ There is no other configuration for this appender.
The array that stores log events is shared across all recording appender instances, and is accessible from the recording module. `require('<LOG4JS LIB DIR>/appenders/recording')` returns a module with the following functions exported:
* `replay` - returns `Array<LogEvent>` - get all the events recorded.
* `playback` - synonym for `replay`
* `reset` - clears the array of events recorded.
* `erase` - synonyms for `reset`
- `replay` - returns `Array<LogEvent>` - get all the events recorded.
- `playback` - synonym for `replay`
- `reset` - clears the array of events recorded.
- `erase` - synonyms for `reset`
## Example
```javascript
const recording = require('log4js/lib/appenders/recording');
const log4js = require('log4js');
const recording = require("log4js/lib/appenders/recording");
const log4js = require("log4js");
log4js.configure({
appenders: { vcr: { type: 'recording' } },
categories: { default: { appenders: ['vcr'], level: 'info' } }
appenders: { vcr: { type: "recording" } },
categories: { default: { appenders: ["vcr"], level: "info" } },
});
const logger = log4js.getLogger();

View File

@ -4,14 +4,14 @@ This appender writes all log events to the standard error stream.
# Configuration
* `type` - `stderr`
* `layout` - `object` (optional, defaults to colouredLayout) - see [layouts](layouts.md)
- `type` - `stderr`
- `layout` - `object` (optional, defaults to colouredLayout) - see [layouts](layouts.md)
# Example
```javascript
log4js.configure({
appenders: { err: { type: 'stderr' } },
categories: { default: { appenders: ['err'], level: 'ERROR' } }
appenders: { err: { type: "stderr" } },
categories: { default: { appenders: ["err"], level: "ERROR" } },
});
```

View File

@ -4,13 +4,14 @@ This appender writes all log events to the standard output stream. It is the def
# Configuration
* `type` - `stdout`
* `layout` - `object` (optional, defaults to colouredLayout) - see [layouts](layouts.md)
- `type` - `stdout`
- `layout` - `object` (optional, defaults to colouredLayout) - see [layouts](layouts.md)
# Example
```javascript
log4js.configure({
appenders: { 'out': { type: 'stdout' } },
categories: { default: { appenders: ['out'], level: 'info' } }
appenders: { out: { type: "stdout" } },
categories: { default: { appenders: ["out"], level: "info" } },
});
```

View File

@ -4,20 +4,22 @@ Strictly speaking, this is not an appender - but it is configured as one. The TC
## Configuration
* `type` - `tcp-server`
* `port` - `integer` (optional, defaults to `5000`) - the port to listen on
* `host` - `string` (optional, defaults to `localhost`) - the host/IP address to listen on
- `type` - `tcp-server`
- `port` - `integer` (optional, defaults to `5000`) - the port to listen on
- `host` - `string` (optional, defaults to `localhost`) - the host/IP address to listen on
## Example (master)
```javascript
log4js.configure({
appenders: {
file: { type: 'file', filename: 'all-the-logs.log' },
server: { type: 'tcp-server', host: '0.0.0.0' }
file: { type: "file", filename: "all-the-logs.log" },
server: { type: "tcp-server", host: "0.0.0.0" },
},
categories: {
default: { appenders: ['file'], level: 'info' }
}
default: { appenders: ["file"], level: "info" },
},
});
```
This creates a log server listening on port 5000, on all IP addresses the host has assigned to it. Note that the appender is not included in the appenders listed for the categories. All events received on the socket will be forwarded to the other appenders, as if they had originated on the same server.

View File

@ -4,21 +4,23 @@ The TCP appender sends log events to a master server over TCP sockets. It can be
## Configuration
* `type` - `tcp`
* `port` - `integer` (optional, defaults to `5000`) - the port to send to
* `host` - `string` (optional, defaults to `localhost`) - the host/IP address to send to
* `endMsg` - `string` (optional, defaults to `__LOG4JS__`) - the delimiter that marks the end of a log message
* `layout` - `object` (optional, defaults to a serialized log event) - see [layouts](layouts.md)
- `type` - `tcp`
- `port` - `integer` (optional, defaults to `5000`) - the port to send to
- `host` - `string` (optional, defaults to `localhost`) - the host/IP address to send to
- `endMsg` - `string` (optional, defaults to `__LOG4JS__`) - the delimiter that marks the end of a log message
- `layout` - `object` (optional, defaults to a serialized log event) - see [layouts](layouts.md)
## Example
```javascript
log4js.configure({
appenders: {
network: { type: 'tcp', host: 'log.server' }
network: { type: "tcp", host: "log.server" },
},
categories: {
default: { appenders: ['network'], level: 'error' }
}
default: { appenders: ["network"], level: "error" },
},
});
```
This will send all error messages to `log.server:5000`.

View File

@ -2,7 +2,7 @@
`Level` - a log level is the severity or priority of a log event (debug, info, etc). Whether an _appender_ will see the event or not is determined by the _category_'s level. If this is less than or equal to the event's level, it will be sent to the category's appender(s).
`Category` - a label for grouping log events. This can be based on module (e.g. 'auth', 'payment', 'http'), or anything you like. Log events with the same _category_ will go to the same _appenders_. Log4js supports a hierarchy for categories, using dots to separate layers - for example, log events in the category 'myapp.submodule' will use the level for 'myapp' if none is defined for 'myapp.submodule', and also any appenders defined for 'myapp'. (This behaviour can be disabled by setting inherit=false on the sub-category.) The category for log events is defined when you get a _Logger_ from log4js (`log4js.getLogger('somecategory')`).
`Category` - a label for grouping log events. This can be based on module (e.g. 'auth', 'payment', 'http'), or anything you like. Log events with the same _category_ will go to the same _appenders_. Log4js supports a hierarchy for categories, using dots to separate layers - for example, log events in the category 'myapp.submodule' will use the level for 'myapp' if none is defined for 'myapp.submodule', and also any appenders defined for 'myapp'. (This behaviour can be disabled by setting inherit=false on the sub-category.) The category for log events is defined when you get a _Logger_ from log4js (`log4js.getLogger('somecategory')`).
`Appender` - appenders are responsible for output of log events. They may write events to files, send emails, store them in a database, or anything. Most appenders use _layouts_ to serialise the events to strings for output.

View File

@ -3,18 +3,19 @@
log4js no longer supports node versions less than 6.
The following appenders have been removed from the core, and moved to their own projects:
* [gelf](https://github.com/log4js-node/gelf)
* [hipchat](https://github.com/log4js-node/hipchat)
* [logFaces-HTTP](https://github.com/log4js-node/logFaces-HTTP)
* [logFaces-UDP](https://github.com/log4js-node/logFaces-UDP)
* [loggly](https://github.com/log4js-node/loggly)
* [logstashHTTP](https://github.com/log4js-node/logstashHTTP)
* [logstashUDP](https://github.com/log4js-node/logstashUDP)
* [mailgun](https://github.com/log4js-node/mailgun)
* [rabbitmq](https://github.com/log4js-node/rabbitmq)
* [redis](https://github.com/log4js-node/redis)
* [slack](https://github.com/log4js-node/slack)
* [smtp](https://github.com/log4js-node/smtp)
- [gelf](https://github.com/log4js-node/gelf)
- [hipchat](https://github.com/log4js-node/hipchat)
- [logFaces-HTTP](https://github.com/log4js-node/logFaces-HTTP)
- [logFaces-UDP](https://github.com/log4js-node/logFaces-UDP)
- [loggly](https://github.com/log4js-node/loggly)
- [logstashHTTP](https://github.com/log4js-node/logstashHTTP)
- [logstashUDP](https://github.com/log4js-node/logstashUDP)
- [mailgun](https://github.com/log4js-node/mailgun)
- [rabbitmq](https://github.com/log4js-node/rabbitmq)
- [redis](https://github.com/log4js-node/redis)
- [slack](https://github.com/log4js-node/slack)
- [smtp](https://github.com/log4js-node/smtp)
If you were using them, you'll need to `npm i @log4js-node/<appender>`.

View File

@ -1,4 +1,4 @@
# Working with webpack
# Working with webpack
Log4js uses dynamic require for loading appenders. Webpack doesn't know at build time which appender will be used at runtime so a small workaround is necessary.
@ -10,4 +10,3 @@ Configuration.prototype.loadAppenderModule = function(type) {
return stdout;
};
```

View File

@ -7,6 +7,7 @@ Log4js can load appenders from outside its core set. To add a custom appender, t
When log4js parses your configuration, it loops through the defined appenders. For each one, it will `require` the appender initially using the `type` value prepended with './appenders' as the module identifier - this is to try loading from the core appenders first. If that fails (the module could not be found in the core appenders), then log4js will try to require the module using variations of the `type` value.
Log4js checks the following places (in this order) for appenders based on the type value:
1. Bundled core appenders (within appenders directory): `require('./' + type)`
2. node_modules: `require(type)`
3. relative to the main file of your application: `require(path.dirname(require.main.filename) + '/' + type)`
@ -17,15 +18,17 @@ If that fails, an error will be raised.
## Appender Modules
An appender module should export a single function called `configure`. The function should accept the following arguments:
* `config` - `object` - the appender's configuration object
* `layouts` - `module` - gives access to the [layouts](layouts.md) module, which most appenders will need
* `layout` - `function(type, config)` - this is the main function that appenders will use to find a layout
* `findAppender` - `function(name)` - if your appender is a wrapper around another appender (like the [logLevelFilter](logLevelFilter.md) for example), this function can be used to find another appender by name
* `levels` - `module` - gives access to the [levels](levels.md) module, which most appenders will need
- `config` - `object` - the appender's configuration object
- `layouts` - `module` - gives access to the [layouts](layouts.md) module, which most appenders will need
- `layout` - `function(type, config)` - this is the main function that appenders will use to find a layout
- `findAppender` - `function(name)` - if your appender is a wrapper around another appender (like the [logLevelFilter](logLevelFilter.md) for example), this function can be used to find another appender by name
- `levels` - `module` - gives access to the [levels](levels.md) module, which most appenders will need
`configure` should return a function which accepts a logEvent, which is the appender itself. One of the simplest examples is the [stdout](stdout.md) appender. Let's run through the code.
## Example
```javascript
// This is the function that generates an appender function
function stdoutAppender(layout, timezoneOffset) {
@ -57,6 +60,7 @@ exports.configure = configure;
It's a good idea to implement a `shutdown` function on your appender instances. This function will get called by `log4js.shutdown` and signals that `log4js` has been asked to stop logging. Usually this is because of a fatal exception, or the application is being stopped. Your shutdown function should make sure that all asynchronous operations finish, and that any resources are cleaned up. The function must be named `shutdown`, take one callback argument, and be a property of the appender instance. Let's add a shutdown function to the `stdout` appender as an example.
## Example (shutdown)
```javascript
// This is the function that generates an appender function
function stdoutAppender(layout, timezoneOffset) {
@ -67,7 +71,7 @@ function stdoutAppender(layout, timezoneOffset) {
// add a shutdown function.
appender.shutdown = (done) => {
process.stdout.write('', done);
process.stdout.write("", done);
};
return appender;

View File

@ -5,9 +5,9 @@ const log4js = require('../lib/log4js');
log4js.configure({
appenders: {
out: { type: 'stdout' }
out: { type: 'stdout' },
},
categories: { default: { appenders: ['out'], level: 'debug' } }
categories: { default: { appenders: ['out'], level: 'debug' } },
});
let logger;

View File

@ -1,16 +1,20 @@
const log4js = require('../lib/log4js');
log4js.addLayout('json', config => function (logEvent) {
return JSON.stringify(logEvent) + config.separator;
});
log4js.addLayout(
'json',
(config) =>
function (logEvent) {
return JSON.stringify(logEvent) + config.separator;
}
);
log4js.configure({
appenders: {
out: { type: 'stdout', layout: { type: 'json', separator: ',' } }
out: { type: 'stdout', layout: { type: 'json', separator: ',' } },
},
categories: {
default: { appenders: ['out'], level: 'info' }
}
default: { appenders: ['out'], level: 'info' },
},
});
const logger = log4js.getLogger('json-test');

View File

@ -5,12 +5,15 @@ const log4js = require('../lib/log4js');
log4js.configure({
appenders: {
file: {
type: 'dateFile', filename: 'thing.log', numBackups: 3, pattern: '.mm'
}
type: 'dateFile',
filename: 'thing.log',
numBackups: 3,
pattern: '.mm',
},
},
categories: {
default: { appenders: ['file'], level: 'debug' }
}
default: { appenders: ['file'], level: 'debug' },
},
});
const logger = log4js.getLogger('thing');

View File

@ -11,12 +11,12 @@ const app = express();
log4js.configure({
appenders: {
console: { type: 'console' },
file: { type: 'file', filename: 'logs/log4jsconnect.log' }
file: { type: 'file', filename: 'logs/log4jsconnect.log' },
},
categories: {
default: { appenders: ['console'], level: 'debug' },
log4jslog: { appenders: ['file'], level: 'debug' }
}
log4jslog: { appenders: ['file'], level: 'debug' },
},
});
// define logger

View File

@ -11,12 +11,12 @@ if (cluster.isMaster) {
master: {
type: 'multiprocess',
mode: 'master',
appender: 'console'
}
appender: 'console',
},
},
categories: {
default: { appenders: ['console'], level: 'info' }
}
default: { appenders: ['console'], level: 'info' },
},
});
console.info('Master creating %d workers', numCPUs);
@ -30,11 +30,11 @@ if (cluster.isMaster) {
} else {
log4js.configure({
appenders: {
worker: { type: 'multiprocess', mode: 'worker' }
worker: { type: 'multiprocess', mode: 'worker' },
},
categories: {
default: { appenders: ['worker'], level: 'info' }
}
default: { appenders: ['worker'], level: 'info' },
},
});
const logger = log4js.getLogger('example-socket');

View File

@ -5,13 +5,13 @@ const log4js = require('../lib/log4js');
log4js.configure({
appenders: {
cheeseLogs: { type: 'file', filename: 'cheese.log' },
console: { type: 'console' }
console: { type: 'console' },
},
categories: {
cheese: { appenders: ['cheeseLogs'], level: 'error' },
another: { appenders: ['console'], level: 'trace' },
default: { appenders: ['console', 'cheeseLogs'], level: 'trace' }
}
default: { appenders: ['console', 'cheeseLogs'], level: 'trace' },
},
});
// a custom logger outside of the log4js/lib/appenders directory can be accessed like so
@ -25,7 +25,10 @@ const logger = log4js.getLogger('cheese');
const otherLogger = log4js.getLogger();
// this will get coloured output on console, and appear in cheese.log
otherLogger.error('AAArgh! Something went wrong', { some: 'otherObject', useful_for: 'debug purposes' });
otherLogger.error('AAArgh! Something went wrong', {
some: 'otherObject',
useful_for: 'debug purposes',
});
otherLogger.log('This should appear as info output');
// these will not appear (logging level beneath error)

View File

@ -6,23 +6,27 @@ const log4js = require('../lib/log4js');
log4js.configure({
appenders: {
cheese: { type: 'file', filename: 'cheese.log' }
cheese: { type: 'file', filename: 'cheese.log' },
},
categories: {
default: { appenders: ['cheese'], level: 'debug' }
}
default: { appenders: ['cheese'], level: 'debug' },
},
});
const logger = log4js.getLogger('cheese');
const http = require('http');
http.createServer((request, response) => {
response.writeHead(200, { 'Content-Type': 'text/plain' });
const rd = Math.random() * 50;
logger.info(`hello ${rd}`);
response.write('hello ');
if (Math.floor(rd) === 30) {
log4js.shutdown(() => { process.exit(1); });
}
response.end();
}).listen(4444);
http
.createServer((request, response) => {
response.writeHead(200, { 'Content-Type': 'text/plain' });
const rd = Math.random() * 50;
logger.info(`hello ${rd}`);
response.write('hello ');
if (Math.floor(rd) === 30) {
log4js.shutdown(() => {
process.exit(1);
});
}
response.end();
})
.listen(4444);

View File

@ -3,7 +3,7 @@ const log4js = require('../lib/log4js');
log4js.configure({
appenders: { cheese: { type: 'file', filename: 'cheese.log' } },
categories: { default: { appenders: ['cheese'], level: 'error' } }
categories: { default: { appenders: ['cheese'], level: 'error' } },
});
const logger = log4js.getLogger('cheese');

View File

@ -10,13 +10,15 @@ log4js.configure({
appenders: {
hipchat: {
type: 'hipchat',
hipchat_token: process.env.HIPCHAT_TOKEN || '< User token with Notification Privileges >',
hipchat_room: process.env.HIPCHAT_ROOM || '< Room ID or Name >'
}
hipchat_token:
process.env.HIPCHAT_TOKEN ||
'< User token with Notification Privileges >',
hipchat_room: process.env.HIPCHAT_ROOM || '< Room ID or Name >',
},
},
categories: {
default: { appenders: ['hipchat'], level: 'trace' }
}
default: { appenders: ['hipchat'], level: 'trace' },
},
});
const logger = log4js.getLogger('hipchat');
@ -27,7 +29,6 @@ logger.trace('Test Trace Message');
logger.fatal('Test Fatal Message');
logger.error('Test Error Message');
// alternative configuration demonstrating callback + custom layout
// /////////////////////////////////////////////////////////////////
@ -37,7 +38,9 @@ log4js.configure({
appenders: {
hipchat: {
type: 'hipchat',
hipchat_token: process.env.HIPCHAT_TOKEN || '< User token with Notification Privileges >',
hipchat_token:
process.env.HIPCHAT_TOKEN ||
'< User token with Notification Privileges >',
hipchat_room: process.env.HIPCHAT_ROOM || '< Room ID or Name >',
hipchat_from: 'Mr. Semantics',
hipchat_notify: false,
@ -47,10 +50,10 @@ log4js.configure({
}
console.log('mr semantics callback success');
},
layout: { type: 'basic' }
}
layout: { type: 'basic' },
},
},
categories: { default: { appenders: ['hipchat'], level: 'trace' } }
categories: { default: { appenders: ['hipchat'], level: 'trace' } },
});
logger.info('Test customLayout from Mr. Semantics');

View File

@ -2,11 +2,11 @@ const log4js = require('../lib/log4js');
log4js.configure({
appenders: {
out: { type: 'stdout', layout: { type: 'messagePassThrough' } }
out: { type: 'stdout', layout: { type: 'messagePassThrough' } },
},
categories: {
default: { appenders: ['out'], level: 'info' }
}
default: { appenders: ['out'], level: 'info' },
},
});
const logger = log4js.getLogger('thing');

View File

@ -8,24 +8,24 @@ log4js.configure({
maxLogSize: 100000,
backups: 5,
keepFileExt: true,
compress: true
}
compress: true,
},
},
categories: {
default: { appenders: ['handler'], level: 'debug' },
handler: { appenders: ['handler'], level: 'debug' },
}
},
});
const logsToTest = [
'handler'
];
const logsToTest = ['handler'];
const logStartDate = new Date();
const loggers = logsToTest.map(log => log4js.getLogger(log));
const loggers = logsToTest.map((log) => log4js.getLogger(log));
// write out a lot
setInterval(() => {
loggers.forEach(logger => logger.info(`TESTING LOGGER!!!!!!${logStartDate}`));
loggers.forEach((logger) =>
logger.info(`TESTING LOGGER!!!!!!${logStartDate}`)
);
}, 10);

View File

@ -3,18 +3,18 @@ const log4js = require('../lib/log4js');
log4js.configure({
appenders: {
console: {
type: 'console'
type: 'console',
},
file: {
type: 'file',
filename: 'tmp-test.log',
maxLogSize: 1024,
backups: 3
}
backups: 3,
},
},
categories: {
default: { appenders: ['console', 'file'], level: 'info' }
}
default: { appenders: ['console', 'file'], level: 'info' },
},
});
const log = log4js.getLogger('test');

View File

@ -1,33 +1,31 @@
const log4js = require('../lib/log4js');
log4js.configure(
{
appenders: {
file: {
type: 'file',
filename: 'important-things.log',
maxLogSize: 10 * 1024 * 1024, // = 10Mb
backups: 5, // keep five backup files
compress: true, // compress the backups
encoding: 'utf-8',
mode: 0o0640,
flags: 'w+'
},
dateFile: {
type: 'dateFile',
filename: 'more-important-things.log',
pattern: 'yyyy-MM-dd-hh',
compress: true
},
out: {
type: 'stdout'
}
log4js.configure({
appenders: {
file: {
type: 'file',
filename: 'important-things.log',
maxLogSize: 10 * 1024 * 1024, // = 10Mb
backups: 5, // keep five backup files
compress: true, // compress the backups
encoding: 'utf-8',
mode: 0o0640,
flags: 'w+',
},
categories: {
default: { appenders: ['file', 'dateFile', 'out'], level: 'trace' }
}
}
);
dateFile: {
type: 'dateFile',
filename: 'more-important-things.log',
pattern: 'yyyy-MM-dd-hh',
compress: true,
},
out: {
type: 'stdout',
},
},
categories: {
default: { appenders: ['file', 'dateFile', 'out'], level: 'trace' },
},
});
const logger = log4js.getLogger('things');
logger.debug('This little thing went to market');

View File

@ -12,13 +12,14 @@ log4js.configure({
application: 'MY-NODEJS', // (optional) name of the application (domain)
remoteHost: 'localhost', // (optional) logFaces server host or IP address
port: 55201, // (optional) logFaces UDP receiver port (must use JSON format)
layout: { // (optional) the layout to use for messages
layout: {
// (optional) the layout to use for messages
type: 'pattern',
pattern: '%m'
}
}
pattern: '%m',
},
},
},
categories: { default: { appenders: ['logFaces'], level: 'info' } }
categories: { default: { appenders: ['logFaces'], level: 'info' } },
});
const logger = log4js.getLogger('myLogger');

View File

@ -6,19 +6,19 @@ const log4js = require('../lib/log4js');
log4js.configure({
appenders: {
console: {
type: 'console'
type: 'console',
},
loggly: {
type: 'loggly',
token: '12345678901234567890',
subdomain: 'your-subdomain',
tags: ['test']
}
tags: ['test'],
},
},
categories: {
default: { appenders: ['console'], level: 'info' },
loggly: { appenders: ['loggly'], level: 'info' }
}
loggly: { appenders: ['loggly'], level: 'info' },
},
});
const logger = log4js.getLogger('loggly');

View File

@ -3,7 +3,7 @@ const log4js = require('../lib/log4js');
log4js.configure({
appenders: {
console: {
type: 'console'
type: 'console',
},
logstash: {
url: 'http://172.17.0.5:9200/_bulk',
@ -13,13 +13,13 @@ log4js.configure({
application: 'logstash-log4js',
layout: {
type: 'pattern',
pattern: '%m'
}
}
pattern: '%m',
},
},
},
categories: {
default: { appenders: ['console', 'logstash'], level: 'info' }
}
default: { appenders: ['console', 'logstash'], level: 'info' },
},
});
const logger = log4js.getLogger('myLogger');

View File

@ -14,26 +14,27 @@ const log4js = require('../lib/log4js');
log4js.configure({
appenders: {
console: {
type: 'console'
type: 'console',
},
logstash: {
host: '127.0.0.1',
port: 10001,
type: 'logstashUDP',
logType: 'myAppType', // Optional, defaults to 'category'
fields: { // Optional, will be added to the 'fields' object in logstash
fields: {
// Optional, will be added to the 'fields' object in logstash
field1: 'value1',
field2: 'value2'
field2: 'value2',
},
layout: {
type: 'pattern',
pattern: '%m'
}
}
pattern: '%m',
},
},
},
categories: {
default: { appenders: ['console', 'logstash'], level: 'info' }
}
default: { appenders: ['console', 'logstash'], level: 'info' },
},
});
const logger = log4js.getLogger('myLogger');

View File

@ -1,35 +1,33 @@
const log4js = require('../lib/log4js');
log4js.configure(
{
appenders: {
logs: {
type: 'file',
filename: 'memory-test.log'
},
console: {
type: 'stdout',
},
file: {
type: 'file',
filename: 'memory-usage.log',
layout: {
type: 'messagePassThrough'
}
}
log4js.configure({
appenders: {
logs: {
type: 'file',
filename: 'memory-test.log',
},
categories: {
default: { appenders: ['console'], level: 'info' },
'memory-test': { appenders: ['logs'], level: 'info' },
'memory-usage': { appenders: ['console', 'file'], level: 'info' }
}
}
);
console: {
type: 'stdout',
},
file: {
type: 'file',
filename: 'memory-usage.log',
layout: {
type: 'messagePassThrough',
},
},
},
categories: {
default: { appenders: ['console'], level: 'info' },
'memory-test': { appenders: ['logs'], level: 'info' },
'memory-usage': { appenders: ['console', 'file'], level: 'info' },
},
});
const logger = log4js.getLogger('memory-test');
const usage = log4js.getLogger('memory-usage');
for (let i = 0; i < 1000000; i += 1) {
if ((i % 5000) === 0) {
if (i % 5000 === 0) {
usage.info('%d %d', i, process.memoryUsage().rss);
}
logger.info('Doing something.');

View File

@ -8,14 +8,16 @@ log4js.configure({
type: 'pattern',
pattern: '%[%r (%x{pid}) %p %c -%] %m%n',
tokens: {
pid: function () { return process.pid; }
}
}
}
pid: function () {
return process.pid;
},
},
},
},
},
categories: {
default: { appenders: ['out'], level: 'info' }
}
default: { appenders: ['out'], level: 'info' },
},
});
const logger = log4js.getLogger('app');

View File

@ -4,13 +4,13 @@ const log4js = require('../lib/log4js');
// `pm2 install pm2-intercom`
log4js.configure({
appenders: {
out: { type: 'file', filename: 'pm2logs.log' }
out: { type: 'file', filename: 'pm2logs.log' },
},
categories: {
default: { appenders: ['out'], level: 'info' }
default: { appenders: ['out'], level: 'info' },
},
pm2: true,
pm2InstanceVar: 'INSTANCE_ID'
pm2InstanceVar: 'INSTANCE_ID',
});
const logger = log4js.getLogger('app');
logger.info("I'm forever blowing bubbles ", process.env.INSTANCE_ID);

View File

@ -1,9 +1,11 @@
{ "apps": [
{ "name": "testing",
"script": "pm2.js",
"instances": 0,
"instance_var": "INSTANCE_ID",
"exec_mode": "cluster"
{
"apps": [
{
"name": "testing",
"script": "pm2.js",
"instances": 0,
"instance_var": "INSTANCE_ID",
"exec_mode": "cluster"
}
]
}
]
}

View File

@ -5,13 +5,13 @@ const log4js = require('../lib/log4js');
log4js.configure({
appenders: {
out: {
type: 'console'
type: 'console',
},
file: {
type: 'dateFile',
filename: 'logs/log.txt',
pattern: 'yyyyMMdd',
alwaysIncludePattern: false
alwaysIncludePattern: false,
},
mq: {
type: '@log4js-node/rabbitmq',
@ -25,15 +25,15 @@ log4js.configure({
durable: true,
layout: {
type: 'pattern',
pattern: '%d{yyyy-MM-dd hh:mm:ss:SSS}#%p#%m'
}
}
pattern: '%d{yyyy-MM-dd hh:mm:ss:SSS}#%p#%m',
},
},
},
categories: {
default: { appenders: ['out'], level: 'info' },
dateFile: { appenders: ['file'], level: 'info' },
rabbitmq: { appenders: ['mq'], level: 'info' }
}
rabbitmq: { appenders: ['mq'], level: 'info' },
},
});
const log = log4js.getLogger('console');

View File

@ -5,13 +5,13 @@ const log4js = require('../lib/log4js');
log4js.configure({
appenders: {
out: {
type: 'console'
type: 'console',
},
file: {
type: 'dateFile',
filename: 'logs/log.txt',
pattern: 'yyyyMMdd',
alwaysIncludePattern: false
alwaysIncludePattern: false,
},
db: {
type: '@log4js-node/redis',
@ -21,15 +21,15 @@ log4js.configure({
channel: 'q_log',
layout: {
type: 'pattern',
pattern: '%d{yyyy-MM-dd hh:mm:ss:SSS}#%p#%m'
}
}
pattern: '%d{yyyy-MM-dd hh:mm:ss:SSS}#%p#%m',
},
},
},
categories: {
default: { appenders: ['out'], level: 'info' },
dateFile: { appenders: ['file'], level: 'info' },
redis: { appenders: ['db'], level: 'info' }
}
redis: { appenders: ['db'], level: 'info' },
},
});
const log = log4js.getLogger('console');

View File

@ -9,12 +9,12 @@ log4js.configure({
channel_id: '#CHANNEL',
username: 'USERNAME',
format: 'text',
icon_url: 'ICON_URL'
}
icon_url: 'ICON_URL',
},
},
categories: {
default: { appenders: ['slack'], level: 'info' }
}
default: { appenders: ['slack'], level: 'info' },
},
});
const logger = log4js.getLogger('slack');

View File

@ -6,7 +6,7 @@ const log4js = require('../lib/log4js');
log4js.configure({
appenders: {
out: {
type: 'console'
type: 'console',
},
mail: {
type: '@log4js-node/smtp',
@ -19,16 +19,16 @@ log4js.configure({
port: 465,
auth: {
user: 'someone@gmail',
pass: '********************'
pass: '********************',
},
debug: true
}
}
debug: true,
},
},
},
categories: {
default: { appenders: ['out'], level: 'info' },
mailer: { appenders: ['mail'], level: 'info' }
}
mailer: { appenders: ['mail'], level: 'info' },
},
});
const log = log4js.getLogger('test');
const logmailer = log4js.getLogger('mailer');

View File

@ -1,22 +1,22 @@
const log4js = require('../lib/log4js');
log4js.configure({
"appenders": {
"console-appender": {
"type": "console",
"layout": {
"type": "pattern",
"pattern": "%[[%p]%] - %10.-100f{2} | %7.12l:%7.12o - %[%m%]"
}
}
appenders: {
'console-appender': {
type: 'console',
layout: {
type: 'pattern',
pattern: '%[[%p]%] - %10.-100f{2} | %7.12l:%7.12o - %[%m%]',
},
},
},
"categories": {
"default": {
"appenders": ["console-appender"],
"enableCallStack": true,
"level": "info"
}
}
})
categories: {
default: {
appenders: ['console-appender'],
enableCallStack: true,
level: 'info',
},
},
});
log4js.getLogger().info('This should not cause problems');

View File

@ -38,11 +38,17 @@ class LoggingEvent {
// duck-typing for Error object
if (value && value.message && value.stack) {
// eslint-disable-next-line prefer-object-spread
value = Object.assign({message: value.message, stack: value.stack}, value);
value = Object.assign(
{ message: value.message, stack: value.stack },
value
);
}
// JSON.stringify({a: parseInt('abc'), b: 1/0, c: -1/0}) returns {a: null, b: null, c: null}.
// The following allows us to serialize to NaN, Infinity and -Infinity correctly.
else if (typeof value === 'number' && (Number.isNaN(value) || !Number.isFinite(value))) {
else if (
typeof value === 'number' &&
(Number.isNaN(value) || !Number.isFinite(value))
) {
value = value.toString();
}
// JSON.stringify([undefined]) returns [null].
@ -60,7 +66,9 @@ class LoggingEvent {
const rehydratedEvent = flatted.parse(serialised, (key, value) => {
if (value && value.message && value.stack) {
const fakeError = new Error(value);
Object.keys(value).forEach((k) => { fakeError[k] = value[k]; });
Object.keys(value).forEach((k) => {
fakeError[k] = value[k];
});
value = fakeError;
}
return value;
@ -70,7 +78,7 @@ class LoggingEvent {
fileName: rehydratedEvent.fileName,
lineNumber: rehydratedEvent.lineNumber,
columnNumber: rehydratedEvent.columnNumber,
callStack: rehydratedEvent.callStack
callStack: rehydratedEvent.callStack,
};
event = new LoggingEvent(
rehydratedEvent.categoryName,
@ -83,11 +91,12 @@ class LoggingEvent {
event.pid = rehydratedEvent.pid;
event.cluster = rehydratedEvent.cluster;
} catch (e) {
event = new LoggingEvent(
'log4js',
levels.ERROR,
['Unable to parse log:', serialised, 'because: ', e]
);
event = new LoggingEvent('log4js', levels.ERROR, [
'Unable to parse log:',
serialised,
'because: ',
e,
]);
}
return event;

View File

@ -31,7 +31,7 @@ function adapter(configAdapter, config) {
function fileAppenderAdapter(config) {
const configAdapter = {
maxLogSize: maxFileSizeUnitTransform
maxLogSize: maxFileSizeUnitTransform,
};
return adapter(configAdapter, config);
}
@ -39,7 +39,8 @@ function fileAppenderAdapter(config) {
const adapters = {
dateFile: fileAppenderAdapter,
file: fileAppenderAdapter,
fileSync: fileAppenderAdapter
fileSync: fileAppenderAdapter,
};
module.exports.modifyConfig = config => (adapters[config.type] ? adapters[config.type](config) : config);
module.exports.modifyConfig = (config) =>
adapters[config.type] ? adapters[config.type](config) : config;

View File

@ -4,17 +4,17 @@ const os = require('os');
const eol = os.EOL;
function openTheStream(filename, pattern, options) {
const stream = new streams.DateRollingFileStream(
filename,
pattern,
options
);
const stream = new streams.DateRollingFileStream(filename, pattern, options);
stream.on('error', (err) => {
// eslint-disable-next-line no-console
console.error('log4js.dateFileAppender - Writing to file %s, error happened ', filename, err);
console.error(
'log4js.dateFileAppender - Writing to file %s, error happened ',
filename,
err
);
});
stream.on("drain", () => {
process.emit("log4js:pause", false);
stream.on('drain', () => {
process.emit('log4js:pause', false);
});
return stream;
}
@ -28,13 +28,7 @@ function openTheStream(filename, pattern, options) {
* @param options - options to be passed to the underlying stream
* @param timezoneOffset - optional timezone offset in minutes (default system local)
*/
function appender(
filename,
pattern,
layout,
options,
timezoneOffset
) {
function appender(filename, pattern, layout, options, timezoneOffset) {
// the options for file appender use maxLogSize, but the docs say any file appender
// options should work for dateFile as well.
options.maxSize = options.maxLogSize;
@ -45,8 +39,8 @@ function appender(
if (!writer.writable) {
return;
}
if (!writer.write(layout(logEvent, timezoneOffset) + eol, "utf8")) {
process.emit("log4js:pause", true);
if (!writer.write(layout(logEvent, timezoneOffset) + eol, 'utf8')) {
process.emit('log4js:pause', true);
}
};

View File

@ -26,8 +26,15 @@ function mainSighupHandler() {
* @param options - options to be passed to the underlying stream
* @param timezoneOffset - optional timezone offset in minutes (default system local)
*/
function fileAppender(file, layout, logSize, numBackups, options, timezoneOffset) {
if (typeof file !== "string" || file.length === 0) {
function fileAppender(
file,
layout,
logSize,
numBackups,
options,
timezoneOffset
) {
if (typeof file !== 'string' || file.length === 0) {
throw new Error(`Invalid filename: ${file}`);
} else if (file.endsWith(path.sep)) {
throw new Error(`Filename is a directory: ${file}`);
@ -37,15 +44,20 @@ function fileAppender(file, layout, logSize, numBackups, options, timezoneOffset
file = file.replace(new RegExp(`^~(?=${path.sep}.+)`), os.homedir());
}
file = path.normalize(file);
numBackups = (!numBackups && numBackups !== 0) ? 5 : numBackups;
numBackups = !numBackups && numBackups !== 0 ? 5 : numBackups;
debug(
'Creating file appender (',
file, ', ',
logSize, ', ',
numBackups, ', ',
options, ', ',
timezoneOffset, ')'
file,
', ',
logSize,
', ',
numBackups,
', ',
options,
', ',
timezoneOffset,
')'
);
function openTheStream(filePath, fileSize, numFiles, opt) {
@ -57,10 +69,14 @@ function fileAppender(file, layout, logSize, numBackups, options, timezoneOffset
);
stream.on('error', (err) => {
// eslint-disable-next-line no-console
console.error('log4js.fileAppender - Writing to file %s, error happened ', filePath, err);
console.error(
'log4js.fileAppender - Writing to file %s, error happened ',
filePath,
err
);
});
stream.on('drain', () => {
process.emit("log4js:pause", false);
process.emit('log4js:pause', false);
});
return stream;
}
@ -74,18 +90,20 @@ function fileAppender(file, layout, logSize, numBackups, options, timezoneOffset
if (options.removeColor === true) {
// eslint-disable-next-line no-control-regex
const regex = /\x1b[[0-9;]*m/g;
loggingEvent.data = loggingEvent.data.map(d => {
loggingEvent.data = loggingEvent.data.map((d) => {
if (typeof d === 'string') return d.replace(regex, '');
return d;
});
}
if (!writer.write(layout(loggingEvent, timezoneOffset) + eol, "utf8")) {
if (!writer.write(layout(loggingEvent, timezoneOffset) + eol, 'utf8')) {
process.emit('log4js:pause', true);
}
};
app.reopen = function () {
writer.end(() => { writer = openTheStream(file, logSize, numBackups, options); });
writer.end(() => {
writer = openTheStream(file, logSize, numBackups, options);
});
};
app.sighupHandler = function () {

View File

@ -10,9 +10,8 @@ function touchFile(file, options) {
const mkdir = (dir) => {
try {
return fs.mkdirSync(dir, { recursive: true });
}
// backward-compatible fs.mkdirSync for nodejs pre-10.12.0 (without recursive option)
catch (e) {
} catch (e) {
// backward-compatible fs.mkdirSync for nodejs pre-10.12.0 (without recursive option)
// recursive creation of parent first
if (e.code === 'ENOENT') {
mkdir(path.dirname(dir));
@ -41,7 +40,7 @@ function touchFile(file, options) {
mkdir(path.dirname(file));
// try to throw EISDIR, EROFS, EACCES
fs.appendFileSync(file, "", { mode: options.mode, flag: options.flags });
fs.appendFileSync(file, '', { mode: options.mode, flag: options.flags });
}
class RollingFileSync {
@ -74,7 +73,11 @@ class RollingFileSync {
}
shouldRoll() {
debug('should roll with current size %d, and max size %d', this.currentSize, this.size);
debug(
'should roll with current size %d, and max size %d',
this.currentSize,
this.size
);
return this.currentSize >= this.size;
}
@ -87,7 +90,9 @@ class RollingFileSync {
}
function index(filename_) {
return parseInt(filename_.slice((`${path.basename(filename)}.`).length), 10) || 0;
return (
parseInt(filename_.slice(`${path.basename(filename)}.`.length), 10) || 0
);
}
function byIndex(a, b) {
@ -109,7 +114,10 @@ class RollingFileSync {
}
debug(`Renaming ${fileToRename} -> ${filename}.${idx + 1}`);
fs.renameSync(path.join(path.dirname(filename), fileToRename), `${filename}.${idx + 1}`);
fs.renameSync(
path.join(path.dirname(filename), fileToRename),
`${filename}.${idx + 1}`
);
}
}
@ -118,7 +126,11 @@ class RollingFileSync {
debug('Renaming the old files');
const files = fs.readdirSync(path.dirname(filename));
files.filter(justTheseFiles).sort(byIndex).reverse().forEach(increaseFileIndex);
files
.filter(justTheseFiles)
.sort(byIndex)
.reverse()
.forEach(increaseFileIndex);
}
debug('Rolling, rolling, rolling');
@ -129,7 +141,6 @@ class RollingFileSync {
write(chunk, encoding) {
const that = this;
function writeTheChunk() {
debug('writing the chunk to the file');
that.currentSize += chunk.length;
@ -138,7 +149,6 @@ class RollingFileSync {
debug('in write');
if (this.shouldRoll()) {
this.currentSize = 0;
this.roll(this.filename);
@ -161,8 +171,15 @@ class RollingFileSync {
* @param options - options to be passed to the underlying stream
* @param timezoneOffset - optional timezone offset in minutes (default system local)
*/
function fileAppender(file, layout, logSize, numBackups, options, timezoneOffset) {
if (typeof file !== "string" || file.length === 0) {
function fileAppender(
file,
layout,
logSize,
numBackups,
options,
timezoneOffset
) {
if (typeof file !== 'string' || file.length === 0) {
throw new Error(`Invalid filename: ${file}`);
} else if (file.endsWith(path.sep)) {
throw new Error(`Filename is a directory: ${file}`);
@ -172,38 +189,38 @@ function fileAppender(file, layout, logSize, numBackups, options, timezoneOffset
file = file.replace(new RegExp(`^~(?=${path.sep}.+)`), os.homedir());
}
file = path.normalize(file);
numBackups = (!numBackups && numBackups !== 0) ? 5 : numBackups;
numBackups = !numBackups && numBackups !== 0 ? 5 : numBackups;
debug(
'Creating fileSync appender (',
file, ', ',
logSize, ', ',
numBackups, ', ',
options, ', ',
timezoneOffset, ')'
file,
', ',
logSize,
', ',
numBackups,
', ',
options,
', ',
timezoneOffset,
')'
);
function openTheStream(filePath, fileSize, numFiles) {
let stream;
if (fileSize) {
stream = new RollingFileSync(
filePath,
fileSize,
numFiles,
options
);
stream = new RollingFileSync(filePath, fileSize, numFiles, options);
} else {
stream = (((f) => {
stream = ((f) => {
// touch the file to apply flags (like w to truncate the file)
touchFile(f, options);
return {
write(data) {
fs.appendFileSync(f, data);
}
},
};
}))(filePath);
})(filePath);
}
return stream;
@ -225,7 +242,7 @@ function configure(config, layouts) {
const options = {
flags: config.flags || 'a',
encoding: config.encoding || 'utf8',
mode: config.mode || 0o600
mode: config.mode || 0o600,
};
return fileAppender(

View File

@ -37,18 +37,22 @@ const tryLoading = (modulePath, config) => {
}
};
const loadAppenderModule = (type, config) => coreAppenders.get(type)
|| tryLoading(`./${type}`, config)
|| tryLoading(type, config)
|| (require.main && require.main.filename && tryLoading(path.join(path.dirname(require.main.filename), type), config))
|| tryLoading(path.join(process.cwd(), type), config);
const loadAppenderModule = (type, config) =>
coreAppenders.get(type) ||
tryLoading(`./${type}`, config) ||
tryLoading(type, config) ||
(require.main &&
require.main.filename &&
tryLoading(path.join(path.dirname(require.main.filename), type), config)) ||
tryLoading(path.join(process.cwd(), type), config);
const appendersLoading = new Set();
const getAppender = (name, config) => {
if (appenders.has(name)) return appenders.get(name);
if (!config.appenders[name]) return false;
if (appendersLoading.has(name)) throw new Error(`Dependency loop detected for appender ${name}.`);
if (appendersLoading.has(name))
throw new Error(`Dependency loop detected for appender ${name}.`);
appendersLoading.add(name);
debug(`Creating appender ${name}`);
@ -62,7 +66,8 @@ const getAppender = (name, config) => {
const createAppender = (name, config) => {
const appenderConfig = config.appenders[name];
const appenderModule = appenderConfig.type.configure
? appenderConfig.type : loadAppenderModule(appenderConfig.type, config);
? appenderConfig.type
: loadAppenderModule(appenderConfig.type, config);
configuration.throwExceptionIf(
config,
configuration.not(appenderModule),
@ -71,31 +76,45 @@ const createAppender = (name, config) => {
if (appenderModule.appender) {
process.emitWarning(
`Appender ${appenderConfig.type} exports an appender function.`,
"DeprecationWarning", "log4js-node-DEP0001"
'DeprecationWarning',
'log4js-node-DEP0001'
);
debug(
'[log4js-node-DEP0001]',
`DEPRECATION: Appender ${appenderConfig.type} exports an appender function.`
);
debug("[log4js-node-DEP0001]",
`DEPRECATION: Appender ${appenderConfig.type} exports an appender function.`);
}
if (appenderModule.shutdown) {
process.emitWarning(
`Appender ${appenderConfig.type} exports a shutdown function.`,
"DeprecationWarning", "log4js-node-DEP0002"
'DeprecationWarning',
'log4js-node-DEP0002'
);
debug(
'[log4js-node-DEP0002]',
`DEPRECATION: Appender ${appenderConfig.type} exports a shutdown function.`
);
debug("[log4js-node-DEP0002]",
`DEPRECATION: Appender ${appenderConfig.type} exports a shutdown function.`);
}
debug(`${name}: clustering.isMaster ? ${clustering.isMaster()}`);
debug(`${name}: appenderModule is ${require('util').inspect(appenderModule)}`); // eslint-disable-line global-require
return clustering.onlyOnMaster(() => {
debug(`calling appenderModule.configure for ${name} / ${appenderConfig.type}`);
return appenderModule.configure(
adapters.modifyConfig(appenderConfig),
layouts,
appender => getAppender(appender, config),
levels
);
}, /* istanbul ignore next: fn never gets called by non-master yet needed to pass config validation */ () => {});
debug(
// eslint-disable-next-line global-require
`${name}: appenderModule is ${require('util').inspect(appenderModule)}`
);
return clustering.onlyOnMaster(
() => {
debug(
`calling appenderModule.configure for ${name} / ${appenderConfig.type}`
);
return appenderModule.configure(
adapters.modifyConfig(appenderConfig),
layouts,
(appender) => getAppender(appender, config),
levels
);
},
/* istanbul ignore next: fn never gets called by non-master yet needed to pass config validation */ () => {}
);
};
const setup = (config) => {
@ -106,14 +125,17 @@ const setup = (config) => {
}
const usedAppenders = [];
Object.values(config.categories).forEach(category => {
Object.values(config.categories).forEach((category) => {
usedAppenders.push(...category.appenders);
});
Object.keys(config.appenders).forEach((name) => {
// dodgy hard-coding of special case for tcp-server and multiprocess which may not have
// any categories associated with it, but needs to be started up anyway
if (usedAppenders.includes(name) || config.appenders[name].type === 'tcp-server'
|| config.appenders[name].type === 'multiprocess') {
if (
usedAppenders.includes(name) ||
config.appenders[name].type === 'tcp-server' ||
config.appenders[name].type === 'multiprocess'
) {
getAppender(name, config);
}
});

View File

@ -3,7 +3,10 @@ function logLevelFilter(minLevelString, maxLevelString, appender, levels) {
const maxLevel = levels.getLevel(maxLevelString, levels.FATAL);
return (logEvent) => {
const eventLevel = logEvent.level;
if (minLevel.isLessThanOrEqualTo(eventLevel) && maxLevel.isGreaterThanOrEqualTo(eventLevel)) {
if (
minLevel.isLessThanOrEqualTo(eventLevel) &&
maxLevel.isGreaterThanOrEqualTo(eventLevel)
) {
appender(logEvent);
}
};

View File

@ -1,10 +1,9 @@
const debug = require('debug')('log4js:multiFile');
const path = require('path');
const fileAppender = require('./file');
const findFileKey = (property, event) => event[property] || event.context[property];
const findFileKey = (property, event) =>
event[property] || event.context[property];
module.exports.configure = (config, layouts) => {
debug('Creating a multi-file appender');
@ -49,7 +48,10 @@ module.exports.configure = (config, layouts) => {
timers.set(fileKey, {
timeout: config.timeout,
lastUsed: Date.now(),
interval: setInterval(checkForTimeout.bind(null, fileKey), config.timeout)
interval: setInterval(
checkForTimeout.bind(null, fileKey),
config.timeout
),
});
}
} else if (config.timeout) {

View File

@ -1,5 +1,3 @@
const debug = require('debug')('log4js:multiprocess');
const net = require('net');
const LoggingEvent = require('../LoggingEvent');
@ -55,7 +53,7 @@ function logServer(config, actualAppender, levels) {
level: levels.ERROR,
data: ['A worker log process hung up unexpectedly', error],
remoteAddress: clientSocket.remoteAddress,
remotePort: clientSocket.remotePort
remotePort: clientSocket.remotePort,
};
actualAppender(loggingEvent);
}
@ -65,11 +63,15 @@ function logServer(config, actualAppender, levels) {
clientSocket.on('error', handleError);
});
server.listen(config.loggerPort || 5000, config.loggerHost || 'localhost', (e) => {
debug('(master) master server listening, error was ', e);
// allow the process to exit, if this is the only socket active
server.unref();
});
server.listen(
config.loggerPort || 5000,
config.loggerHost || 'localhost',
(e) => {
debug('(master) master server listening, error was ', e);
// allow the process to exit, if this is the only socket active
server.unref();
}
);
function app(event) {
debug('(master) log event sent directly to actual appender (local event)');
@ -106,9 +108,14 @@ function workerAppender(config) {
function createSocket() {
debug(
`(worker) worker appender creating socket to ${config.loggerHost || 'localhost'}:${config.loggerPort || 5000}`
`(worker) worker appender creating socket to ${
config.loggerHost || 'localhost'
}:${config.loggerPort || 5000}`
);
socket = net.createConnection(
config.loggerPort || 5000,
config.loggerHost || 'localhost'
);
socket = net.createConnection(config.loggerPort || 5000, config.loggerHost || 'localhost');
socket.on('connect', () => {
debug('(worker) worker socket connected');
emptyBuffer();
@ -129,7 +136,9 @@ function workerAppender(config) {
if (canWrite) {
write(loggingEvent);
} else {
debug('(worker) worker buffering log event because it cannot write at the moment');
debug(
'(worker) worker buffering log event because it cannot write at the moment'
);
buffer.push(loggingEvent);
}
}
@ -171,7 +180,9 @@ function configure(config, layouts, findAppender, levels) {
appender = findAppender(config.appender);
if (!appender) {
debug(`actual appender "${config.appender}" not found`);
throw new Error(`multiprocess master appender "${config.appender}" not defined`);
throw new Error(
`multiprocess master appender "${config.appender}" not defined`
);
}
}
return createAppender(config, appender, levels);

View File

@ -1,5 +1,3 @@
const debug = require('debug')('log4js:noLogFilter');
/**
@ -8,7 +6,7 @@ const debug = require('debug')('log4js:noLogFilter');
* @returns {string[]} a filtered string array with not empty or null regexp
*/
function removeNullOrEmptyRegexp(regexp) {
const filtered = regexp.filter(el => ((el != null) && (el !== '')));
const filtered = regexp.filter((el) => el != null && el !== '');
return filtered;
}
@ -27,8 +25,10 @@ function noLogFilter(filters, appender) {
}
filters = removeNullOrEmptyRegexp(filters);
const regex = new RegExp(filters.join('|'), 'i');
if (filters.length === 0
|| logEvent.data.findIndex(value => regex.test(value)) < 0) {
if (
filters.length === 0 ||
logEvent.data.findIndex((value) => regex.test(value)) < 0
) {
debug('Not excluded, sending to appender');
appender(logEvent);
}

View File

@ -1,12 +1,12 @@
const debug = require('debug')('log4js:recording');
const recordedEvents = [];
function configure() {
return function (logEvent) {
debug(`received logEvent, number of events now ${recordedEvents.length + 1}`);
debug(
`received logEvent, number of events now ${recordedEvents.length + 1}`
);
debug('log event was ', logEvent);
recordedEvents.push(logEvent);
};
@ -25,5 +25,5 @@ module.exports = {
replay,
playback: replay,
reset,
erase: reset
erase: reset,
};

View File

@ -1,5 +1,3 @@
function stderrAppender(layout, timezoneOffset) {
return (loggingEvent) => {
process.stderr.write(`${layout(loggingEvent, timezoneOffset)}\n`);

View File

@ -1,5 +1,3 @@
function stdoutAppender(layout, timezoneOffset) {
return (loggingEvent) => {
process.stdout.write(`${layout(loggingEvent, timezoneOffset)}\n`);

View File

@ -20,9 +20,11 @@ exports.configure = (config) => {
} else {
dataSoFar = '';
}
events.filter(e => e.length).forEach((e) => {
clustering.send(LoggingEvent.deserialise(e));
});
events
.filter((e) => e.length)
.forEach((e) => {
clustering.send(LoggingEvent.deserialise(e));
});
} else {
dataSoFar = '';
}
@ -42,6 +44,6 @@ exports.configure = (config) => {
shutdown: (cb) => {
debug('shutdown called.');
server.close(cb);
}
},
};
};

View File

@ -1,5 +1,3 @@
const debug = require('debug')('log4js:tcp');
const net = require('net');
@ -24,9 +22,16 @@ function appender(config, layout) {
}
function createSocket() {
debug(`appender creating socket to ${config.host || 'localhost'}:${config.port || 5000}`);
debug(
`appender creating socket to ${config.host || 'localhost'}:${
config.port || 5000
}`
);
endMsg = `${config.endMsg || '__LOG4JS__'}`;
socket = net.createConnection(config.port || 5000, config.host || 'localhost');
socket = net.createConnection(
config.port || 5000,
config.host || 'localhost'
);
socket.on('connect', () => {
debug('socket connected');
emptyBuffer();

View File

@ -22,7 +22,6 @@ function inheritFromParent(config, category, categoryName) {
const parentCategoryName = categoryName.slice(0, lastDotIndex);
let parentCategory = config.categories[parentCategoryName];
if (!parentCategory) {
// parent is missing, so implicitly create it, so that it can inherit from its parents
parentCategory = { inherit: true, appenders: [] };
@ -33,10 +32,12 @@ function inheritFromParent(config, category, categoryName) {
// if the parent is not in the config (because we just created it above),
// and it inherited a valid configuration, add it to config.categories
if (!config.categories[parentCategoryName]
&& parentCategory.appenders
&& parentCategory.appenders.length
&& parentCategory.level) {
if (
!config.categories[parentCategoryName] &&
parentCategory.appenders &&
parentCategory.appenders.length &&
parentCategory.level
) {
config.categories[parentCategoryName] = parentCategory;
}
@ -52,7 +53,6 @@ function inheritFromParent(config, category, categoryName) {
category.parent = parentCategory;
}
/**
* Walk all categories in the config, and pull down any configuration from parent to child.
* This includes inherited appenders, and level, where level is not set.
@ -69,7 +69,9 @@ function addCategoryInheritance(config) {
});
}
configuration.addPreProcessingListener(config => addCategoryInheritance(config));
configuration.addPreProcessingListener((config) =>
addCategoryInheritance(config)
);
configuration.addListener((config) => {
configuration.throwExceptionIf(
@ -91,7 +93,7 @@ configuration.addListener((config) => {
config,
[
configuration.not(category.appenders),
configuration.not(category.level)
configuration.not(category.level),
],
`category "${name}" is not valid (must be an object with properties "appenders" and "level")`
);
@ -127,8 +129,8 @@ configuration.addListener((config) => {
configuration.throwExceptionIf(
config,
configuration.not(levels.getLevel(category.level)),
`category "${name}" is not valid (level "${category.level}" not recognised;`
+ ` valid levels are ${levels.levels.join(', ')})`
`category "${name}" is not valid (level "${category.level}" not recognised;` +
` valid levels are ${levels.levels.join(', ')})`
);
});
@ -152,14 +154,11 @@ const setup = (config) => {
category.appenders.forEach((appender) => {
categoryAppenders.push(appenders.get(appender));
debug(`Creating category ${name}`);
categories.set(
name,
{
appenders: categoryAppenders,
level: levels.getLevel(category.level),
enableCallStack: category.enableCallStack || false
}
);
categories.set(name, {
appenders: categoryAppenders,
level: levels.getLevel(category.level),
enableCallStack: category.enableCallStack || false,
});
});
});
};
@ -181,7 +180,9 @@ const configForCategory = (category) => {
let sourceCategoryConfig;
if (category.indexOf('.') > 0) {
debug(`configForCategory: ${category} has hierarchy, cloning from parents`);
sourceCategoryConfig = { ...configForCategory(category.slice(0, category.lastIndexOf('.'))) };
sourceCategoryConfig = {
...configForCategory(category.slice(0, category.lastIndexOf('.'))),
};
} else {
if (!categories.has('default')) {
setup({ categories: { default: { appenders: ['out'], level: 'OFF' } } });
@ -193,14 +194,16 @@ const configForCategory = (category) => {
return sourceCategoryConfig;
};
const appendersForCategory = category => configForCategory(category).appenders;
const appendersForCategory = (category) =>
configForCategory(category).appenders;
const getLevelForCategory = category => configForCategory(category).level;
const getLevelForCategory = (category) => configForCategory(category).level;
const setLevelForCategory = (category, level) => {
configForCategory(category).level = level;
};
const getEnableCallStackForCategory = category => configForCategory(category).enableCallStack === true;
const getEnableCallStackForCategory = (category) =>
configForCategory(category).enableCallStack === true;
const setEnableCallStackForCategory = (category, useCallStack) => {
configForCategory(category).enableCallStack = useCallStack;
};

View File

@ -1,54 +1,55 @@
const debug = require("debug")("log4js:clustering");
const LoggingEvent = require("./LoggingEvent");
const configuration = require("./configuration");
const debug = require('debug')('log4js:clustering');
const LoggingEvent = require('./LoggingEvent');
const configuration = require('./configuration');
let disabled = false;
let cluster = null;
try {
// eslint-disable-next-line global-require
cluster = require("cluster");
cluster = require('cluster');
} catch (e) {
debug("cluster module not present");
debug('cluster module not present');
disabled = true;
}
const listeners = [];
let pm2 = false;
let pm2InstanceVar = "NODE_APP_INSTANCE";
let pm2InstanceVar = 'NODE_APP_INSTANCE';
const isPM2Master = () => pm2 && process.env[pm2InstanceVar] === "0";
const isMaster = () => disabled || (cluster && cluster.isMaster) || isPM2Master();
const isPM2Master = () => pm2 && process.env[pm2InstanceVar] === '0';
const isMaster = () =>
disabled || (cluster && cluster.isMaster) || isPM2Master();
const sendToListeners = logEvent => {
listeners.forEach(l => l(logEvent));
const sendToListeners = (logEvent) => {
listeners.forEach((l) => l(logEvent));
};
// in a multi-process node environment, worker loggers will use
// process.send
const receiver = (worker, message) => {
// prior to node v6, the worker parameter was not passed (args were message, handle)
debug("cluster message received from worker ", worker, ": ", message);
debug('cluster message received from worker ', worker, ': ', message);
if (worker.topic && worker.data) {
message = worker;
worker = undefined;
}
if (message && message.topic && message.topic === "log4js:message") {
debug("received message: ", message.data);
if (message && message.topic && message.topic === 'log4js:message') {
debug('received message: ', message.data);
const logEvent = LoggingEvent.deserialise(message.data);
sendToListeners(logEvent);
}
};
if (!disabled) {
configuration.addListener(config => {
configuration.addListener((config) => {
// clear out the listeners, because configure has been called.
listeners.length = 0;
({
pm2,
disableClustering: disabled,
pm2InstanceVar = "NODE_APP_INSTANCE"
pm2InstanceVar = 'NODE_APP_INSTANCE',
} = config);
debug(`clustering disabled ? ${disabled}`);
@ -59,25 +60,25 @@ if (!disabled) {
// just in case configure is called after shutdown
if (pm2) {
process.removeListener("message", receiver);
process.removeListener('message', receiver);
}
if (cluster && cluster.removeListener) {
cluster.removeListener("message", receiver);
cluster.removeListener('message', receiver);
}
if (disabled || config.disableClustering) {
debug("Not listening for cluster messages, because clustering disabled.");
debug('Not listening for cluster messages, because clustering disabled.');
} else if (isPM2Master()) {
// PM2 cluster support
// PM2 runs everything as workers - install pm2-intercom for this to work.
// we only want one of the app instances to write logs
debug("listening for PM2 broadcast messages");
process.on("message", receiver);
debug('listening for PM2 broadcast messages');
process.on('message', receiver);
} else if (cluster && cluster.isMaster) {
debug("listening for cluster messages");
cluster.on("message", receiver);
debug('listening for cluster messages');
cluster.on('message', receiver);
} else {
debug("not listening for messages, because we are not a master process");
debug('not listening for messages, because we are not a master process');
}
});
}
@ -85,20 +86,20 @@ if (!disabled) {
module.exports = {
onlyOnMaster: (fn, notMaster) => (isMaster() ? fn() : notMaster),
isMaster,
send: msg => {
send: (msg) => {
if (isMaster()) {
sendToListeners(msg);
} else {
if (!pm2) {
msg.cluster = {
workerId: cluster.worker.id,
worker: process.pid
worker: process.pid,
};
}
process.send({ topic: "log4js:message", data: msg.serialise() });
process.send({ topic: 'log4js:message', data: msg.serialise() });
}
},
onMessage: listener => {
onMessage: (listener) => {
listeners.push(listener);
}
},
};

View File

@ -1,18 +1,18 @@
const util = require('util');
const debug = require('debug')('log4js:configuration');
const preProcessingListeners = [];
const listeners = [];
const not = thing => !thing;
const not = (thing) => !thing;
const anObject = thing => thing && typeof thing === 'object' && !Array.isArray(thing);
const anObject = (thing) =>
thing && typeof thing === 'object' && !Array.isArray(thing);
const validIdentifier = thing => /^[A-Za-z][A-Za-z0-9_]*$/g.test(thing);
const validIdentifier = (thing) => /^[A-Za-z][A-Za-z0-9_]*$/g.test(thing);
const anInteger = thing => thing && typeof thing === 'number' && Number.isInteger(thing);
const anInteger = (thing) =>
thing && typeof thing === 'number' && Number.isInteger(thing);
const addListener = (fn) => {
listeners.push(fn);
@ -21,15 +21,20 @@ const addListener = (fn) => {
const addPreProcessingListener = (fn) => {
preProcessingListeners.push(fn);
debug(`Added pre-processing listener, now ${preProcessingListeners.length} listeners`);
debug(
`Added pre-processing listener, now ${preProcessingListeners.length} listeners`
);
};
const throwExceptionIf = (config, checks, message) => {
const tests = Array.isArray(checks) ? checks : [checks];
tests.forEach((test) => {
if (test) {
throw new Error(`Problem with log4js configuration: (${util.inspect(config, { depth: 5 })})`
+ ` - ${message}`);
throw new Error(
`Problem with log4js configuration: (${util.inspect(config, {
depth: 5,
})}) - ${message}`
);
}
});
};
@ -39,11 +44,11 @@ const configure = (candidate) => {
throwExceptionIf(candidate, not(anObject(candidate)), 'must be an object.');
debug(`Calling pre-processing listeners (${preProcessingListeners.length})`);
preProcessingListeners.forEach(listener => listener(candidate));
preProcessingListeners.forEach((listener) => listener(candidate));
debug('Configuration pre-processing finished.');
debug(`Calling configuration listeners (${listeners.length})`);
listeners.forEach(listener => listener(candidate));
listeners.forEach((listener) => listener(candidate));
debug('Configuration finished.');
};
@ -55,5 +60,5 @@ module.exports = {
anObject,
anInteger,
validIdentifier,
not
not,
};

View File

@ -1,9 +1,9 @@
/* eslint no-underscore-dangle: ["error", { "allow": ["__statusCode", "_remoteAddress", "__headers", "_logging"] }] */
const levels = require("./levels");
const levels = require('./levels');
const DEFAULT_FORMAT =
":remote-addr - -" +
':remote-addr - -' +
' ":method :url HTTP/:http-version"' +
' :status :content-length ":referrer"' +
' ":user-agent"';
@ -32,7 +32,7 @@ function getUrl(req) {
* @return {Array}
*/
function assembleTokens(req, res, customTokens) {
const arrayUniqueTokens = array => {
const arrayUniqueTokens = (array) => {
const a = array.concat();
for (let i = 0; i < a.length; ++i) {
for (let j = i + 1; j < a.length; ++j) {
@ -47,53 +47,53 @@ function assembleTokens(req, res, customTokens) {
};
const defaultTokens = [];
defaultTokens.push({ token: ":url", replacement: getUrl(req) });
defaultTokens.push({ token: ":protocol", replacement: req.protocol });
defaultTokens.push({ token: ":hostname", replacement: req.hostname });
defaultTokens.push({ token: ":method", replacement: req.method });
defaultTokens.push({ token: ':url', replacement: getUrl(req) });
defaultTokens.push({ token: ':protocol', replacement: req.protocol });
defaultTokens.push({ token: ':hostname', replacement: req.hostname });
defaultTokens.push({ token: ':method', replacement: req.method });
defaultTokens.push({
token: ":status",
replacement: res.__statusCode || res.statusCode
token: ':status',
replacement: res.__statusCode || res.statusCode,
});
defaultTokens.push({
token: ":response-time",
replacement: res.responseTime
token: ':response-time',
replacement: res.responseTime,
});
defaultTokens.push({ token: ":date", replacement: new Date().toUTCString() });
defaultTokens.push({ token: ':date', replacement: new Date().toUTCString() });
defaultTokens.push({
token: ":referrer",
replacement: req.headers.referer || req.headers.referrer || ""
token: ':referrer',
replacement: req.headers.referer || req.headers.referrer || '',
});
defaultTokens.push({
token: ":http-version",
replacement: `${req.httpVersionMajor}.${req.httpVersionMinor}`
token: ':http-version',
replacement: `${req.httpVersionMajor}.${req.httpVersionMinor}`,
});
defaultTokens.push({
token: ":remote-addr",
token: ':remote-addr',
replacement:
req.headers["x-forwarded-for"] ||
req.headers['x-forwarded-for'] ||
req.ip ||
req._remoteAddress ||
(req.socket &&
(req.socket.remoteAddress ||
(req.socket.socket && req.socket.socket.remoteAddress)))
(req.socket.socket && req.socket.socket.remoteAddress))),
});
defaultTokens.push({
token: ":user-agent",
replacement: req.headers["user-agent"]
token: ':user-agent',
replacement: req.headers['user-agent'],
});
defaultTokens.push({
token: ":content-length",
token: ':content-length',
replacement:
res.getHeader("content-length") ||
(res.__headers && res.__headers["Content-Length"]) ||
"-"
res.getHeader('content-length') ||
(res.__headers && res.__headers['Content-Length']) ||
'-',
});
defaultTokens.push({
token: /:req\[([^\]]+)]/g,
replacement(_, field) {
return req.headers[field.toLowerCase()];
}
},
});
defaultTokens.push({
token: /:res\[([^\]]+)]/g,
@ -102,7 +102,7 @@ function assembleTokens(req, res, customTokens) {
res.getHeader(field.toLowerCase()) ||
(res.__headers && res.__headers[field])
);
}
},
});
return arrayUniqueTokens(customTokens.concat(defaultTokens));
@ -157,14 +157,16 @@ function createNoLogCondition(nolog) {
regexp = nolog;
}
if (typeof nolog === "string") {
if (typeof nolog === 'string') {
regexp = new RegExp(nolog);
}
if (Array.isArray(nolog)) {
// convert to strings
const regexpsAsStrings = nolog.map(reg => (reg.source ? reg.source : reg));
regexp = new RegExp(regexpsAsStrings.join("|"));
const regexpsAsStrings = nolog.map((reg) =>
reg.source ? reg.source : reg
);
regexp = new RegExp(regexpsAsStrings.join('|'));
}
return regexp;
@ -190,7 +192,7 @@ function matchRules(statusCode, currentLevel, ruleSet) {
let level = currentLevel;
if (ruleSet) {
const matchedRule = ruleSet.find(rule => {
const matchedRule = ruleSet.find((rule) => {
let ruleMatched = false;
if (rule.from && rule.to) {
ruleMatched = statusCode >= rule.from && statusCode <= rule.to;
@ -237,7 +239,7 @@ function matchRules(statusCode, currentLevel, ruleSet) {
* @api public
*/
module.exports = function getLogger(logger4js, options) {
if (typeof options === "string" || typeof options === "function") {
if (typeof options === 'string' || typeof options === 'function') {
options = { format: options };
} else {
options = options || {};
@ -255,7 +257,7 @@ module.exports = function getLogger(logger4js, options) {
// nologs
if (nolog && nolog.test(req.originalUrl)) return next();
if (thisLogger.isLevelEnabled(level) || options.level === "auto") {
if (thisLogger.isLevelEnabled(level) || options.level === 'auto') {
const start = new Date();
const { writeHead } = res;
@ -280,7 +282,7 @@ module.exports = function getLogger(logger4js, options) {
finished = true;
res.responseTime = new Date() - start;
// status code response level handling
if (res.statusCode && options.level === "auto") {
if (res.statusCode && options.level === 'auto') {
level = levels.INFO;
if (res.statusCode >= 300) level = levels.WARN;
if (res.statusCode >= 400) level = levels.ERROR;
@ -289,19 +291,19 @@ module.exports = function getLogger(logger4js, options) {
const combinedTokens = assembleTokens(req, res, options.tokens || []);
if (options.context) thisLogger.addContext("res", res);
if (typeof fmt === "function") {
const line = fmt(req, res, str => format(str, combinedTokens));
if (options.context) thisLogger.addContext('res', res);
if (typeof fmt === 'function') {
const line = fmt(req, res, (str) => format(str, combinedTokens));
if (line) thisLogger.log(level, line);
} else {
thisLogger.log(level, format(fmt, combinedTokens));
}
if (options.context) thisLogger.removeContext("res");
if (options.context) thisLogger.removeContext('res');
};
res.on("end", handler);
res.on("finish", handler);
res.on("error", handler);
res.on("close", handler);
res.on('end', handler);
res.on('finish', handler);
res.on('error', handler);
res.on('close', handler);
}
// ensure next gets always called

View File

@ -21,7 +21,7 @@ const styles = {
green: [32, 39],
magenta: [35, 39],
red: [91, 39],
yellow: [33, 39]
yellow: [33, 39],
};
function colorizeStart(style) {
@ -61,7 +61,9 @@ function timestampLevelAndCategory(loggingEvent, colour) {
* @author Stephan Strittmatter
*/
function basicLayout(loggingEvent) {
return timestampLevelAndCategory(loggingEvent) + util.format(...loggingEvent.data);
return (
timestampLevelAndCategory(loggingEvent) + util.format(...loggingEvent.data)
);
}
/**
@ -69,7 +71,10 @@ function basicLayout(loggingEvent) {
* same as basicLayout, but with colours.
*/
function colouredLayout(loggingEvent) {
return timestampLevelAndCategory(loggingEvent, loggingEvent.level.colour) + util.format(...loggingEvent.data);
return (
timestampLevelAndCategory(loggingEvent, loggingEvent.level.colour) +
util.format(...loggingEvent.data)
);
}
function messagePassThroughLayout(loggingEvent) {
@ -125,7 +130,8 @@ function dummyLayout(loggingEvent) {
*/
function patternLayout(pattern, tokens) {
const TTCC_CONVERSION_PATTERN = '%r %p %c - %m%n';
const regex = /%(-?[0-9]+)?(\.?-?[0-9]+)?([[\]cdhmnprzxXyflos%])(\{([^}]+)\})?|([^%]+)/;
const regex =
/%(-?[0-9]+)?(\.?-?[0-9]+)?([[\]cdhmnprzxXyflos%])(\{([^}]+)\})?|([^%]+)/;
pattern = pattern || TTCC_CONVERSION_PATTERN;
@ -135,7 +141,9 @@ function patternLayout(pattern, tokens) {
const precision = parseInt(specifier, 10);
const loggerNameBits = loggerName.split('.');
if (precision < loggerNameBits.length) {
loggerName = loggerNameBits.slice(loggerNameBits.length - precision).join('.');
loggerName = loggerNameBits
.slice(loggerNameBits.length - precision)
.join('.');
}
}
return loggerName;
@ -157,26 +165,32 @@ function patternLayout(pattern, tokens) {
break;
case 'ABSOLUTE':
process.emitWarning(
"Pattern %d{ABSOLUTE} is deprecated in favor of %d{ABSOLUTETIME}. " +
"Please use %d{ABSOLUTETIME} instead.",
"DeprecationWarning", "log4js-node-DEP0003"
'Pattern %d{ABSOLUTE} is deprecated in favor of %d{ABSOLUTETIME}. ' +
'Please use %d{ABSOLUTETIME} instead.',
'DeprecationWarning',
'log4js-node-DEP0003'
);
debug("[log4js-node-DEP0003]",
"DEPRECATION: Pattern %d{ABSOLUTE} is deprecated and replaced by %d{ABSOLUTETIME}.");
// falls through
debug(
'[log4js-node-DEP0003]',
'DEPRECATION: Pattern %d{ABSOLUTE} is deprecated and replaced by %d{ABSOLUTETIME}.'
);
// falls through
case 'ABSOLUTETIME':
case 'ABSOLUTETIME_FORMAT':
format = dateFormat.ABSOLUTETIME_FORMAT;
break;
case 'DATE':
process.emitWarning(
"Pattern %d{DATE} is deprecated due to the confusion it causes when used. " +
"Please use %d{DATETIME} instead.",
"DeprecationWarning", "log4js-node-DEP0004"
'Pattern %d{DATE} is deprecated due to the confusion it causes when used. ' +
'Please use %d{DATETIME} instead.',
'DeprecationWarning',
'log4js-node-DEP0004'
);
debug("[log4js-node-DEP0004]",
"DEPRECATION: Pattern %d{DATE} is deprecated and replaced by %d{DATETIME}.");
// falls through
debug(
'[log4js-node-DEP0004]',
'DEPRECATION: Pattern %d{DATE} is deprecated and replaced by %d{DATETIME}.'
);
// falls through
case 'DATETIME':
case 'DATETIME_FORMAT':
format = dateFormat.DATETIME_FORMAT;
@ -221,7 +235,9 @@ function patternLayout(pattern, tokens) {
}
function pid(loggingEvent) {
return loggingEvent && loggingEvent.pid ? loggingEvent.pid.toString() : process.pid.toString();
return loggingEvent && loggingEvent.pid
? loggingEvent.pid.toString()
: process.pid.toString();
}
function clusterInfo() {
@ -233,7 +249,9 @@ function patternLayout(pattern, tokens) {
function userDefined(loggingEvent, specifier) {
if (typeof tokens[specifier] !== 'undefined') {
return typeof tokens[specifier] === 'function' ? tokens[specifier](loggingEvent) : tokens[specifier];
return typeof tokens[specifier] === 'function'
? tokens[specifier](loggingEvent)
: tokens[specifier];
}
return null;
@ -266,7 +284,9 @@ function patternLayout(pattern, tokens) {
// posix: file:///hello/world/foo.txt -> /hello/world/foo.txt -> /hello/world/foo.txt
// win32: file:///C:/path/foo.txt -> /C:/path/foo.txt -> \C:\path\foo.txt -> C:\path\foo.txt
// win32: file://nas/foo.txt -> //nas/foo.txt -> nas\foo.txt -> \\nas\foo.txt
filepath = path.normalize(filepath.replace(new RegExp(`^${urlPrefix}`), ''));
filepath = path.normalize(
filepath.replace(new RegExp(`^${urlPrefix}`), '')
);
if (process.platform === 'win32') {
if (filepath.startsWith('\\')) {
filepath = filepath.slice(1);
@ -321,7 +341,7 @@ function patternLayout(pattern, tokens) {
f: fileName,
l: lineNumber,
o: columnNumber,
s: callStack
s: callStack,
};
function replaceToken(conversionCharacter, loggingEvent, specifier) {
@ -385,7 +405,11 @@ function patternLayout(pattern, tokens) {
} else {
// Create a raw replacement string based on the conversion
// character and specifier
const replacement = replaceToken(conversionCharacter, loggingEvent, specifier);
const replacement = replaceToken(
conversionCharacter,
loggingEvent,
specifier
);
formattedString += truncateAndPad(replacement, truncation, padding);
}
searchString = searchString.slice(result.index + result[0].length);
@ -395,24 +419,24 @@ function patternLayout(pattern, tokens) {
}
const layoutMakers = {
messagePassThrough () {
messagePassThrough() {
return messagePassThroughLayout;
},
basic () {
basic() {
return basicLayout;
},
colored () {
colored() {
return colouredLayout;
},
coloured () {
coloured() {
return colouredLayout;
},
pattern (config) {
pattern(config) {
return patternLayout(config && config.pattern, config && config.tokens);
},
dummy () {
dummy() {
return dummyLayout;
}
},
};
module.exports = {
@ -422,10 +446,10 @@ module.exports = {
colouredLayout,
coloredLayout: colouredLayout,
dummyLayout,
addLayout (name, serializerGenerator) {
addLayout(name, serializerGenerator) {
layoutMakers[name] = serializerGenerator;
},
layout (name, config) {
layout(name, config) {
return layoutMakers[name] && layoutMakers[name](config);
}
},
};

View File

@ -1,11 +1,15 @@
const configuration = require('./configuration');
const validColours = [
'white', 'grey', 'black',
'blue', 'cyan', 'green',
'magenta', 'red', 'yellow'
'white',
'grey',
'black',
'blue',
'cyan',
'green',
'magenta',
'red',
'yellow',
];
class Level {
@ -52,7 +56,9 @@ class Level {
levelStr,
customLevels[l].colour
);
const existingLevelIndex = Level.levels.findIndex(lvl => lvl.levelStr === levelStr);
const existingLevelIndex = Level.levels.findIndex(
(lvl) => lvl.levelStr === levelStr
);
if (existingLevelIndex > -1) {
Level.levels[existingLevelIndex] = Level[levelStr];
} else {
@ -63,7 +69,6 @@ class Level {
}
}
isLessThanOrEqualTo(otherLevel) {
if (typeof otherLevel === 'string') {
otherLevel = Level.getLevel(otherLevel);
@ -96,7 +101,7 @@ Level.addLevels({
ERROR: { value: 40000, colour: 'red' },
FATAL: { value: 50000, colour: 'magenta' },
MARK: { value: 9007199254740992, colour: 'grey' }, // 2^53
OFF: { value: Number.MAX_VALUE, colour: 'grey' }
OFF: { value: Number.MAX_VALUE, colour: 'grey' },
});
configuration.addListener((config) => {

View File

@ -18,28 +18,28 @@
* @since 2005-05-20
* Website: http://log4js.berlios.de
*/
const debug = require("debug")("log4js:main");
const fs = require("fs");
const deepClone = require("rfdc")({ proto: true });
const configuration = require("./configuration");
const layouts = require("./layouts");
const levels = require("./levels");
const appenders = require("./appenders");
const categories = require("./categories");
const Logger = require("./logger");
const clustering = require("./clustering");
const connectLogger = require("./connect-logger");
const recordingModule = require("./appenders/recording");
const debug = require('debug')('log4js:main');
const fs = require('fs');
const deepClone = require('rfdc')({ proto: true });
const configuration = require('./configuration');
const layouts = require('./layouts');
const levels = require('./levels');
const appenders = require('./appenders');
const categories = require('./categories');
const Logger = require('./logger');
const clustering = require('./clustering');
const connectLogger = require('./connect-logger');
const recordingModule = require('./appenders/recording');
let enabled = false;
function sendLogEventToAppender(logEvent) {
if (!enabled) return;
debug("Received log event ", logEvent);
debug('Received log event ', logEvent);
const categoryAppenders = categories.appendersForCategory(
logEvent.categoryName
);
categoryAppenders.forEach(appender => {
categoryAppenders.forEach((appender) => {
appender(logEvent);
});
}
@ -47,7 +47,7 @@ function sendLogEventToAppender(logEvent) {
function loadConfigurationFile(filename) {
debug(`Loading configuration from ${filename}`);
try {
return JSON.parse(fs.readFileSync(filename, "utf8"));
return JSON.parse(fs.readFileSync(filename, 'utf8'));
} catch (e) {
throw new Error(
`Problem reading config from file "${filename}". Error was ${e.message}`,
@ -64,7 +64,7 @@ function configure(configurationFileOrObject) {
let configObject = configurationFileOrObject;
if (typeof configObject === "string") {
if (typeof configObject === 'string') {
configObject = loadConfigurationFile(configurationFileOrObject);
}
debug(`Configuration is ${configObject}`);
@ -80,7 +80,7 @@ function configure(configurationFileOrObject) {
}
function recording() {
return recordingModule
return recordingModule;
}
/**
@ -92,7 +92,7 @@ function recording() {
* as the first argument.
*/
function shutdown(cb) {
debug("Shutdown called. Disabling all log writing.");
debug('Shutdown called. Disabling all log writing.');
// First, disable all writing to appenders. This prevents appenders from
// not being able to be drained because of run-away log writes.
enabled = false;
@ -110,7 +110,7 @@ function shutdown(cb) {
0
);
if (shutdownFunctions === 0) {
debug("No appenders with shutdown functions found.");
debug('No appenders with shutdown functions found.');
return cb !== undefined && cb();
}
@ -122,13 +122,15 @@ function shutdown(cb) {
completed += 1;
debug(`Appender shutdowns complete: ${completed} / ${shutdownFunctions}`);
if (completed >= shutdownFunctions) {
debug("All shutdown functions completed.");
debug('All shutdown functions completed.');
if (cb) {
cb(error);
}
}
}
appendersToCheck.filter(a => a.shutdown).forEach(a => a.shutdown(complete));
appendersToCheck
.filter((a) => a.shutdown)
.forEach((a) => a.shutdown(complete));
return null;
}
@ -143,12 +145,12 @@ function getLogger(category) {
if (!enabled) {
configure(
process.env.LOG4JS_CONFIG || {
appenders: { out: { type: "stdout" } },
categories: { default: { appenders: ["out"], level: "OFF" } }
appenders: { out: { type: 'stdout' } },
categories: { default: { appenders: ['out'], level: 'OFF' } },
}
);
}
return new Logger(category || "default");
return new Logger(category || 'default');
}
/**

View File

@ -1,17 +1,17 @@
/* eslint no-underscore-dangle: ["error", { "allow": ["_log"] }] */
const debug = require("debug")("log4js:logger");
const LoggingEvent = require("./LoggingEvent");
const levels = require("./levels");
const clustering = require("./clustering");
const categories = require("./categories");
const configuration = require("./configuration");
const debug = require('debug')('log4js:logger');
const LoggingEvent = require('./LoggingEvent');
const levels = require('./levels');
const clustering = require('./clustering');
const categories = require('./categories');
const configuration = require('./configuration');
const stackReg = /at (?:(.+)\s+\()?(?:(.+?):(\d+)(?::(\d+))?|([^)]+))\)?/;
function defaultParseCallStack(data, skipIdx = 4) {
try {
const stacklines = data.stack.split("\n").slice(skipIdx);
const stacklines = data.stack.split('\n').slice(skipIdx);
const lineMatch = stackReg.exec(stacklines[0]);
/* istanbul ignore else: failsafe */
if (lineMatch && lineMatch.length === 6) {
@ -20,14 +20,14 @@ function defaultParseCallStack(data, skipIdx = 4) {
fileName: lineMatch[2],
lineNumber: parseInt(lineMatch[3], 10),
columnNumber: parseInt(lineMatch[4], 10),
callStack: stacklines.join("\n")
callStack: stacklines.join('\n'),
};
} else { // eslint-disable-line no-else-return
// eslint-disable-next-line no-else-return
} else {
// will never get here unless nodejs has changes to Error
console.error('log4js.logger - defaultParseCallStack error'); // eslint-disable-line no-console
}
}
catch (err) {
} catch (err) {
// will never get error unless nodejs has breaking changes to Error
console.error('log4js.logger - defaultParseCallStack error', err); // eslint-disable-line no-console
}
@ -49,7 +49,7 @@ function defaultParseCallStack(data, skipIdx = 4) {
class Logger {
constructor(name) {
if (!name) {
throw new Error("No category provided.");
throw new Error('No category provided.');
}
this.category = name;
this.context = {};
@ -84,7 +84,11 @@ class Logger {
if (!logLevel) {
if (configuration.validIdentifier(level) && args.length > 0) {
// logLevel not found but of valid signature, WARN before fallback to INFO
this.log(levels.WARN, 'log4js:logger.log: valid log-level not found as first parameter given:', level);
this.log(
levels.WARN,
'log4js:logger.log: valid log-level not found as first parameter given:',
level
);
this.log(levels.INFO, `[${level}]`, ...args);
} else {
// apart from fallback, allow .log(...args) to be synonym with .log("INFO", ...args)
@ -132,7 +136,7 @@ function addLevelMethods(target) {
const level = levels.getLevel(target);
const levelStrLower = level.toString().toLowerCase();
const levelMethod = levelStrLower.replace(/_([a-z])/g, g =>
const levelMethod = levelStrLower.replace(/_([a-z])/g, (g) =>
g[1].toUpperCase()
);
const isLevelMethod = levelMethod[0].toUpperCase() + levelMethod.slice(1);

View File

@ -33,7 +33,8 @@
"node": ">=8.0"
},
"scripts": {
"pretest": "eslint \"lib/**/*.js\" \"test/**/*.js\"",
"pretest": "prettier --check . && eslint \"lib/**/*.js\" \"test/**/*.js\"",
"prettier:fix": "prettier --write .",
"test": "tap \"test/tap/**/*.js\" --cov --timeout=45",
"typings": "tsc -p types/tsconfig.json",
"codecov": "tap \"test/tap/**/*.js\" --cov --coverage-report=lcov && codecov"

View File

@ -1,11 +1,11 @@
{
"extends": "../.eslintrc",
"rules": {
"no-plusplus": 0,
"rules": {
"no-plusplus": 0,
"global-require": 0,
"no-mixed-operators": 0,
"no-underscore-dangle": 0,
"guard-for-in": 0,
"no-restricted-syntax": ["error", "WithStatement"]
}
}
}

View File

@ -5,7 +5,7 @@ if (process.argv.indexOf('start-multiprocess-worker') >= 0) {
appenders: {
multi: { type: 'multiprocess', mode: 'worker', loggerPort: port },
},
categories: { default: { appenders: ['multi'], level: 'debug' } }
categories: { default: { appenders: ['multi'], level: 'debug' } },
});
log4js.getLogger('worker').info('Logging from worker');
log4js.shutdown(() => {

View File

@ -1,13 +1,15 @@
const sandbox = require("@log4js-node/sandboxed-module");
const sandbox = require('@log4js-node/sandboxed-module');
sandbox.configure({
sourceTransformers: {
nyc(source) {
if (this.filename.indexOf("node_modules") > -1) {
if (this.filename.indexOf('node_modules') > -1) {
return source;
}
const nyc = new (require("nyc"))({});
return nyc.instrumenter().instrumentSync(source, this.filename, { registerMap: () => {} });
}
}
const nyc = new (require('nyc'))({});
return nyc
.instrumenter()
.instrumentSync(source, this.filename, { registerMap: () => {} });
},
},
});

View File

@ -1,80 +1,85 @@
const flatted = require("flatted");
const { test } = require("tap");
const LoggingEvent = require("../../lib/LoggingEvent");
const levels = require("../../lib/levels");
const flatted = require('flatted');
const { test } = require('tap');
const LoggingEvent = require('../../lib/LoggingEvent');
const levels = require('../../lib/levels');
test("LoggingEvent", batch => {
batch.test("should serialise to flatted", t => {
const event = new LoggingEvent("cheese", levels.DEBUG, ["log message", parseInt("abc", 10), 1/0, -1/0, undefined], {
user: "bob"
});
test('LoggingEvent', (batch) => {
batch.test('should serialise to flatted', (t) => {
const event = new LoggingEvent(
'cheese',
levels.DEBUG,
['log message', parseInt('abc', 10), 1 / 0, -1 / 0, undefined],
{
user: 'bob',
}
);
// set the event date to a known value
event.startTime = new Date(Date.UTC(2018, 1, 4, 18, 30, 23, 10));
const rehydratedEvent = flatted.parse(event.serialise());
t.equal(rehydratedEvent.startTime, "2018-02-04T18:30:23.010Z");
t.equal(rehydratedEvent.categoryName, "cheese");
t.equal(rehydratedEvent.level.levelStr, "DEBUG");
t.equal(rehydratedEvent.startTime, '2018-02-04T18:30:23.010Z');
t.equal(rehydratedEvent.categoryName, 'cheese');
t.equal(rehydratedEvent.level.levelStr, 'DEBUG');
t.equal(rehydratedEvent.data.length, 5);
t.equal(rehydratedEvent.data[0], "log message");
t.equal(rehydratedEvent.data[1], "NaN");
t.equal(rehydratedEvent.data[2], "Infinity");
t.equal(rehydratedEvent.data[3], "-Infinity");
t.equal(rehydratedEvent.data[4], "undefined");
t.equal(rehydratedEvent.context.user, "bob");
t.equal(rehydratedEvent.data[0], 'log message');
t.equal(rehydratedEvent.data[1], 'NaN');
t.equal(rehydratedEvent.data[2], 'Infinity');
t.equal(rehydratedEvent.data[3], '-Infinity');
t.equal(rehydratedEvent.data[4], 'undefined');
t.equal(rehydratedEvent.context.user, 'bob');
t.end();
});
batch.test("should deserialise from flatted", t => {
batch.test('should deserialise from flatted', (t) => {
const dehydratedEvent = flatted.stringify({
startTime: "2018-02-04T10:25:23.010Z",
categoryName: "biscuits",
startTime: '2018-02-04T10:25:23.010Z',
categoryName: 'biscuits',
level: {
levelStr: "INFO"
levelStr: 'INFO',
},
data: ["some log message", { x: 1 }],
context: { thing: "otherThing" },
pid: "1234",
functionName: "bound",
fileName: "domain.js",
data: ['some log message', { x: 1 }],
context: { thing: 'otherThing' },
pid: '1234',
functionName: 'bound',
fileName: 'domain.js',
lineNumber: 421,
columnNumber: 15,
callStack: "at bound (domain.js:421:15)\n"
callStack: 'at bound (domain.js:421:15)\n',
});
const event = LoggingEvent.deserialise(dehydratedEvent);
t.type(event, LoggingEvent);
t.same(event.startTime, new Date(Date.UTC(2018, 1, 4, 10, 25, 23, 10)));
t.equal(event.categoryName, "biscuits");
t.equal(event.categoryName, 'biscuits');
t.same(event.level, levels.INFO);
t.equal(event.data[0], "some log message");
t.equal(event.data[0], 'some log message');
t.equal(event.data[1].x, 1);
t.equal(event.context.thing, "otherThing");
t.equal(event.pid, "1234");
t.equal(event.functionName, "bound");
t.equal(event.fileName, "domain.js");
t.equal(event.context.thing, 'otherThing');
t.equal(event.pid, '1234');
t.equal(event.functionName, 'bound');
t.equal(event.fileName, 'domain.js');
t.equal(event.lineNumber, 421);
t.equal(event.columnNumber, 15);
t.equal(event.callStack, "at bound (domain.js:421:15)\n");
t.equal(event.callStack, 'at bound (domain.js:421:15)\n');
t.end();
});
batch.test("Should correct construct with/without location info", t => {
batch.test('Should correct construct with/without location info', (t) => {
// console.log([Error('123').stack.split('\n').slice(1).join('\n')])
const callStack =
" at repl:1:14\n at ContextifyScript.Script.runInThisContext (vm.js:50:33)\n at REPLServer.defaultEval (repl.js:240:29)\n at bound (domain.js:301:14)\n at REPLServer.runBound [as eval] (domain.js:314:12)\n at REPLServer.onLine (repl.js:468:10)\n at emitOne (events.js:121:20)\n at REPLServer.emit (events.js:211:7)\n at REPLServer.Interface._onLine (readline.js:280:10)\n at REPLServer.Interface._line (readline.js:629:8)"; // eslint-disable-line max-len
const fileName = "/log4js-node/test/tap/layouts-test.js";
' at repl:1:14\n at ContextifyScript.Script.runInThisContext (vm.js:50:33)\n at REPLServer.defaultEval (repl.js:240:29)\n at bound (domain.js:301:14)\n at REPLServer.runBound [as eval] (domain.js:314:12)\n at REPLServer.onLine (repl.js:468:10)\n at emitOne (events.js:121:20)\n at REPLServer.emit (events.js:211:7)\n at REPLServer.Interface._onLine (readline.js:280:10)\n at REPLServer.Interface._line (readline.js:629:8)'; // eslint-disable-line max-len
const fileName = '/log4js-node/test/tap/layouts-test.js';
const lineNumber = 1;
const columnNumber = 14;
const location = {
fileName,
lineNumber,
columnNumber,
callStack
callStack,
};
const event = new LoggingEvent(
"cheese",
'cheese',
levels.DEBUG,
["log message"],
{ user: "bob" },
['log message'],
{ user: 'bob' },
location
);
t.equal(event.fileName, fileName);
@ -82,8 +87,8 @@ test("LoggingEvent", batch => {
t.equal(event.columnNumber, columnNumber);
t.equal(event.callStack, callStack);
const event2 = new LoggingEvent("cheese", levels.DEBUG, ["log message"], {
user: "bob"
const event2 = new LoggingEvent('cheese', levels.DEBUG, ['log message'], {
user: 'bob',
});
t.equal(event2.fileName, undefined);
t.equal(event2.lineNumber, undefined);

View File

@ -1,111 +1,108 @@
const { test } = require("tap");
const { test } = require('tap');
const categories = {
default: { appenders: ["filtered"], level: "debug" }
}
default: { appenders: ['filtered'], level: 'debug' },
};
let log4js;
let recording;
test("log4js appender dependencies", batch => {
test('log4js appender dependencies', (batch) => {
batch.beforeEach(() => {
log4js = require("../../lib/log4js");
recording = require("../../lib/appenders/recording");
log4js = require('../../lib/log4js');
recording = require('../../lib/appenders/recording');
});
batch.afterEach(() => {
recording.erase();
});
batch.test("in order", t => {
batch.test('in order', (t) => {
const config = {
categories,
appenders: {
recorder: { type: "recording" },
recorder: { type: 'recording' },
filtered: {
type: "logLevelFilter",
appender: "recorder",
level: "ERROR"
}
}
};
t.test('should resolve if defined in dependency order', assert => {
assert.doesNotThrow(() => {
log4js.configure(config);
}, 'this should not trigger an error');
assert.end();
});
const logger = log4js.getLogger("logLevelTest");
logger.debug("this should not trigger an event");
logger.error("this should, though");
const logEvents = recording.replay();
t.test(
"should process log events normally",
assert => {
assert.equal(logEvents.length, 1);
assert.equal(logEvents[0].data[0], "this should, though");
assert.end();
}
);
t.end();
});
batch.test("not in order", t => {
const config = {
categories,
appenders: {
filtered: {
type: "logLevelFilter",
appender: "recorder",
level: "ERROR"
type: 'logLevelFilter',
appender: 'recorder',
level: 'ERROR',
},
recorder: { type: "recording" },
}
},
};
t.test('should resolve if defined out of dependency order', assert => {
t.test('should resolve if defined in dependency order', (assert) => {
assert.doesNotThrow(() => {
log4js.configure(config);
}, 'this should not trigger an error');
assert.end();
});
const logger = log4js.getLogger("logLevelTest");
logger.debug("this should not trigger an event");
logger.error("this should, though");
const logger = log4js.getLogger('logLevelTest');
logger.debug('this should not trigger an event');
logger.error('this should, though');
const logEvents = recording.replay();
t.test(
"should process log events normally",
assert => {
assert.equal(logEvents.length, 1);
assert.equal(logEvents[0].data[0], "this should, though");
assert.end();
}
);
t.test('should process log events normally', (assert) => {
assert.equal(logEvents.length, 1);
assert.equal(logEvents[0].data[0], 'this should, though');
assert.end();
});
t.end();
});
batch.test("with dependency loop", t => {
batch.test('not in order', (t) => {
const config = {
categories,
appenders: {
filtered: {
type: "logLevelFilter",
appender: "filtered2",
level: "ERROR"
type: 'logLevelFilter',
appender: 'recorder',
level: 'ERROR',
},
recorder: { type: 'recording' },
},
};
t.test('should resolve if defined out of dependency order', (assert) => {
assert.doesNotThrow(() => {
log4js.configure(config);
}, 'this should not trigger an error');
assert.end();
});
const logger = log4js.getLogger('logLevelTest');
logger.debug('this should not trigger an event');
logger.error('this should, though');
const logEvents = recording.replay();
t.test('should process log events normally', (assert) => {
assert.equal(logEvents.length, 1);
assert.equal(logEvents[0].data[0], 'this should, though');
assert.end();
});
t.end();
});
batch.test('with dependency loop', (t) => {
const config = {
categories,
appenders: {
filtered: {
type: 'logLevelFilter',
appender: 'filtered2',
level: 'ERROR',
},
filtered2: {
type: "logLevelFilter",
appender: "filtered",
level: "ERROR"
type: 'logLevelFilter',
appender: 'filtered',
level: 'ERROR',
},
recorder: { type: "recording" },
}
recorder: { type: 'recording' },
},
};
t.test('should throw an error if if a dependency loop is found', assert => {
assert.throws(() => {
log4js.configure(config);
}, 'Dependency loop detected for appender filtered.');
assert.end();
});
t.test(
'should throw an error if if a dependency loop is found',
(assert) => {
assert.throws(() => {
log4js.configure(config);
}, 'Dependency loop detected for appender filtered.');
assert.end();
}
);
t.end();
});
batch.end();

View File

@ -1,101 +1,101 @@
const { test } = require("tap");
const log4js = require("../../lib/log4js");
const recording = require("../../lib/appenders/recording");
const { test } = require('tap');
const log4js = require('../../lib/log4js');
const recording = require('../../lib/appenders/recording');
test("log4js categoryFilter", batch => {
test('log4js categoryFilter', (batch) => {
batch.beforeEach(() => {
recording.reset();
});
batch.test("appender should exclude categories", t => {
batch.test('appender should exclude categories', (t) => {
log4js.configure({
appenders: {
recorder: { type: "recording" },
recorder: { type: 'recording' },
filtered: {
type: "categoryFilter",
exclude: "web",
appender: "recorder"
}
type: 'categoryFilter',
exclude: 'web',
appender: 'recorder',
},
},
categories: { default: { appenders: ["filtered"], level: "DEBUG" } }
categories: { default: { appenders: ['filtered'], level: 'DEBUG' } },
});
const webLogger = log4js.getLogger("web");
const appLogger = log4js.getLogger("app");
const webLogger = log4js.getLogger('web');
const appLogger = log4js.getLogger('app');
webLogger.debug("This should not get logged");
appLogger.debug("This should get logged");
webLogger.debug("Hello again");
webLogger.debug('This should not get logged');
appLogger.debug('This should get logged');
webLogger.debug('Hello again');
log4js
.getLogger("db")
.debug("This should be included by the appender anyway");
.getLogger('db')
.debug('This should be included by the appender anyway');
const logEvents = recording.replay();
t.equal(logEvents.length, 2);
t.equal(logEvents[0].data[0], "This should get logged");
t.equal(logEvents[0].data[0], 'This should get logged');
t.equal(
logEvents[1].data[0],
"This should be included by the appender anyway"
'This should be included by the appender anyway'
);
t.end();
});
batch.test("appender should exclude categories", t => {
batch.test('appender should exclude categories', (t) => {
log4js.configure({
appenders: {
recorder: { type: "recording" },
recorder: { type: 'recording' },
filtered: {
type: "categoryFilter",
exclude: ["app", "web"],
appender: "recorder"
}
type: 'categoryFilter',
exclude: ['app', 'web'],
appender: 'recorder',
},
},
categories: { default: { appenders: ["filtered"], level: "DEBUG" } }
categories: { default: { appenders: ['filtered'], level: 'DEBUG' } },
});
const webLogger = log4js.getLogger("web");
const appLogger = log4js.getLogger("app");
const webLogger = log4js.getLogger('web');
const appLogger = log4js.getLogger('app');
webLogger.debug("This should not get logged");
appLogger.debug("This should get logged");
webLogger.debug("Hello again");
webLogger.debug('This should not get logged');
appLogger.debug('This should get logged');
webLogger.debug('Hello again');
log4js
.getLogger("db")
.debug("This should be included by the appender anyway");
.getLogger('db')
.debug('This should be included by the appender anyway');
const logEvents = recording.replay();
t.equal(logEvents.length, 1);
t.equal(
logEvents[0].data[0],
"This should be included by the appender anyway"
'This should be included by the appender anyway'
);
t.end();
});
batch.test("should not really need a category filter any more", t => {
batch.test('should not really need a category filter any more', (t) => {
log4js.configure({
appenders: { recorder: { type: "recording" } },
appenders: { recorder: { type: 'recording' } },
categories: {
default: { appenders: ["recorder"], level: "DEBUG" },
web: { appenders: ["recorder"], level: "OFF" }
}
default: { appenders: ['recorder'], level: 'DEBUG' },
web: { appenders: ['recorder'], level: 'OFF' },
},
});
const appLogger = log4js.getLogger("app");
const webLogger = log4js.getLogger("web");
const appLogger = log4js.getLogger('app');
const webLogger = log4js.getLogger('web');
webLogger.debug("This should not get logged");
appLogger.debug("This should get logged");
webLogger.debug("Hello again");
webLogger.debug('This should not get logged');
appLogger.debug('This should get logged');
webLogger.debug('Hello again');
log4js
.getLogger("db")
.debug("This should be included by the appender anyway");
.getLogger('db')
.debug('This should be included by the appender anyway');
const logEvents = recording.replay();
t.equal(logEvents.length, 2);
t.equal(logEvents[0].data[0], "This should get logged");
t.equal(logEvents[0].data[0], 'This should get logged');
t.equal(
logEvents[1].data[0],
"This should be included by the appender anyway"
'This should be included by the appender anyway'
);
t.end();
});

View File

@ -1,62 +1,62 @@
const { test } = require("tap");
const cluster = require("cluster");
const log4js = require("../../lib/log4js");
const recorder = require("../../lib/appenders/recording");
const { test } = require('tap');
const cluster = require('cluster');
const log4js = require('../../lib/log4js');
const recorder = require('../../lib/appenders/recording');
log4js.configure({
appenders: {
vcr: { type: "recording" }
vcr: { type: 'recording' },
},
categories: { default: { appenders: ["vcr"], level: "debug" } }
categories: { default: { appenders: ['vcr'], level: 'debug' } },
});
if (cluster.isMaster) {
cluster.fork();
const masterLogger = log4js.getLogger("master");
const masterLogger = log4js.getLogger('master');
const masterPid = process.pid;
masterLogger.info("this is master");
masterLogger.info('this is master');
let workerLevel;
cluster.on("message", (worker, message) => {
cluster.on('message', (worker, message) => {
if (worker.type || worker.topic) {
message = worker;
}
if (message.type && message.type === "::testing") {
if (message.type && message.type === '::testing') {
workerLevel = message.level;
}
});
cluster.on("exit", worker => {
cluster.on('exit', (worker) => {
const workerPid = worker.process.pid;
const logEvents = recorder.replay();
test("cluster master", batch => {
batch.test("events should be logged", t => {
test('cluster master', (batch) => {
batch.test('events should be logged', (t) => {
t.equal(logEvents.length, 3);
t.equal(logEvents[0].categoryName, "master");
t.equal(logEvents[0].categoryName, 'master');
t.equal(logEvents[0].pid, masterPid);
t.equal(logEvents[1].categoryName, "worker");
t.equal(logEvents[1].categoryName, 'worker');
t.equal(logEvents[1].pid, workerPid);
// serialising errors with stacks intact
t.type(logEvents[1].data[1], "Error");
t.match(logEvents[1].data[1].stack, "Error: oh dear");
t.type(logEvents[1].data[1], 'Error');
t.match(logEvents[1].data[1].stack, 'Error: oh dear');
// serialising circular references in objects
t.type(logEvents[1].data[2], "object");
t.type(logEvents[1].data[2].me, "object");
t.type(logEvents[1].data[2], 'object');
t.type(logEvents[1].data[2].me, 'object');
// serialising errors with custom properties
t.type(logEvents[1].data[3], "Error");
t.match(logEvents[1].data[3].stack, "Error: wtf");
t.equal(logEvents[1].data[3].alert, "chartreuse");
t.type(logEvents[1].data[3], 'Error');
t.match(logEvents[1].data[3].stack, 'Error: wtf');
t.equal(logEvents[1].data[3].alert, 'chartreuse');
// serialising things that are not errors, but look a bit like them
t.type(logEvents[1].data[4], "object");
t.equal(logEvents[1].data[4].stack, "this is not a stack trace");
t.type(logEvents[1].data[4], 'object');
t.equal(logEvents[1].data[4].stack, 'this is not a stack trace');
t.equal(logEvents[2].categoryName, "log4js");
t.equal(logEvents[2].level.toString(), "ERROR");
t.equal(logEvents[2].data[0], "Unable to parse log:");
t.equal(logEvents[2].categoryName, 'log4js');
t.equal(logEvents[2].level.toString(), 'ERROR');
t.equal(logEvents[2].data[0], 'Unable to parse log:');
t.end();
});
@ -64,37 +64,37 @@ if (cluster.isMaster) {
batch.end();
});
test("cluster worker", batch => {
batch.test("logger should get correct config", t => {
t.equal(workerLevel, "DEBUG");
test('cluster worker', (batch) => {
batch.test('logger should get correct config', (t) => {
t.equal(workerLevel, 'DEBUG');
t.end();
});
batch.end();
});
});
} else {
const workerLogger = log4js.getLogger("worker");
const workerLogger = log4js.getLogger('worker');
// test for serialising circular references
const circle = {};
circle.me = circle;
// test for serialising errors with their own properties
const someError = new Error("wtf");
someError.alert = "chartreuse";
const someError = new Error('wtf');
someError.alert = 'chartreuse';
// test for serialising things that look like errors but aren't.
const notAnError = { stack: "this is not a stack trace" };
const notAnError = { stack: 'this is not a stack trace' };
workerLogger.info(
"this is worker",
new Error("oh dear"),
'this is worker',
new Error('oh dear'),
circle,
someError,
notAnError
);
// can't run the test in the worker, things get weird
process.send({
type: "::testing",
level: workerLogger.level.toString()
type: '::testing',
level: workerLogger.level.toString(),
});
// test sending a badly-formed log message
process.send({ topic: "log4js:message", data: { cheese: "gouda" } });
process.send({ topic: 'log4js:message', data: { cheese: 'gouda' } });
cluster.worker.disconnect();
}

View File

@ -1,326 +1,357 @@
const { test } = require("tap");
const log4js = require("../../lib/log4js");
const categories = require("../../lib/categories");
const { test } = require('tap');
const log4js = require('../../lib/log4js');
const categories = require('../../lib/categories');
test("log4js category inherit all appenders from direct parent", batch => {
batch.test("should inherit appenders from direct parent", t => {
test('log4js category inherit all appenders from direct parent', (batch) => {
batch.test('should inherit appenders from direct parent', (t) => {
const config = {
appenders: {
stdout1: { type: "dummy-appender", label: "stdout1" },
stdout2: { type: "dummy-appender", label: "stdout2" }
stdout1: { type: 'dummy-appender', label: 'stdout1' },
stdout2: { type: 'dummy-appender', label: 'stdout2' },
},
categories: {
default: { appenders: ["stdout1"], level: "ERROR" },
catA: { appenders: ["stdout1", "stdout2"], level: "INFO" },
"catA.catB": { level: "DEBUG" }
}
default: { appenders: ['stdout1'], level: 'ERROR' },
catA: { appenders: ['stdout1', 'stdout2'], level: 'INFO' },
'catA.catB': { level: 'DEBUG' },
},
};
log4js.configure(config);
const childCategoryName = "catA.catB";
const childCategoryName = 'catA.catB';
const childAppenders = categories.appendersForCategory(childCategoryName);
const childLevel = categories.getLevelForCategory(childCategoryName);
t.ok(childAppenders);
t.equal(childAppenders.length, 2, "inherited 2 appenders");
t.ok(childAppenders.some(a => a.label === "stdout1"), "inherited stdout1");
t.ok(childAppenders.some(a => a.label === "stdout2"), "inherited stdout2");
t.equal(childLevel.levelStr, "DEBUG", "child level overrides parent");
t.equal(childAppenders.length, 2, 'inherited 2 appenders');
t.ok(
childAppenders.some((a) => a.label === 'stdout1'),
'inherited stdout1'
);
t.ok(
childAppenders.some((a) => a.label === 'stdout2'),
'inherited stdout2'
);
t.equal(childLevel.levelStr, 'DEBUG', 'child level overrides parent');
t.end();
});
batch.test(
"multiple children should inherit config from shared parent",
t => {
'multiple children should inherit config from shared parent',
(t) => {
const config = {
appenders: {
stdout1: { type: "dummy-appender", label: "stdout1" },
stdout2: { type: "dummy-appender", label: "stdout2" }
stdout1: { type: 'dummy-appender', label: 'stdout1' },
stdout2: { type: 'dummy-appender', label: 'stdout2' },
},
categories: {
default: { appenders: ["stdout1"], level: "ERROR" },
catA: { appenders: ["stdout1"], level: "INFO" },
"catA.catB.cat1": { level: "DEBUG" }, // should get sdtout1, DEBUG
"catA.catB.cat2": { appenders: ["stdout2"] } // should get sdtout1,sdtout2, INFO
}
default: { appenders: ['stdout1'], level: 'ERROR' },
catA: { appenders: ['stdout1'], level: 'INFO' },
'catA.catB.cat1': { level: 'DEBUG' }, // should get sdtout1, DEBUG
'catA.catB.cat2': { appenders: ['stdout2'] }, // should get sdtout1,sdtout2, INFO
},
};
log4js.configure(config);
const child1CategoryName = "catA.catB.cat1";
const child1Appenders = categories.appendersForCategory(
child1CategoryName
);
const child1CategoryName = 'catA.catB.cat1';
const child1Appenders =
categories.appendersForCategory(child1CategoryName);
const child1Level = categories.getLevelForCategory(child1CategoryName);
t.equal(child1Appenders.length, 1, "inherited 1 appender");
t.equal(child1Appenders.length, 1, 'inherited 1 appender');
t.ok(
child1Appenders.some(a => a.label === "stdout1"),
"inherited stdout1"
child1Appenders.some((a) => a.label === 'stdout1'),
'inherited stdout1'
);
t.equal(child1Level.levelStr, "DEBUG", "child level overrides parent");
t.equal(child1Level.levelStr, 'DEBUG', 'child level overrides parent');
const child2CategoryName = "catA.catB.cat2";
const child2Appenders = categories.appendersForCategory(
child2CategoryName
);
const child2CategoryName = 'catA.catB.cat2';
const child2Appenders =
categories.appendersForCategory(child2CategoryName);
const child2Level = categories.getLevelForCategory(child2CategoryName);
t.ok(child2Appenders);
t.equal(
child2Appenders.length,
2,
"inherited 1 appenders, plus its original"
'inherited 1 appenders, plus its original'
);
t.ok(
child2Appenders.some(a => a.label === "stdout1"),
"inherited stdout1"
child2Appenders.some((a) => a.label === 'stdout1'),
'inherited stdout1'
);
t.ok(child2Appenders.some(a => a.label === "stdout2"), "kept stdout2");
t.equal(child2Level.levelStr, "INFO", "inherited parent level");
t.ok(
child2Appenders.some((a) => a.label === 'stdout2'),
'kept stdout2'
);
t.equal(child2Level.levelStr, 'INFO', 'inherited parent level');
t.end();
}
);
batch.test("should inherit appenders from multiple parents", t => {
batch.test('should inherit appenders from multiple parents', (t) => {
const config = {
appenders: {
stdout1: { type: "dummy-appender", label: "stdout1" },
stdout2: { type: "dummy-appender", label: "stdout2" }
stdout1: { type: 'dummy-appender', label: 'stdout1' },
stdout2: { type: 'dummy-appender', label: 'stdout2' },
},
categories: {
default: { appenders: ["stdout1"], level: "ERROR" },
catA: { appenders: ["stdout1"], level: "INFO" },
"catA.catB": { appenders: ["stdout2"], level: "INFO" }, // should get stdout1 and stdout2
"catA.catB.catC": { level: "DEBUG" } // should get stdout1 and stdout2
}
default: { appenders: ['stdout1'], level: 'ERROR' },
catA: { appenders: ['stdout1'], level: 'INFO' },
'catA.catB': { appenders: ['stdout2'], level: 'INFO' }, // should get stdout1 and stdout2
'catA.catB.catC': { level: 'DEBUG' }, // should get stdout1 and stdout2
},
};
log4js.configure(config);
const childCategoryName = "catA.catB.catC";
const childCategoryName = 'catA.catB.catC';
const childAppenders = categories.appendersForCategory(childCategoryName);
t.ok(childAppenders);
t.equal(childAppenders.length, 2, "inherited 2 appenders");
t.ok(childAppenders.some(a => a.label === "stdout1"), "inherited stdout1");
t.ok(childAppenders.some(a => a.label === "stdout1"), "inherited stdout1");
const firstParentName = "catA.catB";
const firstParentAppenders = categories.appendersForCategory(
firstParentName
t.equal(childAppenders.length, 2, 'inherited 2 appenders');
t.ok(
childAppenders.some((a) => a.label === 'stdout1'),
'inherited stdout1'
);
t.ok(
childAppenders.some((a) => a.label === 'stdout1'),
'inherited stdout1'
);
const firstParentName = 'catA.catB';
const firstParentAppenders =
categories.appendersForCategory(firstParentName);
t.ok(firstParentAppenders);
t.equal(firstParentAppenders.length, 2, "ended up with 2 appenders");
t.equal(firstParentAppenders.length, 2, 'ended up with 2 appenders');
t.ok(
firstParentAppenders.some(a => a.label === "stdout1"),
"inherited stdout1"
firstParentAppenders.some((a) => a.label === 'stdout1'),
'inherited stdout1'
);
t.ok(
firstParentAppenders.some((a) => a.label === 'stdout2'),
'kept stdout2'
);
t.ok(firstParentAppenders.some(a => a.label === "stdout2"), "kept stdout2");
t.end();
});
batch.test(
"should inherit appenders from deep parent with missing direct parent",
t => {
'should inherit appenders from deep parent with missing direct parent',
(t) => {
const config = {
appenders: {
stdout1: { type: "dummy-appender", label: "stdout1" },
stdout2: { type: "dummy-appender", label: "stdout2" }
stdout1: { type: 'dummy-appender', label: 'stdout1' },
stdout2: { type: 'dummy-appender', label: 'stdout2' },
},
categories: {
default: { appenders: ["stdout1"], level: "ERROR" },
catA: { appenders: ["stdout1"], level: "INFO" },
default: { appenders: ['stdout1'], level: 'ERROR' },
catA: { appenders: ['stdout1'], level: 'INFO' },
// no catA.catB, but should get created, with stdout1
"catA.catB.catC": { level: "DEBUG" } // should get stdout1
}
'catA.catB.catC': { level: 'DEBUG' }, // should get stdout1
},
};
log4js.configure(config);
const childCategoryName = "catA.catB.catC";
const childCategoryName = 'catA.catB.catC';
const childAppenders = categories.appendersForCategory(childCategoryName);
t.ok(childAppenders);
t.equal(childAppenders.length, 1, "inherited 1 appenders");
t.equal(childAppenders.length, 1, 'inherited 1 appenders');
t.ok(
childAppenders.some(a => a.label === "stdout1"),
"inherited stdout1"
childAppenders.some((a) => a.label === 'stdout1'),
'inherited stdout1'
);
const firstParentCategoryName = "catA.catB";
const firstParentCategoryName = 'catA.catB';
const firstParentAppenders = categories.appendersForCategory(
firstParentCategoryName
);
t.ok(firstParentAppenders, "catA.catB got created implicitily");
t.ok(firstParentAppenders, 'catA.catB got created implicitily');
t.equal(
firstParentAppenders.length,
1,
"created with 1 inherited appender"
'created with 1 inherited appender'
);
t.ok(
firstParentAppenders.some(a => a.label === "stdout1"),
"inherited stdout1"
firstParentAppenders.some((a) => a.label === 'stdout1'),
'inherited stdout1'
);
t.end();
}
);
batch.test("should deal gracefully with missing parent", t => {
batch.test('should deal gracefully with missing parent', (t) => {
const config = {
appenders: {
stdout1: { type: "dummy-appender", label: "stdout1" },
stdout2: { type: "dummy-appender", label: "stdout2" }
stdout1: { type: 'dummy-appender', label: 'stdout1' },
stdout2: { type: 'dummy-appender', label: 'stdout2' },
},
categories: {
default: { appenders: ["stdout1"], level: "ERROR" },
default: { appenders: ['stdout1'], level: 'ERROR' },
// no catA nor catA.catB, but should get created, with default values
"catA.catB.catC": { appenders: ["stdout2"], level: "DEBUG" } // should get stdout2, DEBUG
}
'catA.catB.catC': { appenders: ['stdout2'], level: 'DEBUG' }, // should get stdout2, DEBUG
},
};
log4js.configure(config);
const childCategoryName = "catA.catB.catC";
const childCategoryName = 'catA.catB.catC';
const childAppenders = categories.appendersForCategory(childCategoryName);
t.ok(childAppenders);
t.equal(childAppenders.length, 1);
t.ok(childAppenders.some(a => a.label === "stdout2"));
t.ok(childAppenders.some((a) => a.label === 'stdout2'));
t.end();
});
batch.test(
"should not get duplicate appenders if parent has the same one",
t => {
'should not get duplicate appenders if parent has the same one',
(t) => {
const config = {
appenders: {
stdout1: { type: "dummy-appender", label: "stdout1" },
stdout2: { type: "dummy-appender", label: "stdout2" }
stdout1: { type: 'dummy-appender', label: 'stdout1' },
stdout2: { type: 'dummy-appender', label: 'stdout2' },
},
categories: {
default: { appenders: ["stdout1"], level: "ERROR" },
catA: { appenders: ["stdout1", "stdout2"], level: "INFO" },
"catA.catB": { appenders: ["stdout1"], level: "DEBUG" }
}
default: { appenders: ['stdout1'], level: 'ERROR' },
catA: { appenders: ['stdout1', 'stdout2'], level: 'INFO' },
'catA.catB': { appenders: ['stdout1'], level: 'DEBUG' },
},
};
log4js.configure(config);
const childCategoryName = "catA.catB";
const childCategoryName = 'catA.catB';
const childAppenders = categories.appendersForCategory(childCategoryName);
t.ok(childAppenders);
t.equal(childAppenders.length, 2, "inherited 1 appender");
t.equal(childAppenders.length, 2, 'inherited 1 appender');
t.ok(
childAppenders.some(a => a.label === "stdout1"),
"still have stdout1"
childAppenders.some((a) => a.label === 'stdout1'),
'still have stdout1'
);
t.ok(
childAppenders.some(a => a.label === "stdout2"),
"inherited stdout2"
childAppenders.some((a) => a.label === 'stdout2'),
'inherited stdout2'
);
t.end();
}
);
batch.test("inherit:falses should disable inheritance", t => {
batch.test('inherit:falses should disable inheritance', (t) => {
const config = {
appenders: {
stdout1: { type: "dummy-appender", label: "stdout1" },
stdout2: { type: "dummy-appender", label: "stdout2" }
stdout1: { type: 'dummy-appender', label: 'stdout1' },
stdout2: { type: 'dummy-appender', label: 'stdout2' },
},
categories: {
default: { appenders: ["stdout1"], level: "ERROR" },
catA: { appenders: ["stdout1"], level: "INFO" },
"catA.catB": { appenders: ["stdout2"], level: "INFO", inherit: false } // should not inherit from catA
}
default: { appenders: ['stdout1'], level: 'ERROR' },
catA: { appenders: ['stdout1'], level: 'INFO' },
'catA.catB': { appenders: ['stdout2'], level: 'INFO', inherit: false }, // should not inherit from catA
},
};
log4js.configure(config);
const childCategoryName = "catA.catB";
const childCategoryName = 'catA.catB';
const childAppenders = categories.appendersForCategory(childCategoryName);
t.ok(childAppenders);
t.equal(childAppenders.length, 1, "inherited no appender");
t.ok(childAppenders.some(a => a.label === "stdout2"), "kept stdout2");
t.end();
});
batch.test("inheritance should stop if direct parent has inherit off", t => {
const config = {
appenders: {
stdout1: { type: "dummy-appender", label: "stdout1" },
stdout2: { type: "dummy-appender", label: "stdout2" }
},
categories: {
default: { appenders: ["stdout1"], level: "ERROR" },
catA: { appenders: ["stdout1"], level: "INFO" },
"catA.catB": { appenders: ["stdout2"], level: "INFO", inherit: false }, // should not inherit from catA
"catA.catB.catC": { level: "DEBUG" } // should inherit from catB only
}
};
log4js.configure(config);
const childCategoryName = "catA.catB.catC";
const childAppenders = categories.appendersForCategory(childCategoryName);
t.ok(childAppenders);
t.equal(childAppenders.length, 1, "inherited 1 appender");
t.ok(childAppenders.some(a => a.label === "stdout2"), "inherited stdout2");
const firstParentCategoryName = "catA.catB";
const firstParentAppenders = categories.appendersForCategory(
firstParentCategoryName
t.equal(childAppenders.length, 1, 'inherited no appender');
t.ok(
childAppenders.some((a) => a.label === 'stdout2'),
'kept stdout2'
);
t.ok(firstParentAppenders);
t.equal(firstParentAppenders.length, 1, "did not inherit new appenders");
t.ok(firstParentAppenders.some(a => a.label === "stdout2"), "kept stdout2");
t.end();
});
batch.test("should inherit level when it is missing", t => {
batch.test(
'inheritance should stop if direct parent has inherit off',
(t) => {
const config = {
appenders: {
stdout1: { type: 'dummy-appender', label: 'stdout1' },
stdout2: { type: 'dummy-appender', label: 'stdout2' },
},
categories: {
default: { appenders: ['stdout1'], level: 'ERROR' },
catA: { appenders: ['stdout1'], level: 'INFO' },
'catA.catB': {
appenders: ['stdout2'],
level: 'INFO',
inherit: false,
}, // should not inherit from catA
'catA.catB.catC': { level: 'DEBUG' }, // should inherit from catB only
},
};
log4js.configure(config);
const childCategoryName = 'catA.catB.catC';
const childAppenders = categories.appendersForCategory(childCategoryName);
t.ok(childAppenders);
t.equal(childAppenders.length, 1, 'inherited 1 appender');
t.ok(
childAppenders.some((a) => a.label === 'stdout2'),
'inherited stdout2'
);
const firstParentCategoryName = 'catA.catB';
const firstParentAppenders = categories.appendersForCategory(
firstParentCategoryName
);
t.ok(firstParentAppenders);
t.equal(firstParentAppenders.length, 1, 'did not inherit new appenders');
t.ok(
firstParentAppenders.some((a) => a.label === 'stdout2'),
'kept stdout2'
);
t.end();
}
);
batch.test('should inherit level when it is missing', (t) => {
const config = {
appenders: {
stdout1: { type: "dummy-appender", label: "stdout1" },
stdout2: { type: "dummy-appender", label: "stdout2" }
stdout1: { type: 'dummy-appender', label: 'stdout1' },
stdout2: { type: 'dummy-appender', label: 'stdout2' },
},
categories: {
default: { appenders: ["stdout1"], level: "ERROR" },
catA: { appenders: ["stdout1"], level: "INFO" },
default: { appenders: ['stdout1'], level: 'ERROR' },
catA: { appenders: ['stdout1'], level: 'INFO' },
// no catA.catB, but should get created, with stdout1, level INFO
"catA.catB.catC": {} // should get stdout1, level INFO
}
'catA.catB.catC': {}, // should get stdout1, level INFO
},
};
log4js.configure(config);
const childCategoryName = "catA.catB.catC";
const childCategoryName = 'catA.catB.catC';
const childLevel = categories.getLevelForCategory(childCategoryName);
t.equal(childLevel.levelStr, "INFO", "inherited level");
t.equal(childLevel.levelStr, 'INFO', 'inherited level');
const firstParentCategoryName = "catA.catB";
const firstParentCategoryName = 'catA.catB';
const firstParentLevel = categories.getLevelForCategory(
firstParentCategoryName
);
t.equal(
firstParentLevel.levelStr,
"INFO",
"generate parent inherited level from base"
'INFO',
'generate parent inherited level from base'
);
t.end();

View File

@ -1,15 +1,15 @@
const { test } = require("tap");
const sandbox = require("@log4js-node/sandboxed-module");
const realFS = require("fs");
const { test } = require('tap');
const sandbox = require('@log4js-node/sandboxed-module');
const realFS = require('fs');
const modulePath = "some/path/to/mylog4js.json";
const modulePath = 'some/path/to/mylog4js.json';
const pathsChecked = [];
let fakeFS = {};
let dependencies;
let fileRead;
test("log4js configure", batch => {
test('log4js configure', (batch) => {
batch.beforeEach(() => {
fileRead = 0;
@ -22,50 +22,50 @@ test("log4js configure", batch => {
config: {
appenders: {
console: {
type: "console",
layout: { type: "messagePassThrough" }
}
type: 'console',
layout: { type: 'messagePassThrough' },
},
},
categories: {
default: {
appenders: ["console"],
level: "INFO"
}
}
appenders: ['console'],
level: 'INFO',
},
},
},
readdirSync: dir => require("fs").readdirSync(dir),
readdirSync: (dir) => require('fs').readdirSync(dir),
readFileSync: (file, encoding) => {
fileRead += 1;
batch.type(file, "string");
batch.type(file, 'string');
batch.equal(file, modulePath);
batch.equal(encoding, "utf8");
batch.equal(encoding, 'utf8');
return JSON.stringify(fakeFS.config);
},
statSync: path => {
statSync: (path) => {
pathsChecked.push(path);
if (path === modulePath) {
return { mtime: new Date() };
}
throw new Error("no such file");
}
throw new Error('no such file');
},
};
dependencies = {
requires: {
fs: fakeFS
}
fs: fakeFS,
},
};
});
batch.test(
"when configuration file loaded via LOG4JS_CONFIG env variable",
t => {
process.env.LOG4JS_CONFIG = "some/path/to/mylog4js.json";
'when configuration file loaded via LOG4JS_CONFIG env variable',
(t) => {
process.env.LOG4JS_CONFIG = 'some/path/to/mylog4js.json';
const log4js = sandbox.require("../../lib/log4js", dependencies);
const log4js = sandbox.require('../../lib/log4js', dependencies);
log4js.getLogger("test-logger");
t.equal(fileRead, 1, "should load the specified local config file");
log4js.getLogger('test-logger');
t.equal(fileRead, 1, 'should load the specified local config file');
delete process.env.LOG4JS_CONFIG;
@ -74,24 +74,24 @@ test("log4js configure", batch => {
);
batch.test(
"when configuration is set via configure() method call, return the log4js object",
t => {
'when configuration is set via configure() method call, return the log4js object',
(t) => {
const log4js = sandbox
.require("../../lib/log4js", dependencies)
.require('../../lib/log4js', dependencies)
.configure(fakeFS.config);
t.type(
log4js,
"object",
"Configure method call should return the log4js object!"
'object',
'Configure method call should return the log4js object!'
);
const log = log4js.getLogger("daemon");
const log = log4js.getLogger('daemon');
t.type(
log,
"object",
"log4js object, returned by configure(...) method should be able to create log object."
'object',
'log4js object, returned by configure(...) method should be able to create log object.'
);
t.type(log.info, "function");
t.type(log.info, 'function');
t.end();
}

View File

@ -1,17 +1,16 @@
const { test } = require("tap");
const util = require("util");
const path = require("path");
const sandbox = require("@log4js-node/sandboxed-module");
const debug = require("debug")("log4js:test.configuration-validation");
const deepFreeze = require("deep-freeze");
const fs = require("fs");
const log4js = require("../../lib/log4js");
const configuration = require("../../lib/configuration");
const { test } = require('tap');
const util = require('util');
const path = require('path');
const sandbox = require('@log4js-node/sandboxed-module');
const debug = require('debug')('log4js:test.configuration-validation');
const deepFreeze = require('deep-freeze');
const fs = require('fs');
const log4js = require('../../lib/log4js');
const configuration = require('../../lib/configuration');
const removeFiles = async filenames => {
if (!Array.isArray(filenames))
filenames = [filenames];
const promises = filenames.map(filename => fs.promises.unlink(filename));
const removeFiles = async (filenames) => {
if (!Array.isArray(filenames)) filenames = [filenames];
const promises = filenames.map((filename) => fs.promises.unlink(filename));
await Promise.allSettled(promises);
};
@ -29,12 +28,12 @@ const testAppender = (label, result) => ({
result.layouts = layouts;
result.findAppender = findAppender;
return {};
}
},
});
test("log4js configuration validation", batch => {
batch.test("should give error if config is just plain silly", t => {
[null, undefined, "", " ", []].forEach(config => {
test('log4js configuration validation', (batch) => {
batch.test('should give error if config is just plain silly', (t) => {
[null, undefined, '', ' ', []].forEach((config) => {
const expectedError = new Error(
`Problem with log4js configuration: (${util.inspect(
config
@ -46,7 +45,7 @@ test("log4js configuration validation", batch => {
t.end();
});
batch.test("should give error if config is an empty object", t => {
batch.test('should give error if config is an empty object', (t) => {
t.throws(
() => log4js.configure({}),
'- must have a property "appenders" of type object.'
@ -54,7 +53,7 @@ test("log4js configuration validation", batch => {
t.end();
});
batch.test("should give error if config has no appenders", t => {
batch.test('should give error if config has no appenders', (t) => {
t.throws(
() => log4js.configure({ categories: {} }),
'- must have a property "appenders" of type object.'
@ -62,15 +61,15 @@ test("log4js configuration validation", batch => {
t.end();
});
batch.test("should give error if config has no categories", t => {
batch.test('should give error if config has no categories', (t) => {
t.throws(
() => log4js.configure({ appenders: { out: { type: "stdout" } } }),
() => log4js.configure({ appenders: { out: { type: 'stdout' } } }),
'- must have a property "categories" of type object.'
);
t.end();
});
batch.test("should give error if appenders is not an object", t => {
batch.test('should give error if appenders is not an object', (t) => {
t.throws(
() => log4js.configure({ appenders: [], categories: [] }),
'- must have a property "appenders" of type object.'
@ -78,77 +77,77 @@ test("log4js configuration validation", batch => {
t.end();
});
batch.test("should give error if appenders are not all valid", t => {
batch.test('should give error if appenders are not all valid', (t) => {
t.throws(
() =>
log4js.configure({ appenders: { thing: "cheese" }, categories: {} }),
log4js.configure({ appenders: { thing: 'cheese' }, categories: {} }),
'- appender "thing" is not valid (must be an object with property "type")'
);
t.end();
});
batch.test("should require at least one appender", t => {
batch.test('should require at least one appender', (t) => {
t.throws(
() => log4js.configure({ appenders: {}, categories: {} }),
"- must define at least one appender."
'- must define at least one appender.'
);
t.end();
});
batch.test("should give error if categories are not all valid", t => {
batch.test('should give error if categories are not all valid', (t) => {
t.throws(
() =>
log4js.configure({
appenders: { stdout: { type: "stdout" } },
categories: { thing: "cheese" }
appenders: { stdout: { type: 'stdout' } },
categories: { thing: 'cheese' },
}),
'- category "thing" is not valid (must be an object with properties "appenders" and "level")'
);
t.end();
});
batch.test("should give error if default category not defined", t => {
batch.test('should give error if default category not defined', (t) => {
t.throws(
() =>
log4js.configure({
appenders: { stdout: { type: "stdout" } },
categories: { thing: { appenders: ["stdout"], level: "ERROR" } }
appenders: { stdout: { type: 'stdout' } },
categories: { thing: { appenders: ['stdout'], level: 'ERROR' } },
}),
'- must define a "default" category.'
);
t.end();
});
batch.test("should require at least one category", t => {
batch.test('should require at least one category', (t) => {
t.throws(
() =>
log4js.configure({
appenders: { stdout: { type: "stdout" } },
categories: {}
appenders: { stdout: { type: 'stdout' } },
categories: {},
}),
"- must define at least one category."
'- must define at least one category.'
);
t.end();
});
batch.test("should give error if category.appenders is not an array", t => {
batch.test('should give error if category.appenders is not an array', (t) => {
t.throws(
() =>
log4js.configure({
appenders: { stdout: { type: "stdout" } },
categories: { thing: { appenders: {}, level: "ERROR" } }
appenders: { stdout: { type: 'stdout' } },
categories: { thing: { appenders: {}, level: 'ERROR' } },
}),
'- category "thing" is not valid (appenders must be an array of appender names)'
);
t.end();
});
batch.test("should give error if category.appenders is empty", t => {
batch.test('should give error if category.appenders is empty', (t) => {
t.throws(
() =>
log4js.configure({
appenders: { stdout: { type: "stdout" } },
categories: { thing: { appenders: [], level: "ERROR" } }
appenders: { stdout: { type: 'stdout' } },
categories: { thing: { appenders: [], level: 'ERROR' } },
}),
'- category "thing" is not valid (appenders must contain at least one appender name)'
);
@ -156,13 +155,13 @@ test("log4js configuration validation", batch => {
});
batch.test(
"should give error if categories do not refer to valid appenders",
t => {
'should give error if categories do not refer to valid appenders',
(t) => {
t.throws(
() =>
log4js.configure({
appenders: { stdout: { type: "stdout" } },
categories: { thing: { appenders: ["cheese"], level: "ERROR" } }
appenders: { stdout: { type: 'stdout' } },
categories: { thing: { appenders: ['cheese'], level: 'ERROR' } },
}),
'- category "thing" is not valid (appender "cheese" is not defined)'
);
@ -170,12 +169,12 @@ test("log4js configuration validation", batch => {
}
);
batch.test("should give error if category level is not valid", t => {
batch.test('should give error if category level is not valid', (t) => {
t.throws(
() =>
log4js.configure({
appenders: { stdout: { type: "stdout" } },
categories: { default: { appenders: ["stdout"], level: "Biscuits" } }
appenders: { stdout: { type: 'stdout' } },
categories: { default: { appenders: ['stdout'], level: 'Biscuits' } },
}),
'- category "default" is not valid (level "Biscuits" not recognised; valid levels are ALL, TRACE'
);
@ -183,19 +182,19 @@ test("log4js configuration validation", batch => {
});
batch.test(
"should give error if category enableCallStack is not valid",
t => {
'should give error if category enableCallStack is not valid',
(t) => {
t.throws(
() =>
log4js.configure({
appenders: { stdout: { type: "stdout" } },
appenders: { stdout: { type: 'stdout' } },
categories: {
default: {
appenders: ["stdout"],
level: "Debug",
enableCallStack: "123"
}
}
appenders: ['stdout'],
level: 'Debug',
enableCallStack: '123',
},
},
}),
'- category "default" is not valid (enableCallStack must be boolean type)'
);
@ -203,49 +202,49 @@ test("log4js configuration validation", batch => {
}
);
batch.test("should give error if appender type cannot be found", t => {
batch.test('should give error if appender type cannot be found', (t) => {
t.throws(
() =>
log4js.configure({
appenders: { thing: { type: "cheese" } },
categories: { default: { appenders: ["thing"], level: "ERROR" } }
appenders: { thing: { type: 'cheese' } },
categories: { default: { appenders: ['thing'], level: 'ERROR' } },
}),
'- appender "thing" is not valid (type "cheese" could not be found)'
);
t.end();
});
batch.test("should create appender instances", t => {
batch.test('should create appender instances', (t) => {
const thing = {};
const sandboxedLog4js = sandbox.require("../../lib/log4js", {
const sandboxedLog4js = sandbox.require('../../lib/log4js', {
requires: {
cheese: testAppender("cheesy", thing)
cheese: testAppender('cheesy', thing),
},
ignoreMissing: true
ignoreMissing: true,
});
sandboxedLog4js.configure({
appenders: { thing: { type: "cheese" } },
categories: { default: { appenders: ["thing"], level: "ERROR" } }
appenders: { thing: { type: 'cheese' } },
categories: { default: { appenders: ['thing'], level: 'ERROR' } },
});
t.ok(thing.configureCalled);
t.equal(thing.type, "cheese");
t.equal(thing.type, 'cheese');
t.end();
});
batch.test(
"should use provided appender instance if instance provided",
t => {
'should use provided appender instance if instance provided',
(t) => {
const thing = {};
const cheese = testAppender("cheesy", thing);
const sandboxedLog4js = sandbox.require("../../lib/log4js", {
ignoreMissing: true
const cheese = testAppender('cheesy', thing);
const sandboxedLog4js = sandbox.require('../../lib/log4js', {
ignoreMissing: true,
});
sandboxedLog4js.configure({
appenders: { thing: { type: cheese } },
categories: { default: { appenders: ["thing"], level: "ERROR" } }
categories: { default: { appenders: ['thing'], level: 'ERROR' } },
});
t.ok(thing.configureCalled);
@ -254,8 +253,8 @@ test("log4js configuration validation", batch => {
}
);
batch.test("should not throw error if configure object is freezed", t => {
const testFile = "test/tap/freeze-date-file-test";
batch.test('should not throw error if configure object is freezed', (t) => {
const testFile = 'test/tap/freeze-date-file-test';
t.teardown(async () => {
await removeFiles(testFile);
});
@ -264,188 +263,193 @@ test("log4js configuration validation", batch => {
deepFreeze({
appenders: {
dateFile: {
type: "dateFile",
type: 'dateFile',
filename: testFile,
alwaysIncludePattern: false
}
alwaysIncludePattern: false,
},
},
categories: {
default: { appenders: ["dateFile"], level: log4js.levels.ERROR }
}
default: { appenders: ['dateFile'], level: log4js.levels.ERROR },
},
})
)
);
t.end();
});
batch.test("should load appenders from core first", t => {
batch.test('should load appenders from core first', (t) => {
const result = {};
const sandboxedLog4js = sandbox.require("../../lib/log4js", {
const sandboxedLog4js = sandbox.require('../../lib/log4js', {
requires: {
"./cheese": testAppender("correct", result),
cheese: testAppender("wrong", result)
'./cheese': testAppender('correct', result),
cheese: testAppender('wrong', result),
},
ignoreMissing: true
ignoreMissing: true,
});
sandboxedLog4js.configure({
appenders: { thing: { type: "cheese" } },
categories: { default: { appenders: ["thing"], level: "ERROR" } }
appenders: { thing: { type: 'cheese' } },
categories: { default: { appenders: ['thing'], level: 'ERROR' } },
});
t.ok(result.configureCalled);
t.equal(result.type, "cheese");
t.equal(result.label, "correct");
t.equal(result.type, 'cheese');
t.equal(result.label, 'correct');
t.end();
});
batch.test(
"should load appenders relative to main file if not in core, or node_modules",
t => {
'should load appenders relative to main file if not in core, or node_modules',
(t) => {
const result = {};
const mainPath = path.dirname(require.main.filename);
const sandboxConfig = {
ignoreMissing: true,
requires: {}
requires: {},
};
sandboxConfig.requires[`${mainPath}/cheese`] = testAppender(
"correct",
'correct',
result
);
// add this one, because when we're running coverage the main path is a bit different
sandboxConfig.requires[
`${path.join(mainPath, "../../node_modules/nyc/bin/cheese")}`
] = testAppender("correct", result);
`${path.join(mainPath, '../../node_modules/nyc/bin/cheese')}`
] = testAppender('correct', result);
// in tap v15, the main path is at root of log4js (run `DEBUG=log4js:appenders npm test > /dev/null` to check)
sandboxConfig.requires[
`${path.join(mainPath, "../../cheese")}`
] = testAppender("correct", result);
sandboxConfig.requires[`${path.join(mainPath, '../../cheese')}`] =
testAppender('correct', result);
// in node v6, there's an extra layer of node modules for some reason, so add this one to work around it
sandboxConfig.requires[
`${path.join(
mainPath,
"../../node_modules/tap/node_modules/nyc/bin/cheese"
'../../node_modules/tap/node_modules/nyc/bin/cheese'
)}`
] = testAppender("correct", result);
] = testAppender('correct', result);
const sandboxedLog4js = sandbox.require(
"../../lib/log4js",
'../../lib/log4js',
sandboxConfig
);
sandboxedLog4js.configure({
appenders: { thing: { type: "cheese" } },
categories: { default: { appenders: ["thing"], level: "ERROR" } }
appenders: { thing: { type: 'cheese' } },
categories: { default: { appenders: ['thing'], level: 'ERROR' } },
});
t.ok(result.configureCalled);
t.equal(result.type, "cheese");
t.equal(result.label, "correct");
t.equal(result.type, 'cheese');
t.equal(result.label, 'correct');
t.end();
}
);
batch.test(
"should load appenders relative to process.cwd if not found in core, node_modules",
t => {
'should load appenders relative to process.cwd if not found in core, node_modules',
(t) => {
const result = {};
const fakeProcess = new Proxy(process, {
get(target, key) {
if (key === "cwd") {
return () => "/var/lib/cheese";
if (key === 'cwd') {
return () => '/var/lib/cheese';
}
return target[key];
}
},
});
// windows file paths are different to unix, so let's make this work for both.
const requires = {};
requires[path.join("/var", "lib", "cheese", "cheese")] = testAppender("correct", result);
requires[path.join('/var', 'lib', 'cheese', 'cheese')] = testAppender(
'correct',
result
);
const sandboxedLog4js = sandbox.require("../../lib/log4js", {
const sandboxedLog4js = sandbox.require('../../lib/log4js', {
ignoreMissing: true,
requires,
globals: {
process: fakeProcess
}
process: fakeProcess,
},
});
sandboxedLog4js.configure({
appenders: { thing: { type: "cheese" } },
categories: { default: { appenders: ["thing"], level: "ERROR" } }
appenders: { thing: { type: 'cheese' } },
categories: { default: { appenders: ['thing'], level: 'ERROR' } },
});
t.ok(result.configureCalled);
t.equal(result.type, "cheese");
t.equal(result.label, "correct");
t.equal(result.type, 'cheese');
t.equal(result.label, 'correct');
t.end();
}
);
batch.test("should pass config, layout, findAppender to appenders", t => {
batch.test('should pass config, layout, findAppender to appenders', (t) => {
const result = {};
const sandboxedLog4js = sandbox.require("../../lib/log4js", {
const sandboxedLog4js = sandbox.require('../../lib/log4js', {
ignoreMissing: true,
requires: {
cheese: testAppender("cheesy", result),
notCheese: testAppender("notCheesy", {})
}
cheese: testAppender('cheesy', result),
notCheese: testAppender('notCheesy', {}),
},
});
sandboxedLog4js.configure({
appenders: {
thing: { type: "cheese", foo: "bar" },
thing2: { type: "notCheese" }
thing: { type: 'cheese', foo: 'bar' },
thing2: { type: 'notCheese' },
},
categories: { default: { appenders: ["thing"], level: "ERROR" } }
categories: { default: { appenders: ['thing'], level: 'ERROR' } },
});
t.ok(result.configureCalled);
t.equal(result.type, "cheese");
t.equal(result.config.foo, "bar");
t.type(result.layouts, "object");
t.type(result.layouts.basicLayout, "function");
t.type(result.findAppender, "function");
t.type(result.findAppender("thing2"), "object");
t.equal(result.type, 'cheese');
t.equal(result.config.foo, 'bar');
t.type(result.layouts, 'object');
t.type(result.layouts.basicLayout, 'function');
t.type(result.findAppender, 'function');
t.type(result.findAppender('thing2'), 'object');
t.end();
});
batch.test(
"should not give error if level object is used instead of string",
t => {
'should not give error if level object is used instead of string',
(t) => {
t.doesNotThrow(() =>
log4js.configure({
appenders: { thing: { type: "stdout" } },
appenders: { thing: { type: 'stdout' } },
categories: {
default: { appenders: ["thing"], level: log4js.levels.ERROR }
}
default: { appenders: ['thing'], level: log4js.levels.ERROR },
},
})
);
t.end();
}
);
batch.test("should not create appender instance if not used in categories", t => {
const used = {};
const notUsed = {};
const sandboxedLog4js = sandbox.require("../../lib/log4js", {
requires: {
cat: testAppender("meow", used),
dog: testAppender("woof", notUsed)
},
ignoreMissing: true
});
batch.test(
'should not create appender instance if not used in categories',
(t) => {
const used = {};
const notUsed = {};
const sandboxedLog4js = sandbox.require('../../lib/log4js', {
requires: {
cat: testAppender('meow', used),
dog: testAppender('woof', notUsed),
},
ignoreMissing: true,
});
sandboxedLog4js.configure({
appenders: { used: { type: "cat" }, notUsed: { type: "dog" } },
categories: { default: { appenders: ["used"], level: "ERROR" } }
});
sandboxedLog4js.configure({
appenders: { used: { type: 'cat' }, notUsed: { type: 'dog' } },
categories: { default: { appenders: ['used'], level: 'ERROR' } },
});
t.ok(used.configureCalled);
t.notOk(notUsed.configureCalled);
t.end();
});
t.ok(used.configureCalled);
t.notOk(notUsed.configureCalled);
t.end();
}
);
batch.end();
});

View File

@ -1,8 +1,8 @@
/* eslint max-classes-per-file: ["error", 2] */
const { test } = require("tap");
const EE = require("events").EventEmitter;
const levels = require("../../lib/levels");
const { test } = require('tap');
const EE = require('events').EventEmitter;
const levels = require('../../lib/levels');
class MockLogger {
constructor() {
@ -32,8 +32,8 @@ function MockRequest(remoteAddr, method, originalUrl) {
this.socket = { remoteAddress: remoteAddr };
this.originalUrl = originalUrl;
this.method = method;
this.httpVersionMajor = "5";
this.httpVersionMinor = "0";
this.httpVersionMajor = '5';
this.httpVersionMinor = '0';
this.headers = {};
}
@ -45,7 +45,7 @@ class MockResponse extends EE {
}
end() {
this.emit("finish");
this.emit('finish');
}
setHeader(key, value) {
@ -61,10 +61,10 @@ class MockResponse extends EE {
}
}
test("log4js connect logger", batch => {
const clm = require("../../lib/connect-logger");
test('log4js connect logger', (batch) => {
const clm = require('../../lib/connect-logger');
batch.test("with context config", t => {
batch.test('with context config', (t) => {
const ml = new MockLogger();
const cl = clm(ml, { context: true });
@ -72,18 +72,18 @@ test("log4js connect logger", batch => {
ml.contexts = [];
});
t.test("response should be included in context", assert => {
t.test('response should be included in context', (assert) => {
const { contexts } = ml;
const req = new MockRequest(
"my.remote.addr",
"GET",
"http://url/hoge.png"
'my.remote.addr',
'GET',
'http://url/hoge.png'
); // not gif
const res = new MockResponse(200);
cl(req, res, () => {});
res.end("chunk", "encoding");
res.end('chunk', 'encoding');
assert.type(contexts, "Array");
assert.type(contexts, 'Array');
assert.equal(contexts.length, 1);
assert.type(contexts[0].res, MockResponse);
assert.end();
@ -92,7 +92,7 @@ test("log4js connect logger", batch => {
t.end();
});
batch.test("without context config", t => {
batch.test('without context config', (t) => {
const ml = new MockLogger();
const cl = clm(ml, {});
@ -100,18 +100,18 @@ test("log4js connect logger", batch => {
ml.contexts = [];
});
t.test("response should not be included in context", assert => {
t.test('response should not be included in context', (assert) => {
const { contexts } = ml;
const req = new MockRequest(
"my.remote.addr",
"GET",
"http://url/hoge.png"
'my.remote.addr',
'GET',
'http://url/hoge.png'
); // not gif
const res = new MockResponse(200);
cl(req, res, () => {});
res.end("chunk", "encoding");
res.end('chunk', 'encoding');
assert.type(contexts, "Array");
assert.type(contexts, 'Array');
assert.equal(contexts.length, 1);
assert.type(contexts[0].res, undefined);
assert.end();

View File

@ -1,17 +1,17 @@
/* eslint max-classes-per-file: ["error", 2] */
const { test } = require("tap");
const EE = require("events").EventEmitter;
const levels = require("../../lib/levels");
const { test } = require('tap');
const EE = require('events').EventEmitter;
const levels = require('../../lib/levels');
class MockLogger {
constructor() {
this.level = levels.TRACE;
this.messages = [];
this.log = function(level, message) {
this.log = function (level, message) {
this.messages.push({ level, message });
};
this.isLevelEnabled = function(level) {
this.isLevelEnabled = function (level) {
return level.isGreaterThanOrEqualTo(this.level);
};
}
@ -22,8 +22,8 @@ function MockRequest(remoteAddr, method, originalUrl, headers, url, custom) {
this.originalUrl = originalUrl;
this.url = url;
this.method = method;
this.httpVersionMajor = "5";
this.httpVersionMinor = "0";
this.httpVersionMajor = '5';
this.httpVersionMinor = '0';
this.headers = headers || {};
if (custom) {
@ -33,7 +33,7 @@ function MockRequest(remoteAddr, method, originalUrl, headers, url, custom) {
}
const self = this;
Object.keys(this.headers).forEach(key => {
Object.keys(this.headers).forEach((key) => {
self.headers[key.toLowerCase()] = self.headers[key];
});
}
@ -45,7 +45,7 @@ class MockResponse extends EE {
}
end() {
this.emit("finish");
this.emit('finish');
}
setHeader(key, value) {
@ -73,7 +73,7 @@ function request(
custom = undefined
) {
const req = new MockRequest(
"my.remote.addr",
'my.remote.addr',
method,
originalUrl,
reqHeaders,
@ -88,302 +88,302 @@ function request(
}
cl(req, res, next);
res.writeHead(code, resHeaders);
res.end("chunk", "encoding");
res.end('chunk', 'encoding');
}
test("log4js connect logger", batch => {
const clm = require("../../lib/connect-logger");
batch.test("getConnectLoggerModule", t => {
t.type(clm, "function", "should return a connect logger factory");
test('log4js connect logger', (batch) => {
const clm = require('../../lib/connect-logger');
batch.test('getConnectLoggerModule', (t) => {
t.type(clm, 'function', 'should return a connect logger factory');
t.test(
'should take a log4js logger and return a "connect logger"',
assert => {
(assert) => {
const ml = new MockLogger();
const cl = clm(ml);
assert.type(cl, "function");
assert.type(cl, 'function');
assert.end();
}
);
t.test("log events", assert => {
t.test('log events', (assert) => {
const ml = new MockLogger();
const cl = clm(ml);
request(cl, "GET", "http://url", 200);
request(cl, 'GET', 'http://url', 200);
const { messages } = ml;
assert.type(messages, "Array");
assert.type(messages, 'Array');
assert.equal(messages.length, 1);
assert.ok(levels.INFO.isEqualTo(messages[0].level));
assert.match(messages[0].message, "GET");
assert.match(messages[0].message, "http://url");
assert.match(messages[0].message, "my.remote.addr");
assert.match(messages[0].message, "200");
assert.match(messages[0].message, 'GET');
assert.match(messages[0].message, 'http://url');
assert.match(messages[0].message, 'my.remote.addr');
assert.match(messages[0].message, '200');
assert.end();
});
t.test("log events with level below logging level", assert => {
t.test('log events with level below logging level', (assert) => {
const ml = new MockLogger();
ml.level = levels.FATAL;
const cl = clm(ml);
request(cl, "GET", "http://url", 200);
request(cl, 'GET', 'http://url', 200);
assert.type(ml.messages, "Array");
assert.type(ml.messages, 'Array');
assert.equal(ml.messages.length, 0);
assert.end();
});
t.test("log events with non-default level and custom format", assert => {
t.test('log events with non-default level and custom format', (assert) => {
const ml = new MockLogger();
ml.level = levels.INFO;
const cl = clm(ml, { level: levels.WARN, format: ":method :url" });
request(cl, "GET", "http://url", 200);
const cl = clm(ml, { level: levels.WARN, format: ':method :url' });
request(cl, 'GET', 'http://url', 200);
const { messages } = ml;
assert.type(messages, Array);
assert.equal(messages.length, 1);
assert.ok(levels.WARN.isEqualTo(messages[0].level));
assert.equal(messages[0].message, "GET http://url");
assert.equal(messages[0].message, 'GET http://url');
assert.end();
});
t.test("adding multiple loggers should only log once", assert => {
t.test('adding multiple loggers should only log once', (assert) => {
const ml = new MockLogger();
ml.level = levels.INFO;
const cl = clm(ml, { level: levels.WARN, format: ":method :url" });
const nextLogger = clm(ml, { level: levels.INFO, format: ":method" });
request(cl, "GET", "http://url", 200, null, null, nextLogger);
const cl = clm(ml, { level: levels.WARN, format: ':method :url' });
const nextLogger = clm(ml, { level: levels.INFO, format: ':method' });
request(cl, 'GET', 'http://url', 200, null, null, nextLogger);
const { messages } = ml;
assert.type(messages, Array);
assert.equal(messages.length, 1);
assert.ok(levels.WARN.isEqualTo(messages[0].level));
assert.equal(messages[0].message, "GET http://url");
assert.equal(messages[0].message, 'GET http://url');
assert.end();
});
t.end();
});
batch.test("logger with options as string", t => {
batch.test('logger with options as string', (t) => {
const ml = new MockLogger();
ml.level = levels.INFO;
const cl = clm(ml, ":method :url");
request(cl, "POST", "http://meh", 200);
const cl = clm(ml, ':method :url');
request(cl, 'POST', 'http://meh', 200);
const { messages } = ml;
t.equal(messages[0].message, "POST http://meh");
t.equal(messages[0].message, 'POST http://meh');
t.end();
});
batch.test("auto log levels", t => {
batch.test('auto log levels', (t) => {
const ml = new MockLogger();
ml.level = levels.INFO;
const cl = clm(ml, { level: "auto", format: ":method :url" });
request(cl, "GET", "http://meh", 200);
request(cl, "GET", "http://meh", 201);
request(cl, "GET", "http://meh", 302);
request(cl, "GET", "http://meh", 404);
request(cl, "GET", "http://meh", 500);
const cl = clm(ml, { level: 'auto', format: ':method :url' });
request(cl, 'GET', 'http://meh', 200);
request(cl, 'GET', 'http://meh', 201);
request(cl, 'GET', 'http://meh', 302);
request(cl, 'GET', 'http://meh', 404);
request(cl, 'GET', 'http://meh', 500);
const { messages } = ml;
t.test("should use INFO for 2xx", assert => {
t.test('should use INFO for 2xx', (assert) => {
assert.ok(levels.INFO.isEqualTo(messages[0].level));
assert.ok(levels.INFO.isEqualTo(messages[1].level));
assert.end();
});
t.test("should use WARN for 3xx", assert => {
t.test('should use WARN for 3xx', (assert) => {
assert.ok(levels.WARN.isEqualTo(messages[2].level));
assert.end();
});
t.test("should use ERROR for 4xx", assert => {
t.test('should use ERROR for 4xx', (assert) => {
assert.ok(levels.ERROR.isEqualTo(messages[3].level));
assert.end();
});
t.test("should use ERROR for 5xx", assert => {
t.test('should use ERROR for 5xx', (assert) => {
assert.ok(levels.ERROR.isEqualTo(messages[4].level));
assert.end();
});
t.end();
});
batch.test("logger with status code rules applied", t => {
batch.test('logger with status code rules applied', (t) => {
const ml = new MockLogger();
ml.level = levels.DEBUG;
const clr = [
{ codes: [201, 304], level: levels.DEBUG.toString() },
{ from: 200, to: 299, level: levels.DEBUG.toString() },
{ from: 300, to: 399, level: levels.INFO.toString() }
{ from: 300, to: 399, level: levels.INFO.toString() },
];
const cl = clm(ml, {
level: "auto",
format: ":method :url",
statusRules: clr
level: 'auto',
format: ':method :url',
statusRules: clr,
});
request(cl, "GET", "http://meh", 200);
request(cl, "GET", "http://meh", 201);
request(cl, "GET", "http://meh", 302);
request(cl, "GET", "http://meh", 304);
request(cl, "GET", "http://meh", 404);
request(cl, "GET", "http://meh", 500);
request(cl, 'GET', 'http://meh', 200);
request(cl, 'GET', 'http://meh', 201);
request(cl, 'GET', 'http://meh', 302);
request(cl, 'GET', 'http://meh', 304);
request(cl, 'GET', 'http://meh', 404);
request(cl, 'GET', 'http://meh', 500);
const { messages } = ml;
t.test("should use DEBUG for 2xx", assert => {
t.test('should use DEBUG for 2xx', (assert) => {
assert.ok(levels.DEBUG.isEqualTo(messages[0].level));
assert.ok(levels.DEBUG.isEqualTo(messages[1].level));
assert.end();
});
t.test("should use WARN for 3xx, DEBUG for 304", assert => {
t.test('should use WARN for 3xx, DEBUG for 304', (assert) => {
assert.ok(levels.INFO.isEqualTo(messages[2].level));
assert.ok(levels.DEBUG.isEqualTo(messages[3].level));
assert.end();
});
t.test("should use ERROR for 4xx", assert => {
t.test('should use ERROR for 4xx', (assert) => {
assert.ok(levels.ERROR.isEqualTo(messages[4].level));
assert.end();
});
t.test("should use ERROR for 5xx", assert => {
t.test('should use ERROR for 5xx', (assert) => {
assert.ok(levels.ERROR.isEqualTo(messages[5].level));
assert.end();
});
t.end();
});
batch.test("format using a function", t => {
batch.test('format using a function', (t) => {
const ml = new MockLogger();
ml.level = levels.INFO;
const cl = clm(ml, () => "I was called");
request(cl, "GET", "http://blah", 200);
const cl = clm(ml, () => 'I was called');
request(cl, 'GET', 'http://blah', 200);
t.equal(ml.messages[0].message, "I was called");
t.equal(ml.messages[0].message, 'I was called');
t.end();
});
batch.test("format using a function that also uses tokens", t => {
batch.test('format using a function that also uses tokens', (t) => {
const ml = new MockLogger();
ml.level = levels.INFO;
const cl = clm(
ml,
(req, res, tokenReplacer) => `${req.method} ${tokenReplacer(":status")}`
(req, res, tokenReplacer) => `${req.method} ${tokenReplacer(':status')}`
);
request(cl, "GET", "http://blah", 200);
request(cl, 'GET', 'http://blah', 200);
t.equal(ml.messages[0].message, "GET 200");
t.equal(ml.messages[0].message, 'GET 200');
t.end();
});
batch.test(
"format using a function, but do not log anything if the function returns nothing",
t => {
'format using a function, but do not log anything if the function returns nothing',
(t) => {
const ml = new MockLogger();
ml.level = levels.INFO;
const cl = clm(ml, () => null);
request(cl, "GET", "http://blah", 200);
request(cl, 'GET', 'http://blah', 200);
t.equal(ml.messages.length, 0);
t.end();
}
);
batch.test("format that includes request headers", t => {
batch.test('format that includes request headers', (t) => {
const ml = new MockLogger();
ml.level = levels.INFO;
const cl = clm(ml, ":req[Content-Type]");
request(cl, "GET", "http://blah", 200, {
"Content-Type": "application/json"
const cl = clm(ml, ':req[Content-Type]');
request(cl, 'GET', 'http://blah', 200, {
'Content-Type': 'application/json',
});
t.equal(ml.messages[0].message, "application/json");
t.equal(ml.messages[0].message, 'application/json');
t.end();
});
batch.test("format that includes response headers", t => {
batch.test('format that includes response headers', (t) => {
const ml = new MockLogger();
ml.level = levels.INFO;
const cl = clm(ml, ":res[Content-Type]");
request(cl, "GET", "http://blah", 200, null, {
"Content-Type": "application/cheese"
const cl = clm(ml, ':res[Content-Type]');
request(cl, 'GET', 'http://blah', 200, null, {
'Content-Type': 'application/cheese',
});
t.equal(ml.messages[0].message, "application/cheese");
t.equal(ml.messages[0].message, 'application/cheese');
t.end();
});
batch.test("url token should check originalUrl and url", t => {
batch.test('url token should check originalUrl and url', (t) => {
const ml = new MockLogger();
const cl = clm(ml, ":url");
request(cl, "GET", null, 200, null, null, null, "http://cheese");
const cl = clm(ml, ':url');
request(cl, 'GET', null, 200, null, null, null, 'http://cheese');
t.equal(ml.messages[0].message, "http://cheese");
t.equal(ml.messages[0].message, 'http://cheese');
t.end();
});
batch.test("log events with custom token", t => {
batch.test('log events with custom token', (t) => {
const ml = new MockLogger();
ml.level = levels.INFO;
const cl = clm(ml, {
level: levels.INFO,
format: ":method :url :custom_string",
format: ':method :url :custom_string',
tokens: [
{
token: ":custom_string",
replacement: "fooBAR"
}
]
token: ':custom_string',
replacement: 'fooBAR',
},
],
});
request(cl, "GET", "http://url", 200);
request(cl, 'GET', 'http://url', 200);
t.type(ml.messages, "Array");
t.type(ml.messages, 'Array');
t.equal(ml.messages.length, 1);
t.ok(levels.INFO.isEqualTo(ml.messages[0].level));
t.equal(ml.messages[0].message, "GET http://url fooBAR");
t.equal(ml.messages[0].message, 'GET http://url fooBAR');
t.end();
});
batch.test("log events with custom override token", t => {
batch.test('log events with custom override token', (t) => {
const ml = new MockLogger();
ml.level = levels.INFO;
const cl = clm(ml, {
level: levels.INFO,
format: ":method :url :date",
format: ':method :url :date',
tokens: [
{
token: ":date",
replacement: "20150310"
}
]
token: ':date',
replacement: '20150310',
},
],
});
request(cl, "GET", "http://url", 200);
request(cl, 'GET', 'http://url', 200);
t.type(ml.messages, "Array");
t.type(ml.messages, 'Array');
t.equal(ml.messages.length, 1);
t.ok(levels.INFO.isEqualTo(ml.messages[0].level));
t.equal(ml.messages[0].message, "GET http://url 20150310");
t.equal(ml.messages[0].message, 'GET http://url 20150310');
t.end();
});
batch.test("log events with custom format", t => {
batch.test('log events with custom format', (t) => {
const ml = new MockLogger();
const body = { say: "hi!" };
const body = { say: 'hi!' };
ml.level = levels.INFO;
const cl = clm(ml, {
level: levels.INFO,
format: (req, res, format) =>
format(`:method :url ${JSON.stringify(req.body)}`)
format(`:method :url ${JSON.stringify(req.body)}`),
});
request(
cl,
"POST",
"http://url",
'POST',
'http://url',
200,
{ "Content-Type": "application/json" },
{ 'Content-Type': 'application/json' },
null,
null,
null,
@ -396,43 +396,43 @@ test("log4js connect logger", batch => {
});
batch.test(
"handle weird old node versions where socket contains socket",
t => {
'handle weird old node versions where socket contains socket',
(t) => {
const ml = new MockLogger();
const cl = clm(ml, ":remote-addr");
const req = new MockRequest(null, "GET", "http://blah");
req.socket = { socket: { remoteAddress: "this is weird" } };
const cl = clm(ml, ':remote-addr');
const req = new MockRequest(null, 'GET', 'http://blah');
req.socket = { socket: { remoteAddress: 'this is weird' } };
const res = new MockResponse();
cl(req, res, () => {});
res.writeHead(200, {});
res.end("chunk", "encoding");
res.end('chunk', 'encoding');
t.equal(ml.messages[0].message, "this is weird");
t.equal(ml.messages[0].message, 'this is weird');
t.end();
}
);
batch.test(
"handles as soon as any of the events end/finish/error/close triggers (only once)",
t => {
'handles as soon as any of the events end/finish/error/close triggers (only once)',
(t) => {
const ml = new MockLogger();
const cl = clm(ml, ":remote-addr");
const req = new MockRequest(null, "GET", "http://blah");
req.socket = { socket: { remoteAddress: "this is weird" } };
const cl = clm(ml, ':remote-addr');
const req = new MockRequest(null, 'GET', 'http://blah');
req.socket = { socket: { remoteAddress: 'this is weird' } };
const res = new MockResponse();
cl(req, res, () => {});
res.writeHead(200, {});
t.equal(ml.messages.length, 0);
res.emit("end");
res.emit("finish");
res.emit("error");
res.emit("close");
res.emit('end');
res.emit('finish');
res.emit('error');
res.emit('close');
t.equal(ml.messages.length, 1);
t.equal(ml.messages[0].message, "this is weird");
t.equal(ml.messages[0].message, 'this is weird');
t.end();
}
);

View File

@ -1,19 +1,19 @@
/* eslint max-classes-per-file: ["error", 2] */
const { test } = require("tap");
const EE = require("events").EventEmitter;
const levels = require("../../lib/levels");
const { test } = require('tap');
const EE = require('events').EventEmitter;
const levels = require('../../lib/levels');
class MockLogger {
constructor() {
this.messages = [];
this.level = levels.TRACE;
this.log = function(level, message) {
this.log = function (level, message) {
this.messages.push({ level, message });
};
this.isLevelEnabled = function(level) {
this.isLevelEnabled = function (level) {
return level.isGreaterThanOrEqualTo(this.level);
};
}
@ -23,8 +23,8 @@ function MockRequest(remoteAddr, method, originalUrl) {
this.socket = { remoteAddress: remoteAddr };
this.originalUrl = originalUrl;
this.method = method;
this.httpVersionMajor = "5";
this.httpVersionMinor = "0";
this.httpVersionMajor = '5';
this.httpVersionMinor = '0';
this.headers = {};
}
@ -36,7 +36,7 @@ class MockResponse extends EE {
}
end() {
this.emit("finish");
this.emit('finish');
}
setHeader(key, value) {
@ -52,109 +52,109 @@ class MockResponse extends EE {
}
}
test("log4js connect logger", batch => {
const clm = require("../../lib/connect-logger");
test('log4js connect logger', (batch) => {
const clm = require('../../lib/connect-logger');
batch.test("with nolog config", t => {
batch.test('with nolog config', (t) => {
const ml = new MockLogger();
const cl = clm(ml, { nolog: "\\.gif" });
const cl = clm(ml, { nolog: '\\.gif' });
t.beforeEach(() => {
ml.messages = [];
});
t.test("check unmatch url request", assert => {
const {messages} = ml;
t.test('check unmatch url request', (assert) => {
const { messages } = ml;
const req = new MockRequest(
"my.remote.addr",
"GET",
"http://url/hoge.png"
'my.remote.addr',
'GET',
'http://url/hoge.png'
); // not gif
const res = new MockResponse(200);
cl(req, res, () => {});
res.end("chunk", "encoding");
res.end('chunk', 'encoding');
assert.type(messages, "Array");
assert.type(messages, 'Array');
assert.equal(messages.length, 1);
assert.ok(levels.INFO.isEqualTo(messages[0].level));
assert.match(messages[0].message, "GET");
assert.match(messages[0].message, "http://url");
assert.match(messages[0].message, "my.remote.addr");
assert.match(messages[0].message, "200");
assert.match(messages[0].message, 'GET');
assert.match(messages[0].message, 'http://url');
assert.match(messages[0].message, 'my.remote.addr');
assert.match(messages[0].message, '200');
assert.end();
});
t.test("check match url request", assert => {
const {messages} = ml;
t.test('check match url request', (assert) => {
const { messages } = ml;
const req = new MockRequest(
"my.remote.addr",
"GET",
"http://url/hoge.gif"
'my.remote.addr',
'GET',
'http://url/hoge.gif'
); // gif
const res = new MockResponse(200);
cl(req, res, () => {});
res.end("chunk", "encoding");
res.end('chunk', 'encoding');
assert.type(messages, "Array");
assert.type(messages, 'Array');
assert.equal(messages.length, 0);
assert.end();
});
t.end();
});
batch.test("nolog Strings", t => {
batch.test('nolog Strings', (t) => {
const ml = new MockLogger();
const cl = clm(ml, { nolog: "\\.gif|\\.jpe?g" });
const cl = clm(ml, { nolog: '\\.gif|\\.jpe?g' });
t.beforeEach(() => {
ml.messages = [];
});
t.test("check unmatch url request (png)", assert => {
const {messages} = ml;
t.test('check unmatch url request (png)', (assert) => {
const { messages } = ml;
const req = new MockRequest(
"my.remote.addr",
"GET",
"http://url/hoge.png"
'my.remote.addr',
'GET',
'http://url/hoge.png'
); // not gif
const res = new MockResponse(200);
cl(req, res, () => {});
res.end("chunk", "encoding");
res.end('chunk', 'encoding');
assert.equal(messages.length, 1);
assert.ok(levels.INFO.isEqualTo(messages[0].level));
assert.match(messages[0].message, "GET");
assert.match(messages[0].message, "http://url");
assert.match(messages[0].message, "my.remote.addr");
assert.match(messages[0].message, "200");
assert.match(messages[0].message, 'GET');
assert.match(messages[0].message, 'http://url');
assert.match(messages[0].message, 'my.remote.addr');
assert.match(messages[0].message, '200');
assert.end();
});
t.test("check match url request (gif)", assert => {
const {messages} = ml;
t.test('check match url request (gif)', (assert) => {
const { messages } = ml;
const req = new MockRequest(
"my.remote.addr",
"GET",
"http://url/hoge.gif"
'my.remote.addr',
'GET',
'http://url/hoge.gif'
);
const res = new MockResponse(200);
cl(req, res, () => {});
res.end("chunk", "encoding");
res.end('chunk', 'encoding');
assert.equal(messages.length, 0);
assert.end();
});
t.test("check match url request (jpeg)", assert => {
const {messages} = ml;
t.test('check match url request (jpeg)', (assert) => {
const { messages } = ml;
const req = new MockRequest(
"my.remote.addr",
"GET",
"http://url/hoge.jpeg"
'my.remote.addr',
'GET',
'http://url/hoge.jpeg'
);
const res = new MockResponse(200);
cl(req, res, () => {});
res.end("chunk", "encoding");
res.end('chunk', 'encoding');
assert.equal(messages.length, 0);
assert.end();
@ -163,59 +163,59 @@ test("log4js connect logger", batch => {
t.end();
});
batch.test("nolog Array<String>", t => {
batch.test('nolog Array<String>', (t) => {
const ml = new MockLogger();
const cl = clm(ml, { nolog: ["\\.gif", "\\.jpe?g"] });
const cl = clm(ml, { nolog: ['\\.gif', '\\.jpe?g'] });
t.beforeEach(() => {
ml.messages = [];
});
t.test("check unmatch url request (png)", assert => {
const {messages} = ml;
t.test('check unmatch url request (png)', (assert) => {
const { messages } = ml;
const req = new MockRequest(
"my.remote.addr",
"GET",
"http://url/hoge.png"
'my.remote.addr',
'GET',
'http://url/hoge.png'
); // not gif
const res = new MockResponse(200);
cl(req, res, () => {});
res.end("chunk", "encoding");
res.end('chunk', 'encoding');
assert.equal(messages.length, 1);
assert.ok(levels.INFO.isEqualTo(messages[0].level));
assert.match(messages[0].message, "GET");
assert.match(messages[0].message, "http://url");
assert.match(messages[0].message, "my.remote.addr");
assert.match(messages[0].message, "200");
assert.match(messages[0].message, 'GET');
assert.match(messages[0].message, 'http://url');
assert.match(messages[0].message, 'my.remote.addr');
assert.match(messages[0].message, '200');
assert.end();
});
t.test("check match url request (gif)", assert => {
const {messages} = ml;
t.test('check match url request (gif)', (assert) => {
const { messages } = ml;
const req = new MockRequest(
"my.remote.addr",
"GET",
"http://url/hoge.gif"
'my.remote.addr',
'GET',
'http://url/hoge.gif'
); // gif
const res = new MockResponse(200);
cl(req, res, () => {});
res.end("chunk", "encoding");
res.end('chunk', 'encoding');
assert.equal(messages.length, 0);
assert.end();
});
t.test("check match url request (jpeg)", assert => {
const {messages} = ml;
t.test('check match url request (jpeg)', (assert) => {
const { messages } = ml;
const req = new MockRequest(
"my.remote.addr",
"GET",
"http://url/hoge.jpeg"
'my.remote.addr',
'GET',
'http://url/hoge.jpeg'
); // gif
const res = new MockResponse(200);
cl(req, res, () => {});
res.end("chunk", "encoding");
res.end('chunk', 'encoding');
assert.equal(messages.length, 0);
assert.end();
@ -224,7 +224,7 @@ test("log4js connect logger", batch => {
t.end();
});
batch.test("nolog RegExp", t => {
batch.test('nolog RegExp', (t) => {
const ml = new MockLogger();
const cl = clm(ml, { nolog: /\.gif|\.jpe?g/ });
@ -232,51 +232,51 @@ test("log4js connect logger", batch => {
ml.messages = [];
});
t.test("check unmatch url request (png)", assert => {
const {messages} = ml;
t.test('check unmatch url request (png)', (assert) => {
const { messages } = ml;
const req = new MockRequest(
"my.remote.addr",
"GET",
"http://url/hoge.png"
'my.remote.addr',
'GET',
'http://url/hoge.png'
); // not gif
const res = new MockResponse(200);
cl(req, res, () => {});
res.end("chunk", "encoding");
res.end('chunk', 'encoding');
assert.equal(messages.length, 1);
assert.ok(levels.INFO.isEqualTo(messages[0].level));
assert.match(messages[0].message, "GET");
assert.match(messages[0].message, "http://url");
assert.match(messages[0].message, "my.remote.addr");
assert.match(messages[0].message, "200");
assert.match(messages[0].message, 'GET');
assert.match(messages[0].message, 'http://url');
assert.match(messages[0].message, 'my.remote.addr');
assert.match(messages[0].message, '200');
assert.end();
});
t.test("check match url request (gif)", assert => {
const {messages} = ml;
t.test('check match url request (gif)', (assert) => {
const { messages } = ml;
const req = new MockRequest(
"my.remote.addr",
"GET",
"http://url/hoge.gif"
'my.remote.addr',
'GET',
'http://url/hoge.gif'
); // gif
const res = new MockResponse(200);
cl(req, res, () => {});
res.end("chunk", "encoding");
res.end('chunk', 'encoding');
assert.equal(messages.length, 0);
assert.end();
});
t.test("check match url request (jpeg)", assert => {
const {messages} = ml;
t.test('check match url request (jpeg)', (assert) => {
const { messages } = ml;
const req = new MockRequest(
"my.remote.addr",
"GET",
"http://url/hoge.jpeg"
'my.remote.addr',
'GET',
'http://url/hoge.jpeg'
); // gif
const res = new MockResponse(200);
cl(req, res, () => {});
res.end("chunk", "encoding");
res.end('chunk', 'encoding');
assert.equal(messages.length, 0);
assert.end();
@ -285,7 +285,7 @@ test("log4js connect logger", batch => {
t.end();
});
batch.test("nolog Array<RegExp>", t => {
batch.test('nolog Array<RegExp>', (t) => {
const ml = new MockLogger();
const cl = clm(ml, { nolog: [/\.gif/, /\.jpe?g/] });
@ -293,51 +293,51 @@ test("log4js connect logger", batch => {
ml.messages = [];
});
t.test("check unmatch url request (png)", assert => {
const {messages} = ml;
t.test('check unmatch url request (png)', (assert) => {
const { messages } = ml;
const req = new MockRequest(
"my.remote.addr",
"GET",
"http://url/hoge.png"
'my.remote.addr',
'GET',
'http://url/hoge.png'
); // not gif
const res = new MockResponse(200);
cl(req, res, () => {});
res.end("chunk", "encoding");
res.end('chunk', 'encoding');
assert.equal(messages.length, 1);
assert.ok(levels.INFO.isEqualTo(messages[0].level));
assert.match(messages[0].message, "GET");
assert.match(messages[0].message, "http://url");
assert.match(messages[0].message, "my.remote.addr");
assert.match(messages[0].message, "200");
assert.match(messages[0].message, 'GET');
assert.match(messages[0].message, 'http://url');
assert.match(messages[0].message, 'my.remote.addr');
assert.match(messages[0].message, '200');
assert.end();
});
t.test("check match url request (gif)", assert => {
const {messages} = ml;
t.test('check match url request (gif)', (assert) => {
const { messages } = ml;
const req = new MockRequest(
"my.remote.addr",
"GET",
"http://url/hoge.gif"
'my.remote.addr',
'GET',
'http://url/hoge.gif'
); // gif
const res = new MockResponse(200);
cl(req, res, () => {});
res.end("chunk", "encoding");
res.end('chunk', 'encoding');
assert.equal(messages.length, 0);
assert.end();
});
t.test("check match url request (jpeg)", assert => {
const {messages} = ml;
t.test('check match url request (jpeg)', (assert) => {
const { messages } = ml;
const req = new MockRequest(
"my.remote.addr",
"GET",
"http://url/hoge.jpeg"
'my.remote.addr',
'GET',
'http://url/hoge.jpeg'
); // gif
const res = new MockResponse(200);
cl(req, res, () => {});
res.end("chunk", "encoding");
res.end('chunk', 'encoding');
assert.equal(messages.length, 0);
assert.end();

View File

@ -1,58 +1,58 @@
const { test } = require("tap");
const sandbox = require("@log4js-node/sandboxed-module");
const consoleAppender = require("../../lib/appenders/console");
const { test } = require('tap');
const sandbox = require('@log4js-node/sandboxed-module');
const consoleAppender = require('../../lib/appenders/console');
test("log4js console appender", batch => {
batch.test("should export a configure function", t => {
t.type(consoleAppender.configure, "function");
test('log4js console appender', (batch) => {
batch.test('should export a configure function', (t) => {
t.type(consoleAppender.configure, 'function');
t.end();
});
batch.test("should use default layout if none specified", t => {
batch.test('should use default layout if none specified', (t) => {
const messages = [];
const fakeConsole = {
log(msg) {
messages.push(msg);
}
},
};
const log4js = sandbox.require("../../lib/log4js", {
const log4js = sandbox.require('../../lib/log4js', {
globals: {
console: fakeConsole
}
console: fakeConsole,
},
});
log4js.configure({
appenders: { console: { type: "console" } },
categories: { default: { appenders: ["console"], level: "DEBUG" } }
appenders: { console: { type: 'console' } },
categories: { default: { appenders: ['console'], level: 'DEBUG' } },
});
log4js.getLogger().info("blah");
log4js.getLogger().info('blah');
t.match(messages[0], /.*default.*blah/);
t.end();
});
batch.test("should output to console", t => {
batch.test('should output to console', (t) => {
const messages = [];
const fakeConsole = {
log(msg) {
messages.push(msg);
}
},
};
const log4js = sandbox.require("../../lib/log4js", {
const log4js = sandbox.require('../../lib/log4js', {
globals: {
console: fakeConsole
}
console: fakeConsole,
},
});
log4js.configure({
appenders: {
console: { type: "console", layout: { type: "messagePassThrough" } }
console: { type: 'console', layout: { type: 'messagePassThrough' } },
},
categories: { default: { appenders: ["console"], level: "DEBUG" } }
categories: { default: { appenders: ['console'], level: 'DEBUG' } },
});
log4js.getLogger().info("blah");
log4js.getLogger().info('blah');
t.equal(messages[0], "blah");
t.equal(messages[0], 'blah');
t.end();
});

View File

@ -1,12 +1,12 @@
/* eslint max-classes-per-file: ["error", 3] */
const { test } = require("tap");
const path = require("path");
const fs = require("fs");
const EOL = require("os").EOL || "\n";
const format = require("date-format");
const sandbox = require("@log4js-node/sandboxed-module");
const log4js = require("../../lib/log4js");
const { test } = require('tap');
const path = require('path');
const fs = require('fs');
const EOL = require('os').EOL || '\n';
const format = require('date-format');
const sandbox = require('@log4js-node/sandboxed-module');
const log4js = require('../../lib/log4js');
function removeFile(filename) {
try {
@ -16,24 +16,24 @@ function removeFile(filename) {
}
}
test("../../lib/appenders/dateFile", batch => {
batch.test("with default settings", t => {
const testFile = path.join(__dirname, "date-appender-default.log");
test('../../lib/appenders/dateFile', (batch) => {
batch.test('with default settings', (t) => {
const testFile = path.join(__dirname, 'date-appender-default.log');
log4js.configure({
appenders: { date: { type: "dateFile", filename: testFile } },
categories: { default: { appenders: ["date"], level: "DEBUG" } }
appenders: { date: { type: 'dateFile', filename: testFile } },
categories: { default: { appenders: ['date'], level: 'DEBUG' } },
});
const logger = log4js.getLogger("default-settings");
const logger = log4js.getLogger('default-settings');
logger.info("This should be in the file.");
logger.info('This should be in the file.');
t.teardown(() => {
removeFile("date-appender-default.log");
removeFile('date-appender-default.log');
});
setTimeout(() => {
fs.readFile(testFile, "utf8", (err, contents) => {
t.match(contents, "This should be in the file");
fs.readFile(testFile, 'utf8', (err, contents) => {
t.match(contents, 'This should be in the file');
t.match(
contents,
/\[\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}] \[INFO] default-settings - /
@ -43,30 +43,30 @@ test("../../lib/appenders/dateFile", batch => {
}, 100);
});
batch.test("configure with dateFileAppender", t => {
batch.test('configure with dateFileAppender', (t) => {
log4js.configure({
appenders: {
date: {
type: "dateFile",
filename: "test/tap/date-file-test.log",
pattern: "-yyyy-MM-dd",
layout: { type: "messagePassThrough" }
}
type: 'dateFile',
filename: 'test/tap/date-file-test.log',
pattern: '-yyyy-MM-dd',
layout: { type: 'messagePassThrough' },
},
},
categories: { default: { appenders: ["date"], level: "WARN" } }
categories: { default: { appenders: ['date'], level: 'WARN' } },
});
const logger = log4js.getLogger("tests");
logger.info("this should not be written to the file");
logger.warn("this should be written to the file");
const logger = log4js.getLogger('tests');
logger.info('this should not be written to the file');
logger.warn('this should be written to the file');
log4js.shutdown(() => {
fs.readFile(
path.join(__dirname, "date-file-test.log"),
"utf8",
path.join(__dirname, 'date-file-test.log'),
'utf8',
(err, contents) => {
t.match(contents, `this should be written to the file${EOL}`);
t.equal(
contents.indexOf("this should not be written to the file"),
contents.indexOf('this should not be written to the file'),
-1
);
t.end();
@ -75,25 +75,25 @@ test("../../lib/appenders/dateFile", batch => {
});
t.teardown(() => {
removeFile("date-file-test.log");
removeFile('date-file-test.log');
});
});
batch.test("configure with options.alwaysIncludePattern", t => {
batch.test('configure with options.alwaysIncludePattern', (t) => {
const options = {
appenders: {
date: {
category: "tests",
type: "dateFile",
filename: "test/tap/date-file-test",
pattern: "yyyy-MM-dd.log",
category: 'tests',
type: 'dateFile',
filename: 'test/tap/date-file-test',
pattern: 'yyyy-MM-dd.log',
alwaysIncludePattern: true,
layout: {
type: "messagePassThrough"
}
}
type: 'messagePassThrough',
},
},
},
categories: { default: { appenders: ["date"], level: "debug" } }
categories: { default: { appenders: ['date'], level: 'debug' } },
};
const thisTime = format.asString(
@ -101,14 +101,11 @@ test("../../lib/appenders/dateFile", batch => {
new Date()
);
const testFile = `date-file-test.${thisTime}`;
const existingFile = path.join(
__dirname,
testFile
);
fs.writeFileSync(existingFile, `this is existing data${EOL}`, "utf8");
const existingFile = path.join(__dirname, testFile);
fs.writeFileSync(existingFile, `this is existing data${EOL}`, 'utf8');
log4js.configure(options);
const logger = log4js.getLogger("tests");
logger.warn("this should be written to the file with the appended date");
const logger = log4js.getLogger('tests');
logger.warn('this should be written to the file with the appended date');
t.teardown(() => {
removeFile(testFile);
@ -116,38 +113,38 @@ test("../../lib/appenders/dateFile", batch => {
// wait for filesystem to catch up
log4js.shutdown(() => {
fs.readFile(existingFile, "utf8", (err, contents) => {
fs.readFile(existingFile, 'utf8', (err, contents) => {
t.match(
contents,
"this is existing data",
"should not overwrite the file on open (issue #132)"
'this is existing data',
'should not overwrite the file on open (issue #132)'
);
t.match(
contents,
"this should be written to the file with the appended date"
'this should be written to the file with the appended date'
);
t.end();
});
});
});
batch.test("should flush logs on shutdown", t => {
const testFile = path.join(__dirname, "date-appender-flush.log");
batch.test('should flush logs on shutdown', (t) => {
const testFile = path.join(__dirname, 'date-appender-flush.log');
log4js.configure({
appenders: { test: { type: "dateFile", filename: testFile } },
categories: { default: { appenders: ["test"], level: "trace" } }
appenders: { test: { type: 'dateFile', filename: testFile } },
categories: { default: { appenders: ['test'], level: 'trace' } },
});
const logger = log4js.getLogger("default-settings");
const logger = log4js.getLogger('default-settings');
logger.info("1");
logger.info("2");
logger.info("3");
logger.info('1');
logger.info('2');
logger.info('3');
t.teardown(() => {
removeFile("date-appender-flush.log");
removeFile('date-appender-flush.log');
});
log4js.shutdown(() => {
fs.readFile(testFile, "utf8", (err, fileContents) => {
fs.readFile(testFile, 'utf8', (err, fileContents) => {
// 3 lines of output, plus the trailing newline.
t.equal(fileContents.split(EOL).length, 4);
t.match(
@ -159,7 +156,7 @@ test("../../lib/appenders/dateFile", batch => {
});
});
batch.test("should map maxLogSize to maxSize", t => {
batch.test('should map maxLogSize to maxSize', (t) => {
const fakeStreamroller = {};
class DateRollingFileStream {
constructor(filename, pattern, options) {
@ -168,20 +165,20 @@ test("../../lib/appenders/dateFile", batch => {
fakeStreamroller.options = options;
}
on() { } // eslint-disable-line class-methods-use-this
on() {} // eslint-disable-line class-methods-use-this
}
fakeStreamroller.DateRollingFileStream = DateRollingFileStream;
const dateFileAppenderModule = sandbox.require(
"../../lib/appenders/dateFile",
'../../lib/appenders/dateFile',
{
requires: { streamroller: fakeStreamroller }
requires: { streamroller: fakeStreamroller },
}
);
dateFileAppenderModule.configure(
{
filename: "cheese.log",
pattern: "yyyy",
maxLogSize: 100
filename: 'cheese.log',
pattern: 'yyyy',
maxLogSize: 100,
},
{ basicLayout: () => {} }
);
@ -190,7 +187,7 @@ test("../../lib/appenders/dateFile", batch => {
t.end();
});
batch.test("handling of writer.writable", t => {
batch.test('handling of writer.writable', (t) => {
const output = [];
let writable = true;
@ -201,46 +198,51 @@ test("../../lib/appenders/dateFile", batch => {
return true;
}
on() { // eslint-disable-line class-methods-use-this
}
// eslint-disable-next-line class-methods-use-this
on() {}
get writable() { // eslint-disable-line class-methods-use-this
// eslint-disable-next-line class-methods-use-this
get writable() {
return writable;
}
};
const dateFileAppender = sandbox.require("../../lib/appenders/dateFile", {
const dateFileAppender = sandbox.require('../../lib/appenders/dateFile', {
requires: {
streamroller: {
DateRollingFileStream
}
}
DateRollingFileStream,
},
},
});
const appender = dateFileAppender.configure(
{ filename: "test1.log", maxLogSize: 100 },
{ basicLayout(loggingEvent) { return loggingEvent.data; } }
{ filename: 'test1.log', maxLogSize: 100 },
{
basicLayout(loggingEvent) {
return loggingEvent.data;
},
}
);
t.test("should log when writer.writable=true", assert => {
t.test('should log when writer.writable=true', (assert) => {
writable = true;
appender({data: "something to log"});
appender({ data: 'something to log' });
assert.ok(output.length, 1);
assert.match(output[output.length - 1], "something to log");
assert.match(output[output.length - 1], 'something to log');
assert.end();
});
t.test("should not log when writer.writable=false", assert => {
t.test('should not log when writer.writable=false', (assert) => {
writable = false;
appender({data: "this should not be logged"});
appender({ data: 'this should not be logged' });
assert.ok(output.length, 1);
assert.notMatch(output[output.length - 1], "this should not be logged");
assert.notMatch(output[output.length - 1], 'this should not be logged');
assert.end();
});
t.end();
});
batch.test("when underlying stream errors", t => {
batch.test('when underlying stream errors', (t) => {
let consoleArgs;
let errorHandler;
@ -250,7 +252,7 @@ test("../../lib/appenders/dateFile", batch => {
}
on(evt, cb) {
if (evt === "error") {
if (evt === 'error') {
this.errored = true;
errorHandler = cb;
}
@ -261,35 +263,35 @@ test("../../lib/appenders/dateFile", batch => {
return true;
}
};
const dateFileAppender = sandbox.require("../../lib/appenders/dateFile", {
const dateFileAppender = sandbox.require('../../lib/appenders/dateFile', {
globals: {
console: {
error(...args) {
consoleArgs = args;
}
}
},
},
},
requires: {
streamroller: {
DateRollingFileStream
}
}
DateRollingFileStream,
},
},
});
dateFileAppender.configure(
{ filename: "test1.log", maxLogSize: 100 },
{ filename: 'test1.log', maxLogSize: 100 },
{ basicLayout() {} }
);
errorHandler({ error: "aargh" });
errorHandler({ error: 'aargh' });
t.test("should log the error to console.error", assert => {
t.test('should log the error to console.error', (assert) => {
assert.ok(consoleArgs);
assert.equal(
consoleArgs[0],
"log4js.dateFileAppender - Writing to file %s, error happened "
'log4js.dateFileAppender - Writing to file %s, error happened '
);
assert.equal(consoleArgs[1], "test1.log");
assert.equal(consoleArgs[2].error, "aargh");
assert.equal(consoleArgs[1], 'test1.log');
assert.equal(consoleArgs[2].error, 'aargh');
assert.end();
});
t.end();

Some files were not shown because too many files have changed in this diff Show More