eletrotupi / tcc/ commit / 8211b32

thesis: document the VictoriaLogs logging stack

The technologies chapter now covers how the container logs are collected
and queried, since the infrastructure section stopped at persistence and
never mentioned the observability stack
Pedro Lucas Porcellis porcellis@eletrotupi.com · 5 days ago 8211b325c85993a45d2c38f30b068b421a9e6b39 Browse files
Parents e11faeb
Changed files
3
Additions
+178
Deletions
-1
monografia/chapters/tech.tex
1 1
\chapter{Tecnologias}
2 2

3 3
Este capítulo apresenta as principais tecnologias empregadas no desenvolvimento
4 4
do aplicativo, organizadas de acordo com a camada da arquitetura em que atuam:
5 5
\textit{front-end}, \textit{back-end} e infraestrutura/persistência de dados.
157 157
Complementarmente, o Nexo utiliza o Valkey como armazenamento em memória de
158 158
alta velocidade, empregado tanto para cache quanto como \textit{broker} de
159 159
mensagens para o sistema de filas, cujo suas filas são gerenciadas com a
160 160
biblioteca BullMQ para lidar com os \textit{background jobs} enviados. Os
161 161
mesmos são responsáveis por processar os indicadores semanais e diários
162 -
descritos nos capítulos anteriores.
162 +
descritos nos capítulos anteriores. Além disso, o Nexo mantém uma pilha de
163 +
observabilidade dedicada à coleta e à consulta dos \textit{logs} de seus
164 +
contêineres, descrita a seguir, que complementa a persistência dos dados de
165 +
domínio.
163 166

164 167
\subsection{PostgreSQL}
165 168

166 169
PostgreSQL é um sistema gerenciador de banco de dados relacional, de código
167 170
aberto, reconhecido por sua conformidade com o padrão SQL, robustez e suporte
183 186
O Prisma foi escolhido por permitir a manutenção do esquema do banco de dados
184 187
de forma versionada e sincronizada com o código da aplicação, reduzindo a
185 188
chance de divergência entre a estrutura do banco e as entidades manipuladas
186 189
pelo back-end, além de prover checagem de tipos em tempo de compilação para
187 190
as consultas realizadas.
191 +

192 +
\subsection{VictoriaLogs e Vector}
193 +

194 +
Observabilidade, no contexto de sistemas distribuídos, designa a capacidade de
195 +
compreender o estado interno de uma aplicação a partir dos sinais que ela emite
196 +
--- em especial \textit{logs}, métricas e rastros ---, o que permite investigar
197 +
problemas sem conhecer previamente sua causa \citep{opentelemetry:2026}. Entre
198 +
esses sinais, os \textit{logs} são o mais direto: registros datados de eventos
199 +
emitidos pelos serviços. No Nexo, eles cobrem tanto as requisições HTTP
200 +
recebidas pela API quanto as tarefas processadas pelos \textit{workers} de
201 +
segundo plano. A pilha descrita a seguir é dedicada a coletá-los, armazená-los
202 +
e torná-los consultáveis.
203 +

204 +
VictoriaLogs é um sistema de armazenamento e consulta de \textit{logs}
205 +
desenvolvido pela VictoriaMetrics, projetado para operar como um único binário,
206 +
sem dependências externas, e com consumo reduzido de memória e de disco
207 +
\citep{victorialogs:2026}. Vector é um agente de coleta e roteamento de
208 +
observabilidade, também distribuído como um único binário, capaz de ler
209 +
\textit{logs} de diversas fontes --- arquivos, soquetes ou a API do Docker ---,
210 +
transformá-los por meio de uma linguagem própria de remapeamento e enviá-los a
211 +
diferentes destinos \citep{vector:2026}.
212 +

213 +
A motivação para adotar essa combinação, em vez de uma pilha bastante difundida
214 +
como Elasticsearch, Logstash e Kibana (ELK) \citep{elastic:2026}, é
215 +
proporcional ao porte do projeto: o ELK exige múltiplos serviços, e o
216 +
Elasticsearch é executado sobre a máquina virtual Java; segundo a documentação
217 +
do VictoriaLogs, soluções como ele demandam consideravelmente mais memória e
218 +
disco que o VictoriaLogs \citep{victorialogs:2026}. Em um servidor único que
219 +
também hospeda a API, o PostgreSQL e o Valkey, esse custo seria
220 +
desproporcional. VictoriaLogs e Vector cumprem o mesmo papel com dois
221 +
contêineres menores/mais leves.
222 +

