# Instrumentação .NET com Elven Observability

## Sumário

* Visão geral
* Pré-requisitos
* Caminho recomendado
* Quick Start - Docker zero-code (recomendado)
* Quick Start - Zero-code no processo
* Pacotes NuGet (para uso programático)
* Quick Start - ASP.NET Core programático
* Quick Start - Worker Service programático
* Quick Start - Console app
* Configuração por variáveis de ambiente
* Configuração via appsettings.json
* Deploy com Docker
* Guia por tipo de aplicação
* Usando logs, traces e métricas manuais
* Instrumentações automáticas
* Mapeamento de erros
* Recursos e metadados
* Configuração avançada
* Native AOT e trimming
* Validação ponta a ponta
* Boas práticas
* Troubleshooting
* FAQ

***

### Visão geral

A instrumentação .NET da Elven é uma distribuição OpenTelemetry pronta para produção. Ela configura o SDK oficial do OpenTelemetry .NET com os padrões da Elven Observability para o stack LGTM:

* **Grafana Loki** para logs
* **Grafana Tempo** para traces
* **Grafana Mimir** para métricas
* **Grafana** para visualização, correlação e alertas

```
┌──────────────────────────────────────────────────────────────┐
│                    Elven.Observability                        │
│                                                              │
│  ┌────────────────────┐ ┌────────────────────┐ ┌───────────┐ │
│  │ Traces             │ │ Metrics            │ │ Logs      │ │
│  │                    │ │                    │ │           │ │
│  │ - ASP.NET Core     │ │ - Runtime .NET     │ │ - ILogger │ │
│  │ - HttpClient       │ │ - Process          │ │ - Scopes  │ │
│  │ - gRPC             │ │ - HTTP/Kestrel     │ │ - State   │ │
│  │ - SQL/EF/Redis     │ │ - HttpClient       │ │ - Trace   │ │
│  │ - Cloud/Messaging  │ │ - Custom meters    │ │   link    │ │
│  └─────────┬──────────┘ └─────────┬──────────┘ └─────┬─────┘ │
│            │                      │                  │       │
│            └──────────────────────┼──────────────────┘       │
│                                   ▼                          │
│                         OTLP gRPC ou HTTP/protobuf            │
└───────────────────────────────────┬──────────────────────────┘
                                    │
                                    ▼
                    OTLP Collector no ambiente do cliente
                                    │
                                    ▼
                         Elven Observability backend
                                    │
                 ┌──────────────────┼──────────────────┐
                 ▼                  ▼                  ▼
               Tempo              Mimir              Loki
```

| Componente                                      | O que faz                                                                  |
| ----------------------------------------------- | -------------------------------------------------------------------------- |
| **`Elven.Observability`**                       | Pacote principal. Instale este na maioria dos projetos.                    |
| **`Elven.Observability.Hosting`**               | Extensões para `IHostApplicationBuilder` e `IServiceCollection`.           |
| **`Elven.Observability.AspNetCore`**            | Extensões ASP.NET Core, middleware de erros e health check.                |
| **`Elven.Observability.AutoInstrumentation`**   | Plugin para zero-code instrumentation com o CLR profiler do OpenTelemetry. |
| **`elvenobservability/dotnet-instrumentation`** | Imagem Docker com a camada de auto-instrumentação pronta para uso.         |

> **Importante:** em modo Elven, **logs, métricas e traces ficam sempre ligados**. Configurações que tentam desligar um sinal, como `OTEL_LOGS_EXPORTER=none`, são tratadas como erro de configuração.

> **Arquitetura:** o Collector OTLP fica **sempre do lado do cliente**: Kubernetes, ECS, VM, Docker Compose, appliance interno ou outro ambiente gerenciado pelo cliente. A aplicação .NET envia OTLP para esse Collector do cliente, e ele encaminha para os backends Elven.

***

### Pré-requisitos

* **.NET 8 LTS**, **.NET 9** ou **.NET 10**
* Aplicações ASP.NET Core, Worker Services, Generic Host, console apps, gRPC, serviços em container ou jobs
* **Collector OTLP implantado no ambiente do cliente**
* Credenciais da Elven Observability:
  * **Tenant ID**
  * **API Token**
  * **Endpoint OTLP do Collector do cliente**

#### Protocolos suportados

| Protocolo          | Valor           | Uso recomendado                                      |
| ------------------ | --------------- | ---------------------------------------------------- |
| OTLP gRPC          | `grpc`          | Padrão recomendado para aplicações .NET.             |
| OTLP HTTP/protobuf | `http/protobuf` | Útil em ambientes que bloqueiam gRPC ou exigem HTTP. |

#### Targets suportados

| Target framework | Status                      |
| ---------------- | --------------------------- |
| `net8.0`         | Suportado, base mínima LTS. |
| `net9.0`         | Suportado.                  |
| `net10.0`        | Suportado.                  |

***

### Caminho recomendado

Comece sempre pelo caminho com menor atrito operacional. A ideia é instrumentar primeiro, validar os três sinais no Grafana e só depois decidir se precisa de customização no código.

| Prioridade | Caminho                                | Quando usar                                                                             |
| ---------- | -------------------------------------- | --------------------------------------------------------------------------------------- |
| 1          | **Docker zero-code com env vars**      | Recomendado para a maioria dos serviços containerizados. Não exige alterar o código.    |
| 2          | **Zero-code no processo**              | Bom para VM, systemd, processos `dotnet` diretos e ambientes sem rebuild da imagem.     |
| 3          | **Programático ASP.NET Core / Worker** | Use quando precisa de spans, métricas, health check e políticas customizadas no código. |
| 4          | **Console/manual**                     | Use para jobs curtos, CLIs e aplicações sem Host/DI.                                    |

> O endpoint OTLP usado nos exemplos deve apontar para o **Collector do cliente**, não para um collector compartilhado da Elven.

***

### Quick Start - Docker zero-code (recomendado)

Este é o caminho preferencial para aplicações .NET em container. A aplicação não precisa instalar o pacote NuGet nem alterar `Program.cs`; a camada `elvenobservability/dotnet-instrumentation` injeta o CLR profiler do OpenTelemetry e o plugin da Elven.

#### 1. Adicione a camada Elven no Dockerfile

```dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:10.0

COPY --from=elvenobservability/dotnet-instrumentation:latest /otel /otel

ENV CORECLR_ENABLE_PROFILING=1 \
    CORECLR_PROFILER={918728DD-259F-4A6A-AC2B-B85E1B658318} \
    CORECLR_PROFILER_PATH=/otel/current/OpenTelemetry.AutoInstrumentation.Native.so \
    DOTNET_ADDITIONAL_DEPS=/otel/AdditionalDeps \
    DOTNET_SHARED_STORE=/otel/store \
    DOTNET_STARTUP_HOOKS=/otel/net/OpenTelemetry.AutoInstrumentation.StartupHook.dll \
    OTEL_DOTNET_AUTO_HOME=/otel \
    OTEL_DOTNET_AUTO_PLUGINS="Elven.Observability.AutoInstrumentation.ElvenPlugin, Elven.Observability.AutoInstrumentation" \
    OTEL_TRACES_EXPORTER=otlp \
    OTEL_METRICS_EXPORTER=otlp \
    OTEL_LOGS_EXPORTER=otlp \
    OTEL_EXPORTER_OTLP_PROTOCOL=grpc

WORKDIR /app
COPY . /app
ENTRYPOINT ["dotnet", "/app/MinhaAplicacao.dll"]
```

#### 2. Configure por variáveis de ambiente

```bash
docker run --rm \
  -e OTEL_SERVICE_NAME="minha-api" \
  -e OTEL_SERVICE_VERSION="1.0.0" \
  -e ELVEN_SERVICE_NAMESPACE="checkout" \
  -e ELVEN_ENVIRONMENT="production" \
  -e ELVEN_REGION="sa-east-1" \
  -e OTEL_EXPORTER_OTLP_ENDPOINT="https://otel-collector.sua-infra.com:4317" \
  -e OTEL_EXPORTER_OTLP_PROTOCOL="grpc" \
  -e OTEL_TRACES_EXPORTER="otlp" \
  -e OTEL_METRICS_EXPORTER="otlp" \
  -e OTEL_LOGS_EXPORTER="otlp" \
  -e ELVEN_TENANT_ID="seu-tenant-id" \
  -e ELVEN_API_KEY="seu-api-token" \
  -p 8080:8080 \
  minha-api:latest
```

#### 3. Gere tráfego e valide

```bash
curl http://localhost:8080/
```

No Grafana, procure pelo `service.name` configurado em `OTEL_SERVICE_NAME`.

***

### Quick Start - Zero-code no processo

Use este modo em VM, systemd, shells, CI ou qualquer ambiente onde a aplicação roda com `dotnet MinhaAplicacao.dll` e você não quer alterar o código.

> Native AOT não suporta CLR profiler. Para Native AOT, use inicialização programática.

#### Linux/macOS

```bash
curl -sSL https://raw.githubusercontent.com/elven-observability/opentelemetry-instrumentation-dotnet/main/scripts/install.sh | sh

. "$HOME/.otel-dotnet-auto/instrument.sh"

export OTEL_DOTNET_AUTO_PLUGINS="Elven.Observability.AutoInstrumentation.ElvenPlugin, Elven.Observability.AutoInstrumentation"

export OTEL_SERVICE_NAME="minha-api"
export OTEL_SERVICE_VERSION="1.0.0"
export ELVEN_SERVICE_NAMESPACE="checkout"
export ELVEN_ENVIRONMENT="production"

export OTEL_EXPORTER_OTLP_ENDPOINT="https://otel-collector.sua-infra.com:4317"
export OTEL_EXPORTER_OTLP_PROTOCOL="grpc"

export OTEL_TRACES_EXPORTER="otlp"
export OTEL_METRICS_EXPORTER="otlp"
export OTEL_LOGS_EXPORTER="otlp"

export ELVEN_TENANT_ID="seu-tenant-id"
export ELVEN_API_KEY="seu-api-token"

dotnet MinhaAplicacao.dll
```

#### Windows PowerShell

```powershell
iwr https://raw.githubusercontent.com/elven-observability/opentelemetry-instrumentation-dotnet/main/scripts/install.ps1 -OutFile install.ps1
.\install.ps1

$env:CORECLR_ENABLE_PROFILING = "1"
$env:OTEL_DOTNET_AUTO_PLUGINS = "Elven.Observability.AutoInstrumentation.ElvenPlugin, Elven.Observability.AutoInstrumentation"

$env:OTEL_SERVICE_NAME = "minha-api"
$env:OTEL_SERVICE_VERSION = "1.0.0"
$env:ELVEN_SERVICE_NAMESPACE = "checkout"
$env:ELVEN_ENVIRONMENT = "production"

$env:OTEL_EXPORTER_OTLP_ENDPOINT = "https://otel-collector.sua-infra.com:4317"
$env:OTEL_EXPORTER_OTLP_PROTOCOL = "grpc"

$env:OTEL_TRACES_EXPORTER = "otlp"
$env:OTEL_METRICS_EXPORTER = "otlp"
$env:OTEL_LOGS_EXPORTER = "otlp"

$env:ELVEN_TENANT_ID = "seu-tenant-id"
$env:ELVEN_API_KEY = "seu-api-token"

dotnet MinhaAplicacao.dll
```

#### Variáveis principais do profiler

Em ambientes automatizados, confirme se as variáveis abaixo estão presentes:

| Variável                   | Valor Linux comum                                                                              |
| -------------------------- | ---------------------------------------------------------------------------------------------- |
| `CORECLR_ENABLE_PROFILING` | `1`                                                                                            |
| `CORECLR_PROFILER`         | `{918728DD-259F-4A6A-AC2B-B85E1B658318}`                                                       |
| `CORECLR_PROFILER_PATH`    | `/otel/current/OpenTelemetry.AutoInstrumentation.Native.so`                                    |
| `DOTNET_ADDITIONAL_DEPS`   | `/otel/AdditionalDeps`                                                                         |
| `DOTNET_SHARED_STORE`      | `/otel/store`                                                                                  |
| `DOTNET_STARTUP_HOOKS`     | `/otel/net/OpenTelemetry.AutoInstrumentation.StartupHook.dll`                                  |
| `OTEL_DOTNET_AUTO_HOME`    | `/otel`                                                                                        |
| `OTEL_DOTNET_AUTO_PLUGINS` | `Elven.Observability.AutoInstrumentation.ElvenPlugin, Elven.Observability.AutoInstrumentation` |

***

### Pacotes NuGet (para uso programático)

Esta etapa só é necessária quando você quer configurar a observabilidade pelo código, por exemplo com `builder.AddElvenObservability(...)` ou `ElvenObservability.Initialize(...)`. Para Docker zero-code ou profiler zero-code, pule esta seção.

#### Instalação básica

```bash
dotnet add package Elven.Observability
```

#### Verificar instalação

```bash
dotnet list package | grep Elven.Observability
```

#### Pacotes disponíveis

Na maioria dos casos, use apenas `Elven.Observability`. Os pacotes abaixo existem para cenários avançados, composição manual ou redução explícita de dependências:

