forked from JaniAnttonen/winston-loki
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
141 lines (124 loc) · 3.84 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
const Transport = require('winston-transport')
const Batcher = require('./src/batcher')
const { MESSAGE } = require('triple-beam')
/**
* A Winston transport for Grafana Loki.
*
* @class LokiTransport
* @extends {Transport}
*/
class LokiTransport extends Transport {
/**
* Creates an instance of LokiTransport.
* @param {*} options
* @memberof LokiTransport
*/
constructor (options) {
super(options)
// Pass all the given options to batcher
this.batcher = new Batcher({
host: options.host,
basicAuth: options.basicAuth,
headers: options.headers || {},
interval: options.interval,
json: options.json,
batching: options.batching !== false,
clearOnError: options.clearOnError,
onConnectionError: options.onConnectionError,
replaceTimestamp: options.replaceTimestamp !== false,
gracefulShutdown: options.gracefulShutdown !== false,
timeout: options.timeout,
httpAgent: options.httpAgent,
httpsAgent: options.httpsAgent
})
this.useCustomFormat = options.format !== undefined
this.labels = options.labels
this.useWinstonMetaAsLabels = options.useWinstonMetaAsLabels
this.ignoredMeta = options.ignoredMeta || []
}
/**
* An overwrite of winston-transport's log(),
* which the Winston logging library uses
* when pushing logs to a transport.
*
* @param {*} info
* @param {*} callback
* @memberof LokiTransport
*/
log (info, callback) {
// Immediately tell Winston that this transport has received the log.
setImmediate(() => {
this.emit('logged', info)
})
// Deconstruct the log
const { label, labels, timestamp, message, ...rest } = info
const level = info[Symbol.for('level')]
// build custom labels if provided
let lokiLabels = { level: level }
if (this.useWinstonMetaAsLabels) {
// deleting the keys (labels) that we want to ignore from Winston's meta
for (const [key, _] of Object.entries(rest)) {
if (this.ignoredMeta.includes(key)) delete rest[key]
}
lokiLabels = Object.assign(lokiLabels, rest)
} else if (this.labels) {
lokiLabels = Object.assign(lokiLabels, this.labels)
} else {
lokiLabels.job = label
}
lokiLabels = Object.assign(lokiLabels, labels)
// follow the format provided
const line = this.useCustomFormat
? info[MESSAGE]
: `${message} ${
rest && Object.keys(rest).length > 0 ? JSON.stringify(rest) : ''
}`
// Make sure all label values are strings
lokiLabels = Object.fromEntries(Object.entries(lokiLabels).map(([key, value]) => [key, value ? value.toString() : value]))
// Construct the log to fit Grafana Loki's accepted format
let ts
if (timestamp) {
ts = new Date(timestamp)
ts = isNaN(ts) ? Date.now() : ts.valueOf()
} else {
ts = Date.now()
}
const logEntry = {
labels: lokiLabels,
entries: [
{
ts,
line
}
]
}
// Pushes the log to the batcher
this.batcher.pushLogEntry(logEntry).catch(err => {
// eslint-disable-next-line no-console
console.error(err)
})
// Trigger the optional callback
callback()
}
/**
* Flush unsent batched logs to Winston transport and return
* a promise that resolves after response is received from
* the transport. If some (batched or not) logs are being sent
* at the time of call, the promise resolves after the transport
* responds.
*
* As a result the promise returned resolves only when the transport
* has confirmed receiving all the logs sent via log(), info(), etc
* calls preceding the flush() call.
*/
async flush () {
return await this.batcher.waitFlushed();
}
/**
* Send batch to loki when clean up
*/
close () {
this.batcher.close()
}
}
module.exports = LokiTransport