223 +
No Nexo, o fluxo de \textit{logs} começa na própria API, que utiliza a
224 +
biblioteca Pino para emitir um objeto JSON por linha, em vez de texto livre
225 +
\citep{pino:2026}. Como o \textit{logger} só aplica formatação legível quando a
226 +
saída é um terminal interativo, no contêiner --- onde a saída é um
227 +
\textit{pipe} --- os registros permanecem em JSON de linha única. O
228 +
\textit{middleware} \texttt{pino-http} associa a cada requisição um
229 +
identificador (\texttt{X-Request-Id}), convenção difundida para correlacionar
230 +
os registros de uma mesma requisição \citep{heroku:2026}, reaproveitando o
231 +
cabeçalho homônimo quando enviado pelo cliente ou gerando um UUID.
232 +

233 +
O Vector lê essas linhas por meio da API do Docker, cujo \textit{socket} é
234 +
montado no contêiner como volume somente leitura, e aplica uma transformação de
235 +
remapeamento que tenta interpretar cada mensagem como JSON; quando a
236 +
interpretação é bem-sucedida, os campos do objeto são elevados ao nível
237 +
superior do evento, o que torna consultáveis atributos como o nível do
238 +
\textit{log}, a mensagem e o identificador do usuário. O resultado é enviado em
239 +
lote ao \textit{endpoint} de ingestão compatível com a API do Elasticsearch do
240 +
VictoriaLogs, conforme o Código~\ref{lst:vector-pipeline}.
241 +

242 +
\begin{lstlisting}[caption={Configuração do \textit{pipeline} de \textit{logs} no \texttt{vector.yaml}}, label={lst:vector-pipeline}]
243 +
sources:
244 +
  docker:
245 +
    type: docker_logs
246 +

247 +
transforms:
248 +
  parse_json_message:
249 +
    type: remap
250 +
    inputs: [docker]
251 +
    source: |
252 +
      parsed, err = parse_json(.message)
253 +
      if err == null && is_object(parsed) {
254 +
        . = merge(., parsed) ?? .
255 +
      }
256 +

257 +
sinks:
258 +
  victorialogs:
259 +
    type: elasticsearch
260 +
    inputs: [parse_json_message]
261 +
    endpoints: ["http://victorialogs:9428/logs/insert/elasticsearch/"]
262 +
    mode: bulk
263 +
    api_version: v8
264 +
    compression: gzip
265 +
    request:
266 +
      headers:
267 +
        VL-Stream-Fields: label.com.docker.compose.service
268 +
        VL-Time-Field: timestamp
269 +
        VL-Msg-Field: msg,message,_msg
270 +
\end{lstlisting}
271 +

272 +
\sourcecode{Elaborado pelo autor (2026)}
273 +

274 +
O contêiner do VictoriaLogs é configurado com um período de retenção de 14
275 +
dias e um teto de 2\,GiB de uso de disco; ambos são limites deliberados para
276 +
que o armazenamento de \textit{logs} não comprometa o disco da máquina de
277 +
produção, descartando os registros mais antigos quando qualquer um deles é
278 +
atingido. O serviço escuta apenas na interface de \textit{loopback}
279 +
(\texttt{127.0.0.1:9428}), e não em todas as interfaces, de modo que a
280 +
interface de consulta não é exposta diretamente à internet. O acesso externo
281 +
se dá pelo Nginx, que publica o caminho \texttt{/logs/} sob autenticação
282 +
básica HTTP, com credenciais definidas no \textit{vault} criptografado do
283 +
Ansible; a diretiva \texttt{-http.pathPrefix=/logs} no VictoriaLogs garante
284 +
que os caminhos gerados pela interface correspondam ao prefixo publicado
285 +
pelo \textit{proxy}, conforme ilustrado na
286 +
Figura~\ref{fig:screenshot-victorialogs}.
287 +

288 +
\begin{figure}[H]
289 +
\centering
290 +
\caption{Interface de consulta do VictoriaLogs com os \textit{logs} da API}
291 +
\label{fig:screenshot-victorialogs}
292 +
\includegraphics[width=\linewidth]{screenshot-victorialogs.png}
293 +
\source{Elaborado pelo autor (2026)}
294 +
\end{figure}
295 +

296 +
Na captura, a consulta está restrita ao fluxo de \textit{logs} da API, por
297 +
meio do filtro \texttt{label.com.docker.compose.service="api"}. À esquerda,
298 +
o painel de \textit{stream fields} lista os fluxos disponíveis e o volume de
299 +
registros de cada um; na parte superior, um histograma mostra a distribuição
300 +
desses registros em intervalos de 30 minutos ao longo do período
301 +
selecionado; e, na parte inferior, a tabela exibe as entradas
302 +
correspondentes, com data, nível e mensagem, incluindo desde a sequência de
303 +
inicialização da API e de seus \textit{workers} até o envio de notificações.
304 +
Consultas como essa permitem, por exemplo, correlacionar as linhas de uma
305 +
mesma requisição a partir do seu \texttt{X-Request-Id}.
306 +

