cf-nodejs-logging-support
Advanced tools
Comparing version 6.11.0 to 6.12.0
@@ -15,2 +15,4 @@ const util = require("util"); | ||
const MAX_STACKTRACE_SIZE = 55 * 1024; | ||
const LOG_TYPE = "log"; | ||
@@ -94,7 +96,7 @@ const LOGGING_LEVELS = { | ||
var boundServices = parseJSONSafe(process.env.VCAP_SERVICES); | ||
if(boundServices["application-logs"]) { | ||
if (boundServices["application-logs"]) { | ||
cfCustomEnabled = true; | ||
defaultCustomEnabled = false; | ||
} | ||
if(boundServices["cloud-logs"]) { | ||
if (boundServices["cloud-logs"]) { | ||
defaultCustomEnabled = true; | ||
@@ -106,3 +108,3 @@ } | ||
var tmp = {}; | ||
if(value) | ||
if (value) | ||
try { | ||
@@ -138,3 +140,3 @@ tmp = JSON.parse(value); | ||
if (!pass) { | ||
continue; | ||
continue; | ||
} | ||
@@ -288,5 +290,5 @@ } | ||
var setRequestLogLevel = function(level) { | ||
var setRequestLogLevel = function (level) { | ||
levelInt = getLogLevelFromName(level); | ||
if(levelInt != null) { | ||
if (levelInt != null) { | ||
requestLogLevel = level; | ||
@@ -333,3 +335,3 @@ return true; | ||
// due to different rollover times for process.hrtime and now.getTime | ||
if(logObject.written_ts < lastTimestamp) | ||
if (logObject.written_ts < lastTimestamp) | ||
logObject.written_ts += NS_PER_MS; | ||
@@ -461,7 +463,13 @@ lastTimestamp = logObject.written_ts; | ||
var customFieldsFromArgs = {}; | ||
if (typeof args[args.length - 1] === "object") { | ||
if (isValidObject(args[args.length - 1])) { | ||
customFieldsFromArgs = args[args.length - 1]; | ||
var lastArg = args[args.length - 1]; | ||
if (typeof lastArg === "object") { | ||
if (isErrorWithStacktrace(lastArg)) { | ||
logObject.stacktrace = prepareStacktrace(lastArg.stack); | ||
} else if (isValidObject(lastArg)) { | ||
if (isErrorWithStacktrace(lastArg._error)) { | ||
logObject.stacktrace = prepareStacktrace(lastArg._error.stack); | ||
delete lastArg._error; | ||
} | ||
customFieldsFromArgs = lastArg; | ||
} | ||
@@ -567,3 +575,3 @@ args.pop(); | ||
var registerCustomFields = function (fieldNames) { | ||
registeredCustomFields = []; | ||
@@ -651,3 +659,3 @@ | ||
if(defaultCustomEnabled || logObject[key] != null || isSettable(key)) | ||
if (defaultCustomEnabled || logObject[key] != null || isSettable(key)) | ||
logObject[key] = value; | ||
@@ -666,5 +674,5 @@ | ||
var key; | ||
for(var i = 0; i < registeredCustomFields.length; i++) { | ||
for (var i = 0; i < registeredCustomFields.length; i++) { | ||
key = registeredCustomFields[i] | ||
if(customFields[key]) | ||
if (customFields[key]) | ||
res.string.push({ | ||
@@ -676,3 +684,3 @@ "k": key, | ||
} | ||
if(res.string.length > 0) | ||
if (res.string.length > 0) | ||
logObject["#cf"] = res; | ||
@@ -682,6 +690,6 @@ } | ||
var isSettable = function(key) { | ||
var isSettable = function (key) { | ||
if (settableConfig.length == 0) return false; | ||
for(var i = 0; i < settableConfig.length; i++) { | ||
if(settableConfig[i] == key) return true; | ||
for (var i = 0; i < settableConfig.length; i++) { | ||
if (settableConfig[i] == key) return true; | ||
} | ||
@@ -789,2 +797,51 @@ return false; | ||
// check if the given object is an Error with stacktrace using duck typing | ||
var isErrorWithStacktrace = function (obj) { | ||
if (obj && obj.stack && obj.message && typeof obj.stack === "string" && typeof obj.message === "string") { | ||
return true; | ||
} | ||
return false; | ||
} | ||
// Split stacktrace into string array and truncate lines if required by size limitation | ||
// Truncation strategy: Take one line from the top and two lines from the bottom of the stacktrace until limit is reached. | ||
var prepareStacktrace = function (stacktraceStr) { | ||
var fullStacktrace = stacktraceStr.split('\n'); | ||
var totalLineLength = fullStacktrace.reduce((acc, line) => acc + line.length, 0); | ||
if (totalLineLength > MAX_STACKTRACE_SIZE) { | ||
var truncatedStacktrace = []; | ||
var stackA = []; | ||
var stackB = []; | ||
var indexA = 0; | ||
var indexB = fullStacktrace.length - 1; | ||
var currentLength = 73; // set to approx. character count for "truncated" and "omitted" labels | ||
for (let i = 0; i < fullStacktrace.length; i++) { | ||
if (i % 3 == 0) { | ||
let line = fullStacktrace[indexA++]; | ||
if (currentLength + line.length > MAX_STACKTRACE_SIZE) { | ||
break; | ||
} | ||
currentLength += line.length; | ||
stackA.push(line); | ||
} else { | ||
let line = fullStacktrace[indexB--]; | ||
if (currentLength + line.length > MAX_STACKTRACE_SIZE) { | ||
break; | ||
} | ||
currentLength += line.length; | ||
stackB.push(line); | ||
} | ||
} | ||
truncatedStacktrace.push("-------- STACK TRACE TRUNCATED --------"); | ||
truncatedStacktrace = [...truncatedStacktrace, ...stackA]; | ||
truncatedStacktrace.push(`-------- OMITTED ${fullStacktrace.length - (stackA.length + stackB.length)} LINES --------`); | ||
truncatedStacktrace = [...truncatedStacktrace, ...stackB.reverse()]; | ||
return truncatedStacktrace; | ||
} | ||
return fullStacktrace; | ||
} | ||
// writes static field values to the given logObject | ||
@@ -812,4 +869,4 @@ var writeStaticFields = function (logObject) { | ||
//Sets the custom field format by hand. Returns true on correct strings. | ||
var overrideCustomFieldFormat = function(value) { | ||
if(typeof value == "string") { | ||
var overrideCustomFieldFormat = function (value) { | ||
if (typeof value == "string") { | ||
switch (value) { | ||
@@ -816,0 +873,0 @@ case "application-logging": |
@@ -11,5 +11,5 @@ --- | ||
{: .no_toc } | ||
Sometimes it is useful to change the logging level threshold for a specific request. | ||
This can be achieved using a special header field or setting directly within the corresponding request handler. | ||
Changing the logging level threshold affects if logs with a specific level are written. | ||
For debugging purposes it can be useful to change the logging level threshold for specific requests. | ||
This can be achieved using a special header field or setting directly within the corresponding request handler. | ||
Changing the logging level threshold affects if logs with a specific level are written. | ||
It has no effect on the level reported as part of the logs. | ||
@@ -27,18 +27,45 @@ | ||
## Change logging level threshold via header field | ||
You can change the logging level threshold for a specific request by providing a JSON Web Token ([JWT](https://de.wikipedia.org/wiki/JSON_Web_Token)) via the request header. | ||
Using this feature allows you to change the logging level threshold dynamically without the need to redeploy your app. | ||
### 1 Creating a JWT | ||
JWTs are signed claims, which consist of a header, a payload and a signature. | ||
You can create JWTs by using the [TokenCreator](https://github.com/SAP/cf-nodejs-logging-support/tree/master/tools/token-creator) from the tools folder. | ||
You can change the logging level threshold for a specific request by providing a JSON Web Token ([JWT](https://de.wikipedia.org/wiki/JSON_Web_Token)) via the request header. | ||
Using this feature allows changing the logging level threshold dynamically without the need to redeploy your app. | ||
Basically, JWTs are signed using RSA or HMAC signing algorithms. | ||
But we decided to support RSA algorithms (RS256, RS384 and RS512) only. | ||
### 1 Creating a key-pair | ||
To sign and verify JWTs a PEM encoded private key and a matching public key is required. | ||
You can create a key-pair using the following command: | ||
```sh | ||
openssl rsa -in private.pem -outform PEM -pubout -out public.pem | ||
``` | ||
The generated key-pair can be found in `private.pem` and `public.pem` files. | ||
### 2 Creating a JWT | ||
JWTs are signed claims, which consist of a header, a payload, and a signature. | ||
They can be signed using RSA or HMAC signing algorithms. | ||
For this use-case we decided to support RSA algorithms (RS256, RS384 and RS512) only. | ||
In contrast to HMAC algorithms (HS256, HS384 and HS512), RSA algorithms are asymmetric and therefore require key pairs (public and private key). | ||
The tool mentioned above takes a log level, creates a key pair and signs the resulting JWT with the private key. | ||
The payload of a JWT looks like this: | ||
You can create JWTs by using the provided [TokenCreator](https://github.com/SAP/cf-nodejs-logging-support/tree/master/tools/token-creator): | ||
```sh | ||
cd tools/token-creator/ | ||
npm install | ||
node token-creator.js -f <path private.pem> -v <validity period> -i <issuer> <level> | ||
``` | ||
The `<validity period>` sets the number of days the JWT will be valid. | ||
Once the created JWT expired, it can no longer be used for setting logging level threshold. | ||
Provide a numeric input for this placeholder. | ||
Provide a valid e-mail address for the `<issuer>` parameter. | ||
Specify one of the seven supported logging levels for the `<level>` argument: *off*, *error*, *warn*, *info*, *verbose*, *debug*, and *silly*. | ||
The payload of the created JWT has the following structure: | ||
```js | ||
{ | ||
"issuer": "<valid e-mail address>", | ||
"issuer": "<e-mail address>", | ||
"level": "debug", | ||
@@ -50,22 +77,32 @@ "iat": 1506016127, | ||
This library supports seven logging levels: *off*, *error*, *warn*, *info*, *verbose*, *debug* and *silly*. | ||
Make sure that your JWT specifies one of them in order to work correctly. | ||
It is also important to make sure that the JWT has not been expired, when using it. | ||
### 3 Providing the public key | ||
### 2 Providing the public key | ||
The logging library will verify JWTs attached to incoming requests. | ||
In order to do so, the public key (from above) needs to be provided via an environment variable called *DYN_LOG_LEVEL_KEY*: | ||
The logging library will verify JWTs attached to incoming requests. | ||
In order to do so, the public key (from `public.pem` file) needs to be provided via an environment variable called DYN_LOG_LEVEL_KEY: | ||
```text | ||
DYN_LOG_LEVEL_KEY: <encoded public key> | ||
``` | ||
DYN_LOG_LEVEL_KEY: <your public key> | ||
Typically your public key file should have following structure: | ||
```text | ||
-----BEGIN PUBLIC KEY----- | ||
<encoded public key> | ||
-----END PUBLIC KEY----- | ||
``` | ||
Redeploy your app after setting up the environment variable. | ||
Instead of using the whole content of the `public.pem` file, you can also only provide the `<encoded key>` section to the environment variable. | ||
### 3 Attaching JWTs to requests | ||
Provide the created JWTs via a header field named 'SAP-LOG-LEVEL'. The logging level threshold will be set to the provided level for this request and corresponding custom log messages. | ||
Redeploy your app after setting the environment variable. | ||
Note: If the provided JWT cannot be verified, is expired or contains an invalid logging level, the library ignores it and uses the global logging level threshold. | ||
### 4 Attaching JWTs to requests | ||
Provide the created JWT via a header field named 'SAP-LOG-LEVEL'. The logging level threshold will be set to the provided level for this request and corresponding custom log messages. | ||
**Note**: If the provided JWT cannot be verified, is expired, or contains an invalid logging level, the library ignores it and uses the global logging level threshold. | ||
If you want to use another header name for the JWT, you can specify it using an environment variable: | ||
``` | ||
```text | ||
DYN_LOG_HEADER: MY-HEADER-FIELD | ||
@@ -75,6 +112,9 @@ ``` | ||
## Change logging level threshold within request handlers | ||
You can also change the logging level threshold for all requests of a specific request handler by calling: | ||
```js | ||
req.setLoggingLevel("verbose"); | ||
``` | ||
This feature is also available for [Child Loggers](/cf-nodejs-logging-support/advanced-usage/child-loggers#). |
@@ -12,4 +12,4 @@ --- | ||
In addition to request logging this library also supports logging of application messages. | ||
Message logs contain at least some message and also CF metadata. | ||
In addition to request logging this library also supports logging of application messages. | ||
Message logs contain at least some message and also CF metadata. | ||
@@ -26,2 +26,3 @@ <details open markdown="block"> | ||
## Logging levels | ||
Following common logging levels are supported: | ||
@@ -37,2 +38,3 @@ | ||
Set the minimum logging level for logs as follows: | ||
```js | ||
@@ -45,2 +47,3 @@ log.setLoggingLevel("info"); | ||
## Writing message logs | ||
There are so called *convenience methods* available for all supported logging levels. | ||
@@ -50,41 +53,46 @@ These can be called to log a message using the corresponding level. | ||
You can find several usage examples below demonstrating options to be specified when calling a log method. | ||
All methods get called on a `logger` object, which provides a so called *logging context*. | ||
You can find several usage examples below demonstrating options to be specified when calling a log method. | ||
All methods get called on a `logger` object, which provides a so called *logging context*. | ||
You can find more information about logging contexts in the [Logging Contexts](/cf-nodejs-logging-support/general-usage/logging-contexts) chapter. | ||
In the simplest case, `logger` is an instance of imported `log` module. | ||
In the simplest case, `logger` is an instance of imported `log` module. | ||
- Simple message | ||
```js | ||
logger.info("Hello World"); | ||
// ... "msg":"Hello World" ... | ||
``` | ||
- Simple message: | ||
- Message with additional numeric value | ||
```js | ||
logger.info("Listening on port %d", 5000); | ||
// ... "msg":"Listening on port 5000" ... | ||
``` | ||
```js | ||
logger.info("Hello World"); | ||
// ... "msg":"Hello World" ... | ||
``` | ||
- Message with additional string values | ||
```js | ||
logger.info("This %s a %s", "is", "test"); | ||
// ... "msg":"This is a test" ... | ||
``` | ||
- Message with additional numeric value: | ||
- Message with additional json object to be embedded in to the message | ||
```js | ||
logger.info("Test data %j", {"field" :"value"}, {}); | ||
// ... "msg":"Test data {\"field\": \"value\"}" ... | ||
``` | ||
```js | ||
logger.info("Listening on port %d", 5000); | ||
// ... "msg":"Listening on port 5000" ... | ||
``` | ||
In some cases you might want to set the actual logging level from a variable. | ||
Instead of using conditional expressions you can simply use following method, which also supports format features described above. | ||
```js | ||
var level = "debug"; | ||
logger.logMessage(level, "Hello World"); | ||
// ... "msg":"Hello World" ... | ||
``` | ||
- Message with additional string values: | ||
```js | ||
logger.info("This %s a %s", "is", "test"); | ||
// ... "msg":"This is a test" ... | ||
``` | ||
- Message with additional json object to be embedded in to the message: | ||
```js | ||
logger.info("Test data %j", {"field" :"value"}, {}); | ||
// ... "msg":"Test data {\"field\": \"value\"}" ... | ||
``` | ||
- In case you want to set the actual logging level from a variable, you can use following method, which also supports format features described above: | ||
```js | ||
var level = "debug"; | ||
logger.logMessage(level, "Hello World"); | ||
// ... "msg":"Hello World" ... | ||
``` | ||
## Checking log severity levels | ||
It can be useful to check if messages with a specific severity level would be logged. | ||
It can be useful to check if messages with a specific severity level would be logged. | ||
You can check if a logging level is active as follows: | ||
@@ -100,4 +108,5 @@ | ||
There are convenience methods available for this feature: | ||
```js | ||
var isDebugActive = log.isDebug(); | ||
``` | ||
``` |
{ | ||
"name": "cf-nodejs-logging-support", | ||
"version": "6.11.0", | ||
"version": "6.12.0", | ||
"description": "Logging tool for Cloud Foundry", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
158320
45
2089