(
selector: string,
limit: number,
cursor?: string,
)
| 279 | } |
| 280 | |
| 281 | private async queryLokiRange( |
| 282 | selector: string, |
| 283 | limit: number, |
| 284 | cursor?: string, |
| 285 | ): Promise<LokiEntry[]> { |
| 286 | const params = new URLSearchParams({ |
| 287 | query: selector, |
| 288 | direction: 'backward', // Most recent first |
| 289 | limit: limit.toString(), |
| 290 | }); |
| 291 | |
| 292 | if (cursor) { |
| 293 | // End time is the cursor (exclusive) |
| 294 | params.set('end', this.toNanoseconds(new Date(cursor))); |
| 295 | } |
| 296 | |
| 297 | const url = this.resolveUrl(`/loki/api/v1/query_range?${params.toString()}`); |
| 298 | console.log(`[LogStreamService] Querying Loki: ${url}`); |
| 299 | |
| 300 | const response = await fetch(url, { |
| 301 | method: 'GET', |
| 302 | headers: this.buildHeaders(), |
| 303 | }); |
| 304 | |
| 305 | if (!response.ok) { |
| 306 | const errorText = await response.text(); |
| 307 | console.error( |
| 308 | `[LogStreamService] Loki query failed: ${response.status} ${response.statusText} - ${errorText}`, |
| 309 | ); |
| 310 | throw new ServiceUnavailableException( |
| 311 | `Loki query failed: ${response.status} ${response.statusText} - ${errorText}`, |
| 312 | ); |
| 313 | } |
| 314 | |
| 315 | const payload = (await response.json()) as { |
| 316 | data?: { |
| 317 | result?: { |
| 318 | stream?: Record<string, string>; |
| 319 | values?: [string, string][]; |
| 320 | }[]; |
| 321 | }; |
| 322 | }; |
| 323 | |
| 324 | console.log( |
| 325 | `[LogStreamService] Loki response: ${JSON.stringify({ |
| 326 | resultCount: payload.data?.result?.length ?? 0, |
| 327 | totalValues: |
| 328 | payload.data?.result?.reduce((sum, r) => sum + (r.values?.length ?? 0), 0) ?? 0, |
| 329 | })}`, |
| 330 | ); |
| 331 | |
| 332 | const entries: LokiEntry[] = []; |
| 333 | const results = payload.data?.result ?? []; |
| 334 | for (const result of results) { |
| 335 | const streamLabels = result.stream ?? {}; |
| 336 | for (const [timestamp, message] of result.values ?? []) { |
| 337 | entries.push({ |
| 338 | timestamp: this.fromNanoseconds(timestamp), |
no test coverage detected