# Кореляція трейсів і логів

> Дізнайтесь як OBI корелює логи застосунків з розподіленими трейсами для швидшого налагодження та усунення несправностей.

---

LLMS index: [llms.txt](/llms.txt)

---

Інструментування OpenTelemetry eBPF Instrumentation (OBI) корелює логи застосунків з розподіленими трейсами, збагачуючи JSON логи контекстом трасування. OBI не експортує логи; він записує збагачені логи назад до того самого потоку, тоді як трейси експортується через OTLP.

## Огляд {#overview}

Кореляція trace-log зʼєднує два доповнюючи сигнали спостережуваності:

- **Трасування**: Показує проходження запиту крізь сервіси з таймінгом і структурою
- **Логи**: Надають детальну інформацію про події та стан застосунку

За допомогою OBI trace-log correlation, логи з інструментованих процесів автоматично збагачуються контекстом трасування:

- **Trace ID**: Повʼязує запис логу з розподіленим трейсом
- **Span ID**: Повʼязує запис логу з конкретним відрізком трейсу

Це дозволяє вашому бекенду спостережуваності корелювати логи з трейсами з яких вони походять без будь-яких змін у вашому застосунку.

## Як це працює {#how-it-works}

OBI використовує eBPF для того щоб робити інʼєкцію контексту трейсів в логи застосунку на рівні ядра:

1. **Захоплення трасування**: OBI захоплює контекст трасування (ідентифікатор трасування та ідентифікатор відрізка) для всіх відстежуваних операцій
2. **Перехоплення логів**: OBI перехоплює системні виклики запису для збору логів застосунків.
3. **Додавання контексту**: для логів у форматі JSON OBI вводить поля `trace_id` та `span_id`.
4. **Експорт трасування**: логи продовжують надходити через поточний конвеєр логів.
5. **Звʼязування бекенду**: бекенд спостережності повʼязує логи з трасуваннями за допомогою цих ідентифікаторів.

### Технічний підхід {#technical-approach}

OBI виконує кореляцію на рівні ядра без модифікації бінарних файлів застосунків:

- Використовує проби eBPF для перехоплення операцій запису
- Підтримує кешування дескрипторів файлів для покращення продуктивності
- Працює з будь-яким фреймворком логування, який записує логи в форматі JSON

## Налаштування {#configuration}

Кореляція trace-log є доступною, коли експорт трасування є налаштованим та збагачення логів увімкнено для обраних сервісів.

### Базові налаштування {#basic-configuration}

```yaml
# Увімкнення експорту трасувань
otel_traces_export:
  endpoint: http://otel-collector:4318/v1/traces

# Оберіть сервіс для інструментування
discovery:
  instrument:
    - open_ports: '8380'

# Увімкнення збагачення логів для цих сервісів
ebpf:
  log_enricher:
    services:
      - service:
          - open_ports: '8380'
```

Збагачення логів може бути далі налаштоване у `ebpf.log_enricher`:

- `cache_ttl`: час існування дескрипторів файлів кешування
- `cache_size`: максимальна кількість кешованих дескрипторів файлів
- `async_writer_workers`: кількість асинхронних фрагментів записувача
- `async_writer_channel_len`: розмір черги на кожен фрагмент

### Увімкнення кореляції для кожного сервісу {#enabling-correlation-per-service}

OBI збагачує логи JSON для сервісів, перерахованих у `ebpf.log_enricher.services`. Зберігайте вибір сервісів, щоб збагачення відстежувало ті самі процеси.

## Вимоги {#requirements}

### 1. Формат логів JSON {#1-json-log-format}

Кореляція trace-log **вимагає форматованих логів JSON**. OBI робить інʼєкції полів `trace_id` та `span_id` в обʼєкти логів JSON:

**До OBI**:

```json
{ "level": "info", "message": "Request processed", "duration_ms": 125 }
```

**Після збагачення OBI**:

```json
{
  "level": "info",
  "message": "Request processed",
  "duration_ms": 125,
  "trace_id": "4bf92f3577b34da6a3ce929d0e0e4736",
  "span_id": "00f067aa0ba902b7"
}
```

Логи в форматі plain text передаються без змін і **не збагачуються** контекстом трейсів.

#### Обмеження буферизації часу виконання {#runtime-buffering-limitations}

OBI бачить контекст трейсів лише тоді, коли запис логу відбувається в потоці обробки запиту. Середовища виконання, які буферизують stdout асинхронно, можуть порушити це припущення.

