npm package: @prodisco/loki-client
Description: TypeScript client for Grafana Loki REST API
Latest version: 0.1.3

# @prodisco/loki-client

Typed Grafana Loki client for querying logs via the REST API. Provides a clean TypeScript interface with support for LogQL queries, label discovery, and multi-tenant deployments.

## Table of Contents

- [Features](#features)
- [Installation](#installation)
- [Quick Start](#quick-start)
- [API Reference](#api-reference)
- [Types](#types)
- [Time Range Formats](#time-range-formats)
- [Environment Variables](#environment-variables)
- [License](#license)

## Features

- **LogQL Queries**: Execute log and metric queries with full TypeScript support
- **Label Discovery**: Explore available labels and their values
- **Multi-Tenant Support**: Built-in tenant ID header support for multi-tenant Loki
- **Flexible Time Ranges**: Use ISO strings, Unix timestamps, or relative times (e.g., "1h", "30m")

## Installation

```bash
npm install @prodisco/loki-client
```

## Quick Start

```typescript
import { LokiClient } from '@prodisco/loki-client';

// Create client
const client = new LokiClient({ baseUrl: 'http://localhost:3100' });

// 1. DISCOVER available labels
const labels = await client.labels();
console.log(labels); // ["app", "namespace", "pod", ...]

// 2. GET values for a specific label
const namespaces = await client.labelValues('namespace');
console.log(namespaces); // ["default", "kube-system", ...]

// 3. QUERY logs with LogQL
const result = await client.queryRange('{namespace="kube-system"}', {
  since: '1h',
  limit: 100,
});

result.logs.forEach(log => {
  console.log(`[${log.timestamp.toISOString()}] ${log.line}`);
});
```

## API Reference

### LokiClient

Main client for interacting with Grafana Loki.

```typescript
import { LokiClient } from '@prodisco/loki-client';

const client = new LokiClient({
  baseUrl: 'http://localhost:3100',
  tenantId: 'my-tenant',  // optional, for multi-tenant Loki
  timeout: 30000,         // optional, default 30s
});
```

#### Methods

##### `labels(options?: LabelValuesOptions): Promise<string[]>`

Get all label names.

```typescript
const labels = await client.labels();
console.log(labels); // ["app", "namespace", "pod", ...]

// With time range
const recentLabels = await client.labels({ since: '24h' });
```

##### `labelValues(label: string, options?: LabelValuesOptions): Promise<string[]>`

Get all values for a specific label.

```typescript
const namespaces = await client.labelValues('namespace');
console.log(namespaces); // ["default", "kube-system", ...]

// Filter by LogQL query
const appPods = await client.labelValues('pod', {
  query: '{app="nginx"}',
  since: '1h',
});
```

##### `series(selectors: string[], options?): Promise<Record<string, string>[]>`

Get log stream series matching selectors.

```typescript
const series = await client.series(['{namespace="default"}'], { since: '1h' });
console.log(series);
// [{ app: "nginx", namespace: "default", pod: "nginx-abc123" }, ...]
```

##### `queryRange(logQL: string, options?: QueryRangeOptions): Promise<QueryRangeLogsResult>`

Query logs using LogQL. Returns parsed log entries.

```typescript
const result = await client.queryRange('{app="nginx"} |= "error"', {
  since: '1h',
  limit: 100,
  direction: 'backward', // most recent first (default)
});

// Access flattened logs
result.logs.forEach(log => {
  console.log(`[${log.timestamp.toISOString()}] ${log.labels.pod}: ${log.line}`);
});

// Or access grouped by stream
result.streams.forEach(stream => {
  console.log(`Stream: ${JSON.stringify(stream.labels)}`);
  stream.entries.forEach(entry => {
    console.log(`  ${entry.line}`);
  });
});
```

##### `queryRangeMatrix(logQL: string, options?: QueryRangeOptions): Promise<QueryRangeMatrixResult>`

Query for matrix/metric results. Use for LogQL metric queries like `rate()` or `count_over_time()`.

```typescript
const result = await client.queryRangeMatrix('rate({app="nginx"}[5m])', {
  since: '1h',
});

result.metrics.forEach(series => {
  console.log(`Labels: ${JSON.stringify(series.labels)}`);
  series.values.forEach(sample => {
    console.log(`  ${sample.timestamp.toISOString()}: ${sample.value}`);
  });
});
```

##### `ready(): Promise<boolean>`

Check if Loki is ready.

```typescript
const isReady = await client.ready();
console.log(isReady); // true
```

## Types

### LokiClientOptions

```typescript
interface LokiClientOptions {
  baseUrl: string;     // Loki server URL (e.g., "http://localhost:3100")
  tenantId?: string;   // Optional tenant ID for multi-tenant Loki
  timeout?: number;    // Request timeout in milliseconds (default: 30000)
}
```

### QueryRangeOptions

```typescript
interface QueryRangeOptions {
  start?: string | number;           // Start time (ISO string, Unix timestamp, or relative)
  end?: string | number;             // End time
  since?: string;                    // Relative time range (e.g., "1h", "30m", "24h")
  limit?: number;                    // Maximum log entries to return
  direction?: 'forward' | 'backward'; // Query direction (default: "backward")
}
```

### LabelValuesOptions

```typescript
interface LabelValuesOptions {
  start?: string | number;  // Start time
  end?: string | number;    // End time
  since?: string;           // Relative time range
  query?: string;           // LogQL query to filter label values
}
```

### LogEntry

```typescript
interface LogEntry {
  timestamp: Date;                    // Timestamp as Date object
  timestampNanos: string;             // Timestamp in nanoseconds
  line: string;                       // Log line content
  labels: Record<string, string>;     // Stream labels
}
```

### LogStream

```typescript
interface LogStream {
  labels: Record<string, string>;     // Stream labels
  entries: Array<{
    timestamp: Date;
    timestampNanos: string;
    line: string;
  }>;
}
```

### MetricSeries

```typescript
interface MetricSeries {
  labels: Record<string, string>;     // Metric labels
  values: MetricSample[];             // Sample values over time
}

interface MetricSample {
  timestamp: Date;                    // Timestamp as Date object
  value: number;                      // Metric value
}
```

### QueryRangeLogsResult

```typescript
interface QueryRangeLogsResult {
  logs: LogEntry[];                   // Flattened log entries (sorted by timestamp desc)
  streams: LogStream[];               // Raw streams with their labels
  stats?: Record<string, unknown>;    // Statistics from Loki
}
```

### QueryRangeMatrixResult

```typescript
interface QueryRangeMatrixResult {
  metrics: MetricSeries[];            // Metric series
  stats?: Record<string, unknown>;    // Statistics from Loki
}
```

## Time Range Formats

The client accepts multiple time formats:

| Format | Example | Description |
|--------|---------|-------------|
| Relative | `"1h"`, `"30m"`, `"7d"` | Relative to now (s, m, h, d, w) |
| ISO String | `"2024-01-15T10:00:00Z"` | ISO 8601 timestamp |
| Unix Timestamp | `1705312800` | Seconds since epoch |

```typescript
// Using relative time (recommended)
await client.queryRange('{app="nginx"}', { since: '1h' });

// Using explicit start/end
await client.queryRange('{app="nginx"}', {
  start: '2024-01-15T10:00:00Z',
  end: '2024-01-15T11:00:00Z',
});

// Using Unix timestamps
await client.queryRange('{app="nginx"}', {
  start: 1705312800,
  end: 1705316400,
});
```

## Environment Variables

| Variable | Description | Default |
|----------|-------------|---------|
| `LOKI_URL` | Loki server endpoint | - |
| `LOKI_TENANT_ID` | Multi-tenant org ID | - |

## License

MIT