@ditjenpajakri/elasticsearch-logging 2.0.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/apm.env +6 -0
- package/lib.js +223 -146
- package/package.json +2 -1
- package/test +1 -0
package/apm.env
ADDED
package/lib.js
CHANGED
|
@@ -1,172 +1,249 @@
|
|
|
1
1
|
#! /usr/bin/env node
|
|
2
|
-
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
const { Client } = require("@elastic/elasticsearch");
|
|
5
|
+
const split = require("split2");
|
|
6
|
+
const pump = require("pump");
|
|
7
|
+
const fs = require("fs");
|
|
8
|
+
const { configDotenv } = require("dotenv");
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Performs a deep merge of objects and returns new object. Does not modify
|
|
12
|
+
* objects (immutable) and merges arrays via concatenation.
|
|
13
|
+
*
|
|
14
|
+
* @param {...object} objects - Objects to merge
|
|
15
|
+
* @returns {object} New object with merged key/values
|
|
16
|
+
*/
|
|
17
|
+
function mergeDeep(...objects) {
|
|
18
|
+
const isObject = (obj) => obj && typeof obj === "object";
|
|
19
|
+
|
|
20
|
+
return objects.reduce((prev, obj) => {
|
|
21
|
+
Object.keys(obj).forEach((key) => {
|
|
22
|
+
const pVal = prev[key];
|
|
23
|
+
const oVal = obj[key];
|
|
24
|
+
|
|
25
|
+
if (Array.isArray(pVal) && Array.isArray(oVal)) {
|
|
26
|
+
prev[key] = pVal.concat(...oVal);
|
|
27
|
+
} else if (isObject(pVal) && isObject(oVal)) {
|
|
28
|
+
prev[key] = mergeDeep(pVal, oVal);
|
|
29
|
+
} else {
|
|
30
|
+
prev[key] = oVal;
|
|
31
|
+
}
|
|
32
|
+
});
|
|
3
33
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
34
|
+
return prev;
|
|
35
|
+
}, {});
|
|
36
|
+
}
|
|
7
37
|
|
|
8
38
|
function setDateTimeString(value) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
39
|
+
if (typeof value === "object" && value.hasOwnProperty("time")) {
|
|
40
|
+
if (
|
|
41
|
+
(typeof value.time === "string" && value.time.length) ||
|
|
42
|
+
(typeof value.time === "number" && value.time >= 0)
|
|
43
|
+
) {
|
|
44
|
+
return new Date(value.time).toISOString();
|
|
16
45
|
}
|
|
17
|
-
|
|
46
|
+
}
|
|
47
|
+
return new Date().toISOString();
|
|
18
48
|
}
|
|
19
49
|
|
|
20
|
-
function initializeBulkHandler
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
50
|
+
function initializeBulkHandler(opts, client, splitter) {
|
|
51
|
+
const esVersion = Number(opts.esVersion || opts["es-version"]) || 7;
|
|
52
|
+
const index = opts.index || "pino";
|
|
53
|
+
const buildIndexName = typeof index === "function" ? index : null;
|
|
54
|
+
// const type = esVersion >= 7 ? undefined : opts.type || "log";
|
|
55
|
+
const opType = esVersion >= 7 ? opts.opType || opts.op_type : undefined;
|
|
56
|
+
|
|
57
|
+
// Resurrect connection pool on destroy
|
|
58
|
+
splitter.destroy = () => {
|
|
59
|
+
if (typeof client.connectionPool.resurrect === "function") {
|
|
60
|
+
client.connectionPool.resurrect({ name: "elasticsearch-js" });
|
|
32
61
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
doc['@timestamp'] = date
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return {
|
|
48
|
-
create: {
|
|
49
|
-
_index: getIndexName(date),
|
|
50
|
-
// _type: type,
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
},
|
|
54
|
-
onDrop (doc) {
|
|
55
|
-
const error = new Error('Dropped document')
|
|
56
|
-
error.document = doc
|
|
57
|
-
splitter.emit('insertError', error)
|
|
58
|
-
}
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
bulkInsert.then(
|
|
62
|
-
(stats) => splitter.emit('insert', stats),
|
|
63
|
-
(err) => splitter.emit('error', err)
|
|
64
|
-
)
|
|
65
|
-
|
|
66
|
-
function getIndexName (time = new Date().toISOString()) {
|
|
67
|
-
if (buildIndexName) {
|
|
68
|
-
return buildIndexName(time)
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const bulkInsert = client.helpers.bulk({
|
|
65
|
+
datasource: splitter,
|
|
66
|
+
flushBytes: opts.flushBytes || opts["flush-bytes"] || 1000,
|
|
67
|
+
flushInterval: opts.flushInterval || opts["flush-interval"] || 30000,
|
|
68
|
+
refreshOnCompletion: getIndexName(),
|
|
69
|
+
onDocument(doc) {
|
|
70
|
+
const date = doc.time || doc["@timestamp"];
|
|
71
|
+
if (opType === "create") {
|
|
72
|
+
doc["@timestamp"] = date;
|
|
69
73
|
}
|
|
70
|
-
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
create: {
|
|
77
|
+
_index: getIndexName(date),
|
|
78
|
+
// _type: type,
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
},
|
|
82
|
+
onDrop(doc) {
|
|
83
|
+
const error = new Error("Dropped document");
|
|
84
|
+
error.document = doc;
|
|
85
|
+
splitter.emit("insertError", error);
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
bulkInsert.then(
|
|
90
|
+
(stats) => splitter.emit("insert", stats),
|
|
91
|
+
(err) => splitter.emit("error", err)
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
function getIndexName(time = new Date().toISOString()) {
|
|
95
|
+
if (buildIndexName) {
|
|
96
|
+
return buildIndexName(time);
|
|
71
97
|
}
|
|
98
|
+
return index.replace(
|
|
99
|
+
"%{DATE}",
|
|
100
|
+
time.substring ? time.substring(0, 10) : ""
|
|
101
|
+
);
|
|
72
102
|
}
|
|
73
|
-
|
|
103
|
+
}
|
|
74
104
|
|
|
75
105
|
function pinoElasticSearch(opts = {}) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
if (typeof value === 'boolean') {
|
|
87
|
-
this.emit('unknown', line, 'Boolean value ignored')
|
|
88
|
-
return
|
|
89
|
-
}
|
|
90
|
-
if (value === null) {
|
|
91
|
-
this.emit('unknown', line, 'Null value ignored')
|
|
92
|
-
return
|
|
93
|
-
}
|
|
94
|
-
if (typeof value !== 'object') {
|
|
95
|
-
value = {
|
|
96
|
-
data: value,
|
|
97
|
-
['@timestamp']: setDateTimeString(value)
|
|
98
|
-
}
|
|
99
|
-
} else {
|
|
100
|
-
if (value['@timestamp'] === undefined) {
|
|
101
|
-
value['@timestamp'] = setDateTimeString(value)
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
return value
|
|
105
|
-
}, { autoDestroy: true })
|
|
106
|
-
|
|
107
|
-
const clientOpts = {
|
|
108
|
-
node: opts.node,
|
|
109
|
-
auth: opts.auth,
|
|
110
|
-
// cloud: opts.cloud,
|
|
111
|
-
tls: {
|
|
112
|
-
rejectUnauthorized: false
|
|
113
|
-
}
|
|
114
|
-
}
|
|
106
|
+
const splitter = split(
|
|
107
|
+
function (line) {
|
|
108
|
+
let value;
|
|
109
|
+
try {
|
|
110
|
+
value = JSON.parse(line);
|
|
111
|
+
} catch (error) {
|
|
112
|
+
console.log(line);
|
|
113
|
+
// this.emit('unknown', line, error)
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
115
116
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
117
|
+
if (typeof value === "boolean") {
|
|
118
|
+
this.emit("unknown", line, "Boolean value ignored");
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
if (value === null) {
|
|
122
|
+
this.emit("unknown", line, "Null value ignored");
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (typeof value !== "object") {
|
|
126
|
+
value = {
|
|
127
|
+
data: value,
|
|
128
|
+
["@timestamp"]: setDateTimeString(value),
|
|
129
|
+
};
|
|
130
|
+
} else {
|
|
131
|
+
value["message"] = value.msg || "-";
|
|
119
132
|
|
|
120
|
-
|
|
121
|
-
clientOpts.Connection = opts.Connection
|
|
122
|
-
}
|
|
133
|
+
if (value.msg) delete value.msg;
|
|
123
134
|
|
|
124
|
-
|
|
125
|
-
clientOpts.ConnectionPool = opts.ConnectionPool
|
|
126
|
-
}
|
|
135
|
+
value["@timestamp"] = setDateTimeString(value);
|
|
127
136
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
137
|
+
if (value.time) delete value.time;
|
|
138
|
+
}
|
|
139
|
+
return mergeDeep(value, opts.additionalObject);
|
|
140
|
+
},
|
|
141
|
+
{ autoDestroy: true }
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
const clientOpts = {
|
|
145
|
+
node: opts.node,
|
|
146
|
+
auth: opts.auth,
|
|
147
|
+
// cloud: opts.cloud,
|
|
148
|
+
tls: {
|
|
149
|
+
rejectUnauthorized: false,
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
if (opts.caFingerprint) {
|
|
154
|
+
clientOpts.caFingerprint = opts.caFingerprint;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (opts.Connection) {
|
|
158
|
+
clientOpts.Connection = opts.Connection;
|
|
159
|
+
}
|
|
134
160
|
|
|
135
|
-
|
|
161
|
+
if (opts.ConnectionPool) {
|
|
162
|
+
clientOpts.ConnectionPool = opts.ConnectionPool;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const client = new Client(clientOpts);
|
|
166
|
+
console.log("Logging to elasticsearch...");
|
|
167
|
+
console.log(
|
|
168
|
+
"Any line showing up here means that the line was not logged to elasticsearch."
|
|
169
|
+
);
|
|
170
|
+
client.diagnostic.on("resurrect", () => {
|
|
171
|
+
initializeBulkHandler(opts, client, splitter);
|
|
172
|
+
});
|
|
136
173
|
|
|
137
|
-
|
|
174
|
+
initializeBulkHandler(opts, client, splitter);
|
|
175
|
+
|
|
176
|
+
return splitter;
|
|
138
177
|
}
|
|
139
178
|
|
|
140
179
|
function start(opts) {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
180
|
+
console.log("opts", opts);
|
|
181
|
+
const stream = pinoElasticSearch(opts);
|
|
182
|
+
|
|
183
|
+
stream.on("unknown", (line, error) => {
|
|
184
|
+
console.error(
|
|
185
|
+
"Elasticsearch client json error in line:\n" + line + "\nError:",
|
|
186
|
+
error
|
|
187
|
+
);
|
|
188
|
+
});
|
|
189
|
+
stream.on("error", (error) => {
|
|
190
|
+
console.error("Elasticsearch client error:", error);
|
|
191
|
+
});
|
|
192
|
+
stream.on("insertError", (error) => {
|
|
193
|
+
console.log(JSON.stringify(error));
|
|
194
|
+
console.error("Elasticsearch server error:", error);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
pump(process.stdin, stream);
|
|
156
198
|
}
|
|
157
199
|
|
|
158
|
-
module.exports = pinoElasticSearch
|
|
200
|
+
module.exports = pinoElasticSearch;
|
|
159
201
|
|
|
160
202
|
if (require.main === module) {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
203
|
+
const fileEnv = process.argv[2];
|
|
204
|
+
|
|
205
|
+
if (fileEnv && fs.statSync(fileEnv)) {
|
|
206
|
+
configDotenv({
|
|
207
|
+
path: fileEnv,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const additionalFields = Object.keys(process.env).filter((key) =>
|
|
212
|
+
key.startsWith("LOG_ES_VAR_")
|
|
213
|
+
).map(s => s.toLowerCase())
|
|
214
|
+
|
|
215
|
+
const additionalObject = additionalFields.reduce((obj, field) => {
|
|
216
|
+
const value = process.env[field.toLocaleUpperCase()];
|
|
217
|
+
function processKey(f, value) {
|
|
218
|
+
const parts = f ? f.split("_") : [];
|
|
219
|
+
if (parts.length) {
|
|
220
|
+
return processKey(parts.slice(0, parts.length - 1).join("_"), {
|
|
221
|
+
[parts[parts.length - 1]]: value,
|
|
222
|
+
});
|
|
223
|
+
} else {
|
|
224
|
+
if (f) return { [f]: value };
|
|
225
|
+
else return value;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return mergeDeep(obj, processKey(field, value));
|
|
230
|
+
}, {});
|
|
231
|
+
|
|
232
|
+
start({
|
|
233
|
+
node: process.env.LOG_ES_HOST || "http://localhost:9200",
|
|
234
|
+
additionalObject: additionalObject?.log?.es?.var ? additionalObject.log.es.var : {},
|
|
235
|
+
auth: {
|
|
236
|
+
username: process.env.LOG_ES_USER || "elastic",
|
|
237
|
+
password: process.env.LOG_ES_PASS || "changeme",
|
|
238
|
+
},
|
|
239
|
+
index: process.env.LOG_ES_INDEX || "logs-apm-%{DATE}",
|
|
240
|
+
flushBytes: process.env.LOG_ES_FLUSH_BYTES
|
|
241
|
+
? parseInt(process.env.LOG_ES_FLUSH_BYTES)
|
|
242
|
+
: undefined,
|
|
243
|
+
flushInterval: process.env.LOG_ES_FLUSH_INTERVAL
|
|
244
|
+
? parseInt(process.env.LOG_ES_FLUSH_INTERVAL)
|
|
245
|
+
: undefined,
|
|
246
|
+
rejectUnauthorized:
|
|
247
|
+
process.env.LOG_ES_REJECT_UNAUTHORIZED === "true" ? true : false,
|
|
248
|
+
});
|
|
249
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ditjenpajakri/elasticsearch-logging",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "Elasticsearch ",
|
|
5
5
|
"main": "lib.js",
|
|
6
6
|
"bin": {
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"license": "MIT",
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"@elastic/elasticsearch": "8",
|
|
20
|
+
"dotenv": "^16.4.5",
|
|
20
21
|
"pump": "^3.0.0",
|
|
21
22
|
"split2": "^4.2.0"
|
|
22
23
|
},
|
package/test
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"a": "1234"}
|