| Pacote                                                    | Quando usar                                                            |
| --------------------------------------------------------- | ---------------------------------------------------------------------- |
| `Elven.Observability`                                     | Facade principal para aplicações comuns.                               |
| `Elven.Observability.Abstractions`                        | Contratos, opções e nomes estáveis.                                    |
| `Elven.Observability.Core`                                | Resolver de configuração, resource detector, erro e telemetria manual. |
| `Elven.Observability.Hosting`                             | Aplicações com Generic Host ou DI.                                     |
| `Elven.Observability.AspNetCore`                          | ASP.NET Core, middleware e health check.                               |
| `Elven.Observability.Exporters.Otlp`                      | Defaults Elven para OTLP.                                              |
| `Elven.Observability.AutoInstrumentation`                 | Plugin zero-code.                                                      |
| `Elven.Observability.Instrumentation.Sql`                 | SQL Client.                                                            |
| `Elven.Observability.Instrumentation.EntityFrameworkCore` | Entity Framework Core.                                                 |
| `Elven.Observability.Instrumentation.Redis`               | StackExchange.Redis.                                                   |
| `Elven.Observability.Instrumentation.MongoDB`             | MongoDB via `ActivitySource`.                                          |
| `Elven.Observability.Instrumentation.Messaging`           | Kafka, RabbitMQ e MassTransit via sources/meters.                      |
| `Elven.Observability.Instrumentation.Cloud`               | AWS SDK e Azure SDK via sources/meters.                                |

***

### Quick Start - ASP.NET Core programático

#### 1. Instale o pacote

```bash
dotnet add package Elven.Observability
```

#### 2. Configure as variáveis de ambiente

```bash
export OTEL_SERVICE_NAME="minha-api"
export OTEL_SERVICE_VERSION="1.0.0"
export ELVEN_SERVICE_NAMESPACE="checkout"
export ELVEN_ENVIRONMENT="production"
export ELVEN_REGION="sa-east-1"

export OTEL_EXPORTER_OTLP_ENDPOINT="https://otel-collector.sua-infra.com:4317"
export OTEL_EXPORTER_OTLP_PROTOCOL="grpc"

export OTEL_TRACES_EXPORTER="otlp"
export OTEL_METRICS_EXPORTER="otlp"
export OTEL_LOGS_EXPORTER="otlp"

export ELVEN_TENANT_ID="seu-tenant-id"
export ELVEN_API_KEY="seu-api-token"
```

#### 3. Adicione a instrumentação no `Program.cs`

```csharp
using Elven.Observability;

var builder = WebApplication.CreateBuilder(args);

builder.AddElvenObservability();

var app = builder.Build();

app.UseElvenObservability();
app.MapElvenObservabilityHealthCheck();

app.MapGet("/", (ILogger<Program> logger) =>
{
    logger.LogInformation("Request processada com correlação automática.");
    return Results.Ok(new { status = "ok" });
});

app.Run();
```

#### 4. Rode a aplicação

```bash
dotnet run
```

Pronto. A aplicação já envia traces, métricas e logs por OTLP.

***

### Quick Start - Worker Service programático

Use este modo para consumidores, filas, jobs long-running, processadores de eventos e serviços sem HTTP.

```csharp
using System.Diagnostics.Metrics;
using Elven.Observability;

var builder = Host.CreateApplicationBuilder(args);

builder.AddElvenObservability(options =>
{
    options.ServiceName = "worker-pagamentos";
    options.ServiceNamespace = "payments";
    options.EnvironmentName = "production";
});

builder.Services.AddHostedService<Worker>();

await builder.Build().RunAsync();

internal sealed class Worker(ILogger<Worker> logger) : BackgroundService
{
    private readonly Counter<long> processed =
        ElvenTelemetry.Meter.CreateCounter<long>("payments.worker.processed");

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            using var activity = ElvenTelemetry.ActivitySource.StartActivity("payment.process");

            processed.Add(1);
            activity?.SetTag("payment.provider", "acquirer-a");

            logger.LogInformation("Pagamento processado pelo worker.");

            await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);
        }
    }
}
```

***

### Quick Start - Console app

Use a API estática quando a aplicação não usa DI, `Host` ou ASP.NET Core.

```csharp
using Elven.Observability;
using Microsoft.Extensions.Logging;

await using var observability = ElvenObservability.Initialize(options =>
{
    options.ServiceName = "job-fechamento";
    options.ServiceNamespace = "finance";
    options.EnvironmentName = "production";
    options.TenantId = "seu-tenant-id";
    options.ApiKey = Environment.GetEnvironmentVariable("ELVEN_API_KEY");
    options.Endpoint = new Uri("https://otel-collector.sua-infra.com:4317");
});

using (var activity = ElvenTelemetry.ActivitySource.StartActivity("closing.run"))
{
    var logger = observability.LoggerFactory.CreateLogger("Closing.Job");

    activity?.SetTag("job.type", "daily-closing");
    ElvenTelemetry.Meter.CreateCounter<long>("closing.runs").Add(1);

    logger.LogInformation("Fechamento iniciado.");
}

await observability.ForceFlushAsync();
```

> Para jobs curtos, chame `ForceFlushAsync()` antes de encerrar o processo. Isso evita perder spans, métricas ou logs que ainda estejam em buffer.

***

### Configuração por variáveis de ambiente

A configuração segue os padrões OpenTelemetry e adiciona extensões Elven.

#### Identidade do serviço

| Variável                   | Descrição                                                 | Exemplo                               |
| -------------------------- | --------------------------------------------------------- | ------------------------------------- |
| `OTEL_SERVICE_NAME`        | Nome do serviço em traces, métricas e logs.               | `checkout-api`                        |
| `OTEL_SERVICE_VERSION`     | Versão da aplicação.                                      | `1.4.2`                               |
| `ELVEN_SERVICE_NAMESPACE`  | Namespace lógico do serviço.                              | `payments`                            |
| `ELVEN_ENVIRONMENT`        | Ambiente de deploy. Popula `deployment.environment.name`. | `production`                          |
| `ELVEN_REGION`             | Região lógica ou cloud region.                            | `sa-east-1`                           |
| `OTEL_RESOURCE_ATTRIBUTES` | Atributos adicionais separados por vírgula.               | `team=core,service.instance.id=pod-1` |

#### Export OTLP

