@theotherwillembotha/node-red-loki 0.1.0 → 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 -515
- package/build/Nodes.js +61481 -12
- package/build/Plugins.html +37 -0
- package/build/Plugins.js +61405 -54
- 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,517 +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
|
-
input.towb_editorfield,
|
|
734
|
-
select.towb_editorfield,
|
|
735
|
-
textarea.towb_editorfield {
|
|
736
|
-
width: 70% !important;
|
|
737
|
-
}
|
|
738
|
-
|
|
739
|
-
input.towb_editorfield_short,
|
|
740
|
-
select.towb_editorfield_short {
|
|
741
|
-
width: 30% !important;
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
</style>
|
|
745
|
-
<script type="text/javascript">
|
|
746
|
-
// CSS
|
|
747
|
-
dropdownStyles = `
|
|
748
|
-
.loggertype-dropdown {
|
|
749
|
-
position: absolute;
|
|
750
|
-
background: white;
|
|
751
|
-
border: 1px solid #ccc;
|
|
752
|
-
border-radius: 4px;
|
|
753
|
-
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
|
|
754
|
-
max-height: 150px;
|
|
755
|
-
overflow-y: auto;
|
|
756
|
-
z-index: 1000;
|
|
757
|
-
min-width: 150px;
|
|
758
|
-
}
|
|
759
|
-
.loggertype-dropdown .dropdown-item {
|
|
760
|
-
padding: 6px 12px;
|
|
761
|
-
cursor: pointer;
|
|
762
|
-
}
|
|
763
|
-
.loggertype-dropdown .dropdown-item:hover {
|
|
764
|
-
background: #f0f0f0;
|
|
765
|
-
}
|
|
766
|
-
`;
|
|
767
|
-
$('<style>').text(dropdownStyles).appendTo('head');
|
|
768
|
-
|
|
769
|
-
</script>
|
|
770
593
|
<script type="text/javascript">
|
|
771
594
|
RED.nodes.registerType('LokiQueryNode', {
|
|
772
595
|
category: 'loki',
|
|
@@ -878,11 +701,14 @@ Mustache/Handlebars templates build the message string pushed to Loki.
|
|
|
878
701
|
// **** logger **** //
|
|
879
702
|
{
|
|
880
703
|
let node = this;
|
|
704
|
+
let templateConfig = {};
|
|
881
705
|
// controls.
|
|
882
706
|
let logEnabled = $("#node-input-logEnabled");
|
|
883
707
|
let logTemplateEnabled = $("#node-input-logTemplateOverrideEnabled");
|
|
884
708
|
let logTemplateEnabledSection = $("#section_logTemplateOverrideEnabled");
|
|
885
709
|
let logEnabledSection = $("#section_logEnabled");
|
|
710
|
+
let loggerSection = $("#section_loggerconfig");
|
|
711
|
+
loggerSection.hide();
|
|
886
712
|
// functions.
|
|
887
713
|
let getLoggerTypes = async function() {
|
|
888
714
|
let types = [];
|
|
@@ -1006,10 +832,15 @@ Mustache/Handlebars templates build the message string pushed to Loki.
|
|
|
1006
832
|
loggerEditButton.removeClass("disabled")
|
|
1007
833
|
}
|
|
1008
834
|
});
|
|
1009
|
-
//
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
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
|
+
});
|
|
1013
844
|
});
|
|
1014
845
|
}
|
|
1015
846
|
},
|
|
@@ -1023,6 +854,8 @@ Mustache/Handlebars templates build the message string pushed to Loki.
|
|
|
1023
854
|
}
|
|
1024
855
|
// **** logger **** //
|
|
1025
856
|
{
|
|
857
|
+
let node = this;
|
|
858
|
+
let templateConfig = {};
|
|
1026
859
|
node = this;
|
|
1027
860
|
var selectedLogger = $("#logger-selector").val();
|
|
1028
861
|
node.logger = (selectedLogger && selectedLogger !== "_ADD_") ? selectedLogger : '';
|
|
@@ -1191,5 +1024,4 @@ The query editor provides:
|
|
|
1191
1024
|
: *override template* (logconfig) : if checked, a custom logging template can be provided for the log message.
|
|
1192
1025
|
: *logger template* (mustache) : a template in mustache format to generate a message for logging purposes (see LoggerConfig node for more information)
|
|
1193
1026
|
</script>
|
|
1194
|
-
|
|
1195
1027
|
|