# OpenTelemetry Logging Pipeline

GoClaw's logging pipeline writes every log line to the Web UI's live log stream and, when each is enabled, to the console, a rotated file, syslog and an OpenTelemetry OTLP/HTTP exporter.

`internal/logging/pipeline.go`

## Architecture

```mermaid
flowchart TD
    SETUP["<b>logging.Setup(ctx, cfg, logBroadcaster)</b>"] --> MULTI["<b>log.SetOutput(io.MultiWriter(writers...))</b>"]

    MULTI --> W1["<b>LogBroadcaster</b><br/>SSE → Web UI (always)"]
    MULTI --> W2["<b>os.Stdout</b><br/>(if console.enabled)"]
    MULTI --> W3["<b>File</b><br/>(if file.enabled → gateway.log, rotated)"]
    MULTI --> W4["<b>Syslog</b><br/>(if syslog.enabled; not on Windows)"]
    MULTI --> W5["<b>otlpWriter</b><br/>(if otel.enabled + DSN)"]

    W5 --> OTLP["<b>otlploghttp.Exporter → OTLP/HTTP</b><br/>• BatchProcessor → efficiency<br/>• Severity: ERROR/error → SeverityError"]
    SETUP --> CLOSE["<b>CloseFunc</b><br/>Graceful shutdown (5s timeout)"]
```

## Feature Comparison

| # | Feature | OpenClaw | GoClaw | Status |
|---|---------|----------|---------|--------|
| 4.1 | Console logging | ✅ | ✅ `logging.console.enabled` → `os.Stdout` | Done |
| 4.2 | SSE log broadcast | ✅ WebSocket | ✅ `gateway.LogBroadcaster` → Web UI | Done |
| 4.3 | File logging | ✅ | ✅ `logging.file.enabled` → `~/.goclaw/logs/gateway.log`, rotated by size (lumberjack) | Done |
| 4.3a | Syslog | — | ✅ `logging.syslog.enabled` → the OS log on Linux (`journalctl -t goclaw`). On macOS the messages do not reach the unified log (`log show` finds nothing); use the file log. Not on Windows | Partial |
| 4.4 | Multi-writer pipeline | Separate outputs | ✅ `io.MultiWriter(console, SSE, file, syslog, OTEL)` | Done |
| 4.5 | **OpenTelemetry OTLP/HTTP export** | — | ✅ `otlploghttp.Exporter` with batch processor | **GoClaw only** |
| 4.6 | **OTEL severity auto-detection** | — | ✅ a line containing `ERROR` or `error` → `SeverityError`, else `SeverityInfo` | **GoClaw only** |
| 4.7 | **Custom OTEL headers** | — | ✅ `logging.otel.headers` map | **GoClaw only** |
| 4.8 | **Insecure mode** | — | ✅ `logging.otel.insecure` flag | **GoClaw only** |
| 4.9 | **Configurable service name** | — | ✅ `logging.otel.service_name` (default: `goclaw-gateway`) | **GoClaw only** |
| 4.10 | Resource attributes | — | ✅ `semconv.ServiceName` | Done |
| 4.11 | **Graceful shutdown** | — | ✅ `LoggerProvider.Shutdown` (5s timeout), concurrent file closers | **GoClaw only** |
| 4.12 | Structured JSON logging | ✅ | — | **TODO** |

### Config

```yaml
logging:
  console:
    enabled: true
  file:
    enabled: true
    path: "/var/log/goclaw/gateway.log"
    max_size_mb: 100     # rotate at this size (default 100)
    max_backups: 7       # rotated files to keep (default 7)
    max_age_days: 30     # days to keep rotated files (default 30)
    compress: false      # gzip rotated files (default false)
  syslog:
    enabled: false
    tag: "goclaw"        # default "goclaw"
    facility: "daemon"   # "daemon" (default) or "local0"–"local7"
  otel:
    enabled: true
    dsn: "https://otel-collector.example.com/v1/logs"
    service_name: "goclaw-prod"
    headers:
      Authorization: "Bearer <token>"
    insecure: false
```

### Dependencies

| Package | Purpose |
|---------|---------|
| `otlploghttp` | OTLP/HTTP log exporter |
| `sdk/log` | LoggerProvider + BatchProcessor |
| `semconv/v1.26.0` | Resource attribute conventions |
| `gopkg.in/lumberjack.v2` | Log file rotation |

Structured JSON logging is not implemented.