| Variável                      | Descrição                                                  | Default                  |
| ----------------------------- | ---------------------------------------------------------- | ------------------------ |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | Endpoint OTLP do Collector do cliente para os três sinais. | Configure explicitamente |
| `ELVEN_OTLP_ENDPOINT`         | Alias Elven para o endpoint OTLP.                          | -                        |
| `OTEL_EXPORTER_OTLP_PROTOCOL` | `grpc` ou `http/protobuf`.                                 | `grpc`                   |
| `OTEL_EXPORTER_OTLP_HEADERS`  | Headers extras no formato `key=value,key2=value2`.         | -                        |
| `OTEL_EXPORTER_OTLP_TIMEOUT`  | Timeout de export em ms.                                   | `30000`                  |
| `OTEL_METRIC_EXPORT_INTERVAL` | Intervalo de export de métricas em ms.                     | `60000`                  |

#### Autenticação e multi-tenancy

| Variável          | Mapeamento                                           |
| ----------------- | ---------------------------------------------------- |
| `ELVEN_API_KEY`   | Enviado como header `Authorization: Bearer <token>`. |
| `ELVEN_TENANT_ID` | Enviado como header `x-scope-orgid: <tenant>`.       |

Também é possível usar `OTEL_EXPORTER_OTLP_HEADERS`, mas prefira `ELVEN_API_KEY` e `ELVEN_TENANT_ID` para evitar erro de formatação:

```bash
export ELVEN_API_KEY="seu-api-token"
export ELVEN_TENANT_ID="seu-tenant-id"
```

#### Política de sinais sempre ligados

| Variável                | Valor correto |
| ----------------------- | ------------- |
| `OTEL_TRACES_EXPORTER`  | `otlp`        |
| `OTEL_METRICS_EXPORTER` | `otlp`        |
| `OTEL_LOGS_EXPORTER`    | `otlp`        |

Os valores abaixo são inválidos em modo Elven:

```bash
OTEL_TRACES_EXPORTER=none
OTEL_METRICS_EXPORTER=none
OTEL_LOGS_EXPORTER=none
OTEL_TRACES_SAMPLER=always_off
```

Quando isso acontece, a aplicação falha no startup com uma mensagem explícita e o health check de observabilidade fica `Unhealthy`.

#### Sampling

| Variável                  | Descrição                           | Exemplo     |
| ------------------------- | ----------------------------------- | ----------- |
| `OTEL_TRACES_SAMPLER`     | `always_on` ou sampler compatível.  | `always_on` |
| `OTEL_TRACES_SAMPLER_ARG` | Ratio de sampling quando aplicável. | `0.25`      |

O `SamplingRatio` precisa ser maior que `0` e menor ou igual a `1`.

#### Debug

| Variável      | Descrição                                                                        |
| ------------- | -------------------------------------------------------------------------------- |
| `ELVEN_DEBUG` | Quando `true`, `1` ou equivalente, habilita diagnósticos internos mais verbosos. |

***

### Configuração via appsettings.json

A lib lê a seção `ElvenObservability`. Também aceita `Elven:Observability` para compatibilidade com padrões hierárquicos.

```json
{
  "ElvenObservability": {
    "ServiceName": "checkout-api",
    "ServiceNamespace": "payments",
    "ServiceVersion": "1.4.2",
    "EnvironmentName": "production",
    "Region": "sa-east-1",
    "Endpoint": "https://otel-collector.sua-infra.com:4317",
    "Protocol": "Grpc",
    "TenantId": "seu-tenant-id",
    "SamplingRatio": 1.0,
    "MetricExportInterval": "00:01:00",
    "ExporterTimeout": "00:00:30",
    "RedactDbStatements": true,
    "CaptureRequestHeaders": true,
    "CaptureResponseHeaders": true,
    "AllowedRequestHeaders": [
      "x-request-id",
      "user-agent"
    ],
    "AllowedResponseHeaders": [
      "x-request-id"
    ],
    "ResourceAttributes": {
      "team": "checkout",
      "cost.center": "payments"
    }
  }
}
```

> Não grave `ApiKey` em `appsettings.json` versionado. Use secret manager, variável de ambiente, cofre de segredo ou mecanismo equivalente da sua plataforma.

#### Ordem de precedência

A resolução segue esta ordem:

1. Defaults Elven
2. `appsettings.json`
3. Variáveis de ambiente
4. Configuração programática passada no `AddElvenObservability(...)`

***

### Deploy com Docker

#### Modo zero-code com camada Elven

Use este modo como padrão para serviços containerizados. Ele não exige alteração no código da aplicação.

```dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:10.0

COPY --from=elvenobservability/dotnet-instrumentation:latest /otel /otel

ENV CORECLR_ENABLE_PROFILING=1 \
    CORECLR_PROFILER={918728DD-259F-4A6A-AC2B-B85E1B658318} \
    CORECLR_PROFILER_PATH=/otel/current/OpenTelemetry.AutoInstrumentation.Native.so \
    DOTNET_ADDITIONAL_DEPS=/otel/AdditionalDeps \
    DOTNET_SHARED_STORE=/otel/store \
    DOTNET_STARTUP_HOOKS=/otel/net/OpenTelemetry.AutoInstrumentation.StartupHook.dll \
    OTEL_DOTNET_AUTO_HOME=/otel \
    OTEL_DOTNET_AUTO_PLUGINS="Elven.Observability.AutoInstrumentation.ElvenPlugin, Elven.Observability.AutoInstrumentation" \
    OTEL_SERVICE_NAME=minha-api \
    ELVEN_SERVICE_NAMESPACE=checkout \
    ELVEN_ENVIRONMENT=production \
    OTEL_EXPORTER_OTLP_ENDPOINT=https://otel-collector.sua-infra.com:4317 \
    OTEL_EXPORTER_OTLP_PROTOCOL=grpc \
    OTEL_TRACES_EXPORTER=otlp \
    OTEL_METRICS_EXPORTER=otlp \
    OTEL_LOGS_EXPORTER=otlp \
    ELVEN_TENANT_ID=seu-tenant-id

WORKDIR /app
COPY . /app
ENTRYPOINT ["dotnet", "/app/MinhaAplicacao.dll"]
```

#### Docker Compose zero-code

```yaml
services:
  checkout-api:
    image: minha-api:latest
    ports:
      - "8080:8080"
    environment:
      OTEL_SERVICE_NAME: checkout-api
      OTEL_SERVICE_VERSION: 1.4.2
      ELVEN_SERVICE_NAMESPACE: payments
      ELVEN_ENVIRONMENT: production
      ELVEN_REGION: sa-east-1
      OTEL_EXPORTER_OTLP_ENDPOINT: https://otel-collector.sua-infra.com:4317
      OTEL_EXPORTER_OTLP_PROTOCOL: grpc
      OTEL_TRACES_EXPORTER: otlp
      OTEL_METRICS_EXPORTER: otlp
      OTEL_LOGS_EXPORTER: otlp
      ELVEN_TENANT_ID: seu-tenant-id
      ELVEN_API_KEY: ${ELVEN_API_KEY}
```

