@theotherwillembotha/node-red-loki 0.0.56 → 0.3.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/README.md +1 -10
- package/build/GenerateNodes.d.ts +2 -0
- package/build/GenerateNodes.d.ts.map +1 -0
- package/build/GenerateNodes.js +2 -1
- package/build/Nodes.html +347 -512
- package/build/Nodes.js +61481 -12
- package/build/Plugins.html +37 -0
- package/build/Plugins.js +61405 -55
- package/build/index.d.ts +5 -0
- package/build/index.d.ts.map +1 -0
- package/build/loki/node/LokiLoggerConfigNode.d.ts +25 -0
- package/build/loki/node/LokiLoggerConfigNode.d.ts.map +1 -0
- package/build/loki/node/LokiQueryNode.d.ts +21 -0
- package/build/loki/node/LokiQueryNode.d.ts.map +1 -0
- package/build/loki/node/LokiServiceConfigNode.d.ts +17 -0
- package/build/loki/node/LokiServiceConfigNode.d.ts.map +1 -0
- package/build/loki/service/LokiService.d.ts +10 -0
- package/build/loki/service/LokiService.d.ts.map +1 -0
- package/build/loki/service/LokiService.js +19 -5
- package/build/runtime/NodeManagerRuntime.js +140 -0
- package/icons/loki.png +0 -0
- package/package.json +8 -4
package/build/Nodes.html
CHANGED
|
@@ -7,7 +7,339 @@
|
|
|
7
7
|
You have been warned.
|
|
8
8
|
|
|
9
9
|
-->
|
|
10
|
+
<!-- LokiQueryNode -->
|
|
11
|
+
|
|
12
|
+
<script type="text/javascript">
|
|
13
|
+
|
|
14
|
+
window.PluginCore = window.PluginCore || {};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* PluginCore.createLogQLEditor(elementId, initialValue)
|
|
18
|
+
*
|
|
19
|
+
* Creates a Monaco editor pre-configured with the LogQL language:
|
|
20
|
+
* syntax highlighting for stream selectors, pipeline stages, metric
|
|
21
|
+
* functions, and aggregation operators; plus snippet completions.
|
|
22
|
+
*
|
|
23
|
+
* The LogQL language is registered with Monaco lazily on first call.
|
|
24
|
+
*
|
|
25
|
+
* @param {string} elementId - DOM id of the container element.
|
|
26
|
+
* @param {string} initialValue - Starting content of the editor.
|
|
27
|
+
* @returns {{ getValue(): string, dispose(): void \}}
|
|
28
|
+
*/
|
|
29
|
+
PluginCore.createLogQLEditor = function(elementId, initialValue) {
|
|
30
|
+
|
|
31
|
+
// Register the LogQL language with Monaco once per session.
|
|
32
|
+
if (!PluginCore._logqlReady) {
|
|
33
|
+
PluginCore._logqlReady = true;
|
|
34
|
+
|
|
35
|
+
monaco.languages.register({ id: 'logql' });
|
|
36
|
+
|
|
37
|
+
monaco.languages.setLanguageConfiguration('logql', {
|
|
38
|
+
brackets: [['{', '}'], ['[', ']'], ['(', ')']],
|
|
39
|
+
autoClosingPairs: [
|
|
40
|
+
{ open: '{', close: '}' },
|
|
41
|
+
{ open: '[', close: ']' },
|
|
42
|
+
{ open: '(', close: ')' },
|
|
43
|
+
{ open: '"', close: '"' },
|
|
44
|
+
{ open: "'", close: "'" },
|
|
45
|
+
{ open: '`', close: '`' }
|
|
46
|
+
],
|
|
47
|
+
surroundingPairs: [
|
|
48
|
+
{ open: '{', close: '}' },
|
|
49
|
+
{ open: '[', close: ']' },
|
|
50
|
+
{ open: '(', close: ')' },
|
|
51
|
+
{ open: '"', close: '"' },
|
|
52
|
+
{ open: "'", close: "'" }
|
|
53
|
+
]
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
monaco.languages.setMonarchTokensProvider('logql', {
|
|
57
|
+
defaultToken: '',
|
|
58
|
+
tokenPostfix: '.logql',
|
|
59
|
+
|
|
60
|
+
keywords: ['by', 'without', 'on', 'ignoring', 'group_left', 'group_right', 'bool', 'or', 'and', 'unless', 'offset'],
|
|
61
|
+
metricFns: ['rate', 'count_over_time', 'bytes_rate', 'bytes_over_time', 'absent_over_time',
|
|
62
|
+
'sum_over_time', 'avg_over_time', 'min_over_time', 'max_over_time',
|
|
63
|
+
'first_over_time', 'last_over_time', 'stddev_over_time', 'stdvar_over_time',
|
|
64
|
+
'quantile_over_time', 'label_replace', 'vector', 'sort', 'sort_desc'],
|
|
65
|
+
aggregations: ['sum', 'avg', 'min', 'max', 'count', 'stddev', 'stdvar', 'bottomk', 'topk'],
|
|
66
|
+
pipelineStages: ['json', 'logfmt', 'pattern', 'regexp', 'unpack', 'line_format',
|
|
67
|
+
'label_format', 'drop', 'keep', 'decolorize', 'unwrap', 'ip',
|
|
68
|
+
'duration', 'bytes', 'distinct'],
|
|
69
|
+
|
|
70
|
+
tokenizer: {
|
|
71
|
+
root: [
|
|
72
|
+
// Stream selector open brace
|
|
73
|
+
[/\{/, { token: 'delimiter.curly', bracket: '@open', next: '@streamSelector' }],
|
|
74
|
+
// Range vector e.g. [5m]
|
|
75
|
+
[/\[\d+(ms|s|m|h|d|w|y)\]/, 'number.duration'],
|
|
76
|
+
// Pipeline filter operators
|
|
77
|
+
[/[|!][=~]/, 'operator.filter'],
|
|
78
|
+
// Pipe
|
|
79
|
+
[/\|/, 'operator.pipe'],
|
|
80
|
+
// Comparison operators
|
|
81
|
+
[/[><=!]+/, 'operator'],
|
|
82
|
+
// Numbers
|
|
83
|
+
[/\d+(\.\d+)?/, 'number'],
|
|
84
|
+
// Identifiers -> map to keyword/function/aggregation/pipeline categories
|
|
85
|
+
[/[a-zA-Z_]\w*/, {
|
|
86
|
+
cases: {
|
|
87
|
+
'@keywords': 'keyword',
|
|
88
|
+
'@aggregations': 'type.aggregate',
|
|
89
|
+
'@metricFns': 'type.function',
|
|
90
|
+
'@pipelineStages': 'type.pipeline',
|
|
91
|
+
'@default': 'identifier'
|
|
92
|
+
}
|
|
93
|
+
}],
|
|
94
|
+
// String literals
|
|
95
|
+
[/"/, { token: 'string.quote', bracket: '@open', next: '@dblString' }],
|
|
96
|
+
[/'/, { token: 'string.quote', bracket: '@open', next: '@sglString' }],
|
|
97
|
+
[/`/, { token: 'string.quote', bracket: '@open', next: '@rawString' }]
|
|
98
|
+
],
|
|
99
|
+
|
|
100
|
+
streamSelector: [
|
|
101
|
+
[/\}/, { token: 'delimiter.curly', bracket: '@close', next: '@pop' }],
|
|
102
|
+
[/[a-zA-Z_]\w*/, 'attribute.name'],
|
|
103
|
+
[/=~|!=|=|!~/, 'operator'],
|
|
104
|
+
[/"/, { token: 'string.quote', bracket: '@open', next: '@dblString' }],
|
|
105
|
+
[/'/, { token: 'string.quote', bracket: '@open', next: '@sglString' }],
|
|
106
|
+
[/`/, { token: 'string.quote', bracket: '@open', next: '@rawString' }],
|
|
107
|
+
[/,/, 'delimiter']
|
|
108
|
+
],
|
|
109
|
+
|
|
110
|
+
dblString: [
|
|
111
|
+
[/[^"\\]+/, 'string'],
|
|
112
|
+
[/\\./, 'string.escape'],
|
|
113
|
+
[/"/, { token: 'string.quote', bracket: '@close', next: '@pop' }]
|
|
114
|
+
],
|
|
115
|
+
sglString: [
|
|
116
|
+
[/[^'\\]+/, 'string'],
|
|
117
|
+
[/\\./, 'string.escape'],
|
|
118
|
+
[/'/, { token: 'string.quote', bracket: '@close', next: '@pop' }]
|
|
119
|
+
],
|
|
120
|
+
rawString: [
|
|
121
|
+
[/[^`]+/, 'string'],
|
|
122
|
+
[/`/, { token: 'string.quote', bracket: '@close', next: '@pop' }]
|
|
123
|
+
]
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
monaco.languages.registerCompletionItemProvider('logql', {
|
|
128
|
+
triggerCharacters: ['{', '|', ' ', '('],
|
|
129
|
+
provideCompletionItems: function(model, position) {
|
|
130
|
+
var word = model.getWordUntilPosition(position);
|
|
131
|
+
var range = {
|
|
132
|
+
startLineNumber: position.lineNumber,
|
|
133
|
+
endLineNumber: position.lineNumber,
|
|
134
|
+
startColumn: word.startColumn,
|
|
135
|
+
endColumn: word.endColumn
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
var mk = monaco.languages.CompletionItemKind;
|
|
139
|
+
var SR = monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet;
|
|
140
|
+
var suggestions = [];
|
|
141
|
+
|
|
142
|
+
// Stream selector template
|
|
143
|
+
suggestions.push({
|
|
144
|
+
label: '{label="value"}',
|
|
145
|
+
kind: mk.Snippet,
|
|
146
|
+
detail: 'Log stream selector',
|
|
147
|
+
documentation: 'Select log streams matching a set of label matchers.',
|
|
148
|
+
insertText: '{${1:app}="${2:nodered}"}',
|
|
149
|
+
insertTextRules: SR,
|
|
150
|
+
range: range
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// Metric query functions
|
|
154
|
+
[
|
|
155
|
+
{ l: 'rate', d: 'Per-second rate of log entries', s: 'rate({${1:app}="${2}"} [${3:5m}])' },
|
|
156
|
+
{ l: 'count_over_time', d: 'Count of log entries over time range', s: 'count_over_time({${1:app}="${2}"} [${3:5m}])' },
|
|
157
|
+
{ l: 'bytes_rate', d: 'Per-second byte rate of log entries', s: 'bytes_rate({${1:app}="${2}"} [${3:5m}])' },
|
|
158
|
+
{ l: 'bytes_over_time', d: 'Total bytes of log entries over range', s: 'bytes_over_time({${1:app}="${2}"} [${3:5m}])' },
|
|
159
|
+
{ l: 'absent_over_time', d: 'Returns 1 if no entries found in range', s: 'absent_over_time({${1:app}="${2}"} [${3:5m}])' },
|
|
160
|
+
{ l: 'sum_over_time', d: 'Sum of unwrapped values over range', s: 'sum_over_time({${1:app}="${2}"} | unwrap ${3:field} [${4:5m}])' },
|
|
161
|
+
{ l: 'avg_over_time', d: 'Average of unwrapped values over range', s: 'avg_over_time({${1:app}="${2}"} | unwrap ${3:field} [${4:5m}])' },
|
|
162
|
+
{ l: 'min_over_time', d: 'Minimum of unwrapped values over range', s: 'min_over_time({${1:app}="${2}"} | unwrap ${3:field} [${4:5m}])' },
|
|
163
|
+
{ l: 'max_over_time', d: 'Maximum of unwrapped values over range', s: 'max_over_time({${1:app}="${2}"} | unwrap ${3:field} [${4:5m}])' },
|
|
164
|
+
{ l: 'quantile_over_time', d: 'Quantile of unwrapped values', s: 'quantile_over_time(${1:0.99}, {${2:app}="${3}"} | unwrap ${4:field} [${5:5m}])' },
|
|
165
|
+
{ l: 'first_over_time', d: 'First unwrapped value over range', s: 'first_over_time({${1:app}="${2}"} | unwrap ${3:field} [${4:5m}])' },
|
|
166
|
+
{ l: 'last_over_time', d: 'Last unwrapped value over range', s: 'last_over_time({${1:app}="${2}"} | unwrap ${3:field} [${4:5m}])' },
|
|
167
|
+
{ l: 'label_replace', d: 'Replace label value using regex', s: 'label_replace(${1:expr}, "${2:dst_label}", "${3:$1}", "${4:src_label}", "${5:(.*)}")' }
|
|
168
|
+
].forEach(function(f) {
|
|
169
|
+
suggestions.push({ label: f.l, kind: mk.Function, detail: f.d, insertText: f.s, insertTextRules: SR, range: range });
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// Aggregation operators
|
|
173
|
+
[
|
|
174
|
+
{ l: 'sum', d: 'Sum over dimensions' },
|
|
175
|
+
{ l: 'avg', d: 'Average over dimensions' },
|
|
176
|
+
{ l: 'min', d: 'Minimum over dimensions' },
|
|
177
|
+
{ l: 'max', d: 'Maximum over dimensions' },
|
|
178
|
+
{ l: 'count', d: 'Count over dimensions' },
|
|
179
|
+
{ l: 'stddev', d: 'Standard deviation' },
|
|
180
|
+
{ l: 'stdvar', d: 'Standard variance' },
|
|
181
|
+
{ l: 'bottomk', d: 'Smallest k elements', s: 'bottomk(${1:5}, ${2:expr})' },
|
|
182
|
+
{ l: 'topk', d: 'Largest k elements', s: 'topk(${1:5}, ${2:expr})' }
|
|
183
|
+
].forEach(function(a) {
|
|
184
|
+
var snippet = a.s || (a.l + '(${1:by (${2:label})}) (${3:expr})');
|
|
185
|
+
suggestions.push({ label: a.l, kind: mk.Keyword, detail: a.d, insertText: snippet, insertTextRules: SR, range: range });
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
// Pipeline stages
|
|
189
|
+
[
|
|
190
|
+
{ l: '|= "..."', s: '|= "${1}"', d: 'Filter: line contains string' },
|
|
191
|
+
{ l: '!= "..."', s: '!= "${1}"', d: 'Filter: line does not contain string' },
|
|
192
|
+
{ l: '|~ "..."', s: '|~ "${1}"', d: 'Filter: line matches regex' },
|
|
193
|
+
{ l: '!~ "..."', s: '!~ "${1}"', d: 'Filter: line does not match regex' },
|
|
194
|
+
{ l: '| json', s: '| json', d: 'Parse JSON log line' },
|
|
195
|
+
{ l: '| logfmt', s: '| logfmt', d: 'Parse logfmt log line' },
|
|
196
|
+
{ l: '| pattern "..."', s: '| pattern "${1:<_> level=<level> <_>}"', d: 'Parse using pattern' },
|
|
197
|
+
{ l: '| regexp "..."', s: '| regexp "${1:(?P<level>[a-z]+)}"', d: 'Parse using named regex groups' },
|
|
198
|
+
{ l: '| line_format "..."', s: '| line_format "${1:{{.level\}} {{.msg\}}}"', d: 'Reformat the log line' },
|
|
199
|
+
{ l: '| label_format', s: '| label_format ${1:new_label}=${2:old_label}', d: 'Rename or transform labels' },
|
|
200
|
+
{ l: '| unwrap', s: '| unwrap ${1:label}', d: 'Extract numeric value for metric queries' },
|
|
201
|
+
{ l: '| unpack', s: '| unpack', d: 'Unpack JSON log fields into labels' },
|
|
202
|
+
{ l: '| drop', s: '| drop ${1:label}', d: 'Drop specified labels' },
|
|
203
|
+
{ l: '| keep', s: '| keep ${1:label}', d: 'Keep only specified labels' },
|
|
204
|
+
{ l: '| decolorize', s: '| decolorize', d: 'Strip ANSI colour codes' }
|
|
205
|
+
].forEach(function(p) {
|
|
206
|
+
suggestions.push({ label: p.l, kind: mk.Operator, detail: p.d, insertText: p.s, insertTextRules: SR, range: range });
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// Keywords
|
|
210
|
+
['by', 'without', 'on', 'or', 'and', 'unless', 'bool', 'offset', 'ignoring'].forEach(function(kw) {
|
|
211
|
+
suggestions.push({ label: kw, kind: mk.Keyword, insertText: kw, range: range });
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
return { suggestions: suggestions };
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
var editor = monaco.editor.create(document.getElementById(elementId), {
|
|
220
|
+
value: initialValue || '',
|
|
221
|
+
language: 'logql',
|
|
222
|
+
automaticLayout: true,
|
|
223
|
+
minimap: { enabled: false },
|
|
224
|
+
lineNumbers: 'off',
|
|
225
|
+
wordWrap: 'on',
|
|
226
|
+
scrollBeyondLastLine: false,
|
|
227
|
+
fontSize: 13,
|
|
228
|
+
overviewRulerLanes: 0,
|
|
229
|
+
folding: false,
|
|
230
|
+
padding: { top: 6, bottom: 6 }
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
return {
|
|
234
|
+
getValue: function() { return editor.getValue(); },
|
|
235
|
+
dispose: function() { editor.dispose(); }
|
|
236
|
+
};
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
</script>
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
<!-- logger -->
|
|
243
|
+
|
|
244
|
+
<style>
|
|
245
|
+
.editorgroupborder {
|
|
246
|
+
border-width:1px;
|
|
247
|
+
border-style:solid;
|
|
248
|
+
border-color:lightgray;
|
|
249
|
+
padding: 3px;
|
|
250
|
+
margin-top: 2px;
|
|
251
|
+
margin-bottom: 2px;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
.editorsectionheading {
|
|
255
|
+
font-weight: 600;
|
|
256
|
+
margin-bottom:1px !important;
|
|
257
|
+
margin-top:6px !important;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
.nomargin {
|
|
261
|
+
margin-bottom:1px !important;
|
|
262
|
+
margin-top:1px !important;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
.slidecontainer {
|
|
266
|
+
width: 100%;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
.slider {
|
|
270
|
+
-webkit-appearance: none;
|
|
271
|
+
appearance: none;
|
|
272
|
+
width: 100%;
|
|
273
|
+
height: 25px;
|
|
274
|
+
background: #d3d3d3;
|
|
275
|
+
outline: none;
|
|
276
|
+
opacity: 0.7;
|
|
277
|
+
-webkit-transition: .2s;
|
|
278
|
+
transition: opacity .2s;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
.slider:hover {
|
|
282
|
+
opacity: 1;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
.slider::-webkit-slider-thumb {
|
|
286
|
+
-webkit-appearance: none;
|
|
287
|
+
appearance: none;
|
|
288
|
+
width: 25px;
|
|
289
|
+
height: 25px;
|
|
290
|
+
background: #04AA6D;
|
|
291
|
+
cursor: pointer;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
.slider::-moz-range-thumb {
|
|
295
|
+
width: 25px;
|
|
296
|
+
height: 25px;
|
|
297
|
+
background: #04AA6D;
|
|
298
|
+
cursor: pointer;
|
|
299
|
+
}
|
|
10
300
|
|
|
301
|
+
.towb_editorlabel {
|
|
302
|
+
width: 120px !important;
|
|
303
|
+
}
|
|
304
|
+
input.towb_editorfield, select.towb_editorfield, textarea.towb_editorfield {
|
|
305
|
+
width: 70% !important;
|
|
306
|
+
}
|
|
307
|
+
input.towb_editorfield_short, select.towb_editorfield_short {
|
|
308
|
+
width: 30% !important;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
</style>
|
|
312
|
+
|
|
313
|
+
<script type="text/javascript">
|
|
314
|
+
|
|
315
|
+
// CSS
|
|
316
|
+
dropdownStyles = `
|
|
317
|
+
.loggertype-dropdown {
|
|
318
|
+
position: absolute;
|
|
319
|
+
background: white;
|
|
320
|
+
border: 1px solid #ccc;
|
|
321
|
+
border-radius: 4px;
|
|
322
|
+
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
|
|
323
|
+
max-height: 150px;
|
|
324
|
+
overflow-y: auto;
|
|
325
|
+
z-index: 1000;
|
|
326
|
+
min-width: 150px;
|
|
327
|
+
}
|
|
328
|
+
.loggertype-dropdown .dropdown-item {
|
|
329
|
+
padding: 6px 12px;
|
|
330
|
+
cursor: pointer;
|
|
331
|
+
}
|
|
332
|
+
.loggertype-dropdown .dropdown-item:hover {
|
|
333
|
+
background: #f0f0f0;
|
|
334
|
+
}
|
|
335
|
+
`;
|
|
336
|
+
$('<style>').text(dropdownStyles).appendTo('head');
|
|
337
|
+
|
|
338
|
+
</script>
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
|
|
11
343
|
<script type="text/javascript">
|
|
12
344
|
RED.nodes.registerType('LokiServiceConfigNode', {
|
|
13
345
|
category: 'config',
|
|
@@ -130,6 +462,7 @@ Shared connection configuration for a Grafana Loki instance. Referenced by the L
|
|
|
130
462
|
: *username* (string) : Username for basic authentication. Leave blank if auth is not required.
|
|
131
463
|
: *auth token* (string) : Password or token for basic authentication.
|
|
132
464
|
</script>
|
|
465
|
+
|
|
133
466
|
<script type="text/javascript">
|
|
134
467
|
RED.nodes.registerType('LokiLoggerConfigNode', {
|
|
135
468
|
category: 'config',
|
|
@@ -256,514 +589,7 @@ Mustache/Handlebars templates build the message string pushed to Loki.
|
|
|
256
589
|
- [Mustache - Wikipedia](https://en.wikipedia.org/wiki/Mustache_(template_system))
|
|
257
590
|
- [Mustache - Playground](https://jgonggrijp.gitlab.io/wontache/playground.html)
|
|
258
591
|
</script>
|
|
259
|
-
<!-- LokiQueryNode -->
|
|
260
|
-
<script type="text/javascript">
|
|
261
|
-
window.PluginCore = window.PluginCore || {};
|
|
262
|
-
/**
|
|
263
|
-
* PluginCore.createLogQLEditor(elementId, initialValue)
|
|
264
|
-
*
|
|
265
|
-
* Creates a Monaco editor pre-configured with the LogQL language:
|
|
266
|
-
* syntax highlighting for stream selectors, pipeline stages, metric
|
|
267
|
-
* functions, and aggregation operators; plus snippet completions.
|
|
268
|
-
*
|
|
269
|
-
* The LogQL language is registered with Monaco lazily on first call.
|
|
270
|
-
*
|
|
271
|
-
* @param {string} elementId - DOM id of the container element.
|
|
272
|
-
* @param {string} initialValue - Starting content of the editor.
|
|
273
|
-
* @returns {{ getValue(): string, dispose(): void \}}
|
|
274
|
-
*/
|
|
275
|
-
PluginCore.createLogQLEditor = function(elementId, initialValue) {
|
|
276
|
-
// Register the LogQL language with Monaco once per session.
|
|
277
|
-
if (!PluginCore._logqlReady) {
|
|
278
|
-
PluginCore._logqlReady = true;
|
|
279
|
-
monaco.languages.register({
|
|
280
|
-
id: 'logql'
|
|
281
|
-
});
|
|
282
|
-
monaco.languages.setLanguageConfiguration('logql', {
|
|
283
|
-
brackets: [
|
|
284
|
-
['{', '}'],
|
|
285
|
-
['[', ']'],
|
|
286
|
-
['(', ')']
|
|
287
|
-
],
|
|
288
|
-
autoClosingPairs: [{
|
|
289
|
-
open: '{',
|
|
290
|
-
close: '}'
|
|
291
|
-
}, {
|
|
292
|
-
open: '[',
|
|
293
|
-
close: ']'
|
|
294
|
-
}, {
|
|
295
|
-
open: '(',
|
|
296
|
-
close: ')'
|
|
297
|
-
}, {
|
|
298
|
-
open: '"',
|
|
299
|
-
close: '"'
|
|
300
|
-
}, {
|
|
301
|
-
open: "'",
|
|
302
|
-
close: "'"
|
|
303
|
-
}, {
|
|
304
|
-
open: '`',
|
|
305
|
-
close: '`'
|
|
306
|
-
}],
|
|
307
|
-
surroundingPairs: [{
|
|
308
|
-
open: '{',
|
|
309
|
-
close: '}'
|
|
310
|
-
}, {
|
|
311
|
-
open: '[',
|
|
312
|
-
close: ']'
|
|
313
|
-
}, {
|
|
314
|
-
open: '(',
|
|
315
|
-
close: ')'
|
|
316
|
-
}, {
|
|
317
|
-
open: '"',
|
|
318
|
-
close: '"'
|
|
319
|
-
}, {
|
|
320
|
-
open: "'",
|
|
321
|
-
close: "'"
|
|
322
|
-
}]
|
|
323
|
-
});
|
|
324
|
-
monaco.languages.setMonarchTokensProvider('logql', {
|
|
325
|
-
defaultToken: '',
|
|
326
|
-
tokenPostfix: '.logql',
|
|
327
|
-
keywords: ['by', 'without', 'on', 'ignoring', 'group_left', 'group_right', 'bool', 'or', 'and', 'unless', 'offset'],
|
|
328
|
-
metricFns: ['rate', 'count_over_time', 'bytes_rate', 'bytes_over_time', 'absent_over_time', 'sum_over_time', 'avg_over_time', 'min_over_time', 'max_over_time', 'first_over_time', 'last_over_time', 'stddev_over_time', 'stdvar_over_time', 'quantile_over_time', 'label_replace', 'vector', 'sort', 'sort_desc'],
|
|
329
|
-
aggregations: ['sum', 'avg', 'min', 'max', 'count', 'stddev', 'stdvar', 'bottomk', 'topk'],
|
|
330
|
-
pipelineStages: ['json', 'logfmt', 'pattern', 'regexp', 'unpack', 'line_format', 'label_format', 'drop', 'keep', 'decolorize', 'unwrap', 'ip', 'duration', 'bytes', 'distinct'],
|
|
331
|
-
tokenizer: {
|
|
332
|
-
root: [
|
|
333
|
-
// Stream selector open brace
|
|
334
|
-
[/\{/, {
|
|
335
|
-
token: 'delimiter.curly',
|
|
336
|
-
bracket: '@open',
|
|
337
|
-
next: '@streamSelector'
|
|
338
|
-
}],
|
|
339
|
-
// Range vector e.g. [5m]
|
|
340
|
-
[/\[\d+(ms|s|m|h|d|w|y)\]/, 'number.duration'],
|
|
341
|
-
// Pipeline filter operators
|
|
342
|
-
[/[|!][=~]/, 'operator.filter'],
|
|
343
|
-
// Pipe
|
|
344
|
-
[/\|/, 'operator.pipe'],
|
|
345
|
-
// Comparison operators
|
|
346
|
-
[/[><=!]+/, 'operator'],
|
|
347
|
-
// Numbers
|
|
348
|
-
[/\d+(\.\d+)?/, 'number'],
|
|
349
|
-
// Identifiers -> map to keyword/function/aggregation/pipeline categories
|
|
350
|
-
[/[a-zA-Z_]\w*/, {
|
|
351
|
-
cases: {
|
|
352
|
-
'@keywords': 'keyword',
|
|
353
|
-
'@aggregations': 'type.aggregate',
|
|
354
|
-
'@metricFns': 'type.function',
|
|
355
|
-
'@pipelineStages': 'type.pipeline',
|
|
356
|
-
'@default': 'identifier'
|
|
357
|
-
}
|
|
358
|
-
}],
|
|
359
|
-
// String literals
|
|
360
|
-
[/"/, {
|
|
361
|
-
token: 'string.quote',
|
|
362
|
-
bracket: '@open',
|
|
363
|
-
next: '@dblString'
|
|
364
|
-
}],
|
|
365
|
-
[/'/, {
|
|
366
|
-
token: 'string.quote',
|
|
367
|
-
bracket: '@open',
|
|
368
|
-
next: '@sglString'
|
|
369
|
-
}],
|
|
370
|
-
[/`/, {
|
|
371
|
-
token: 'string.quote',
|
|
372
|
-
bracket: '@open',
|
|
373
|
-
next: '@rawString'
|
|
374
|
-
}]
|
|
375
|
-
],
|
|
376
|
-
streamSelector: [
|
|
377
|
-
[/\}/, {
|
|
378
|
-
token: 'delimiter.curly',
|
|
379
|
-
bracket: '@close',
|
|
380
|
-
next: '@pop'
|
|
381
|
-
}],
|
|
382
|
-
[/[a-zA-Z_]\w*/, 'attribute.name'],
|
|
383
|
-
[/=~|!=|=|!~/, 'operator'],
|
|
384
|
-
[/"/, {
|
|
385
|
-
token: 'string.quote',
|
|
386
|
-
bracket: '@open',
|
|
387
|
-
next: '@dblString'
|
|
388
|
-
}],
|
|
389
|
-
[/'/, {
|
|
390
|
-
token: 'string.quote',
|
|
391
|
-
bracket: '@open',
|
|
392
|
-
next: '@sglString'
|
|
393
|
-
}],
|
|
394
|
-
[/`/, {
|
|
395
|
-
token: 'string.quote',
|
|
396
|
-
bracket: '@open',
|
|
397
|
-
next: '@rawString'
|
|
398
|
-
}],
|
|
399
|
-
[/,/, 'delimiter']
|
|
400
|
-
],
|
|
401
|
-
dblString: [
|
|
402
|
-
[/[^"\\]+/, 'string'],
|
|
403
|
-
[/\\./, 'string.escape'],
|
|
404
|
-
[/"/, {
|
|
405
|
-
token: 'string.quote',
|
|
406
|
-
bracket: '@close',
|
|
407
|
-
next: '@pop'
|
|
408
|
-
}]
|
|
409
|
-
],
|
|
410
|
-
sglString: [
|
|
411
|
-
[/[^'\\]+/, 'string'],
|
|
412
|
-
[/\\./, 'string.escape'],
|
|
413
|
-
[/'/, {
|
|
414
|
-
token: 'string.quote',
|
|
415
|
-
bracket: '@close',
|
|
416
|
-
next: '@pop'
|
|
417
|
-
}]
|
|
418
|
-
],
|
|
419
|
-
rawString: [
|
|
420
|
-
[/[^`]+/, 'string'],
|
|
421
|
-
[/`/, {
|
|
422
|
-
token: 'string.quote',
|
|
423
|
-
bracket: '@close',
|
|
424
|
-
next: '@pop'
|
|
425
|
-
}]
|
|
426
|
-
]
|
|
427
|
-
}
|
|
428
|
-
});
|
|
429
|
-
monaco.languages.registerCompletionItemProvider('logql', {
|
|
430
|
-
triggerCharacters: ['{', '|', ' ', '('],
|
|
431
|
-
provideCompletionItems: function(model, position) {
|
|
432
|
-
var word = model.getWordUntilPosition(position);
|
|
433
|
-
var range = {
|
|
434
|
-
startLineNumber: position.lineNumber,
|
|
435
|
-
endLineNumber: position.lineNumber,
|
|
436
|
-
startColumn: word.startColumn,
|
|
437
|
-
endColumn: word.endColumn
|
|
438
|
-
};
|
|
439
|
-
var mk = monaco.languages.CompletionItemKind;
|
|
440
|
-
var SR = monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet;
|
|
441
|
-
var suggestions = [];
|
|
442
|
-
// Stream selector template
|
|
443
|
-
suggestions.push({
|
|
444
|
-
label: '{label="value"}',
|
|
445
|
-
kind: mk.Snippet,
|
|
446
|
-
detail: 'Log stream selector',
|
|
447
|
-
documentation: 'Select log streams matching a set of label matchers.',
|
|
448
|
-
insertText: '{${1:app}="${2:nodered}"}',
|
|
449
|
-
insertTextRules: SR,
|
|
450
|
-
range: range
|
|
451
|
-
});
|
|
452
|
-
// Metric query functions
|
|
453
|
-
[{
|
|
454
|
-
l: 'rate',
|
|
455
|
-
d: 'Per-second rate of log entries',
|
|
456
|
-
s: 'rate({${1:app}="${2}"} [${3:5m}])'
|
|
457
|
-
}, {
|
|
458
|
-
l: 'count_over_time',
|
|
459
|
-
d: 'Count of log entries over time range',
|
|
460
|
-
s: 'count_over_time({${1:app}="${2}"} [${3:5m}])'
|
|
461
|
-
}, {
|
|
462
|
-
l: 'bytes_rate',
|
|
463
|
-
d: 'Per-second byte rate of log entries',
|
|
464
|
-
s: 'bytes_rate({${1:app}="${2}"} [${3:5m}])'
|
|
465
|
-
}, {
|
|
466
|
-
l: 'bytes_over_time',
|
|
467
|
-
d: 'Total bytes of log entries over range',
|
|
468
|
-
s: 'bytes_over_time({${1:app}="${2}"} [${3:5m}])'
|
|
469
|
-
}, {
|
|
470
|
-
l: 'absent_over_time',
|
|
471
|
-
d: 'Returns 1 if no entries found in range',
|
|
472
|
-
s: 'absent_over_time({${1:app}="${2}"} [${3:5m}])'
|
|
473
|
-
}, {
|
|
474
|
-
l: 'sum_over_time',
|
|
475
|
-
d: 'Sum of unwrapped values over range',
|
|
476
|
-
s: 'sum_over_time({${1:app}="${2}"} | unwrap ${3:field} [${4:5m}])'
|
|
477
|
-
}, {
|
|
478
|
-
l: 'avg_over_time',
|
|
479
|
-
d: 'Average of unwrapped values over range',
|
|
480
|
-
s: 'avg_over_time({${1:app}="${2}"} | unwrap ${3:field} [${4:5m}])'
|
|
481
|
-
}, {
|
|
482
|
-
l: 'min_over_time',
|
|
483
|
-
d: 'Minimum of unwrapped values over range',
|
|
484
|
-
s: 'min_over_time({${1:app}="${2}"} | unwrap ${3:field} [${4:5m}])'
|
|
485
|
-
}, {
|
|
486
|
-
l: 'max_over_time',
|
|
487
|
-
d: 'Maximum of unwrapped values over range',
|
|
488
|
-
s: 'max_over_time({${1:app}="${2}"} | unwrap ${3:field} [${4:5m}])'
|
|
489
|
-
}, {
|
|
490
|
-
l: 'quantile_over_time',
|
|
491
|
-
d: 'Quantile of unwrapped values',
|
|
492
|
-
s: 'quantile_over_time(${1:0.99}, {${2:app}="${3}"} | unwrap ${4:field} [${5:5m}])'
|
|
493
|
-
}, {
|
|
494
|
-
l: 'first_over_time',
|
|
495
|
-
d: 'First unwrapped value over range',
|
|
496
|
-
s: 'first_over_time({${1:app}="${2}"} | unwrap ${3:field} [${4:5m}])'
|
|
497
|
-
}, {
|
|
498
|
-
l: 'last_over_time',
|
|
499
|
-
d: 'Last unwrapped value over range',
|
|
500
|
-
s: 'last_over_time({${1:app}="${2}"} | unwrap ${3:field} [${4:5m}])'
|
|
501
|
-
}, {
|
|
502
|
-
l: 'label_replace',
|
|
503
|
-
d: 'Replace label value using regex',
|
|
504
|
-
s: 'label_replace(${1:expr}, "${2:dst_label}", "${3:$1}", "${4:src_label}", "${5:(.*)}")'
|
|
505
|
-
}].forEach(function(f) {
|
|
506
|
-
suggestions.push({
|
|
507
|
-
label: f.l,
|
|
508
|
-
kind: mk.Function,
|
|
509
|
-
detail: f.d,
|
|
510
|
-
insertText: f.s,
|
|
511
|
-
insertTextRules: SR,
|
|
512
|
-
range: range
|
|
513
|
-
});
|
|
514
|
-
});
|
|
515
|
-
// Aggregation operators
|
|
516
|
-
[{
|
|
517
|
-
l: 'sum',
|
|
518
|
-
d: 'Sum over dimensions'
|
|
519
|
-
}, {
|
|
520
|
-
l: 'avg',
|
|
521
|
-
d: 'Average over dimensions'
|
|
522
|
-
}, {
|
|
523
|
-
l: 'min',
|
|
524
|
-
d: 'Minimum over dimensions'
|
|
525
|
-
}, {
|
|
526
|
-
l: 'max',
|
|
527
|
-
d: 'Maximum over dimensions'
|
|
528
|
-
}, {
|
|
529
|
-
l: 'count',
|
|
530
|
-
d: 'Count over dimensions'
|
|
531
|
-
}, {
|
|
532
|
-
l: 'stddev',
|
|
533
|
-
d: 'Standard deviation'
|
|
534
|
-
}, {
|
|
535
|
-
l: 'stdvar',
|
|
536
|
-
d: 'Standard variance'
|
|
537
|
-
}, {
|
|
538
|
-
l: 'bottomk',
|
|
539
|
-
d: 'Smallest k elements',
|
|
540
|
-
s: 'bottomk(${1:5}, ${2:expr})'
|
|
541
|
-
}, {
|
|
542
|
-
l: 'topk',
|
|
543
|
-
d: 'Largest k elements',
|
|
544
|
-
s: 'topk(${1:5}, ${2:expr})'
|
|
545
|
-
}].forEach(function(a) {
|
|
546
|
-
var snippet = a.s || (a.l + '(${1:by (${2:label})}) (${3:expr})');
|
|
547
|
-
suggestions.push({
|
|
548
|
-
label: a.l,
|
|
549
|
-
kind: mk.Keyword,
|
|
550
|
-
detail: a.d,
|
|
551
|
-
insertText: snippet,
|
|
552
|
-
insertTextRules: SR,
|
|
553
|
-
range: range
|
|
554
|
-
});
|
|
555
|
-
});
|
|
556
|
-
// Pipeline stages
|
|
557
|
-
[{
|
|
558
|
-
l: '|= "..."',
|
|
559
|
-
s: '|= "${1}"',
|
|
560
|
-
d: 'Filter: line contains string'
|
|
561
|
-
}, {
|
|
562
|
-
l: '!= "..."',
|
|
563
|
-
s: '!= "${1}"',
|
|
564
|
-
d: 'Filter: line does not contain string'
|
|
565
|
-
}, {
|
|
566
|
-
l: '|~ "..."',
|
|
567
|
-
s: '|~ "${1}"',
|
|
568
|
-
d: 'Filter: line matches regex'
|
|
569
|
-
}, {
|
|
570
|
-
l: '!~ "..."',
|
|
571
|
-
s: '!~ "${1}"',
|
|
572
|
-
d: 'Filter: line does not match regex'
|
|
573
|
-
}, {
|
|
574
|
-
l: '| json',
|
|
575
|
-
s: '| json',
|
|
576
|
-
d: 'Parse JSON log line'
|
|
577
|
-
}, {
|
|
578
|
-
l: '| logfmt',
|
|
579
|
-
s: '| logfmt',
|
|
580
|
-
d: 'Parse logfmt log line'
|
|
581
|
-
}, {
|
|
582
|
-
l: '| pattern "..."',
|
|
583
|
-
s: '| pattern "${1:<_> level=<level> <_>}"',
|
|
584
|
-
d: 'Parse using pattern'
|
|
585
|
-
}, {
|
|
586
|
-
l: '| regexp "..."',
|
|
587
|
-
s: '| regexp "${1:(?P<level>[a-z]+)}"',
|
|
588
|
-
d: 'Parse using named regex groups'
|
|
589
|
-
}, {
|
|
590
|
-
l: '| line_format "..."',
|
|
591
|
-
s: '| line_format "${1:{{.level\}} {{.msg\}}}"',
|
|
592
|
-
d: 'Reformat the log line'
|
|
593
|
-
}, {
|
|
594
|
-
l: '| label_format',
|
|
595
|
-
s: '| label_format ${1:new_label}=${2:old_label}',
|
|
596
|
-
d: 'Rename or transform labels'
|
|
597
|
-
}, {
|
|
598
|
-
l: '| unwrap',
|
|
599
|
-
s: '| unwrap ${1:label}',
|
|
600
|
-
d: 'Extract numeric value for metric queries'
|
|
601
|
-
}, {
|
|
602
|
-
l: '| unpack',
|
|
603
|
-
s: '| unpack',
|
|
604
|
-
d: 'Unpack JSON log fields into labels'
|
|
605
|
-
}, {
|
|
606
|
-
l: '| drop',
|
|
607
|
-
s: '| drop ${1:label}',
|
|
608
|
-
d: 'Drop specified labels'
|
|
609
|
-
}, {
|
|
610
|
-
l: '| keep',
|
|
611
|
-
s: '| keep ${1:label}',
|
|
612
|
-
d: 'Keep only specified labels'
|
|
613
|
-
}, {
|
|
614
|
-
l: '| decolorize',
|
|
615
|
-
s: '| decolorize',
|
|
616
|
-
d: 'Strip ANSI colour codes'
|
|
617
|
-
}].forEach(function(p) {
|
|
618
|
-
suggestions.push({
|
|
619
|
-
label: p.l,
|
|
620
|
-
kind: mk.Operator,
|
|
621
|
-
detail: p.d,
|
|
622
|
-
insertText: p.s,
|
|
623
|
-
insertTextRules: SR,
|
|
624
|
-
range: range
|
|
625
|
-
});
|
|
626
|
-
});
|
|
627
|
-
// Keywords
|
|
628
|
-
['by', 'without', 'on', 'or', 'and', 'unless', 'bool', 'offset', 'ignoring'].forEach(function(kw) {
|
|
629
|
-
suggestions.push({
|
|
630
|
-
label: kw,
|
|
631
|
-
kind: mk.Keyword,
|
|
632
|
-
insertText: kw,
|
|
633
|
-
range: range
|
|
634
|
-
});
|
|
635
|
-
});
|
|
636
|
-
return {
|
|
637
|
-
suggestions: suggestions
|
|
638
|
-
};
|
|
639
|
-
}
|
|
640
|
-
});
|
|
641
|
-
}
|
|
642
|
-
var editor = monaco.editor.create(document.getElementById(elementId), {
|
|
643
|
-
value: initialValue || '',
|
|
644
|
-
language: 'logql',
|
|
645
|
-
automaticLayout: true,
|
|
646
|
-
minimap: {
|
|
647
|
-
enabled: false
|
|
648
|
-
},
|
|
649
|
-
lineNumbers: 'off',
|
|
650
|
-
wordWrap: 'on',
|
|
651
|
-
scrollBeyondLastLine: false,
|
|
652
|
-
fontSize: 13,
|
|
653
|
-
overviewRulerLanes: 0,
|
|
654
|
-
folding: false,
|
|
655
|
-
padding: {
|
|
656
|
-
top: 6,
|
|
657
|
-
bottom: 6
|
|
658
|
-
}
|
|
659
|
-
});
|
|
660
|
-
return {
|
|
661
|
-
getValue: function() {
|
|
662
|
-
return editor.getValue();
|
|
663
|
-
},
|
|
664
|
-
dispose: function() {
|
|
665
|
-
editor.dispose();
|
|
666
|
-
}
|
|
667
|
-
};
|
|
668
|
-
};
|
|
669
|
-
|
|
670
|
-
</script>
|
|
671
|
-
<!-- logger -->
|
|
672
|
-
<style>
|
|
673
|
-
.editorgroupborder {
|
|
674
|
-
border-width: 1px;
|
|
675
|
-
border-style: solid;
|
|
676
|
-
border-color: lightgray;
|
|
677
|
-
padding: 3px;
|
|
678
|
-
margin-top: 2px;
|
|
679
|
-
margin-bottom: 2px;
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
.editorsectionheading {
|
|
683
|
-
font-weight: 600;
|
|
684
|
-
margin-bottom: 1px !important;
|
|
685
|
-
margin-top: 6px !important;
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
.nomargin {
|
|
689
|
-
margin-bottom: 1px !important;
|
|
690
|
-
margin-top: 1px !important;
|
|
691
|
-
}
|
|
692
592
|
|
|
693
|
-
.slidecontainer {
|
|
694
|
-
width: 100%;
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
.slider {
|
|
698
|
-
-webkit-appearance: none;
|
|
699
|
-
appearance: none;
|
|
700
|
-
width: 100%;
|
|
701
|
-
height: 25px;
|
|
702
|
-
background: #d3d3d3;
|
|
703
|
-
outline: none;
|
|
704
|
-
opacity: 0.7;
|
|
705
|
-
-webkit-transition: .2s;
|
|
706
|
-
transition: opacity .2s;
|
|
707
|
-
}
|
|
708
|
-
|
|
709
|
-
.slider:hover {
|
|
710
|
-
opacity: 1;
|
|
711
|
-
}
|
|
712
|
-
|
|
713
|
-
.slider::-webkit-slider-thumb {
|
|
714
|
-
-webkit-appearance: none;
|
|
715
|
-
appearance: none;
|
|
716
|
-
width: 25px;
|
|
717
|
-
height: 25px;
|
|
718
|
-
background: #04AA6D;
|
|
719
|
-
cursor: pointer;
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
.slider::-moz-range-thumb {
|
|
723
|
-
width: 25px;
|
|
724
|
-
height: 25px;
|
|
725
|
-
background: #04AA6D;
|
|
726
|
-
cursor: pointer;
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
.towb_editorlabel {
|
|
730
|
-
width: 120px !important;
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
.towb_editorfield {
|
|
734
|
-
width: 70% !important;
|
|
735
|
-
}
|
|
736
|
-
|
|
737
|
-
.towb_editorfield_short {
|
|
738
|
-
width: 30% !important;
|
|
739
|
-
}
|
|
740
|
-
|
|
741
|
-
</style>
|
|
742
|
-
<script type="text/javascript">
|
|
743
|
-
// CSS
|
|
744
|
-
dropdownStyles = `
|
|
745
|
-
.loggertype-dropdown {
|
|
746
|
-
position: absolute;
|
|
747
|
-
background: white;
|
|
748
|
-
border: 1px solid #ccc;
|
|
749
|
-
border-radius: 4px;
|
|
750
|
-
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
|
|
751
|
-
max-height: 150px;
|
|
752
|
-
overflow-y: auto;
|
|
753
|
-
z-index: 1000;
|
|
754
|
-
min-width: 150px;
|
|
755
|
-
}
|
|
756
|
-
.loggertype-dropdown .dropdown-item {
|
|
757
|
-
padding: 6px 12px;
|
|
758
|
-
cursor: pointer;
|
|
759
|
-
}
|
|
760
|
-
.loggertype-dropdown .dropdown-item:hover {
|
|
761
|
-
background: #f0f0f0;
|
|
762
|
-
}
|
|
763
|
-
`;
|
|
764
|
-
$('<style>').text(dropdownStyles).appendTo('head');
|
|
765
|
-
|
|
766
|
-
</script>
|
|
767
593
|
<script type="text/javascript">
|
|
768
594
|
RED.nodes.registerType('LokiQueryNode', {
|
|
769
595
|
category: 'loki',
|
|
@@ -875,11 +701,14 @@ Mustache/Handlebars templates build the message string pushed to Loki.
|
|
|
875
701
|
// **** logger **** //
|
|
876
702
|
{
|
|
877
703
|
let node = this;
|
|
704
|
+
let templateConfig = {};
|
|
878
705
|
// controls.
|
|
879
706
|
let logEnabled = $("#node-input-logEnabled");
|
|
880
707
|
let logTemplateEnabled = $("#node-input-logTemplateOverrideEnabled");
|
|
881
708
|
let logTemplateEnabledSection = $("#section_logTemplateOverrideEnabled");
|
|
882
709
|
let logEnabledSection = $("#section_logEnabled");
|
|
710
|
+
let loggerSection = $("#section_loggerconfig");
|
|
711
|
+
loggerSection.hide();
|
|
883
712
|
// functions.
|
|
884
713
|
let getLoggerTypes = async function() {
|
|
885
714
|
let types = [];
|
|
@@ -1003,10 +832,15 @@ Mustache/Handlebars templates build the message string pushed to Loki.
|
|
|
1003
832
|
loggerEditButton.removeClass("disabled")
|
|
1004
833
|
}
|
|
1005
834
|
});
|
|
1006
|
-
//
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
835
|
+
// Show the logger section only if at least one logger type is registered.
|
|
836
|
+
// If nodered_logging (or any other logger provider) is not installed, the section stays hidden.
|
|
837
|
+
getLoggerTypes().then(loggerTypes => {
|
|
838
|
+
if (loggerTypes.length === 0) return;
|
|
839
|
+
loggerSection.show();
|
|
840
|
+
updateLoggerSelectorList().then(() => {
|
|
841
|
+
loggerSelector.val(node.logger);
|
|
842
|
+
loggerSelector.trigger('change');
|
|
843
|
+
});
|
|
1010
844
|
});
|
|
1011
845
|
}
|
|
1012
846
|
},
|
|
@@ -1020,6 +854,8 @@ Mustache/Handlebars templates build the message string pushed to Loki.
|
|
|
1020
854
|
}
|
|
1021
855
|
// **** logger **** //
|
|
1022
856
|
{
|
|
857
|
+
let node = this;
|
|
858
|
+
let templateConfig = {};
|
|
1023
859
|
node = this;
|
|
1024
860
|
var selectedLogger = $("#logger-selector").val();
|
|
1025
861
|
node.logger = (selectedLogger && selectedLogger !== "_ADD_") ? selectedLogger : '';
|
|
@@ -1188,5 +1024,4 @@ The query editor provides:
|
|
|
1188
1024
|
: *override template* (logconfig) : if checked, a custom logging template can be provided for the log message.
|
|
1189
1025
|
: *logger template* (mustache) : a template in mustache format to generate a message for logging purposes (see LoggerConfig node for more information)
|
|
1190
1026
|
</script>
|
|
1191
|
-
|
|
1192
1027
|
|