MCPcopy Create free account
hub / github.com/ShipSecAI/studio / LokiLogClient

Class LokiLogClient

backend/src/logging/loki.client.ts:13–66  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

11}
12
13export class LokiLogClient {
14 constructor(private readonly config: LokiLogClientConfig) {}
15
16 async push(labels: Record<string, string>, lines: LokiStreamLine[]): Promise<void> {
17 if (lines.length === 0) {
18 return;
19 }
20
21 const url = this.resolveUrl('/loki/api/v1/push');
22 const headers: Record<string, string> = {
23 'Content-Type': 'application/json',
24 };
25
26 if (this.config.tenantId) {
27 headers['X-Scope-OrgID'] = this.config.tenantId;
28 }
29
30 if (this.config.username && this.config.password) {
31 const credentials = Buffer.from(`${this.config.username}:${this.config.password}`).toString(
32 'base64',
33 );
34 headers.Authorization = `Basic ${credentials}`;
35 }
36
37 const body = JSON.stringify({
38 streams: [
39 {
40 stream: labels,
41 values: lines.map((line) => [this.toNanoseconds(line.timestamp), line.message]),
42 },
43 ],
44 });
45
46 const response = await fetch(url, {
47 method: 'POST',
48 headers,
49 body,
50 });
51
52 if (!response.ok) {
53 const errorText = await response.text();
54 throw new Error(`Loki push failed: ${response.status} ${response.statusText} - ${errorText}`);
55 }
56 }
57
58 private resolveUrl(path: string): string {
59 const base = this.config.baseUrl.replace(/\/+$/, '');
60 return `${base}${path}`;
61 }
62
63 private toNanoseconds(date: Date): string {
64 return (BigInt(date.getTime()) * 1000000n).toString();
65 }
66}

Callers

nothing calls this directly

Calls

no outgoing calls

Tested by

no test coverage detected