UNPKG

@myunisoft/loki

Version:

Node.js Grafana Loki SDK

344 lines (336 loc) 10.3 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var src_exports = {}; __export(src_exports, { GrafanaApi: () => GrafanaApi }); module.exports = __toCommonJS(src_exports); // src/class/ApiCredential.class.ts var ApiCredential = class _ApiCredential { authorization; userAgent; static buildAuthorizationHeader(authorizationOptions) { switch (authorizationOptions.type) { case "bearer": return `Bearer ${authorizationOptions.token}`; case "classic": { const { username, password } = authorizationOptions; return Buffer.from(`${username}:${password}`).toString("base64"); } case "custom": default: return authorizationOptions.authorization; } } constructor(authorizationOptions, userAgent) { this.authorization = authorizationOptions ? _ApiCredential.buildAuthorizationHeader(authorizationOptions) : null; this.userAgent = userAgent ?? null; } get httpOptions() { return { headers: { ...this.authorization === null ? {} : { authorization: this.authorization }, ...this.userAgent === null ? {} : { "User-Agent": this.userAgent } } }; } }; // src/class/Datasources.class.ts var httpie = __toESM(require("@myunisoft/httpie")); var Datasources = class { remoteApiURL; credential; constructor(remoteApiURL, credential) { this.remoteApiURL = remoteApiURL; this.credential = credential; } async all() { const uri = new URL( "/api/datasources", this.remoteApiURL ); const { data } = await httpie.get( uri, this.credential.httpOptions ); return data; } async byId(id) { const uri = new URL( `/api/datasources/${id}`, this.remoteApiURL ); const { data } = await httpie.get( uri, this.credential.httpOptions ); return data; } async byName(name) { const uri = new URL( `/api/datasources/name/${name}`, this.remoteApiURL ); const { data } = await httpie.get( uri, this.credential.httpOptions ); return data; } async byUID(uid) { const uri = new URL( `/api/datasources/uid/${uid}`, this.remoteApiURL ); const { data } = await httpie.get( uri, this.credential.httpOptions ); return data; } async idByName(name) { const uri = new URL( `/api/datasources/id/${name}`, this.remoteApiURL ); const { data } = await httpie.get( uri, this.credential.httpOptions ); return data.id; } }; // src/class/Loki.class.ts var httpie2 = __toESM(require("@myunisoft/httpie")); var import_auto_url = __toESM(require("@openally/auto-url")); var import_logql = require("@sigyn/logql"); var import_pattern = require("@sigyn/pattern"); // src/utils.ts var import_dayjs = __toESM(require("dayjs")); var import_ms = __toESM(require("ms")); function durationToUnixTimestamp(duration) { if (typeof duration === "number") { return String(duration); } if (!Object.is(Number(duration), NaN)) { return duration; } return (0, import_dayjs.default)().subtract((0, import_ms.default)(duration), "milliseconds").unix().toString(); } function transformStreamOrMatrixValue(value) { const [unixEpoch, log] = value; return { date: import_dayjs.default.unix(Number(unixEpoch) / 1e6), log }; } function inlineLogs(result) { if (result.status !== "success") { return null; } const flatLogs = result.data.result.flatMap( (host) => host.values.map(transformStreamOrMatrixValue) ).sort((left, right) => left.date.isBefore(right.date) ? 1 : -1); if (flatLogs.length === 0) { return null; } return { values: flatLogs.map((row) => row.log), timerange: [flatLogs.at(0).date.unix(), flatLogs.at(-1).date.unix()] }; } function streamOrMatrixTimeRange(result) { if (result.length === 0) { return null; } const flatLogs = result.flatMap( (host) => [ transformStreamOrMatrixValue(host.values.at(0)), transformStreamOrMatrixValue(host.values.at(-1)) ] ).sort((left, right) => left.date.isBefore(right.date) ? 1 : -1); return [ flatLogs.at(0).date.unix(), flatLogs.at(-1).date.unix() ]; } // src/class/Loki.class.ts function kDurationTransformer(value) { return durationToUnixTimestamp(value); } var kAutoURLGrafanaTransformer = { start: kDurationTransformer, end: kDurationTransformer }; var Loki = class { remoteApiURL; credential; constructor(remoteApiURL, credential) { this.remoteApiURL = remoteApiURL; this.credential = credential; } #fetchQueryRange(logQL, options = {}) { const { limit = 100 } = options; const query = typeof logQL === "string" ? logQL : logQL.toString(); const uri = (0, import_auto_url.default)( new URL("loki/api/v1/query_range", this.remoteApiURL), { ...options, limit, query }, kAutoURLGrafanaTransformer ); return httpie2.get( uri, this.credential.httpOptions ); } async queryRangeMatrix(logQL, options = {}) { if (import_logql.LogQL.type(logQL) === "query") { throw new Error("Log queries must use `queryRangeStream` method"); } const { data } = await this.#fetchQueryRange( logQL, options ); return { metrics: data.data.result.map((result) => { return { labels: result.metric, values: result.values }; }), timerange: streamOrMatrixTimeRange(data.data.result) }; } async queryRangeStream(logQL, options = {}) { if (import_logql.LogQL.type(logQL) === "metric") { throw new Error("Metric queries must use `queryRangeMatrix` method"); } const { pattern = new import_pattern.NoopPattern() } = options; const parser = pattern instanceof import_pattern.NoopPattern ? pattern : new import_pattern.Pattern(pattern); const { data } = await this.#fetchQueryRange( logQL, options ); return { streams: data.data.result.map((result) => { return { labels: result.stream, values: result.values.map(([unixEpoch, log]) => [unixEpoch, ...parser.executeOnLogs([log])]).filter((log) => log.length > 1) }; }), timerange: streamOrMatrixTimeRange(data.data.result) }; } async queryRange(logQL, options = {}) { const { pattern = new import_pattern.NoopPattern() } = options; const parser = pattern instanceof import_pattern.NoopPattern ? pattern : new import_pattern.Pattern(pattern); const { data } = await this.#fetchQueryRange(logQL, options); const inlinedLogs = inlineLogs(data); if (inlinedLogs === null) { return { logs: [], timerange: null }; } return { logs: parser.executeOnLogs(inlinedLogs.values), timerange: inlinedLogs.timerange }; } async labels(options = {}) { const uri = (0, import_auto_url.default)( new URL("loki/api/v1/labels", this.remoteApiURL), options, kAutoURLGrafanaTransformer ); const { data: labels } = await httpie2.get( uri, this.credential.httpOptions ); return labels.status === "success" ? labels.data : []; } async labelValues(label, options = {}) { const uri = (0, import_auto_url.default)( new URL(`loki/api/v1/label/${label}/values`, this.remoteApiURL), options, kAutoURLGrafanaTransformer ); const { data: labelValues } = await httpie2.get( uri, this.credential.httpOptions ); return labelValues.status === "success" ? labelValues.data : []; } async series(...match) { const uri = new URL(`loki/api/v1/series`, this.remoteApiURL); const { data: listSeries } = await httpie2.get(uri, { querystring: new URLSearchParams( match.map((selector) => ["match", new import_logql.StreamSelector(selector).toString()]) ) }); return listSeries.status === "success" ? listSeries.data : []; } async push(logs) { const uri = new URL("loki/api/v1/push", this.remoteApiURL); const { headers } = this.credential.httpOptions; await httpie2.post(uri, { body: { streams: Array.from(logs) }, headers: { ...headers, "Content-Type": "application/json" } }); } }; // src/class/GrafanaApi.class.ts var GrafanaApi = class { remoteApiURL; credential; Datasources; Loki; constructor(options) { const { authentication, userAgent, remoteApiURL } = options; this.credential = new ApiCredential(authentication, userAgent); this.remoteApiURL = typeof remoteApiURL === "string" ? new URL(remoteApiURL) : remoteApiURL; this.Datasources = new Datasources( this.remoteApiURL, this.credential ); this.Loki = new Loki( this.remoteApiURL, this.credential ); } }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { GrafanaApi });