Skip to content

Commit 1ce20de

Browse files
committed
Simplify changes for custom fields and custom field names
1 parent 36853ea commit 1ce20de

File tree

5 files changed

+96
-157
lines changed

5 files changed

+96
-157
lines changed

.eslintrc

Lines changed: 0 additions & 12 deletions
This file was deleted.

.eslintrc.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
rules:
2+
semi: error
3+
quotes: [error, single, {allowTemplateLiterals: true}]
4+
indent: [error, 4]
5+
linebreak-style: [error, unix]
6+
no-unused-vars: warn
7+
env:
8+
es6: true
9+
node: true
10+
extends: eslint:recommended

README.md

Lines changed: 41 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -26,87 +26,57 @@ You must add the following to the list of appenders in your `log4js.json` file:
2626

2727
```
2828
{
29-
"type": "log4js-logmet-appender",
30-
"options": {
31-
"level": "INFO"
32-
}
29+
"type": "log4js-logmet-appender"
3330
}
3431
```
35-
You may substitute `INFO` with your own level above (for ex: `WARN`, `ERROR`, etc). Please check [log4js](https://www.npmjs.com/package/log4js) documentation for level based filtering since there might be some differences between versions.
3632

37-
To use custom fields to send logmet, you should define `eventMap` object under `options`. If it is not defined then default mapping is used.
33+
Using this configuration log messages to logmet will contain the following fields:
34+
- `component`: value of the environment variable `log4js_logmet_component`
35+
- `host-ip`: value of the environmental variable `CF_INSTANCE_IP`
36+
- `instance-id`: value of the environmental variable `CF_INSTANCE_INDEX`
37+
- `loglevel`: logging level (eg. `INFO`)
38+
- `logtime`: time message was logged in ISO string format (eg. `2011-10-05T14:48:00.000Z`)
39+
- `message`: log message/data, data array elements are concatenated with ` | ` delimeter
3840

39-
Default Event object (object sending to Logmet) consists of `component`, `host-ip`, `instance-id`, `loglevel`, `logtime`, `message` values.
41+
### Custom Fields
4042

41-
For custom fields, values can be map are given below:
42-
- `component`: The name of your component / service.
43-
- `host-ip`: The cloudfoundary ip defined as environmental variable CF_INSTANCE_IP
44-
- `instance-id`: The cloudfounrday index defined as environmental variable CF_INSTANCE_INDEX
45-
- `loglevel`: Logging level string. (exp. TRACE, DEBUG, INFO, WARN, ERROR, FATAL)
46-
- `logtime`: Logging time as ISO string format (exp. 2011-10-05T14:48:00.000Z)
47-
- `message`: Log message based on log4js configuration. If log data is multiple string, then is is merged with ` | ` delimiter.
48-
- `process.*` : To reach variables from `process` within two layer. (exp. process.pid, process.env.USER)
49-
- `data.*` : To reach variables from optional `object` type from log data within two layer. This is useful, if you want to pass a custom object and use the value in your logs withing a custom field. (exp. You can pass `{clientId: '123'}` object to log and map it as `CLIENT_ID: data.clientId`)
43+
To use custom fields, you should define an `options.fields` object.
44+
Note: If a `fields` object is defined, none of the default fields will be sent.
5045

51-
Example `log4js.json` file given below (for log4js version 0.6.38 and below).
52-
```
53-
{
54-
"appenders": [
55-
{
56-
"type": "console",
57-
"layout": { "type": "coloured" }
58-
},
59-
{
60-
"type": "logLevelFilter",
61-
"level": "INFO",
62-
"appender": {
63-
"type": "log4js-logmet-appender",
64-
"options": {
65-
"eventMap": {
66-
"TIMESTAMP": "logtime",
67-
"PRIORITY": "loglevel",
68-
"MESSAGE": "message",
69-
"SERVICE": "component",
70-
"_PID": "process.pid",
71-
"USER": "process.env.USER",
72-
"CUSTOM_ENV": "process.env.CUSTOM_VARIABLE"
73-
"TEST": "data.test",
74-
"TEST12": "data.test1.test2"
75-
}
76-
}
77-
}
78-
}
79-
],
80-
"replaceConsole": true
81-
}
82-
```
83-
And example `debug` logging call;
84-
```
85-
var log4js = require('log4js');
86-
log4js.configure('/path/to/log4js.json');
87-
var logger = log4js.getLogger();
88-
logger.level = 'debug';
89-
const objectToTest = { test: 'test 1 is good', test1: { test2: 'test 2 is better' } };
90-
logger.debug("Some debug messages", objectToTest);
91-
```
92-
This example prints coloured console log and filters `INFO` levels for `log4js-logmet-appender` appender.
93-
`eventMap` determines the custom mapping for the object sending logmet. Values will be replaced based on event mapping and will send to logmet.
46+
You can choose names for the log level (`"level_field_name"`), timestamp (`"timestamp_field_name"`) and data/message (`"data_field_name"`) fields. You may also add more custom static fields (under the `"static_fields"` object), fields that have node `process` object properties as values (under the `"process_fields"` object), and fields that have environment variables as values (under the `"env_fields"` object).
9447

95-
Example data to be sent to logmet based on given `log4js.json`
48+
Example with custom fields:
9649
```
97-
{
98-
"TIMESTAMP": "2011-10-05T14:48:00.000Z",
99-
"PRIORITY": "INFO",
100-
"MESSAGE": "Your log message",
101-
"SERVICE": "name of your component",
102-
"_PID": 1234,
103-
"USER": "Username of the process",
104-
"CUSTOM_ENV": "Environmental value defined as CUSTOM_VARIABLE",
105-
"TEST": "test 1 is good",
106-
"TEST12": "test 2 is better"
50+
{
51+
"type": "log4js-logmet-appender",
52+
"options": {
53+
"fields": {
54+
"level_field_name": "PRIORITY",
55+
"timestamp_field_name": "TIMESTAMP",
56+
"data_field_name": "MESSAGE"
57+
58+
"static_fields": {
59+
"COMPONENT": "otc-api"
60+
},
61+
62+
"process_fields": {
63+
"PROCESS_ID": "pid"
64+
},
65+
66+
"env_fields": {
67+
"INSTANCE_INDEX": "CF_INSTANCE_INDEX"
68+
}
69+
}
70+
}
10771
}
10872
```
10973

74+
You may also define custom dynamic fields by setting `options.fields` `"data_field_augment"` to `true` and passing a single object argument to the logger. Object keys will be mapped to field names.
75+
76+
Example: `logger.info({ MESSAGE: "hello", FLAG: "urgent" });`
77+
78+
Note: If `"data_field_augment"` is set to `true` and a single object argument is NOT passed to the logger, the log message/data will still be sent as a regular field if `"data_field_name"` is also set.
79+
11080
## Pending work
11181
No automated tests currently
11282

@@ -120,4 +90,4 @@ Contributions are welcome via Pull Requests. Please submit your very first Pull
12090

12191
```
12292
Signed-off-by: John Doe <john.doe@example.org>
123-
```
93+
```

index.js

Lines changed: 44 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,7 @@ module.exports = {
1919
shutdown: shutdown
2020
};
2121

22-
const messageSeperator = " | ";
23-
const defaultEventMap = {
24-
'component': 'component',
25-
'host-ip': 'host-ip',
26-
'instance-id':'instance-id',
27-
'loglevel': 'loglevel',
28-
'logtime': 'logtime',
29-
'message': 'message'
30-
};
31-
32-
function sendData(level, options, tlsOpts, log) {
22+
function sendData(options, tlsOpts, log) {
3323
var event = buildEvent(log, options);
3424
logmetConnection.producer.sendData(event, log.categoryName, options.space_id, function(/*error, status*/) {
3525
// message is dropped if an error is returned, errors already logged by logmet client
@@ -38,67 +28,37 @@ function sendData(level, options, tlsOpts, log) {
3828

3929
function buildEvent(log, options) {
4030
// build up message to send
41-
var event = {};
42-
if(options && options.event_map){
43-
for (var key in options.event_map) {
44-
if (!options.event_map.hasOwnProperty(key)) continue;
45-
var value = options.event_map[key];
46-
event[key] = getValueForMapping(value, options, log);
47-
}
48-
}
49-
return event;
50-
}
31+
let event = {};
32+
let fields = options.fields;
5133

52-
function getValueForMapping(key, options, log){
53-
var valueMapped;
54-
if(key === 'component'){
55-
valueMapped = options.component;
56-
} else if(key === 'host-ip'){
57-
valueMapped = process.env.CF_INSTANCE_IP;
58-
} else if(key === 'instance-id'){
59-
valueMapped = process.env.CF_INSTANCE_INDEX;
60-
} else if(key === 'loglevel'){
61-
valueMapped = log.level && log.level.levelStr;
62-
} else if(key === 'logtime'){
63-
valueMapped = log.startTime && log.startTime.toISOString();
64-
} else if(key === 'message'){
65-
valueMapped = log.data && log.data.reduce(function (a, b) {
66-
return ((typeof(a)==="object" && JSON.stringify(a)) || a) + messageSeperator + ((typeof(b)==="object" && JSON.stringify(b)) || b);
67-
});
68-
} else if(key.startsWith('process.') && key.length > 8){
69-
var processValue = key.substring(8).split('.');
70-
if(processValue.length == 1){
71-
valueMapped = process[processValue[0]];
72-
} else if (processValue.length == 2){
73-
valueMapped = process[processValue[0]] && process[processValue[0]][processValue[1]];
74-
}
75-
else{
76-
//do nothing - currently only support for 2 layer process information
77-
}
78-
} else if(key.startsWith('data.') && key.length > 5){ //Custom data parsing using log data array's first `object` type element
79-
var logDataObject = log.data && log.data.find(function(element) {
80-
return (typeof(element)==="object");
81-
});
82-
if(logDataObject){
83-
var dataValue = key.substring(5).split('.');
84-
if(dataValue.length == 1){
85-
valueMapped = logDataObject[dataValue[0]];
86-
} else if (dataValue.length == 2){
87-
valueMapped = logDataObject[dataValue[0]] && logDataObject[dataValue[0]][dataValue[1]];
88-
} else {
89-
//do nothing - currently only support for 2 layer data information
90-
}
91-
}
92-
else{
93-
//do nothing - there is no data object
34+
if (fields.level_field_name) {
35+
event[fields.level_field_name] = log.level && log.level.levelStr;
36+
}
37+
if (fields.timestamp_field_name) {
38+
event[fields.timestamp_field_name] = log.startTime && log.startTime.toISOString();
39+
}
40+
if (fields.data_field_augment && log.data && log.data.length === 1 && typeof log.data[0] === 'object') {
41+
let customDynamicFields = log.data[0];
42+
for (let customDynamicFieldName in customDynamicFields) {
43+
event[customDynamicFieldName] = customDynamicFields[customDynamicFieldName];
9444
}
95-
} else {
96-
// No valid mapping found for given key.
45+
} else if (fields.data_field_name) {
46+
event[fields.data_field_name] = log.data && log.data.join(' | ');
47+
}
48+
for (let staticFieldName in fields.static_fields) {
49+
event[staticFieldName] = fields.static_fields[staticFieldName];
50+
}
51+
for (let processFieldName in fields.process_fields) {
52+
event[processFieldName] = process[fields.process_fields[processFieldName]];
9753
}
98-
return valueMapped;
54+
for (let envFieldName in fields.env_fields) {
55+
event[envFieldName] = process.env[fields.env_fields[envFieldName]];
56+
}
57+
58+
return event;
9959
}
10060

101-
function appender(level, options, tlsOpts) {
61+
function appender(options, tlsOpts) {
10262
logmetConnection.producer = new logmet.LogmetProducer(tlsOpts.host, tlsOpts.port, options.space_id, options.logging_token, false, {bufferSize: logmetConnection.BUFFER_SIZE});
10363
logmetConnection.producer.connect(function(error, status) {
10464
if (error) {
@@ -107,17 +67,30 @@ function appender(level, options, tlsOpts) {
10767
util.log('Logmet Appender: LogmetClient is ready to send data.');
10868
}
10969
});
110-
return sendData.bind(this, level, options, tlsOpts);
70+
return sendData.bind(this, options, tlsOpts);
11171
}
11272

11373
function configure(config) {
11474
if (process.env.log4js_logmet_enabled !== 'true') return function() {};
11575

76+
const defaultFields = {
77+
'level_field_name': 'loglevel',
78+
'timestamp_field_name': 'logtime',
79+
'data_field_name': 'message',
80+
81+
'static_fields': {
82+
'component': process.env.log4js_logmet_component || config.options && config.options.component
83+
},
84+
'env_fields': {
85+
'host-ip': 'CF_INSTANCE_IP',
86+
'instance-id': 'CF_INSTANCE_INDEX'
87+
}
88+
};
89+
11690
const options = {
117-
component: process.env.log4js_logmet_component || config.options && config.options.component,
11891
logging_token: process.env.log4js_logmet_logging_token || config.options && config.options.logging_token,
11992
space_id: process.env.log4js_logmet_space_id || config.options && config.options.space_id,
120-
event_map: (config.options && config.options.eventMap) || defaultEventMap
93+
fields: (config.options && config.options.fields) || defaultFields
12194
};
12295

12396
const tlsOpts = {
@@ -143,10 +116,8 @@ function configure(config) {
143116
config.actualAppender = log4js.appenderMakers[config.appender.type](config.appender);
144117
}
145118

146-
const level = log4js.levels[config.options.level] && log4js.levels[config.options.level].level || Number.MIN_VALUE;
147-
148119
util.log('Logmet Appender configured');
149-
return appender(level, options, tlsOpts);
120+
return appender(options, tlsOpts);
150121
}
151122

152123
function shutdown(callback) {

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "log4js-logmet-appender",
3-
"version": "0.2.2",
3+
"version": "0.2.3",
44
"description": "Logmet appender for node-log4js",
55
"author": "Herman Singh Badwal <hermanba@ca.ibm.com>",
66
"license": "MIT",

0 commit comments

Comments
 (0)