<div align="center">

<img src="https://capsule-render.vercel.app/api?type=waving&color=0:E6522C,100:1A1A1A&height=200&section=header&text=viva_telemetry&fontSize=64&fontColor=fff&animation=twinkling&fontAlignY=35&desc=Observability%20suite%20for%20Gleam%20on%20the%20BEAM&descSize=18&descAlignY=55" width="100%"/>

[![Gleam](https://img.shields.io/badge/Gleam-FFAFF3?style=for-the-badge&logo=gleam&logoColor=black)](https://gleam.run/)
[![BEAM](https://img.shields.io/badge/BEAM-A90533?style=for-the-badge&logo=erlang&logoColor=white)](https://www.erlang.org/)
[![OTP](https://img.shields.io/badge/OTP_27+-4B275F?style=for-the-badge)](https://www.erlang.org/doc/design_principles/des_princ)
[![Hex](https://img.shields.io/badge/hex.pm-viva__telemetry-A678DD?style=for-the-badge&logo=hex&logoColor=white)](https://hex.pm/packages/viva_telemetry)
[![Prometheus](https://img.shields.io/badge/Prometheus-export-E6522C?style=for-the-badge&logo=prometheus&logoColor=white)](https://prometheus.io/)
[![Tests](https://img.shields.io/badge/tests-41_passing-00875A?style=for-the-badge)](./test)
[![Version](https://img.shields.io/badge/version-1.0.101-CD5C5C?style=for-the-badge)](./CHANGELOG.md)
[![License](https://img.shields.io/badge/license-Apache_2.0-228B22?style=for-the-badge)](./LICENSE)

---

*"Logs whisper. Metrics count. Benchmarks judge. The BEAM observes."*

</div>

---

> [!IMPORTANT]
> **viva_telemetry IS NOT A FRAMEWORK.**
> It is **three independent observability surfaces** — structured logging,
> Prometheus-shaped metrics, and statistical benchmarks — that you can pull
> into any Gleam/BEAM app without dragging in a runtime.
>
> Plays well with Erlang `:logger`, Prometheus scrapers, and Grafana.

---

## 🎯 Overview

Observability for Gleam applications on the BEAM. Structured logging,
in-memory metrics, Prometheus export, BEAM memory visibility, and small
statistical benchmarks — without forcing a large framework into your
application.

The three surfaces (`log`, `metrics`, `bench`) are deliberately independent:
import only what you need, configure each per process.

| Property              | Value                                                |
| :-------------------- | :--------------------------------------------------- |
| **Language**          | Pure Gleam (type-safe functional)                    |
| **Runtime**           | BEAM / OTP 27+                                       |
| **Logging backend**   | Erlang `:logger`, console, JSON file                 |
| **Metrics backend**   | ETS-backed counters, gauges, histograms              |
| **Export format**     | Prometheus text (`# HELP` / `# TYPE`)                |
| **Tests**             | 41 passing                                           |
| **Public API**        | `viva_telemetry/{log,metrics,bench}`                 |

---

## ⚡ Quick Start

```bash
gleam add viva_telemetry@1
```

### Log, count, time — in one file

```gleam
import viva_telemetry/bench
import viva_telemetry/log
import viva_telemetry/metrics

pub fn main() {
  log.configure_erlang(log.info_level)
  log.info("Server started", [#("port", "8080")])

  let requests = metrics.counter("http_requests_total")
  metrics.inc(requests)

  bench.run("my_function", fn() { heavy_work() })
  |> bench.print()
}
```

<details>
<summary><strong>📋 Prerequisites</strong></summary>

| Tool       | Version    | Required for     |
| :--------- | :--------- | :--------------- |
| Gleam      | `>= 1.11`  | Build / runtime  |
| Erlang/OTP | `>= 27`    | BEAM runtime     |

Zero NIFs. Zero C dependencies. Pure BEAM.

</details>

---

## 🏗️ Architecture

```
   ┌──────────────────────────────────────────────────────────┐
   │                Gleam application code                    │
   │      viva_telemetry/log · /metrics · /bench              │
   └────────┬─────────────────┬──────────────────┬────────────┘
            │                 │                  │
   ┌────────▼─────┐  ┌────────▼────────┐  ┌──────▼─────────┐
   │   Logging    │  │     Metrics     │  │   Benchmarks   │
   │              │  │                 │  │                │
   │ levels       │  │ counter         │  │ warmup         │
   │ handlers     │  │ gauge           │  │ samples        │
   │ context      │  │ histogram       │  │ percentiles    │
   │ sampling     │  │ Prometheus      │  │ ips / speedup  │
   │ named logger │  │ BEAM memory     │  │ JSON / MD      │
   └──────┬───────┘  └────────┬────────┘  └────────────────┘
          │                   │
   ┌──────▼─────┐     ┌───────▼──────┐
   │ Erlang     │     │ ETS-backed   │
   │ :logger    │     │ atomic ops   │
   └────────────┘     └──────────────┘
```

<details>
<summary><strong>📋 Core Modules</strong></summary>

| Module                            | Description                                                |
| :-------------------------------- | :--------------------------------------------------------- |
| `viva_telemetry/log`              | Structured logs, named loggers, context, sampling, handlers |
| `viva_telemetry/log/level`        | RFC 5424 levels (trace → emergency)                        |
| `viva_telemetry/log/entry`        | Internal log record type                                   |
| `viva_telemetry/log/handler`      | Console / JSON file / custom handler dispatch              |
| `viva_telemetry/metrics`          | Counter, gauge, histogram, BEAM memory, Prometheus export  |
| `viva_telemetry/bench`            | Warmup, samples, percentiles, IPS, JSON/Markdown export    |
| `viva_telemetry_ffi.erl`          | Erlang FFI: process dict, time, JSON glue                  |
| `viva_telemetry_metrics_ffi.erl`  | ETS metric storage + atomic counters                       |

</details>

---

## 🧬 Design Principles

| Principle                       | Description                                                       |
| :------------------------------ | :---------------------------------------------------------------- |
| **Three independent surfaces**  | `log`, `metrics`, `bench` are import-only-what-you-need           |
| **BEAM-native**                 | ETS for metrics, `:logger` for logs, no external runtime needed   |
| **Process-local config**        | Handler config & `with_context` data live per process             |
| **Prometheus-shaped metrics**   | Output drops straight into any scraper, no adapters               |
| **No surprises**                | Negative counter increments ignored; gauge updates serialized     |

---

## 📊 Modules Walkthrough

### Logging

```gleam
import viva_telemetry/log

// Recommended on the BEAM
log.configure_erlang(log.info_level)

log.info("User logged in", [
  #("user_id", "42"),
  #("ip", "192.168.1.1"),
])
```

<details>
<summary><strong>Handler options</strong></summary>

```gleam
log.configure_erlang(log.info_level)
log.configure_erlang_with_name(log.info_level, "my_app")
log.configure_console(log.debug_level)
log.configure_json("app.jsonl", log.info_level)
log.configure_full(log.debug_level, "app.jsonl", log.info_level)
```

</details>

<details>
<summary><strong>Named loggers, context, sampling, lazy logs</strong></summary>

```gleam
import gleam/int
import gleam/option.{Some}

let logger =
  log.logger("app.http")
  |> log.with_field("request_id", "abc123")
  |> log.with_int("attempt", 1)
  |> log.with_option("user_id", Some(42), int.to_string)

logger
|> log.logger_info_with("Request completed", [#("status", "200")])

log.with_context([#("request_id", "abc123")], fn() {
  log.debug("Processing request", [])
})

log.debug_lazy(fn() { "expensive: " <> expensive_to_string(data) }, [])

log.sampled(log.trace_level, 0.01, "Hot path", [])
```

</details>

### Metrics

```gleam
import viva_telemetry/metrics

let requests = metrics.counter("http_requests_total")
metrics.inc(requests)
metrics.inc_by(requests, 5)

let connections = metrics.gauge("active_connections")
metrics.set(connections, 42.0)

let latency =
  metrics.histogram_with_labels_and_description(
    "request_duration_seconds",
    [0.1, 0.5, 1.0],
    [#("route", "/users")],
    "Request duration in seconds.",
  )

let result = metrics.time_ms(latency, fn() { do_work() })
io.println(metrics.to_prometheus())
```

<details>
<summary><strong>Prometheus output</strong></summary>

```text
# HELP request_duration_seconds Request duration in seconds.
# TYPE request_duration_seconds histogram
request_duration_seconds_bucket{le="0.5",route="/users"} 1
request_duration_seconds_bucket{le="+Inf",route="/users"} 1
request_duration_seconds_sum{route="/users"} 0.25
request_duration_seconds_count{route="/users"} 1
# TYPE beam_memory_total_bytes gauge
beam_memory_total_bytes 12345678
```

</details>

### Benchmarks

```gleam
import viva_telemetry/bench

bench.run("fib_recursive", fn() { fib(30) })
|> bench.print()

let slow = bench.run("v1", fn() { algo_v1() })
let fast = bench.run("v2", fn() { algo_v2() })

bench.compare(slow, fast)
|> bench.print_comparison()

bench.to_json(result)
bench.to_markdown_table([slow, fast])
```

Warmup, percentiles (p50/p95/p99), 95% confidence intervals, IPS, and
optional JSON/Markdown export — all in memory, no external profiler.

---

## 🗺️ Roadmap

| Phase                                            | Status |
| :----------------------------------------------- | :----: |
| Structured logging (console / JSON / Erlang)     | ✅     |
| Named loggers + context propagation              | ✅     |
| Lazy logs + sampling                             | ✅     |
| ETS-backed counters / gauges / histograms        | ✅     |
| Prometheus text export with `HELP` / `TYPE`      | ✅     |
| BEAM memory metrics                              | ✅     |
| Statistical benchmarks (warmup + percentiles)    | ✅     |
| Bench comparison + JSON / Markdown export        | ✅     |
| OpenTelemetry OTLP exporter                      | ⏳     |
| Distributed tracing primitives                   | ⏳     |
| Exemplars on histograms                          | ⏳     |
| Built-in `/metrics` HTTP handler                 | ⏳     |

---

## 🤝 Contributing

```bash
git checkout -b feature/your-feature
gleam test                  # 41 tests
gleam format --check src test
gleam docs build
```

See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.

---

## 📚 Documentation

- **[Hex.pm package](https://hex.pm/packages/viva_telemetry)**
- **[HexDocs reference](https://hexdocs.pm/viva_telemetry/)**
- **[CHANGELOG](./CHANGELOG.md)** — release history.
- **[CONTRIBUTING](./CONTRIBUTING.md)** — guidelines.
- **[SECURITY](./SECURITY.md)** — vulnerability reporting.

### Local development

```bash
make test      # run tests
make bench     # run benchmark example
make log       # run logging example
make metrics   # run metrics example
make docs      # generate HexDocs locally
```

---

## 🌌 VIVA Ecosystem

| Package          | Purpose                                       |
| :--------------- | :-------------------------------------------- |
| `viva_math`      | Mathematical foundations                      |
| `viva_emotion`   | PAD emotional dynamics                        |
| `viva_tensor`    | FP8 LLM inference on the BEAM                 |
| `viva_aion`      | Time perception                               |
| `viva_glyph`     | Symbolic language                             |
| **`viva_telemetry`** | **Observability (this package)**          |

---

## 💡 Inspiration

- **Logging**: Erlang `:logger`, glimt, glog, structlog, zap, tracing
- **Metrics**: Prometheus + BEAM telemetry conventions
- **Benchmarking**: criterion, benchee, hyperfine

---

<div align="center">

**Star if BEAM observability deserves a Gleam-native voice ⭐**

[![GitHub stars](https://img.shields.io/github/stars/gabrielmaialva33/viva_telemetry?style=social)](https://github.com/gabrielmaialva33/viva_telemetry)

*Created by Gabriel Maia · Apache-2.0 License*

<img src="https://capsule-render.vercel.app/api?type=waving&color=0:1A1A1A,100:E6522C&height=100&section=footer" width="100%"/>

</div>
