Skip to content

Commit

Permalink
feat(loki): add push api
Browse files Browse the repository at this point in the history
  • Loading branch information
PierreDemailly committed Dec 27, 2024
1 parent 140e805 commit f5cd4b7
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 1 deletion.
28 changes: 28 additions & 0 deletions docs/Loki.md
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,34 @@ async series<T = Record<string, string>>(
): Promise<T[]>
```

### push(logs: LokiIngestLogs): Promise< void >
Send log entries to Loki.

```ts
const logs: LokiIngestLogs[] = [
{
stream: { app: "foo" },
values: [["173532887432100000", "hello world"]]
}
];
await api.Loki.push(logs);
```

The `LokiIngestLogs` type is defined as follows:

```ts
export type LogEntry = [unixEpoch: string, log: string];
export type LogEntryWithMetadata = [unixEpoch: string, log: string, metadata: Record<string, string>];

export interface LokiIngestLogs {
stream: Record<string, string | number | boolean>;
values: (LogEntry | LogEntryWithMetadata)[];
}
```

> [!IMPORTANT]
> The unixEpoch must be in **nanoseconds**
## Pattern usage

**queryRange** and **queryRangeStream** APIs allow the usage of pattern.
Expand Down
16 changes: 15 additions & 1 deletion src/class/Loki.class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ import {
RawQueryRangeResponse,
QueryRangeLogsResponse,
QueryRangeStreamResponse,
QueryRangeMatrixResponse
QueryRangeMatrixResponse,
LokiIngestLogs
} from "../types.js";
import { ApiCredential } from "./ApiCredential.class.js";

Expand Down Expand Up @@ -235,4 +236,17 @@ export class Loki {

return listSeries.status === "success" ? listSeries.data : [];
}

async push(logs: LokiIngestLogs[]): Promise<void> {
const uri = new URL("loki/api/v1/push", this.remoteApiURL);
const { headers } = this.credential.httpOptions;

await httpie.post(uri, {
body: { streams: logs },
headers: {
...headers,
"Content-Type": "application/json"
}
});
}
}
8 changes: 8 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,11 @@ export interface QueryRangeMatrixResponse {
}

export type { TimeRange };

export type LogEntry = [unixEpoch: string, log: string];
export type LogEntryWithMetadata = [unixEpoch: string, log: string, metadata: Record<string, string>];

export interface LokiIngestLogs {
stream: Record<string, string | number | boolean>;
values: (LogEntry | LogEntryWithMetadata)[];
}
35 changes: 35 additions & 0 deletions test/Loki.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { MockAgent, setGlobalDispatcher, getGlobalDispatcher } from "@myunisoft/
// Import Internal Dependencies
import {
GrafanaApi,
LogEntry,
LokiIngestLogs,
LokiStandardBaseResponse
} from "../src/index.js";
import { mockMatrixResponse, mockStreamResponse } from "./utils/logs.factory.js";
Expand Down Expand Up @@ -383,6 +385,39 @@ describe("GrafanaApi.Loki", () => {
assert.strictEqual(result.length, 0);
});
});

describe("push", () => {
const agentPoolInterceptor = kMockAgent.get(kDummyURL);

before(() => {
process.env.GRAFANA_API_TOKEN = "";
setGlobalDispatcher(kMockAgent);
});

after(() => {
delete process.env.GRAFANA_API_TOKEN;
setGlobalDispatcher(kDefaultDispatcher);
});

it("should call POST /loki/api/v1/push with the provided logs", async() => {
const dummyLogs: LokiIngestLogs[] = [
{
stream: { app: "foo" },
values: [["173532887432100000", "hello world"]]
}
];

const agentPoolInterceptor = kMockAgent.get(kDummyURL);
agentPoolInterceptor
.intercept({
path: (path) => path.includes("loki/api/v1/push"),
method: "POST"
}).reply(204);

const sdk = new GrafanaApi({ remoteApiURL: kDummyURL });
await assert.doesNotReject(async() => await sdk.Loki.push(dummyLogs));
});
});
});

function mockLabelResponse<T>(
Expand Down

0 comments on commit f5cd4b7

Please sign in to comment.