Skip to content

Commit 36853ea

Browse files
Dogukan Erenelsimonkotwicz
authored andcommitted
Add support for custom fields and custom field names
1 parent 18a4608 commit 36853ea

File tree

2 files changed

+142
-13
lines changed

2 files changed

+142
-13
lines changed

README.md

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,80 @@ You must add the following to the list of appenders in your `log4js.json` file:
3232
}
3333
}
3434
```
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.
3536

36-
You may substitute `INFO` with your own level above (for ex: `WARN`, `ERROR`, etc)
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.
38+
39+
Default Event object (object sending to Logmet) consists of `component`, `host-ip`, `instance-id`, `loglevel`, `logtime`, `message` values.
40+
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`)
50+
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.
94+
95+
Example data to be sent to logmet based on given `log4js.json`
96+
```
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"
107+
}
108+
```
37109

38110
## Pending work
39111
No automated tests currently

index.js

Lines changed: 69 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,16 @@ 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+
2232
function sendData(level, options, tlsOpts, log) {
2333
var event = buildEvent(log, options);
2434
logmetConnection.producer.sendData(event, log.categoryName, options.space_id, function(/*error, status*/) {
@@ -28,20 +38,66 @@ function sendData(level, options, tlsOpts, log) {
2838

2939
function buildEvent(log, options) {
3040
// build up message to send
31-
var logData = log.data && log.data.join(' | ');
32-
33-
var event = {
34-
'component': options.component,
35-
'host-ip': process.env.CF_INSTANCE_IP,
36-
'instance-id': process.env.CF_INSTANCE_INDEX,
37-
'loglevel': log.level && log.level.levelStr,
38-
'logtime': log.startTime && log.startTime.toISOString(),
39-
'message': logData
40-
};
41-
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+
}
4249
return event;
4350
}
4451

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
94+
}
95+
} else {
96+
// No valid mapping found for given key.
97+
}
98+
return valueMapped;
99+
}
100+
45101
function appender(level, options, tlsOpts) {
46102
logmetConnection.producer = new logmet.LogmetProducer(tlsOpts.host, tlsOpts.port, options.space_id, options.logging_token, false, {bufferSize: logmetConnection.BUFFER_SIZE});
47103
logmetConnection.producer.connect(function(error, status) {
@@ -60,7 +116,8 @@ function configure(config) {
60116
const options = {
61117
component: process.env.log4js_logmet_component || config.options && config.options.component,
62118
logging_token: process.env.log4js_logmet_logging_token || config.options && config.options.logging_token,
63-
space_id: process.env.log4js_logmet_space_id || config.options && config.options.space_id
119+
space_id: process.env.log4js_logmet_space_id || config.options && config.options.space_id,
120+
event_map: (config.options && config.options.eventMap) || defaultEventMap
64121
};
65122

66123
const tlsOpts = {

0 commit comments

Comments
 (0)