#### Modo programático

Use este modo quando o pacote NuGet está instalado na aplicação e você quer manter a instrumentação no código.

```dockerfile
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
COPY . .
RUN dotnet publish -c Release -o /app

FROM mcr.microsoft.com/dotnet/aspnet:10.0
WORKDIR /app

ENV OTEL_SERVICE_NAME=checkout-api \
    ELVEN_SERVICE_NAMESPACE=payments \
    ELVEN_ENVIRONMENT=production \
    OTEL_EXPORTER_OTLP_ENDPOINT=https://otel-collector.sua-infra.com:4317 \
    OTEL_EXPORTER_OTLP_PROTOCOL=grpc \
    OTEL_TRACES_EXPORTER=otlp \
    OTEL_METRICS_EXPORTER=otlp \
    OTEL_LOGS_EXPORTER=otlp \
    ELVEN_TENANT_ID=seu-tenant-id

COPY --from=build /app .
ENTRYPOINT ["dotnet", "MinhaAplicacao.dll"]
```

Passe `ELVEN_API_KEY` como segredo no runtime, não no Dockerfile:

```bash
docker run --rm \
  -e ELVEN_API_KEY="seu-api-token" \
  -p 8080:8080 \
  minha-api:latest
```

***

### Guia por tipo de aplicação

#### ASP.NET Core Web API

Use:

```csharp
builder.AddElvenObservability();
app.UseElvenObservability();
app.MapElvenObservabilityHealthCheck();
```

O que é coletado:

* Requests HTTP server
* Route template em `http.route`
* Status code
* Duração
* Exceções
* Logs de `ILogger`
* Correlação `trace_id` e `span_id`
* Métricas ASP.NET Core e Kestrel

#### Minimal APIs

Funciona sem configuração extra:

```csharp
app.MapPost("/orders/{id:int}", (int id, ILogger<Program> logger) =>
{
    logger.LogInformation("Pedido recebido. order_id={OrderId}", id);
    return Results.Accepted();
});
```

#### MVC e Controllers

Também funciona sem configuração extra:

```csharp
[ApiController]
[Route("orders")]
public sealed class OrdersController(ILogger<OrdersController> logger) : ControllerBase
{
    [HttpGet("{id:int}")]
    public IActionResult Get(int id)
    {
        logger.LogInformation("Pedido consultado. order_id={OrderId}", id);
        return Ok(new { id });
    }
}
```

#### gRPC

Instale e configure o serviço normalmente:

```bash
dotnet add package Grpc.AspNetCore
```

```csharp
builder.AddElvenObservability();
builder.Services.AddGrpc();

var app = builder.Build();
app.UseElvenObservability();
app.MapGrpcService<MyGrpcService>();
app.Run();
```

O client gRPC via `Grpc.Net.Client` é instrumentado automaticamente quando `EnableGrpcClientInstrumentation=true`.

#### Worker Service

Use `Host.CreateApplicationBuilder(args)` e `builder.AddElvenObservability()`. Crie spans manuais ao redor de cada unidade de trabalho relevante:

```csharp
using var activity = ElvenTelemetry.ActivitySource.StartActivity("message.consume");
activity?.SetTag("messaging.system", "rabbitmq");
activity?.SetTag("messaging.destination.name", "orders.created");
```

#### Jobs curtos

Sempre finalize com flush:

```csharp
await observability.ForceFlushAsync();
```

#### Lambda e Azure Functions

Para funções com processo .NET moderno, prefira inicialização programática no bootstrap da aplicação ou no host isolado. Em execução curta, garanta flush no final do handler quando possível.

***

### Usando logs, traces e métricas manuais

A instrumentação automática cobre o caminho comum. Para regras de negócio, adicione telemetria manual em pontos relevantes.

#### Logs estruturados

Use `ILogger` normalmente:

```csharp
logger.LogInformation(
    "Pedido aprovado. order_id={OrderId} customer_id={CustomerId} amount={Amount}",
    orderId,
    customerId,
    amount);
```

A lib exporta:

* Mensagem formatada
* Template original
* Propriedades estruturadas
* Scopes
* `trace_id` e `span_id` quando houver span ativo

#### Source-generated logging

Para caminhos quentes, prefira `LoggerMessage`:

```csharp
internal static partial class OrderLogs
{
    [LoggerMessage(
        EventId = 1001,
        Level = LogLevel.Information,
        Message = "Pedido aprovado. order_id={OrderId} amount={Amount}")]
    public static partial void OrderApproved(
        ILogger logger,
        string orderId,
        decimal amount);
}
```

#### Spans manuais

```csharp
using var activity = ElvenTelemetry.ActivitySource.StartActivity("order.reserve_inventory");

activity?.SetTag("order.id", orderId);
activity?.SetTag("inventory.sku", sku);
activity?.SetTag("inventory.quantity", quantity);
```

#### Eventos no span

```csharp
activity?.AddEvent(new ActivityEvent(
    "inventory.reserved",
    tags: new ActivityTagsCollection
    {
        ["inventory.sku"] = sku,
        ["inventory.quantity"] = quantity
    }));
```

#### Métricas customizadas

```csharp
var ordersProcessed =
    ElvenTelemetry.Meter.CreateCounter<long>("orders.processed");

var orderDuration =
    ElvenTelemetry.Meter.CreateHistogram<double>("orders.duration", unit: "ms");

ordersProcessed.Add(1, new KeyValuePair<string, object?>("status", "approved"));
orderDuration.Record(duration.TotalMilliseconds);
```

#### Baggage e propagação

O OpenTelemetry .NET usa W3C TraceContext e Baggage. Para propagar contexto de negócio entre serviços:

```csharp
Baggage.SetBaggage("tenant.id", tenantId);
Activity.Current?.SetTag("tenant.id", tenantId);
```

***

### Instrumentações automáticas

#### Traces

| Área                  | Cobertura                                                            |
| --------------------- | -------------------------------------------------------------------- |
| ASP.NET Core          | Requests, route, status, duração, exceções.                          |
| HttpClient            | Requests outbound, status, duração, exceções.                        |
| gRPC client           | Chamadas `Grpc.Net.Client`.                                          |
| SQL Client            | Queries, comandos, exceções.                                         |
| Entity Framework Core | Operações EF Core.                                                   |
| Redis                 | StackExchange.Redis, statements opcionais.                           |
| AWS SDK               | ActivitySource oficial do AWS SDK.                                   |
| Azure SDK             | ActivitySource dos clients Azure.                                    |
| MongoDB               | Source registration para integração via diagnostic sources.          |
| Kafka                 | Source registration para `Confluent.Kafka.Extensions.OpenTelemetry`. |
| RabbitMQ              | Source registration para `RabbitMQ.Client`.                          |
| MassTransit           | Source e meter registration.                                         |
| Manual                | `ElvenTelemetry.ActivitySource`.                                     |

