# Workers, Stats and Metrics from the Queue

Inspect a running bunqueue system from the Queue: registered workers, per-queue statistics, windowed metrics and event stream trimming.

Canonical: https://bunqueue.dev/guide/queue/metrics/

---

import { Tabs, TabItem } from '@astrojs/starlight/components';

<div class="bq-wrap bq-hero">
  <span class="bq-eyebrow">guide · queue</span>
  <h1 class="bq-hero-h1 bq-bench-h1">Who is connected, <em>and how it is going.</em></h1>
  <p class="bq-hero-sub">Which workers are registered, what the queue has processed, and the time-windowed counters behind a dashboard.</p>
</div>

## Workers and metrics

<Tabs syncKey="lang">
<TabItem label="Bun">

```typescript
const workers = await queue.getWorkers();       // Active workers on this queue
const count = await queue.getWorkersCount();

const completedMetrics = await queue.getMetrics('completed', 0, 100);
const failedMetrics = await queue.getMetrics('failed', 0, 100);

const removed = await queue.trimEvents(1000);  // Keep the newest 1,000 events
```

</TabItem>
<TabItem label="Node.js / Deno">

```typescript
const workers = await queue.getWorkers();       // Active workers on this queue
const count = await queue.getWorkersCount();

const completedMetrics = await queue.getMetrics('completed', 0, 100);
const failedMetrics = await queue.getMetrics('failed', 0, 100);

const removed = await queue.trimEvents(1000);  // Keep the newest 1,000 events
```

</TabItem>
<TabItem label="Python">

```python
workers = queue.get_workers()        # Active workers on this queue
count = queue.get_workers_count()

stats = queue.get_stats()            # Server-wide stats
metrics = queue.get_metrics()        # Server-wide metrics
```

</TabItem>
<TabItem label="PHP">

```php
$workers = $queue->getWorkers();     // Active workers on this queue

$stats = $queue->getStats();         // Server-wide stats
```

</TabItem>
<TabItem label="Go">

```go
workers, _ := queue.GetWorkers()     // Active workers on this queue

stats, _ := queue.GetStats()         // Server-wide stats
```

</TabItem>
<TabItem label="Rust">

The Rust SDK does not expose queue monitoring helpers yet. Read the broker's
Prometheus endpoint instead:

```bash
curl --fail http://localhost:6790/prometheus
```

</TabItem>
<TabItem label="Elixir">

The Elixir SDK does not expose queue monitoring helpers yet. Read the broker's
Prometheus endpoint with the standard Erlang HTTP client instead:

```elixir
:inets.start()
{:ok, {{_, 200, _}, _headers, body}} =
  :httpc.request(~c"http://localhost:6790/prometheus")
```

</TabItem>
</Tabs>

*Worker and stats helpers are not yet exposed in Rust and Elixir; scrape the server's Prometheus-text `/prometheus` endpoint instead. The separate `/metrics` endpoint returns JSON operational metrics. `trimEvents()` and windowed `getMetrics(state, start, end)` exist in the Bun `bunqueue` package only.*

## Metric windows

`getMetrics(type, start = 0, end = -1)` is queue-scoped in both embedded and
TCP mode. It returns:

```typescript
interface QueueMetrics {
  meta: {
    count: number;      // Cumulative terminal jobs for this queue and type
    prevTS: number;     // Timestamp of the most recent terminal job
    prevCount: number;  // Count in its one-minute bucket
  };
  data: number[];       // One-minute buckets, newest first
  count: number;        // Available buckets before pagination
}
```

`start` and `end` are inclusive bucket indexes, not timestamps. Index `0` is
the newest minute and `end: -1` means through the oldest retained minute. Empty
minutes between observed buckets are returned as zero. Unlike BullMQ's Redis
collector, bunqueue includes the current partial minute immediately, so one
finished job produces a visible data point without waiting for the minute to
close. The broker retains at most 20,160 points (two weeks) per queue and state;
`meta.count` remains cumulative when older points age out.

`trimEvents(maxLength)` operates on the separate lifecycle-event journal. It
returns the number of deleted entries, affects only this queue, and is
idempotent: calling it again with the same length returns `0`. The journal is
persistent in the selected configured backend (SQLite or PostgreSQL),
automatically bounded to 10,000 entries per queue, and is deleted together with
queue metrics by `obliterateAsync()`. It is ephemeral in memory-only mode.

## Where to go next

| Guide | What it covers |
|---|---|
| [Queue API](/guide/queue/) | Create a queue in embedded or TCP mode |
| [Adding Jobs](/guide/queue/adding-jobs/) | add, addBulk, priorities, delays, durability |
| [Deduplication and Idempotent Job Adds](/guide/queue/deduplication/) | Idempotent adds, dedup keys, custom job ids |
| [Querying Jobs](/guide/queue/querying/) | Fetch jobs, states, counts and results |
| [Queue Control and Maintenance](/guide/queue/control/) | Pause, drain, obliterate, clean and repair |
| [Progress, Job Logs and Dependencies](/guide/queue/progress/) | Progress, per-job logs and dependencies |
| [Queue Rate Limiting and Global Concurrency](/guide/queue/limits/) | Rate limits and global concurrency caps |
| [Job Schedulers from the Queue](/guide/queue/schedulers/) | Named repeatable schedules from the queue |
| [DLQ Operations from the Queue Object](/guide/queue/dlq/) | Failed-job operations from the Queue object |
| [Namespaces, Auto-Batching and Store-and-Forward](/guide/queue/advanced/) | Namespaces, auto-batching, store-and-forward |
| [JobOptions Reference](/guide/queue/options/) | Every JobOptions field, with defaults |