307 +
Como o Vector não substitui o \textit{driver} de \textit{logs} do Docker, o
308 +
\textit{driver} \texttt{json-file} permanece ativo, porém limitado por
309 +
rotação (10\,MB por arquivo, 3 arquivos por contêiner), de modo que
310 +
\texttt{docker compose logs} continue funcionando sem que o disco do servidor
311 +
cresça indefinidamente. As versões das imagens, a retenção e os limites de
312 +
disco são declarados como variáveis no inventário do Ansible e renderizados
313 +
no arquivo de ambiente, e o arquivo de configuração do Vector é copiado para
314 +
o diretório da aplicação pela \textit{role} \texttt{app}, o que mantém a
315 +
pilha de observabilidade reproduzível pelo mesmo processo de provisionamento
316 +
descrito na Seção~\ref{sec:deploy-and-ci}.
188 317

189 318
\section{Implantação e Desenvolvimento Contínuo} \label{sec:deploy-and-ci}
190 319

191 320
Já os termos \textit{implantação} (\textit{deployment}) e
192 321
\textit{automatização} referem-se ao conjunto de práticas e ferramentas
322 451
ao documentar e automatizar o processo de provisionamento, o projeto
323 452
favorece um modelo mais alinhado aos princípios de privacidade e de
324 453
autonomia sobre os dados dos usuários, permitindo que instituições ou
325 454
indivíduos preocupados com a confidencialidade de dados sensíveis de
326 455
saúde mental hospedem e controlem sua própria instância da aplicação.
/dev/null → monografia/images/screenshot-victorialogs.png
Binary file
monografia/monografia.bib
1 1
@article{Iwaya:22,
2 2
  title={On the privacy of mental health apps},
3 3
  volume={28},
4 4
  DOI={10.1007/s10664-022-10236-0},
5 5
  number={1},
443 443
  url    = {https://www.ansible.com},
444 444
  year   = {2026},
445 445
  note   = {Acessado em 13 jul. 2026}
446 446
},
447 447

448 +
@misc{elastic:2026,
449 +
  author = {{Elastic N.V.}},
450 +
  title  = {Elastic Stack: Elasticsearch, Kibana \& Logstash},
451 +
  url    = {https://www.elastic.co/elastic-stack},
452 +
  year   = {2026},
453 +
  note   = {Acessado em 23 out. 2026}
454 +
},
455 +

456 +
@misc{victorialogs:2026,
457 +
  author = {{VictoriaMetrics}},
458 +
  title  = {VictoriaLogs},
459 +
  url    = {https://docs.victoriametrics.com/victorialogs/},
460 +
  year   = {2026},
461 +
  note   = {Acessado em 23 out. 2026}
462 +
},
463 +

464 +
@misc{vector:2026,
465 +
  author = {{Datadog, Inc.}},
466 +
  title  = {Vector},
467 +
  url    = {https://vector.dev},
468 +
  year   = {2026},
469 +
  note   = {Acessado em 23 out. 2026}
470 +
},
471 +

472 +
@misc{pino:2026,
473 +
  author = {{Pino Contributors}},
474 +
  title  = {Pino: super fast, all natural JSON logger},
475 +
  url    = {https://getpino.io},
476 +
  year   = {2026},
477 +
  note   = {Acessado em 23 out. 2026}
478 +
},
479 +

480 +
@misc{heroku:2026,
481 +
  author = {{Salesforce, Inc.}},
482 +
  title  = {HTTP Request IDs},
483 +
  url    = {https://devcenter.heroku.com/articles/http-request-id},
484 +
  year   = {2026},
485 +
  note   = {Acessado em 23 out. 2026}
486 +
},
487 +

488 +
@misc{opentelemetry:2026,
489 +
  author = {{OpenTelemetry Authors}},
490 +
  title  = {Observability primer},
491 +
  url    = {https://opentelemetry.io/docs/concepts/observability-primer/},
492 +
  year   = {2026},
493 +
  note   = {Acessado em 23 out. 2026}
494 +
},
495 +

448 496
@book{
449 497
  Shafranovich_2005,
450 498
  type={Request for Comments},
451 499
  title={Common Format and MIME Type for Comma-Separated Values (CSV) Files},
452 500
  url={https://datatracker.ietf.org/doc/rfc4180/},
557 605
	titleaddon = {martinfowler.com},
558 606
	author = {Vocke, Ham},
559 607
  year={2012},
560 608
  note = {Acessado em 17 out. 2026}
561 609
}