#### Métricas

| Área                 | Cobertura                                                     |
| -------------------- | ------------------------------------------------------------- |
| Runtime .NET         | GC, heap, allocations, exceptions, threadpool, JIT e runtime. |
| Process              | CPU, memória, threads e handles quando disponível.            |
| ASP.NET Core/Kestrel | Requests, conexões e métricas do servidor.                    |
| HttpClient           | Duração, DNS, TLS e conexões via meters do .NET.              |
| SQL                  | Métricas do provider quando disponíveis.                      |
| AWS                  | Métricas oficiais disponíveis pelo pacote.                    |
| Custom               | `ElvenTelemetry.Meter`.                                       |
| Internas Elven       | Misconfigurações, falhas de exporter e erros classificados.   |

#### Logs

| Fonte        | Cobertura                                     |
| ------------ | --------------------------------------------- |
| `ILogger`    | Logs estruturados, scopes e state values.     |
| ASP.NET Core | Logs do pipeline HTTP e endpoints.            |
| Aplicação    | Logs de negócio com correlação automática.    |
| Elven        | Logs internos de classificação e diagnóstico. |

***

### Mapeamento de erros

O middleware `UseElvenObservability()` e o classificador interno adicionam `error.category` e `error.type` aos spans.

| Caso                                                           | Categoria            |
| -------------------------------------------------------------- | -------------------- |
| `OperationCanceledException`                                   | `cancelled`          |
| `TimeoutException`                                             | `timeout`            |
| `SocketError.HostNotFound`, `NoData`, `TryAgain`               | `dns`                |
| `SocketError.ConnectionRefused`                                | `connection_refused` |
| `HttpRequestException` com certificado/nome remoto suspeito    | `dns_poisoning`      |
| `DbException`, Npgsql, MySql, Mongo, Redis, SqlClient          | `database`           |
| `UnauthorizedAccessException`                                  | `auth`               |
| FluentValidation/DataAnnotations/ValidationException           | `validation`         |
| `ArgumentException`, `FormatException`, `JsonException`        | `user_input`         |
| Erro de configuração no startup                                | `configuration`      |
| HTTP `401`, `403`                                              | `auth`               |
| HTTP `422`                                                     | `validation`         |
| HTTP `400`, `404`, `405`, `409`                                | `user_input_error`   |
| Outros HTTP `4xx`                                              | `client_error`       |
| HTTP `5xx`                                                     | `server_error`       |
| gRPC `CANCELLED`                                               | `cancelled`          |
| gRPC `DEADLINE_EXCEEDED`                                       | `timeout`            |
| gRPC `UNAUTHENTICATED`, `PERMISSION_DENIED`                    | `auth`               |
| gRPC `INVALID_ARGUMENT`, `FAILED_PRECONDITION`, `OUT_OF_RANGE` | `validation`         |
| gRPC `UNAVAILABLE`                                             | `network`            |
| gRPC `INTERNAL`, `DATA_LOSS`, `UNKNOWN`                        | `server_error`       |

#### Exemplo de endpoint com erro

```csharp
app.MapGet("/fail/{kind}", (string kind) => kind switch
{
    "validation" => Results.ValidationProblem(
        new Dictionary<string, string[]> { ["name"] = ["required"] }),
    "auth" => Results.Unauthorized(),
    "timeout" => throw new TimeoutException("Timeout simulado"),
    _ => throw new InvalidOperationException("Falha simulada")
});
```

***

### Recursos e metadados

A lib popula resource attributes seguindo semantic conventions estáveis.

| Atributo                      | Origem                                                                      |
| ----------------------------- | --------------------------------------------------------------------------- |
| `service.name`                | `OTEL_SERVICE_NAME` ou `ServiceName`.                                       |
| `service.namespace`           | `ELVEN_SERVICE_NAMESPACE` ou `ServiceNamespace`.                            |
| `service.version`             | `OTEL_SERVICE_VERSION` ou `ServiceVersion`.                                 |
| `service.instance.id`         | `ServiceInstanceId` ou `OTEL_RESOURCE_ATTRIBUTES`.                          |
| `deployment.environment.name` | `ELVEN_ENVIRONMENT` ou `EnvironmentName`.                                   |
| `host.name`                   | `Environment.MachineName`.                                                  |
| `host.id`                     | `/etc/machine-id`, `/var/lib/dbus/machine-id` ou boot id quando disponível. |
| `container.id`                | Detectado a partir de `/proc/self/cgroup` quando disponível.                |
| `k8s.namespace.name`          | `POD_NAMESPACE`, `KUBERNETES_NAMESPACE` ou service account namespace.       |
| `k8s.pod.name`                | `HOSTNAME`.                                                                 |
| `k8s.pod.uid`                 | `POD_UID`.                                                                  |
| `k8s.node.name`               | `NODE_NAME`.                                                                |
| `k8s.cluster.name`            | `CLUSTER_NAME`.                                                             |
| `process.runtime.name`        | `.NET`.                                                                     |
| `process.runtime.version`     | Versão do runtime.                                                          |
| `telemetry.distro.name`       | `elven-observability-dotnet`.                                               |
| `telemetry.distro.version`    | Versão da lib Elven.                                                        |
| `elven.tenant.id`             | `ELVEN_TENANT_ID` ou `TenantId`.                                            |
| `elven.environment`           | `ELVEN_ENVIRONMENT` ou `EnvironmentName`.                                   |
| `elven.region`                | `ELVEN_REGION` ou `Region`.                                                 |

***

### Configuração avançada

#### Configuração programática completa

```csharp
builder.AddElvenObservability(options =>
{
    options.ServiceName = "checkout-api";
    options.ServiceNamespace = "payments";
    options.ServiceVersion = "1.4.2";
    options.ServiceInstanceId = Environment.MachineName;
    options.EnvironmentName = "production";
    options.Region = "sa-east-1";

    options.Endpoint = new Uri("https://otel-collector.sua-infra.com:4317");
    options.Protocol = ElvenOtlpProtocol.Grpc;
    options.TenantId = "seu-tenant-id";
    options.ApiKey = builder.Configuration["ELVEN_API_KEY"];

    options.SamplingRatio = 1.0;
    options.ExporterTimeout = TimeSpan.FromSeconds(30);
    options.MetricExportInterval = TimeSpan.FromSeconds(60);

    options.RedactDbStatements = true;
    options.RedactHeaders = true;
    options.RedactUrlQuery = true;
    options.HashUserIdentifiers = true;

    options.CaptureRequestHeaders = true;
    options.CaptureResponseHeaders = true;
    options.AllowedRequestHeaders = ["x-request-id", "user-agent"];
    options.AllowedResponseHeaders = ["x-request-id"];

    options.ResourceAttributes["team"] = "checkout";
    options.ResourceAttributes["cost.center"] = "payments";
});
```

