@thotischner/observability-mcp 1.1.2 → 1.1.3
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/dist/connectors/loki.d.ts +1 -1
- package/dist/connectors/loki.js +22 -12
- package/package.json +1 -1
|
@@ -18,7 +18,7 @@ export declare class LokiConnector implements ObservabilityConnector {
|
|
|
18
18
|
listServices(): Promise<ServiceInfo[]>;
|
|
19
19
|
queryLogs(params: LogQuery): Promise<LogResult>;
|
|
20
20
|
private getLabelValues;
|
|
21
|
-
private
|
|
21
|
+
private resolveServiceSelector;
|
|
22
22
|
private parseLine;
|
|
23
23
|
private extractTopPatterns;
|
|
24
24
|
private parseTimeRange;
|
package/dist/connectors/loki.js
CHANGED
|
@@ -61,10 +61,14 @@ export class LokiConnector {
|
|
|
61
61
|
const seen = new Map();
|
|
62
62
|
for (const label of this.serviceLabels) {
|
|
63
63
|
const values = await this.getLabelValues(label);
|
|
64
|
-
for (const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
64
|
+
for (const raw of values) {
|
|
65
|
+
// Docker's loki.source.docker writes container names with a leading '/'
|
|
66
|
+
// (Docker API Names[0] convention). Strip it for display so the name
|
|
67
|
+
// matches what the service-name validator and users will pass back in.
|
|
68
|
+
const display = label === "container" ? raw.replace(/^\//, "") : raw;
|
|
69
|
+
if (!seen.has(display)) {
|
|
70
|
+
seen.set(display, {
|
|
71
|
+
name: display,
|
|
68
72
|
source: this.name,
|
|
69
73
|
signalType: "logs",
|
|
70
74
|
labels: { discoveredVia: label },
|
|
@@ -77,11 +81,12 @@ export class LokiConnector {
|
|
|
77
81
|
async queryLogs(params) {
|
|
78
82
|
const { start, end } = this.parseTimeRange(params.duration);
|
|
79
83
|
const limit = Math.min(Math.max(params.limit || 100, 1), 1000);
|
|
80
|
-
// Resolve
|
|
81
|
-
//
|
|
82
|
-
//
|
|
83
|
-
|
|
84
|
-
const
|
|
84
|
+
// Resolve label + actual selector value. For the 'container' label the
|
|
85
|
+
// value stored in Loki may be '/my-app-1' while the caller passes the
|
|
86
|
+
// sanitized 'my-app-1' — return the prefixed form so the LogQL selector
|
|
87
|
+
// matches the real stream.
|
|
88
|
+
const { label: matchedLabel, value: rawValue } = await this.resolveServiceSelector(params.service);
|
|
89
|
+
const service = this.escapeLogQLValue(rawValue);
|
|
85
90
|
let logql = `{${matchedLabel}="${service}"}`;
|
|
86
91
|
if (params.level) {
|
|
87
92
|
const level = this.escapeLogQLValue(params.level);
|
|
@@ -148,13 +153,18 @@ export class LokiConnector {
|
|
|
148
153
|
return [];
|
|
149
154
|
}
|
|
150
155
|
}
|
|
151
|
-
async
|
|
156
|
+
async resolveServiceSelector(service) {
|
|
152
157
|
for (const label of this.serviceLabels) {
|
|
153
158
|
const values = await this.getLabelValues(label);
|
|
154
159
|
if (values.includes(service))
|
|
155
|
-
return label;
|
|
160
|
+
return { label, value: service };
|
|
161
|
+
// Container label values are Docker-prefixed with '/'. The caller can't
|
|
162
|
+
// pass that form (validator rejects '/'), so probe the prefixed variant.
|
|
163
|
+
if (label === "container" && values.includes(`/${service}`)) {
|
|
164
|
+
return { label, value: `/${service}` };
|
|
165
|
+
}
|
|
156
166
|
}
|
|
157
|
-
return this.serviceLabels[0] || "service_name";
|
|
167
|
+
return { label: this.serviceLabels[0] || "service_name", value: service };
|
|
158
168
|
}
|
|
159
169
|
parseLine(line) {
|
|
160
170
|
try {
|
package/package.json
CHANGED