runner logs
Agrupa subcomandos para acesso ao historico de deploys, streaming de logs recentes e estatisticas de uma app.
Use quando: quer inspecionar o que aconteceu em deploys anteriores, acompanhar um deploy em andamento ou obter metricas de sucesso/falha de uma app.
Sintaxe
runner logs <subcomando> <app> [flags]Subcomandos
| Subcomando | Descricao |
|---|---|
tail |
Exibe as ultimas N linhas do log de deploy |
history |
Lista o historico de deploys com status e versao |
registros |
Tentativas de registro (add/adopt), inclusive as recusadas (v2.188.0+) |
stats |
Exibe estatisticas agregadas de deploy (total, sucessos, falhas) |
scrub |
Mascara tokens já gravados nos logs (v2.158.0+) |
rotation |
Retenção dos logs do runner no logrotate (v2.176.0+) |
Flags comuns
| Flag | Default | Descricao |
|---|---|---|
-o, --output <FORMAT> |
human |
Formato de saida: human, json, yaml |
--json |
— | Atalho para --output json |
-h, --help |
— | Exibe ajuda |
runner logs tail
Exibe as ultimas linhas do log do deploy mais recente.
runner logs tail <app> [flags]| Flag | Default | Descricao |
|---|---|---|
-l, --lines <N> |
50 |
Numero de linhas a exibir |
--json |
— | Saida em JSON |
Exemplo
$ runner logs tail meu-app --lines 20
[2026-05-27T14:30:01Z] [PREPARE] Preparando deploy v1.3.0...
[2026-05-27T14:30:02Z] [ARTIFACT] Copiando artefatos...
[2026-05-27T14:30:05Z] [ENV] Gerando ambiente...
[2026-05-27T14:30:06Z] [CONTAINER] Subindo container de teste...
[2026-05-27T14:30:18Z] [HEALTH] healthy (200 OK, 38ms)
[2026-05-27T14:30:18Z] [PROMOTE] Promovendo para producao...
[2026-05-27T14:30:19Z] Deploy concluido: v1.3.0runner logs history
Lista o historico de deploys com versao, resultado e timestamp.
runner logs history <app> [flags]| Flag | Default | Descricao |
|---|---|---|
-l, --limit <N> |
10 |
Numero de entradas a exibir |
--json |
— | Saida em JSON |
v2.175.1+: um rollback que falhou entra no historico como falha, com o erro prefixado por
rollback:. O eventodeploy_rollbackcorrespondente aparece em `runner audit`.
Exemplo
$ runner logs history meu-app -l 5
# versao resultado data
1 v1.3.0 success 2026-05-27T14:30:19Z
2 v1.2.1 success 2026-05-26T09:11:04Z
3 v1.2.0 failure 2026-05-25T18:42:30Z
4 v1.1.0 success 2026-05-24T11:00:15Z
5 v1.0.0 success 2026-05-20T08:30:00ZApp que não existe: um registro recusado não tem histórico de deploy. O
historyde uma app inexistente aponta para `runner logs registros`.
runner logs registros
v2.188.0+. Lista as tentativas de registro (runner add, runner adopt,
runner add --from-compose), registradas ou recusadas, a mais nova
primeiro. Uma recusa no registro (no_git_tags, ckey_too_short,
nome_base_de_outra_app, manifesto inválido…) não aparece no logs history,
porque a app ainda não existe. Ela aparece aqui.
runner logs registros [--repo <repo>] [--app <nome>] [--resultado registrado|recusado] [-l N] [-o json]| Flag | Default | Descricao |
|---|---|---|
--repo <repo> |
— | Só deste repo. Aceita org/app ou a URL, normalizada como no add (sem diferença de caixa) |
--app <nome> |
— | Só desta app: o nome pretendido na recusa, o registrado no sucesso |
--resultado |
— | registrado ou recusado |
-l, --limit <N> (--tail) |
20 |
Quantas tentativas; 0 = todas |
--json |
— | Saida em JSON (prefira -o json) |
Exemplo
$ runner logs registros --repo org/site
QUANDO RESULTADO REPO APP CÓDIGO / MENSAGEM
2026-11-04 14:02:11 registrado org/site site
2026-11-04 13:58:40 recusado org/site site no_git_tags: o repositório não tem nenhuma tag alcançável, mas o `.deploy.yml` declara version.source: git-tag.
2 tentativa(s), 1 recusada(s)JSON
Envelope v2 com command: "logs.registros". Capability:
logs.registros.v1 (runner --capabilities).
{"data": {
"arquivo": "/opt/runner/logs/register-attempts.jsonl",
"total": 1, "recusados": 1, "ilegiveis": 0,
"items": [{
"timestamp": "2026-11-04T13:58:40Z",
"comando": "add",
"repo": "org/site", "branch": "dist", "manifest_path": null,
"instancia": "production", "imagem": null, "app": "site",
"resultado": "recusado",
"codigo": "no_git_tags",
"mensagem": "o repositório não tem nenhuma tag alcançável, mas o `.deploy.yml` declara version.source: git-tag.",
"env_chaves": ["BASE_DOMAIN"],
"deploy": null,
"versao_runner": "2.188.0",
"origem": "api", "ator": "leo", "usuario": "root"
}]
}}| Campo | Significado |
|---|---|
comando |
add, add_from_compose, add_sem_repo, add_pr ou adopt |
app |
Nome pretendido (slug ou project: do manifesto) na recusa; o registrado no sucesso. null quando a recusa veio antes de ler o manifesto |
codigo |
Código estável do erro, o mesmo do -o json do add. runner_erro quando o erro não tem código. null no sucesso |
mensagem |
A primeira linha do erro, sem o código, com no máximo 300 caracteres |
env_chaves |
Só as chaves das --env/--env-file. O valor nunca é gravado |
deploy |
Com add --deploy: o desfecho do primeiro deploy, {"executado", "ok", "codigo", "mensagem"}. null sem --deploy ou quando o registro foi recusado antes dele |
origem |
cli, ou o --trigger do add (o CCS passa api). ator vem do --actor |
ilegiveis |
Linhas do arquivo que não parsearam e ficaram de fora |
env_invalidas |
Entradas de --env sem CHAVE= válida. Só aparece quando há alguma |
Registro que deu certo, deploy que falhou
Com add --deploy, a app fica registrada mesmo quando o primeiro deploy
falha: o add sai com 0 e resultado continua registrado. O desfecho vai em
deploy, com o mesmo código estável do erro e o mesmo filtro de segredo da
recusa:
{"resultado": "registrado", "app": "site",
"deploy": {"executado": true, "ok": false, "codigo": "no_git_tags",
"mensagem": "o repositório não tem nenhuma tag alcançável, mas o `.deploy.yml` declara version.source: git-tag."}}É o caso do version.source: git-tag num repo sem tag. O no_git_tags nasce
na leitura da versão, antes de o deploy entrar no logs history. Por isso
esta linha é o único lugar onde ele fica. Depois de corrigir (crie a tag ou
troque para git-commit), rode runner deploy <app>: não precisa registrar
de novo.
ok: truecommensagem: o deploy subiu com pendência pós-deploy.executado: false: pediu--deploy, registrou, e o deploy não rodou.
add --from-compose grava uma linha por serviço. Uma recusa antes da
descoberta dos serviços grava uma linha só.
O arquivo
/opt/runner/logs/register-attempts.jsonl, uma linha JSON por tentativa, em
0640, como o insecure-events.jsonl:
Nunca grava ckey, token nem valor de env/secret. Das
--env, só as chaves válidas (CHAVE=…). A entrada sem=ou com chave inválida, que oaddignora, só é contada (env_invalidas).Os campos
repo,branch,manifest_path,instanciaeimagemficam como o operador passou, sem credencial colada (https://user:token@…).Na
mensagem, além da máscara de tokens, viram***:- o token e a ckey literal (o seletor do
--ckey, comov2ousym, não); - valores de
--envcom 8+ caracteres; - valores de chaves que o manifesto declara em
secrets:.
Valor curto e comum (
loja,dist,production) fica.- o token e a ckey literal (o seletor do
Gravar é best-effort: se falhar, o
addnão muda de resultado nem de exit code.Rotação: entra no bloco
/opt/runner/logs/*.jsonldo `logs rotation`. A leitura inclui as rotações (.1,.N.gz). O `logs scrub` também o varre.
runner logs stats
Exibe estatisticas agregadas de deploy: total de execucoes, taxa de sucesso e falhas.
runner logs stats <app> [flags]| Flag | Default | Descricao |
|---|---|---|
--json |
— | Saida em JSON |
Exemplo
$ runner logs stats meu-app
app: meu-app
deploys: 45
success: 43 (95.6%)
failure: 2 (4.4%)
last: 2026-05-27T14:30:19Zrunner logs scrub
v2.158.0+. Aplica a máscara de tokens aos logs que já existem e deixa
os arquivos em 0640:
deploy-events.jsonl,fetch-audit.json,insecure-events.jsonle os demais.json,.jsonle.logde/opt/runner/logs/;logs/deploy-audit.jsonelogs/deploy.logde cada app;/var/log/runner-cron.log.
runner logs scrub --dry-run # só conta, não muda nada
runner logs scrub # mascara e corrige permissões
runner logs scrub -o json # envelope logs.scrub, com a contagem por arquivoA máscara troca o corpo de github_pat_, ghp_, gho_, ghu_, ghs_,
ghr_, glpat-, xox[bpare]- e chaves AWS (AKIA…/ASIA…) por ***. A
saída mostra só quantos foram trocados, nunca o valor.
Desde a v2.158.0, o runner aplica essa máscara a tudo que grava nesses
arquivos e os cria em 0640. O scrub serve para o que foi gravado
antes de atualizar.
Mascarar o log não revoga o token. Revogue no GitHub os tokens que apareceram.
runner logs rotation
v2.176.0+. Mostra e configura a retenção dos logs do próprio runner no
/etc/logrotate.d/runner. A tela do CCS usa este comando em Detalhes do
server › Subsistemas.
runner logs rotation status [-o json]
runner logs rotation set [--audit-days N] [--cron-days N] [-o json]O arquivo tem dois blocos, achados pelos caminhos, não pela posição:
| Bloco | Caminhos | Flag |
|---|---|---|
| audit | /opt/runner/logs/*.json e *.jsonl |
--audit-days |
| cron | /var/log/runner-cron.log (ou runner-fetch.log) |
--cron-days |
Os dois valores vão de 1 a 365 e são independentes. A frequência é daily,
então N dias equivale a rotate N. Para o gráfico de 30 dias do CCS, use
--audit-days 35.
status
Só lê: não grava nada, nem o backup. Roda sem root, desde que o arquivo seja legível.
{"rotacao": {
"suportado": true,
"gerido_pelo_runner": false,
"arquivo": "/etc/logrotate.d/runner",
"arquivo_existe": true,
"backup_previsto": "/opt/runner/backups/logrotate/runner-2026-10-29",
"audit": {"dias": 14, "caminhos": ["/opt/runner/logs/*.json", "/opt/runner/logs/*.jsonl"], "historico_dias": 15},
"cron": {"dias": 14, "caminhos": ["/var/log/runner-cron.log"]}
}}| Campo | Significado |
|---|---|
gerido_pelo_runner |
O arquivo tem o cabeçalho # gerido pelo runner (runner logs rotation) |
arquivo_existe |
false quando não há o arquivo. Aí dias é null e caminhos é [] |
backup_previsto |
Só em arquivo manual: onde a primeira gravação vai guardar o original |
audit.historico_dias |
Dias que existem de fato: o deploy-events.jsonl ativo mais cada .N/.N.gz |
avisos |
Só quando há algo a avisar (ver abaixo) |
Sem logrotate no host: {"rotacao": {"suportado": false}}.
set
Exige root. Só este comando escreve no arquivo. O fetch, o self-update e
o serviço nunca tocam nele.
sudo runner logs rotation set --audit-days 35 -o json- Só a linha
rotatedo bloco pedido muda.su,maxsize,compress,copytruncate, os caminhos, os comentários e os blocos que você acrescentou ficam iguais. - Se faltar um dos blocos, o
setacrescenta esse bloco com as diretivas do outro. Osudele é derivado do diretório do log: com escrita de grupo ou grupo diferente de root,su <dono> <grupo>(su root syslogem/var/log,su root devsem/opt/runner/logs). É o que o logrotate exige para não recusar o diretório por "insecure permissions". - Um bloco de uma linha (
caminho { daily }) vira um bloco de várias linhas, com orotatedentro. - O runner não adivinha. Recusa e mostra em
avisos:- um bloco que junta os logs do audit e o do cron (separe em dois blocos);
- um glob que cobre os logs do runner de outro jeito, como
/opt/runner/logs/*.
- Primeira escrita num arquivo manual: o original é copiado (como
cp -p) para/opt/runner/backups/logrotate/runner-AAAA-MM-DD, num diretório 700. Se já houver um backup com a mesma data, o novo leva-1,-2, e assim por diante: nunca sobrescreve. - Validação: antes de trocar, o runner roda
logrotate -dcom o/etc/logrotate.confdo host (os globais valem) apontado para uma cópia de/etc/logrotate.dcom o arquivo novo. Reprova o erro que cita o runner e o erro que o arquivo novo introduz. Erro antigo de outro arquivo do diretório não reprova. Reprovado, nada é trocado. - A troca é atômica (temporário +
rename), com 644 e dono root. - Um
setpor vez: o segundo espera o primeiro (flockem/opt/runner/backups/logrotate/.lock) e lê o que ele gravou.
A saída é o mesmo bloco rotacao do status, relido depois da troca (o valor
que valeu), mais backup_criado: o caminho do backup feito agora, ou null.
Arquivo que repete os caminhos do runner
O logrotate lê todo arquivo de /etc/logrotate.d, menos os que terminam
em extensões como .bak, .old, .orig, .disabled e ~. Uma cópia como
runner.bak-2026-10-29 (o nome termina em -2026-10-29, não em .bak) repete
os caminhos do runner. O logrotate acusa duplicate log entry e falha em
toda execução diária.
Nesse caso, o status traz:
"avisos": ["arquivo `/etc/logrotate.d/runner.bak-2026-10-29` repete os caminhos do runner e faz o logrotate falhar; mova-o para fora de /etc/logrotate.d"]e o set falha com logrotate_invalido até o arquivo sair dali.
Códigos de erro
Com -o json, o erro sai como {"error": "<código>", "message": "…"} e
exit ≠ 0.
| Código | Quando |
|---|---|
rotacao_dias_invalido |
Valor fora de 1–365, não numérico, ou nenhuma flag |
permissao_negada |
set sem root |
logrotate_ausente |
set num host sem logrotate |
logrotate_invalido |
O logrotate -d reprovou o arquivo novo; nada foi trocado |
rotacao_arquivo_ilegivel |
Não deu para ler o /etc/logrotate.d/runner |
rotacao_gravacao_falhou |
Falha ao gravar ou trocar o arquivo; o atual fica intacto |
rotacao_backup_falhou |
Falha ao gravar o backup; nada foi trocado |
rotacao_bloco_combinado |
Um bloco tem os logs do audit e o do cron juntos: separe em dois |
rotacao_bloco_nao_reconhecido |
Um bloco cobre os logs do runner com outro glob (ex.: /opt/runner/logs/*) |
Erros comuns
app not found→ verifique o nome comrunner list; o argumento aceita nome do projeto ou caminho absoluto.no deploy history→ a app ainda nao teve nenhum deploy; executerunner deploy <app>primeiro.