#### Opções principais

| Opção                    | Default              | Descrição                                     |
| ------------------------ | -------------------- | --------------------------------------------- |
| `ServiceName`            | `unknown-service`    | Nome do serviço.                              |
| `ServiceNamespace`       | `default`            | Namespace lógico.                             |
| `Endpoint`               | Collector do cliente | Endpoint OTLP para envio dos três sinais.     |
| `Protocol`               | `Grpc`               | `Grpc` ou `HttpProtobuf`.                     |
| `SamplingRatio`          | `1.0`                | Ratio de traces. Precisa ser maior que `0`.   |
| `ExporterTimeout`        | `30s`                | Timeout do exporter.                          |
| `MetricExportInterval`   | `60s`                | Intervalo de export de métricas.              |
| `RedactDbStatements`     | `true`               | Redacta statements de banco quando suportado. |
| `RedactHeaders`          | `true`               | Política de privacidade para headers.         |
| `RedactUrlQuery`         | `true`               | Evita vazamento de query strings sensíveis.   |
| `HashUserIdentifiers`    | `true`               | Política para identificadores de usuário.     |
| `CaptureRequestHeaders`  | `false`              | Captura headers permitidos em spans HTTP.     |
| `CaptureResponseHeaders` | `false`              | Captura headers permitidos em spans HTTP.     |
| `EnableHealthCheck`      | `true`               | Habilita health check de observabilidade.     |

#### Headers HTTP

Headers só são capturados quando:

1. `CaptureRequestHeaders` ou `CaptureResponseHeaders` está `true`
2. O header está na allowlist correspondente

```csharp
options.CaptureRequestHeaders = true;
options.AllowedRequestHeaders = ["x-request-id", "user-agent"];
```

Os nomes são normalizados para atributos OTel:

| Header             | Atributo                                |
| ------------------ | --------------------------------------- |
| `x-request-id`     | `http.request.header.x_request_id`      |
| `user-agent`       | `http.request.header.user_agent`        |
| `x-elven-response` | `http.response.header.x_elven_response` |

#### HTTP/protobuf

```bash
export OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"
export OTEL_EXPORTER_OTLP_ENDPOINT="https://otel-collector.sua-infra.com:4318"
```

Ou programático:

```csharp
options.Protocol = ElvenOtlpProtocol.HttpProtobuf;
```

#### Redação de statements SQL

Por padrão, statements de banco são redigidos.

```csharp
options.RedactDbStatements = true;
```

Para diagnóstico temporário:

```csharp
options.RedactDbStatements = false;
```

> Use statements completos apenas em ambientes controlados. Queries podem conter dados pessoais, tokens ou payloads sensíveis.

***

### Native AOT e trimming

A inicialização programática é compatível com Native AOT e trimming onde o SDK OpenTelemetry e as instrumentações oficiais permitem.

#### Recomendações para AOT

* Use inicialização programática com `builder.AddElvenObservability()`
* Evite zero-code instrumentation, pois CLR profiler não se aplica a Native AOT
* Desabilite instrumentações que dependem de reflexão se o publish acusar warnings
* Mantenha `TreatWarningsAsErrors=true` no projeto

Exemplo:

```xml
<PropertyGroup>
  <PublishAot>true</PublishAot>
  <PublishTrimmed>true</PublishTrimmed>
  <TrimMode>full</TrimMode>
  <InvariantGlobalization>true</InvariantGlobalization>
</PropertyGroup>
```

Publish:

```bash
dotnet publish -c Release -r linux-x64
```

#### Ajustes comuns para AOT

```csharp
builder.AddElvenObservability(options =>
{
    options.ServiceName = "native-aot-api";
    options.EnableSqlClientInstrumentation = false;
    options.EnableEntityFrameworkCoreInstrumentation = false;
    options.EnableRedisInstrumentation = false;
});
```

***

### Validação ponta a ponta

#### 1. Crie um endpoint de teste

```csharp
app.MapGet("/observability-test/{id:int}", async (
    int id,
    IHttpClientFactory httpClientFactory,
    ILogger<Program> logger) =>
{
    using var activity =
        ElvenTelemetry.ActivitySource.StartActivity("observability.test");

    activity?.SetTag("test.id", id);
    activity?.SetTag("db.system", "postgresql");
    activity?.SetTag("db.query.text", "select * from orders where id = @id");

    ElvenTelemetry.Meter
        .CreateCounter<long>("observability.test.requests")
        .Add(1);

    logger.LogInformation("Teste de observabilidade executado. test_id={TestId}", id);

    var client = httpClientFactory.CreateClient();
    await client.GetAsync("https://example.com");

    return Results.Ok(new
    {
        id,
        traceId = Activity.Current?.TraceId.ToString()
    });
});
```

#### 2. Gere tráfego

```bash
curl http://localhost:8080/observability-test/123
```

#### 3. Gere uma exceção controlada

```csharp
app.MapGet("/observability-error", () =>
{
    throw new InvalidOperationException("Erro sintético para validar traces e logs.");
});
```

```bash
curl -i http://localhost:8080/observability-error
```

#### 4. Valide no Grafana

No Grafana, procure por:

* Serviço `service.name`
* Route HTTP
* `trace_id` retornado no endpoint
* Logs com o mesmo `trace_id`
* Métricas customizadas
* Span com `error.category`
* Resource attributes como `deployment.environment.name`, `service.namespace` e `elven.tenant.id`

#### 5. Health check

```bash
curl http://localhost:8080/health/observability
```

Resposta esperada:

```json
{
  "status": "Healthy"
}
```

***

### Boas práticas

#### Nome de serviço

Use nomes estáveis, curtos e sem ambiente no nome:

```bash
OTEL_SERVICE_NAME=checkout-api
ELVEN_SERVICE_NAMESPACE=payments
ELVEN_ENVIRONMENT=production
```

Evite:

```bash
OTEL_SERVICE_NAME=checkout-api-prod-v2-pod-123
```

#### Cardinalidade

Não use valores de alta cardinalidade como labels de métricas:

* IDs de usuário
* IDs de pedido
* Emails
* Tokens
* URLs completas com query string