- Python у Docker зазвичай потребує `PYTHONUNBUFFERED=1`
- .NET `Console.Out` зазвичай буферизується, коли stdout є pipe; використовуйте `StreamWriter` з `AutoFlush = true`
- Стандартний конвеєр `Microsoft.Extensions.Logging.AddConsole()` у ASP.NET Core не сумісний, оскільки запис відбувається з фонового потоку

### 2. Експорт трасування та збагачення логів увімкнено {2-trace-export-and-log-enrichment-enabled}

Трасування має бути експортованим та збагачення логів увімкненим:

```yaml
otel_traces_export:
  endpoint: http://collector:4318/v1/traces # Обовʼязково

ebpf:
  log_enricher:
    services:
      - service:
          - open_ports: '8380' # Обовʼязково
```

### 3. Ядро Linux {#linux-kernel}

Кореляція trace-log вимагає певних функцій ядра Linux:

- **Linux ядро 6.0+** (потрібне для роботи trace-log кореляції)
- Підтримувані архітектури: x86_64, ARM64
- **BPFFS монтування**: Ядро повинне мати файлову систему BPF змонтовану у `/sys/fs/bpf`
- **Ядро без блокування безпеки**: Потрібне ядро, яке не працює в режимі блокування безпеки (типово для більшості робочих дистрибутивів).

### 4. Фреймворк, що генерує журнали JSON {#4-framework-that-emits-json-logs}

Застосунки мають використовувати налаштовані фреймворки логування для виводу JSON. Приклади:

     <ul class="nav nav-tabs" id="tabs-0" role="tablist">
  <li class="nav-item">
      <button class="nav-link active"
          id="tabs-00-00-tab" data-bs-toggle="tab" data-bs-target="#tabs-00-00" role="tab"
          data-td-tp-persist="python" aria-controls="tabs-00-00" aria-selected="true">
        Python
      </button>
    </li><li class="nav-item">
      <button class="nav-link"
          id="tabs-00-01-tab" data-bs-toggle="tab" data-bs-target="#tabs-00-01" role="tab"
          data-td-tp-persist="go" aria-controls="tabs-00-01" aria-selected="false">
        Go (через zap)
      </button>
    </li><li class="nav-item">
      <button class="nav-link"
          id="tabs-00-02-tab" data-bs-toggle="tab" data-bs-target="#tabs-00-02" role="tab"
          data-td-tp-persist="java" aria-controls="tabs-00-02" aria-selected="false">
        Java (через Logback)
      </button>
    </li><li class="nav-item">
      <button class="nav-link"
          id="tabs-00-03-tab" data-bs-toggle="tab" data-bs-target="#tabs-00-03" role="tab"
          data-td-tp-persist="javascript" aria-controls="tabs-00-03" aria-selected="false">
        Node.js (через pino)
      </button>
    </li>
</ul>

