nsyslog
Version:
Modular new generation log agent. Reads, transform, aggregate, correlate and send logs from sources to destinations
208 lines (183 loc) • 5.41 kB
JavaScript
const
logger = require('../logger'),
extend = require('extend'),
elastic = require('@elastic/elasticsearch'),
Watermark = require('../watermark'),
Queue = require('../queue'),
TLS = require('../tls'),
Input = require('./'),
{timer} = require('../util'),
jsexpr = require('jsexpr');
const { Client } = elastic;
// Default client options for Elasticsearch
const COPTS = {
maxRetries: 5,
requestTimeout: 60000,
sniffOnStart: true
};
// Default configuration values for the ElasticInput
const DEFAULTS = {
url: "http://localhost:9200",
tls: TLS.DEFAULT,
options: {},
};
/**
* ElasticInput class for handling Elasticsearch-based input.
* Extends the base Input class.
*/
class ElasticInput extends Input {
/**
* Constructor for ElasticInput.
* @param {string} id - Unique identifier for the input.
* @param {string} type - Type of the input.
*/
constructor(id, type) {
super(id, type);
}
/**
* Returns the mode of the input.
* @returns {string} The mode of the input (pull).
*/
get mode() {
return Input.MODE.pull;
}
/**
* Configures the ElasticInput with the provided settings.
* @param {Object} config - Configuration object.
* @param {Function} callback - Callback function to signal completion.
*/
async configure(config, callback) {
// Merge default and provided configurations
this.config = config = extend(true, {}, DEFAULTS, config);
config.url = Array.isArray(config.url) ? config.url : [config.url];
// Initialize configuration properties
this.url = config.url;
this.options = extend({}, config.options);
this.istls = config.url.some(url => url.startsWith('https'));
this.index = jsexpr.expr(config.index);
this.query = jsexpr.expr(config.query || { match: {} });
this.batchsize = config.batchsize || 100;
this.ival = parseInt(config.interval) || null;
this.queue = new Queue();
this.sort = config.sort;
this.watermark = new Watermark(config.$datadir);
this.owm = config.watermark || {};
// Configure TLS if HTTPS is used
if (this.istls) {
this.tls = await TLS.configure(config.tls, config.$path);
this.options.agentOptions = this.tls;
}
// Initialize Elasticsearch client
let copts = extend(true, COPTS, config.options, { node: config.url, auth: config.auth, ssl: this.tls });
this.client = new Client(copts);
// Initialize watermark
await this.watermark.start();
this.wm = await this.watermark.get(this.id);
if (!this.wm[this.index]) {
this.wm[this.index] = { last: this.owm };
}
await this.watermark.save(this.wm);
this.reading = null;
callback();
}
/**
* Fetches data from Elasticsearch and processes it.
* @returns {Promise<void>} Resolves when data is fetched and processed.
*/
async fetch() {
if (this.reading) return this.reading;
let wm = this.wm[this.index];
let last = wm.last;
let query = {
index: this.index(last),
size: this.batchsize,
body: { query: this.query(last), sort: this.sort }
};
logger.silly(`${this.id} Elastic query`, this.url, query);
this.reading = new Promise((ok, rej) => {
this.client.search(query, async (err, res) => {
if (err) return rej(err);
let body = res.body;
body.hits.hits.forEach(item => this.queue.push(item._source));
let newm = body.hits.hits.pop();
if (newm) wm.last = newm._source;
return ok();
});
}).then(() => {
this.reading = false;
return this.watermark.save(this.wm);
}).catch(err => {
logger.error(err);
this.reading = false;
return this.watermark.save(this.wm);
});
return this.reading;
}
/**
* Starts the ElasticInput and begins fetching data.
* @param {Function} callback - Callback function to signal completion.
*/
async start(callback) {
try {
this.fetch();
callback();
} catch (err) {
callback(err);
}
}
/**
* Retrieves the next item from the queue.
* @param {Function} callback - Callback function to process the next item.
*/
async next(callback) {
if (!this.queue.size()) {
do {
await this.fetch();
if (!this.queue.size()) {
await timer(500);
}
} while (!this.queue.size());
}
try {
let item = await this.queue.pop(1000);
if (item.err)
callback(item.err);
else
callback(null, { index: this.index, originalMessage: item });
} catch (err) {
callback(err);
}
}
/**
* Stops the ElasticInput and performs cleanup.
* @param {Function} callback - Callback function to signal completion.
*/
async stop(callback) {
await this.watermark.save(this.wm);
callback();
}
/**
* Pauses the ElasticInput by halting data fetching.
* @param {Function} callback - Callback function to signal completion.
*/
async pause(callback) {
await this.watermark.save(this.wm);
callback();
}
/**
* Resumes the ElasticInput by restarting data fetching.
* @param {Function} callback - Callback function to signal completion.
*/
resume(callback) {
callback();
}
/**
* Generates a unique key for the input entry.
* @param {Object} entry - Input entry object.
* @returns {string} Unique key for the entry.
*/
key(entry) {
return `${entry.input}:${entry.type}@${entry.url}`;
}
}
module.exports = ElasticInput;