cloki
Version:
LogQL API with Clickhouse Backend
104 lines (92 loc) • 2.98 kB
JavaScript
/* Elastic Indexing Handler
Accepts JSON formatted requests when the header Content-Type: application/json is sent.
POST /_bulk
POST /<target>/_bulk
*/
const stringify = require('json-stable-stringify')
function handler (req, res) {
const self = this
req.log.debug('ELASTIC Bulk Request')
if (!req.body) {
req.log.error('No Request Body or Target!'+ req.body)
res.code(400).send('{"status":400, "error": { "reason": "No Request Body" } }')
return
}
if (this.readonly) {
req.log.error('Readonly! No push support.')
res.code(400).send('{"status":400, "error": { "reason": "Read Only Mode" } }')
return
}
const doc_target = req.params.target || false;
let streams
if (
req.headers['content-type'] &&
req.headers['content-type'].indexOf('application/x-ndjson') > -1
) {
// ndjson body
streams = req.body.split(/\n/)
} else {
// assume ndjson raw body
streams = req.body.split(/\n/)
}
req.log.debug({ streams}, streams.lenght + ' bulk streams')
var last_tags = false;
if (streams) {
streams.forEach(function (stream) {
try {
stream = JSON.parse(stream)
} catch (err) { req.log.error({ err }); return };
// Allow Index, Create. Discard Delete, Update.
if (stream.delete||stream.update) { last_tags = false; return; }
var command = stream.index || stream.create || false;
if (command && !last_tags) {
last_tags=stream.index;
return;
}
// Data Rows
let finger = null
let JSONLabels = last_tags;
try {
try {
JSONLabels.type = 'elastic'
if (doc_target) JSONLabels._index = doc_target
JSONLabels = Object.fromEntries(Object.entries(JSONLabels).sort())
} catch (err) {
req.log.error({ err })
return
}
// Calculate Fingerprint
const strJson = stringify(JSONLabels)
finger = self.fingerPrint(strJson)
req.log.debug({ JSONLabels, finger }, 'LABELS FINGERPRINT')
// Store Fingerprint
self.bulk_labels.add([[
new Date().toISOString().split('T')[0],
finger,
strJson,
JSONLabels.target || ''
]])
for (const key in JSONLabels) {
req.log.debug({ key, data: JSONLabels[key] }, 'Storing label')
self.labels.add('_LABELS_', key)
self.labels.add(key, JSONLabels[key])
}
} catch (err) {
req.log.error({ err }, 'failed ingesting elastic doc')
}
// Store Elastic Doc Object
const values = [
finger,
BigInt((new Date().getTime() * 1000) + '000'),
null,
JSON.stringify(stream) || stream
]
req.log.debug({ finger, values }, 'store')
self.bulk.add([values])
// Reset State, Expect Command
last_tags = false;
})
}
res.code(200).send('{"took":0, "errors": false }')
}
module.exports = handler