Use esses valores em spans ou logs, com cuidado de privacidade.

#### Logs

Prefira logs estruturados:

```csharp
logger.LogInformation("Pedido aprovado. order_id={OrderId}", orderId);
```

Evite interpolação em logs de caminho quente:

```csharp
logger.LogInformation($"Pedido aprovado. order_id={orderId}");
```

#### Segurança

* Não coloque `ELVEN_API_KEY` no código
* Não versionar tokens em `appsettings.json`
* Redactar statements SQL por padrão
* Capturar headers apenas com allowlist
* Evitar payload bruto em spans e logs

#### Performance

* Use `LoggerMessage` em caminhos muito chamados
* Evite spans manuais em loops internos de altíssima frequência
* Use histogramas para latência de negócio
* Ajuste `SamplingRatio` com cuidado
* Mantenha métricas com baixa cardinalidade

***

### Troubleshooting

#### Nenhum dado aparece no Grafana

Verifique:

```bash
echo $OTEL_SERVICE_NAME
echo $OTEL_EXPORTER_OTLP_ENDPOINT
echo $OTEL_EXPORTER_OTLP_PROTOCOL
echo $OTEL_TRACES_EXPORTER
echo $OTEL_METRICS_EXPORTER
echo $OTEL_LOGS_EXPORTER
echo $ELVEN_TENANT_ID
```

Confirme que:

* `OTEL_TRACES_EXPORTER=otlp`
* `OTEL_METRICS_EXPORTER=otlp`
* `OTEL_LOGS_EXPORTER=otlp`
* `ELVEN_API_KEY` está configurado
* `ELVEN_TENANT_ID` está correto
* A aplicação consegue acessar o endpoint OTLP

#### Erro de autenticação

`ELVEN_API_KEY` é enviado como Bearer automaticamente:

```bash
export ELVEN_API_KEY="seu-api-token"
```

Não inclua a palavra `Bearer` dentro da variável. A lib monta o header final:

```http
Authorization: Bearer seu-api-token
```

#### Erro de tenant

Configure:

```bash
export ELVEN_TENANT_ID="seu-tenant-id"
```

A lib monta:

```http
x-scope-orgid: seu-tenant-id
```

#### A aplicação falha no startup com exporter `none`

Em modo Elven, os três sinais são obrigatórios. Remova valores `none`:

```bash
unset OTEL_TRACES_EXPORTER
unset OTEL_METRICS_EXPORTER
unset OTEL_LOGS_EXPORTER
```

Ou defina explicitamente:

```bash
export OTEL_TRACES_EXPORTER=otlp
export OTEL_METRICS_EXPORTER=otlp
export OTEL_LOGS_EXPORTER=otlp
```

#### gRPC não conecta

Se o ambiente bloqueia gRPC, use HTTP/protobuf:

```bash
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
```

Confirme também se o endpoint aceita o protocolo escolhido.

#### Logs aparecem sem trace\_id

Logs só recebem correlação quando há `Activity.Current`. Em ASP.NET Core isso acontece automaticamente dentro de requests. Em Workers e Console apps, crie um span manual:

```csharp
using var activity = ElvenTelemetry.ActivitySource.StartActivity("job.run");
logger.LogInformation("Rodando job com correlação.");
```

#### Headers não aparecem nos spans

Confirme a allowlist:

```csharp
options.CaptureRequestHeaders = true;
options.AllowedRequestHeaders = ["x-request-id"];
```

Procure o atributo com `_` no lugar de `-`:

```
http.request.header.x_request_id
```

#### Statements SQL não aparecem

Por padrão, statements são redigidos quando a instrumentação suporta essa opção. Para diagnóstico temporário:

```csharp
options.RedactDbStatements = false;
```

#### Health check unhealthy

Consulte `/health/observability`. O motivo mais comum é uma misconfiguração no startup, como:

* Endpoint OTLP inválido
* Sampling ratio `0`
* Exporter `none`
* `OTEL_TRACES_SAMPLER=always_off`

#### Auto-instrumentation não carrega

Verifique:

```bash
echo $CORECLR_ENABLE_PROFILING
echo $CORECLR_PROFILER
echo $CORECLR_PROFILER_PATH
echo $DOTNET_STARTUP_HOOKS
echo $OTEL_DOTNET_AUTO_HOME
echo $OTEL_DOTNET_AUTO_PLUGINS
```

Confirme que o arquivo apontado por `CORECLR_PROFILER_PATH` existe e que a aplicação não está publicada como Native AOT.

***

### FAQ

#### Preciso configurar logs, métricas e traces separadamente?

Não. A chamada `AddElvenObservability()` configura os três sinais.

#### Posso desligar logs, métricas ou traces?

Não em modo Elven. A plataforma espera os três sinais para correlação completa. Configurações como `OTEL_LOGS_EXPORTER=none` são tratadas como erro.

#### O protocolo padrão é qual?

`grpc`.

#### Posso usar HTTP/protobuf?

Sim. Configure:

```bash
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
```

#### O pacote substitui o OpenTelemetry SDK?

Não. Ele configura e padroniza o SDK oficial do OpenTelemetry .NET para a Elven Observability.

#### Funciona com .NET Framework 4.x?

Não. O foco é .NET moderno: `net8.0`, `net9.0` e `net10.0`.

#### Funciona com Native AOT?

Sim para inicialização programática, respeitando as limitações das instrumentações oficiais usadas no projeto. Zero-code via CLR profiler não se aplica a Native AOT.

#### Como envio dados de negócio?

Use `ElvenTelemetry.ActivitySource` para spans e `ElvenTelemetry.Meter` para métricas customizadas. Use `ILogger` para logs estruturados.

#### Como correlacionar logs e traces?

Dentro de uma request ASP.NET Core ou de um span manual, `ILogger` é exportado com contexto de trace e span automaticamente.

#### Como configuro multi-tenancy?

Use:

```bash
export ELVEN_TENANT_ID="seu-tenant-id"
```

A lib envia esse valor no header `x-scope-orgid`.

#### Como configuro autenticação?

Use:

```bash
export ELVEN_API_KEY="seu-api-token"
```

A lib envia esse valor como `Authorization: Bearer <token>`.

#### Qual imagem Docker devo usar para zero-code?

Use:

```
elvenobservability/dotnet-instrumentation:latest
```

#### Como vejo se está funcionando?

Faça uma request na aplicação, gere um log e consulte no Grafana pelo `service.name`. Para ASP.NET Core, também valide:

```bash
curl http://localhost:8080/health/observability
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.elven.works/elven-platform/elven-observability/integracao-e-instrumentacao/.net/instrumentacao-.net-com-elven-observability.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
