Fontes de dados
Plugins de origem — o trait Source e as crates sentry-source-*
Fontes de dados
Toda origem de dados é um plugin por trás do trait
Source.
O core nunca sabe se o evento veio do nginx, de um socket syslog, da API da
Cloudflare ou de captura de pacotes — apenas consome um stream de
RawEvent
por um canal tokio, enriquece com geo/reputação e promove para Event.
/// A plugin that observes accesses and emits raw events.
#[async_trait]
pub trait Source: Send + Sync {
fn name(&self) -> &'static str;
async fn stream(&self) -> Result<mpsc::Receiver<RawEvent>>;
async fn shutdown(&self) -> Result<()> { Ok(()) }
}stream() devolve o receiver de um canal limitado; a source retém o
sender e empurra eventos de forma assíncrona. Dropping o sender sinaliza
fim de stream. shutdown() é um hook opcional chamado no SIGINT/SIGTERM.
Registro no daemon
Sources são compiladas no binário (não há carregamento dinâmico de plugins).
Cada entrada [[source]] do sentry.toml — o struct
SourceConfig
com type + options — vira um braço no match de
daemon::build_registry,
que instancia a crate correspondente e a registra via
RegistryBuilder::register_source.
A source tcp exige além disso a feature de build pcap.
Implementações
| Crate | Origem | Dados do protocolo |
|---|---|---|
sentry-source-nginx | Tail do access.log com suporte a rotação | HttpData |
sentry-source-syslog | Receptor syslog UDP/TCP (RFC 5424 + fallback 3164) | SyslogData |
sentry-source-cloudflare | Polling da Logs API da Cloudflare (NDJSON) | HttpData |
sentry-source-tcp | Captura passiva de pacotes (feature pcap) | TcpData |
nginx — tail do access.log
A
NginxSource
faz tail do arquivo de log (poll de 100 ms), compilando o log_format do
nginx (sintaxe $var) no
LogFormat
parser. No daemon o tail sempre começa do fim do arquivo (start_from_end).
Rotação é detectada por truncamento/encolhimento do arquivo (ou desaparecimento)
e o arquivo é reaberto; linha não-parseável é logada como warn e pulada.
- IP real do cliente: o parser resolve automaticamente por precedência
fixa —
$http_cf_connecting_ip>$http_true_client_ip>$http_x_real_ip> primeiro IP do XFF ($http_x_forwarded_for) >$remote_addr. Com[real_ip]configurado (trusted_proxies+ ranges Cloudflare embutidos), headers de clientes só vencem vindos de proxies confiáveis (guarda anti-spoof); sem[real_ip], a precedência é incondicional. O daemon sempre compila o parser com oTrustSetviacompile_with_trust. - Headers: todo token
http_*dolog_formatvira um header emHttpData.headers($http_cf_connecting_ip→cf-connecting-ip), utilizável em regras DSL viaheader.cf-connecting-ip = ....
[[source]]
type = "nginx"
[source.options]
path = "/var/log/nginx/access.log"
format = '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"'
# Atrás da Cloudflare, adicione o header de client-IP ao log_format do nginx
# E à linha `format` acima, ex.: ... "$http_cf_connecting_ip"'syslog — receptor UDP/TCP
A
SyslogSource
faz bind do socket dentro de stream() (falha de bind sobe como erro em vez
de morrer em silêncio) e cada mensagem vira um evento
ProtocolData::Syslog. O endereço do peer é o client IP.
- Parser: RFC 5424 com fallback automático para RFC 3164
(
parse_syslog). - Framing TCP: RFC 6587 — octet-counting (detectado pelo dígito ASCII inicial) ou newline-terminated.
- Transporte: datagramas UDP ou conexões TCP (cada conexão em sua task).
[[source]]
type = "syslog"
[source.options]
bind = "0.0.0.0:5140" # portas abaixo de 1024 exigem root; 5140 evita
transport = "udp" # udp | tcpO bind default é
0.0.0.0:5140
e o transporte default é udp.
cloudflare — polling da Logs API
A
CloudflareSource
consulta GET /zones/{zone}/logs/received?start=&end= (Logpull Received,
resposta NDJSON) em intervalo fixo e alimenta o mesmo pipeline do tail do
nginx.
- Cursor: a primeira janela começa em
start(RFC 3339, default: agora) e o cursor avança para o timestamp mais novo visto — o polling não repete nem pula janelas. - Dedupe por RayID: janelas repetidas não contam request duas vezes (conjunto limitado a 100 000 RayIDs).
- Intervalo: 30 s por padrão, com piso de 5 s.
- Plano: a Logs API de logs recebidos exige entitlement Enterprise — respostas de erro logam um aviso com essa nota.
- Token: lido da env apontada por
token_env(defaultSENTRY_CF_TOKEN), com permissãoLogs:Read.zone_idé obrigatório.
[[source]]
type = "cloudflare"
[source.options]
zone_id = "023e105f4ecef8ad9ca31a8372d0c353"
# token_env = "SENTRY_CF_TOKEN"
# start = "2026-09-30T00:00:00Z" # cursor da primeira janela (default: agora)
# api_base = "https://api.cloudflare.com/client/v4"tcp — captura passiva + fingerprint de SYN
A
TcpCaptureSource
captura pacotes com a feature pcap (construa com
--features sentry-cli/pcap; exige Npcap no Windows ou CAP_NET_RAW no
Linux). Sem a feature, a lógica pura (fingerprint + tabela de flows) segue
exportada para consumidores inline.
- Filtro:
interfaceé obrigatório;ports(CSV) filtra portas de destino (vazio = todas). - Flows: tabela com eviction FIFO (
flow_cap, default 65 536 flows) e até 8 KiB de payload reassemblado por flow (payload_cap). - Fingerprint de SYN: cada SYN gera a assinatura
window:options:MSS:wscale(SynFingerprint, estilo MuonFP/p0f). A tabela de scanners cobre masscan/zmap/nmap (scanner_name) e alimenta o sinalTcpScannerno scorer — varreduras de porta que o access.log HTTP nunca vê. - Agregação de SYN: dentro da janela
syn_window_ms(default 100 ms,0desliga), todos os SYNs de um mesmo IP colapsam em um evento comTcpData.syn_count— uma rajada vira 1 evento por IP, não um por pacote (SynAggregator). - Backpressure:
channel_buffer(default 1024, mínimo 64) é a fila entre o loop de captura e o daemon — aumente sob captura de alto PPS.
[[source]]
type = "tcp"
[source.options]
interface = "eth0" # obrigatório
ports = "80,443,8443" # filtro de portas de destino; vazio = todas
channel_buffer = 1024 # fila capture -> daemon
syn_window_ms = 100 # coalescência de SYN por IP (0 = off)Fan-in Raw/Processed (edge inline)
As sources passivas produzem RawEvent e o loop de ingest do daemon roda o
pipeline uma vez sobre eles. Em [deployment] mode = "inline" a edge
compartilha o mesmo Arc<Pipeline> e publica eventos já decididos: o
ingestor consome os dois caminhos via o enum
Incoming::{Raw, Processed}
— eventos da edge não re-executam o pipeline (os trackers não contam
duplicado).
Convenções
- Toda crate
sentry-source-*depende apenas desentry-core, nunca de outras plugins. sentry-coreé pure (sem I/O pesado, sem HTTP, sem DB) — define os contratos (Source,RawEvent,ProtocolData).- Canais de evento são limitados, criados por
event_channel; envios usamsend_or_log, que agrega logs de drop por janela de 5 s (sentry_events_dropped_totalpor source) em vez de logar uma linha por evento.