<div class="tab-content" id="tabs-0-content">
    <div class="tab-body tab-pane fade show active"
        id="tabs-00-00" role="tabpanel" aria-labelled-by="tabs-00-00-tab" tabindex="0">
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">json</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">logging</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">JSONFormatter</span><span class="p">(</span><span class="n">logging</span><span class="o">.</span><span class="n">Formatter</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">format</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">record</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="n">log_entry</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="s1">&#39;timestamp&#39;</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">formatTime</span><span class="p">(</span><span class="n">record</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="s1">&#39;level&#39;</span><span class="p">:</span> <span class="n">record</span><span class="o">.</span><span class="n">levelname</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="s1">&#39;message&#39;</span><span class="p">:</span> <span class="n">record</span><span class="o">.</span><span class="n">getMessage</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">            <span class="s1">&#39;module&#39;</span><span class="p">:</span> <span class="n">record</span><span class="o">.</span><span class="n">module</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">json</span><span class="o">.</span><span class="n">dumps</span><span class="p">(</span><span class="n">log_entry</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">logger</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">handler</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">StreamHandler</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">handler</span><span class="o">.</span><span class="n">setFormatter</span><span class="p">(</span><span class="n">JSONFormatter</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"><span class="n">logger</span><span class="o">.</span><span class="n">addHandler</span><span class="p">(</span><span class="n">handler</span><span class="p">)</span>
</span></span></code></pre></div>
    </div>
    <div class="tab-body tab-pane fade"
        id="tabs-00-01" role="tabpanel" aria-labelled-by="tabs-00-01-tab" tabindex="0">
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="s">&#34;go.uber.org/zap&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nx">logger</span><span class="p">,</span><span class="w"> </span><span class="nx">_</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">zap</span><span class="p">.</span><span class="nf">NewProduction</span><span class="p">()</span><span class="w"> </span><span class="c1">// Outputs JSON by default</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">defer</span><span class="w"> </span><span class="nx">logger</span><span class="p">.</span><span class="nf">Sync</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nx">logger</span><span class="p">.</span><span class="nf">Info</span><span class="p">(</span><span class="s">&#34;Request processed&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">zap</span><span class="p">.</span><span class="nf">Duration</span><span class="p">(</span><span class="s">&#34;duration&#34;</span><span class="p">,</span><span class="w"> </span><span class="mi">125</span><span class="o">*</span><span class="nx">time</span><span class="p">.</span><span class="nx">Millisecond</span><span class="p">))</span><span class="w">
</span></span></span></code></pre></div>
    </div>
    <div class="tab-body tab-pane fade"
        id="tabs-00-02" role="tabpanel" aria-labelled-by="tabs-00-02-tab" tabindex="0">
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="nt">&lt;appender</span> <span class="na">name=</span><span class="s">&#34;FILE&#34;</span> <span class="na">class=</span><span class="s">&#34;ch.qos.logback.core.ConsoleAppender&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;encoder</span> <span class="na">class=</span><span class="s">&#34;net.logstash.logback.encoder.LogstashEncoder&#34;</span><span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/appender&gt;</span>
</span></span></code></pre></div>
    </div>
    <div class="tab-body tab-pane fade"
        id="tabs-00-03" role="tabpanel" aria-labelled-by="tabs-00-03-tab" tabindex="0">
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">pino</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;pino&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">logger</span> <span class="o">=</span> <span class="nx">pino</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nx">logger</span><span class="p">.</span><span class="nx">info</span><span class="p">({</span> <span class="nx">duration_ms</span><span class="o">:</span> <span class="mi">125</span> <span class="p">},</span> <span class="s1">&#39;Request processed&#39;</span><span class="p">);</span>
</span></span></code></pre></div>
    </div>
</div>


### 5. Конвеєр постачання логів {#5-log-shipping-pipeline}

OBI збагачує логи на місці. Використовуйте наявний лог-форвардер або колектор для передачі логів у ваш бекенд.

## Зауваження щодо продуктивності {#performance-considerations}

- **Мінімальні накладні витрати**: кореляція використовує проби ядра eBPF з ефективним кешуванням файлових дескрипторів
- **Обмеження кешу**: кеш файлових дескрипторів має обмеження розміру та TTL, щоб запобігти необмеженому використанню памʼяті
- **Асинхронна обробка**: збагачення журналів використовує асинхронні робочі процеси, щоб уникнути переповнення ringbuffer ядра

## Відомі обмеження {#known-limitations}

- **Тільки JSON**: Логи в форматі звичайного тексту не збагачуються контекстом трасування
- **Кеш файлових дескрипторів**: Налаштовано на продуктивність, з налаштованим TTL (типово: 30 хвилин)
- **Тільки в межах відрізку**: Логи збагачуються тільки поки відрізок є активним; логи поза межами відрізку не збагачуються.

## Розвʼязання проблем {#troubleshooting}

### Вміст трасування не зʼявляється в логах {#trace-context-not-appearing-in-logs}

1. **Перевірте формат JSON**: Переконайтесь, що логи застосунків мають відповідний формат JSON

   ```bash
   # Перевка на наявність пошкдження JSON
   cat app.log | jq empty && echo "Valid JSON" || echo "Invalid JSON"
   ```

2. **Перевірте експорт трасування та збагачення логів**:

   ```yaml
   otel_traces_export:
     endpoint: http://collector:4318/v1/traces

   ebpf:
     log_enricher:
       services:
         - service:
             - open_ports: '8380'
   ```

3. **Перевірте ядро Linux**: Кореляція trace-log вимагає Linux

   ```bash
   uname -s  # У відповід маєте отримати "Linux"
   ```

4. **Перевірте конвеєр логів**: Перевірте ваш лог-форвардер, чи пересилає він логи до бекенду

## Що далі? {#whats-next}

- Встановлення [місця призначення експорту](/docs/zero-code/obi/configure/export-data/) для трасувань та метрик
- Більше про OBI як [приймач Колектора](/docs/zero-code/obi/configure/collector-receiver/) для централізованої обробки
