Socket
Socket
Sign inDemoInstall

niveau

Package Overview
Dependencies
19
Maintainers
1
Versions
7
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.0.3 to 0.1.0

2

lib/index.js

@@ -29,3 +29,3 @@ 'use strict';

} catch (e) {
store.emit(new VError(e, 'Failed to parse log config'));
store.emit('error', new VError(e, 'Failed to parse log config'));
}

@@ -32,0 +32,0 @@ });

{
"name": "niveau",
"version": "0.0.3",
"version": "0.1.0",
"description": "Node.js package to switch log level per request in Cloud Foundry",

@@ -12,3 +12,4 @@ "main": "lib",

"coverage": "nyc report --reporter=html && echo Open coverage/index.html",
"redis": "docker run -d -p 6379:6379 --rm redis"
"redis": "docker run -d -p 6379:6379 --rm redis",
"toc": "markdown-toc -i README.md"
},

@@ -53,2 +54,3 @@ "bin": {

"express": "^4.16.2",
"markdown-toc": "^1.2.0",
"mocha": "^5.0.0",

@@ -55,0 +57,0 @@ "nyc": "^11.4.1",

@@ -8,2 +8,24 @@ [![Build Status](https://travis-ci.org/niveau/niveau.svg?branch=master)](https://travis-ci.org/niveau/niveau)

<!-- toc -->
- [Goals](#goals)
- [Requirements](#requirements)
- [Design](#design)
- [Usage](#usage)
* [In the application](#in-the-application)
+ [niveau([options])](#niveauoptions)
+ [Event 'error'](#event-error)
+ [Event 'config'](#event-config)
+ [Event 'request'](#event-request)
+ [Log configuration](#log-configuration)
* [Changing the log level](#changing-the-log-level)
+ [Options](#options)
+ [Invoke via CF task](#invoke-via-cf-task)
+ [Invoke via SSH to the application](#invoke-via-ssh-to-the-application)
+ [Examples](#examples)
- [Contributing](#contributing)
- [Future](#future)
<!-- tocstop -->
## Goals

@@ -62,4 +84,8 @@ * Change the log level without restart - no downtime

});
nv.on('config', config => {
// log configuration changed
});
nv.on('request', (req, config) => {
// set log level for this request to config.level
// request matches logging criteria
// set log level for this request to config.level
});

@@ -69,7 +95,48 @@

app.use(nv);
app.use((req, res, next) => {
// req.logLevel - the log level to be used for this request (if present)
});
```
See example applications in [examples](examples) folder.
#### niveau([options])
* `options` Redis connection [options](https://github.com/NodeRedis/node_redis#rediscreateclient) + additional properties:
* `redisKey` name of the Redis key that stores the configuration, default is `log-config`
Creates _niveau_ middleware. It matches incoming requests against the criteria in the log configuration.
For matching requests it sets `logLevel` property on the request object to the log level from the configuration.
The middleware also listens for log configuration changes and emits some events.
#### Event 'error'
Event arguments:
* `error` an `Error` object
Emitted in case of error, e.g. Redis connection failed.
#### Event 'config'
Event arguments:
* `config` [log configuration](#log-configuration) object
Emitted when log configuration is changed or deleted.
#### Event 'request'
Event arguments:
* `request` [http.IncomingMessage](https://nodejs.org/api/http.html#http_class_http_incomingmessage)
* `config` [log configuration](#log-configuration) object
Emitted when an HTTP request matches the criteria in the log configuration.
#### Log configuration
Log configuration object:
* `request` request matching criteria,
if missing or empty, the log level should be used for all requests
* `url` `RegExp` to match against the request URL
* `ip` `RegExp` to match against the client IP address
* `headers` an object to match against request headers, each values is a `RegExp`
* `level` log level as a string to use for matching requests
### Changing the log level
This package provides an executable script to change the log level.
The provided log level will be used only for HTTP requests that match the given options.
This package provides a command line tool to change the log level.
The provided log level will be used only for HTTP requests that match _all_ the given criteria.
Each command invocation overwrites any previous settings.

@@ -81,8 +148,9 @@

#### Options
* -l, --url \<regex> - matches request URL (without protocol, host, port)
* -h, --header \<name>:\<regex> - matches given request header value
* -i, --ip \<regex> - matches sender IP address
* -x, --expire \<value> - expiration time with `s/m/h` suffix
* -r, --reset - reset log level to default (do not provide <level>)
* \<level> - log level to use for matching requests, supported values depend on your log library
* `-l, --url <regex>` - matches request URL (without protocol, host, port)
* `-h, --header <name>:<regex>` - matches given request header value
* `-i, --ip <regex>` - matches sender IP address
* `-x, --expire <value>` - expiration time with `s/m/h` suffix
* `-r, --reset` - reset log level to default (do not provide level)
* `--help` - print usage
* `<level>` - log level to use for matching requests, supported values depend on your log library

@@ -114,3 +182,3 @@ #### Invoke via CF task

## Test
## Contributing
Install all dependencies:

@@ -124,3 +192,2 @@ ```sh

```
Integration tests require Redis to run on localhost on default port 6379.

@@ -136,8 +203,17 @@ Install [docker], unless you have it already.

```
Generate test coverage report:
```sh
npm run coverage
```
After editing README.md update its table of contents:
```sh
npm run toc
```
## Future
### CF CLI plugin to change log level
Redis uses TCP not HTTP, so it requires a tunnel (cf ssh) to connect it from outside CF. This is an additional obstacle for a CF CLI plugin.
Ideas for [new features](https://github.com/niveau/niveau/labels/enhancement) are tracked in GitHub issues. You are encouraged to comment, add new ideas and contribute in any way.
[ssh]: https://docs.cloudfoundry.org/devguide/deploy-apps/ssh-apps.html
[docker]: https://www.docker.com/community-edition

@@ -14,72 +14,111 @@ #!/usr/bin/env node

const allOptions = {
url: 'l',
header: 'h',
ip: 'i',
expire: 'x',
reset: 'r',
help: undefined,
_: undefined
};
const cmdOptions = cmdParse(process.argv.slice(2), {
alias: {
l: 'url',
h: 'header',
i: 'ip',
x: 'expire',
r: 'reset'
}
alias: _.omit(_.invert(allOptions), undefined)
});
debug('Command line options:', cmdOptions);
if (cmdOptions.reset) {
assert(
!cmdOptions._.length &&
!['url', 'header', 'ip', 'expire'].some(opt => opt in cmdOptions),
'No other options allowed with reset'
);
} else {
assert(cmdOptions._.length === 1, 'Provide exactly one log level');
var level = cmdOptions._[0];
try {
execute(cmdOptions);
} catch (err) {
debug(err);
console.error(err.message);
process.exit(1);
}
let headers = _.reduce(cmdOptions.header, (result, h) => {
let i = h.indexOf(':');
assert(i > 0, 'headers');
result[h.slice(0, i).trim()] = h.slice(i + 1).trim();
}, {});
function execute(cmdOptions) {
function noOptions(options) {
return Object.keys(options).length === 1 && options._.length === 0;
}
let expire;
if (cmdOptions.expire) {
if (!cmdOptions.expire.endsWith('r')) {
expire = timeParse(cmdOptions.expire, 's');
let validOptions = new Set(_.flatten(_.entries(allOptions)));
for (let opt in cmdOptions) {
assert(validOptions.has(opt),
`Invalid option ${opt}. Run 'set-log-level --help' to see usage.`);
}
}
let logConfig = {
request: {
url: cmdOptions.url,
ip: cmdOptions.ip,
headers
},
// requestCounterKey: 'counter-name',
level
};
if (cmdOptions.help || noOptions(cmdOptions)) {
console.log(`Usage: set-log-level [options...] <level>
Options:
-l, --url <regex> - matches request URL (without protocol, host, port)
-h, --header <name>:<regex> - matches given request header value
-i, --ip <regex> - matches sender IP address
-x, --expire <value> - expiration time with s/m/h suffix
-r, --reset - reset log level to default (do not provide level)
--help - print usage
<level> - log level to use for matching requests, supported values depend on your log library
`);
process.exit(1);
}
const client = redis.createClient(readRedisOptions());
if (cmdOptions.reset) {
assert(
!cmdOptions._.length &&
!['url', 'header', 'ip', 'expire'].some(opt => opt in cmdOptions),
'No other options allowed with reset'
);
} else {
assert(cmdOptions._.length === 1,
"Provide exactly one log level. Run 'set-log-level --help' to see usage.");
var level = cmdOptions._[0];
}
client.on("error", function (err) {
console.error(err);
});
let headers = _.reduce(cmdOptions.header, (result, h) => {
let i = h.indexOf(':');
assert(i > 0, `Invalid header ${h}. Run 'set-log-level --help' to see usage.`);
result[h.slice(0, i).trim()] = h.slice(i + 1).trim();
}, {});
client.config('set', 'notify-keyspace-events', 'KA', (err, reply) => {
err ? console.error('notify-keyspace-events', err) :
console.log('notify-keyspace-events', reply);
let expire;
if (cmdOptions.expire) {
if (!cmdOptions.expire.endsWith('r')) {
expire = timeParse(cmdOptions.expire, 's');
}
}
if (cmdOptions.reset) {
debug('redis DEL %s', LOG_CONFIG_KEY);
client.del(LOG_CONFIG_KEY, (err, reply) => {
err ? console.error('redis DEL:', err) : debug('redis:', reply);
client.quit();
});
} else {
debug('redis SET %s', LOG_CONFIG_KEY, logConfig);
let params = [LOG_CONFIG_KEY, JSON.stringify(logConfig)];
expire && params.push('EX', expire);
client.set(params, (err, reply) => {
err ? console.error('redis SET:', err) : debug('redis:', reply);
client.quit();
});
}
});
let logConfig = {
request: {
url: cmdOptions.url,
ip: cmdOptions.ip,
headers
},
// requestCounterKey: 'counter-name',
level
};
const client = redis.createClient(readRedisOptions());
client.on("error", function (err) {
console.error(err);
});
client.config('set', 'notify-keyspace-events', 'KA', (err, reply) => {
err ? console.error('notify-keyspace-events', err) :
console.log('notify-keyspace-events', reply);
if (cmdOptions.reset) {
debug('redis DEL %s', LOG_CONFIG_KEY);
client.del(LOG_CONFIG_KEY, (err, reply) => {
err ? console.error('redis DEL:', err) : debug('redis:', reply);
client.quit();
});
} else {
debug('redis SET %s', LOG_CONFIG_KEY, logConfig);
let params = [LOG_CONFIG_KEY, JSON.stringify(logConfig)];
expire && params.push('EX', expire);
client.set(params, (err, reply) => {
err ? console.error('redis SET:', err) : debug('redis:', reply);
client.quit();
});
}
});
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc