npm package: @gilangjavier/lokctl
Description: Token-optimized Loki CLI
Latest version: 0.1.0

# @gilangjavier/lokctl

[![npm](https://img.shields.io/npm/v/@gilangjavier/lokctl.svg)](https://www.npmjs.com/package/@gilangjavier/lokctl)
[![GitHub](https://img.shields.io/badge/GitHub-gilangjavier%2Flokctl-blue)](https://github.com/gilangjavier/lokctl)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.8-blue)](https://www.typescriptlang.org/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

CLI tool for Grafana Loki with token optimization for AI agents.

## Token Savings

| Operation | Standard API | lokctl | Savings |
|-----------|--------------|--------|---------|
| Query logs | ~3,500 tokens | ~700 tokens | 80% |
| Tail logs | ~2,800 tokens | ~600 tokens | 79% |
| List labels | ~1,200 tokens | ~300 tokens | 75% |
| List series | ~2,000 tokens | ~450 tokens | 78% |
| Get stats | ~1,500 tokens | ~350 tokens | 77% |
| **Average** | | | **78%** |

## For AI Agents

### Installation (Ephemeral)

```bash
npx @gilangjavier/lokctl <command>
```

No installation required. Runs directly via npx.

### Strict Defaults

lokctl enforces strict defaults to prevent runaway token consumption:

- `--since 5m` - Default time range (5 minutes)
- `--limit 100` - Maximum logs returned per query
- **Label selector required** - Query must include a selector like `{app="api"}`

**Error on missing selector:**
```
Error: LogQL selector is required. Example: lokctl query '{app="api"}'
```

### Quick Commands

```bash
# Query logs with label selector (uses --since 5m, --limit 100 by default)
npx @gilangjavier/lokctl query '{app="api"}'

# Query with custom time range and limit
npx @gilangjavier/lokctl query '{app="api"}' --since 15m --limit 50

# Query specific log level
npx @gilangjavier/lokctl query '{app="api"} |= "ERROR"' --since 10m

# Tail logs in real-time (follow mode)
npx @gilangjavier/lokctl tail '{app="api"}' --since 1m

# List all available labels
npx @gilangjavier/lokctl labels

# List series for a label
npx @gilangjavier/lokctl series job --since 30m

# Get query statistics
npx @gilangjavier/lokctl stats '{app="api"}' --since 5m

# Paginated query (page 2)
npx @gilangjavier/lokctl query '{app="api"}' --page 2

# Show current config
npx @gilangjavier/lokctl config

# Check Loki health
npx @gilangjavier/lokctl health
```

### Expected Behavior

**Exit Codes:**
- `0` - Success
- `1` - Error (connection failed, invalid query, config missing, missing selector)

**Output Format:**
- Default: Human-readable table (`timestamp | level | message`)
- `--format table` - Table format (default)
- `--format json` - Structured JSON for programmatic use
- `--format raw` - Raw log lines only

**Profile Selection:**
- `-p, --profile <name>` - Use specific profile from config

**Pagination:**
- `--page <number>` - Cursor-based pagination for large result sets

### Before/After Token Comparison

**Before (direct Loki API calls):**
```
Raw JSON response with metadata, stream objects, structured entries
~3,500 tokens for a typical logs query with 100 entries
```

**After (lokctl):**
```
Clean table output with timestamp | level | message
Consecutive duplicates marked with [xN] count
~700 tokens for the same logs
```

## For Humans

### Installation

```bash
npm install -g @gilangjavier/lokctl
```

### Setup Profile

Initialize configuration:

```bash
lokctl init
```

This creates `~/.config/lokctl/profiles.yaml` with a sample profile. Edit it:

```yaml
active: default
profiles:
  - name: default
    url: http://localhost:3100
    orgId: tenant-a              # Optional: multi-tenant Loki
    tokenFile: ~/.secrets/loki.token  # Optional: bearer token file
```

Auth order (highest first):
1. `LOKI_TOKEN` environment variable
2. `tokenFile` from profile

### All Available Commands

| Command | Description | Options |
|---------|-------------|---------|
| `lokctl init` | Create sample config | `--force` to overwrite |
| `lokctl config` | Show current config | `-p, --profile` |
| `lokctl health` | Check Loki health | `-p, --profile` |
| `lokctl labels` | List available labels | `-p, --profile` |
| `lokctl series <label>` | List series for label | `--since`, `-p, --profile` |
| `lokctl query '<selector>'` | Query logs | `--since`, `--limit`, `--format`, `--page`, `-p` |
| `lokctl tail '<selector>'` | Tail logs real-time | `--since`, `-p, --profile` |
| `lokctl stats '<selector>'` | Query statistics | `--since`, `-p, --profile` |

Global flags available for all commands:
- `-p, --profile <name>` - Use specific profile
- `-o, --format <format>` - Output format: `table`, `json`, or `raw`

### Common Workflows

**Debug application errors:**
```bash
# Filter for error logs only
lokctl query '{app="myapp"} |= "ERROR"' --since 15m

# Include stack traces by increasing limit
lokctl query '{app="myapp"} |= "ERROR"' --since 30m --limit 500

# Tail errors in real-time
lokctl tail '{app="myapp"} |= "ERROR"' --since 1m
```

**Deduplicate noisy logs:**
```bash
# lokctl automatically deduplicates consecutive identical messages
# [x15] indicates a message repeated 15 times
lokctl query '{service="worker"} |= "heartbeat"' --since 10m
```

**Incident investigation:**
```bash
# Query across multiple services
lokctl query '{namespace="production"} |= "timeout"' --since 1h --limit 500

# Use pagination for deep inspection
lokctl query '{namespace="production"} |= "timeout"' --since 1h --page 2
lokctl query '{namespace="production"} |= "timeout"' --since 1h --page 3
```

**Export logs for analysis:**
```bash
# Export as JSON for further processing
lokctl query '{app="api"}' --since 1h --format json > logs.json

# Export raw logs for grep/awk processing
lokctl query '{app="api"}' --since 30m --format raw > logs.txt
```

**Check available labels and values:**
```bash
# List all labels
lokctl labels

# List values for a specific label
lokctl series job --since 1h
```

## Token Optimization Details

### Filtering

lokctl implements intelligent filtering to reduce data volume:

| Filter | Description |
|--------|-------------|
| `--since` | Time range for queries (strict default: 5m) |
| `--limit` | Maximum entries returned (strict default: 100) |
| LogQL selectors | Required label matchers reduce index scans |
| Level extraction | Automatic level detection from message content |

### Deduplication

lokctl automatically deduplicates consecutive identical log messages:

```
# Original noisy logs:
2024-01-01 10:00:00 | INFO | Health check ok
2024-01-01 10:00:01 | INFO | Health check ok
2024-01-01 10:00:02 | INFO | Health check ok

# lokctl output:
2024-01-01 10:00:00 | INFO | [x3] Health check ok
```

Benefits:
- Reduced token consumption for repetitive logs
- Faster identification of unique events
- Cleaner output for incident triage

### Strict Defaults

lokctl enforces strict defaults to prevent accidental token overconsumption:

| Parameter | Default | Purpose |
|-----------|---------|---------|
| `--since` | `5m` | Limits query time window |
| `--limit` | `100` | Caps maximum results per call |
| `--page` | `1` | Cursor-based pagination start |

These defaults are intentionally conservative. Override explicitly when needed.

### Fingerprint Cache

lokctl implements query fingerprint caching to reduce redundant API calls:

1. **Query fingerprinting** - SHA256 hash of normalized query parameters
2. **30-second TTL** - Cache entries expire after 30 seconds
3. **Per-profile cache** - Cache keys include profile name for isolation

Benefits:
- Reduced network latency on repeated calls
- Lower token consumption for unchanged data
- Automatic cache invalidation via TTL

### Cursor Pagination

lokctl implements cursor-based pagination for large result sets:

```bash
# Page 1 (default)
lokctl query '{app="api"}' --limit 100

# Page 2
lokctl query '{app="api"}' --limit 100 --page 2

# Page 3
lokctl query '{app="api"}' --limit 100 --page 3
```

Benefits:
- Predictable token consumption per request
- No duplicate entries across pages
- Efficient for deep log inspection

### Deterministic Output

lokctl produces consistent, predictable output:

| Component | Standard Response | lokctl Output |
|-----------|-------------------|---------------|
| HTTP headers | Full headers | Stripped |
| JSON metadata | `streams`, `values`, nested arrays | Flat table rows |
| Timestamps | Nanosecond Unix timestamps | Human-readable ISO8601 |
| Log levels | Mixed formats | Normalized (DEBUG/INFO/WARN/ERROR/FATAL) |
| Stream labels | Repeated per entry | Deduplicated to column headers |
| Empty fields | Null or empty strings | Omitted from tables |
| Error messages | Verbose stack traces | Clean error messages |

Output is sorted chronologically and formatted consistently across runs.

## Contributing

### For AI Agents

To suggest improvements or test changes:

1. Review the codebase structure in `src/`
2. Identify optimization opportunities
3. Document findings in GitHub issues:
   ```
   https://github.com/gilangjavier/lokctl/issues
   ```

### For Humans

**Development Setup:**

```bash
# Clone the repository
git clone https://github.com/gilangjavier/lokctl.git
cd lokctl

# Install dependencies
npm install

# Build TypeScript
npm run build

# Run tests
npm test

# Run in development mode
npm run dev -- query '{app="api"}' --since 5m
```

**Submitting Changes:**

1. Fork the repository
2. Create a feature branch: `git checkout -b feature/my-change`
3. Make changes and add tests
4. Run the test suite: `npm test`
5. Commit with clear messages
6. Push and open a Pull Request

**Reporting Issues:**

- [GitHub Issues](https://github.com/gilangjavier/lokctl/issues)

Include:
- lokctl version (`lokctl --version`)
- Node.js version (`node --version`)
- Loki version
- Steps to reproduce
- Expected vs actual output

## License

MIT - See [LICENSE](LICENSE) file for details.