@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.
Files changed (4) hide show
  1. package/apm.env +6 -0
  2. package/lib.js +223 -146
  3. package/package.json +2 -1
  4. package/test +1 -0
package/apm.env ADDED
@@ -0,0 +1,6 @@
1
+ LOG_ES_HOST=https://10.244.66.34:9200
2
+ LOG_ES_PASS=jkljkl2020
3
+ LOG_ES_VAR_A_AA=aaa
4
+ LOG_ES_VAR_A_BB=bbb
5
+ LOG_ES_VAR_B=ccc
6
+ LOG_ES_INDEX=test-index-%{DATE}
package/lib.js CHANGED
@@ -1,172 +1,249 @@
1
1
  #! /usr/bin/env node
2
- 'use strict'
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
- const { Client } = require('@elastic/elasticsearch')
5
- const split = require('split2')
6
- const pump = require('pump')
34
+ return prev;
35
+ }, {});
36
+ }
7
37
 
8
38
  function setDateTimeString(value) {
9
- if (typeof value === 'object' && value.hasOwnProperty('time')) {
10
- if (
11
- (typeof value.time === 'string' && value.time.length) ||
12
- (typeof value.time === 'number' && value.time >= 0)
13
- ) {
14
- return new Date(value.time).toISOString()
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
- return new Date().toISOString()
46
+ }
47
+ return new Date().toISOString();
18
48
  }
19
49
 
20
- function initializeBulkHandler (opts, client, splitter) {
21
- const esVersion = Number(opts.esVersion || opts['es-version']) || 7
22
- const index = opts.index || 'pino'
23
- const buildIndexName = typeof index === 'function' ? index : null
24
- const type = esVersion >= 7 ? undefined : (opts.type || 'log')
25
- const opType = esVersion >= 7 ? (opts.opType || opts.op_type) : undefined
26
-
27
- // Resurrect connection pool on destroy
28
- splitter.destroy = () => {
29
- if (typeof client.connectionPool.resurrect === 'function') {
30
- client.connectionPool.resurrect({ name: 'elasticsearch-js' })
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
- console.log(client)
35
-
36
- const bulkInsert = client.helpers.bulk({
37
- datasource: splitter,
38
- flushBytes: opts.flushBytes || opts['flush-bytes'] || 1000,
39
- flushInterval: opts.flushInterval || opts['flush-interval'] || 30000,
40
- refreshOnCompletion: getIndexName(),
41
- onDocument (doc) {
42
- const date = doc.time || doc['@timestamp']
43
- if (opType === 'create') {
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
- return index.replace('%{DATE}', time.substring ? time.substring(0, 10) : "")
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
- const splitter = split(function (line) {
77
- let value
78
- try {
79
- value = JSON.parse(line)
80
- } catch (error) {
81
- console.log(line);
82
- // this.emit('unknown', line, error)
83
- return
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
- if (opts.caFingerprint) {
117
- clientOpts.caFingerprint = opts.caFingerprint
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
- if (opts.Connection) {
121
- clientOpts.Connection = opts.Connection
122
- }
133
+ if (value.msg) delete value.msg;
123
134
 
124
- if (opts.ConnectionPool) {
125
- clientOpts.ConnectionPool = opts.ConnectionPool
126
- }
135
+ value["@timestamp"] = setDateTimeString(value);
127
136
 
128
- const client = new Client(clientOpts)
129
- console.log("Logging to elasticsearch...");
130
- console.log("Any line showing up here means that the line was not logged to elasticsearch.");
131
- client.diagnostic.on('resurrect', () => {
132
- initializeBulkHandler(opts, client, splitter)
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
- initializeBulkHandler(opts, client, splitter)
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
- return splitter
174
+ initializeBulkHandler(opts, client, splitter);
175
+
176
+ return splitter;
138
177
  }
139
178
 
140
179
  function start(opts) {
141
- console.log('opts', opts)
142
- const stream = pinoElasticSearch(opts)
143
-
144
- stream.on('unknown', (line, error) => {
145
- console.error('Elasticsearch client json error in line:\n' + line + '\nError:', error)
146
- })
147
- stream.on('error', (error) => {
148
- console.error('Elasticsearch client error:', error)
149
- })
150
- stream.on('insertError', (error) => {
151
- console.log(JSON.stringify(error))
152
- console.error('Elasticsearch server error:', error)
153
- })
154
-
155
- pump(process.stdin, stream)
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
- start({
162
- node: process.env.LOG_ES_HOST || 'http://localhost:9200',
163
- auth: {
164
- username: process.env.LOG_ES_USER || 'elastic',
165
- password: process.env.LOG_ES_PASS || 'changeme'
166
- },
167
- index: process.env.LOG_ES_INDEX || 'DJP-%{DATE}',
168
- flushBytes: process.env.LOG_ES_FLUSH_BYTES ? parseInt(process.env.LOG_ES_FLUSH_BYTES) : undefined,
169
- flushInterval: process.env.LOG_ES_FLUSH_INTERVAL ? parseInt(process.env.LOG_ES_FLUSH_INTERVAL) : undefined,
170
- rejectUnauthorized: process.env.LOG_ES_REJECT_UNAUTHORIZED === 'true' ? true : false,
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.0.0",
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"}