Skip to main content
Version: Hortval 0.9.5 (unreleased)

Logging

Hortval uses structured logging with configurable level, format, output, and per-service overrides.

Startup lines go to stderr, not to logs.file

The configured destination is installed once the configuration has been read, so the lines emitted before that point are written to stderr and never appear in logs.file. When startup is refused, the log file is not created at all and everything — the JSON line and the human-readable message — goes to stderr.

Keep stderr captured wherever Hortval runs. Under systemd it is routed to journald by default, so nothing is lost, only split across two places. Under the Windows Service Control Manager stderr is attached to nothing and those lines are lost; see Installation for what that means today.

Configuration

logs:
level: info
format: json
output: file
file: "C:\\ProgramData\\hortval\\logs\\hortval.log"
rotate:
max-size-mb: 100
max-backups: 10
services:
DB-Driver: warn
acme-server: debug
tags:
instance: cert-srv-01
region: eu-west

Fields

FieldDefaultDescription
levelinfoGlobal log level: debug, info, warn, error, off. off (alias none) fully suppresses logs and is most useful as a per-service override.
formatjsonLog format: json or text
outputstderrOutput destination: stderr, stdout, or file
fileLog file path. Required if output: file. With rotation enabled it is a naming base, not a file — see Rotation.
rotate.max-size-mb0Size at which a new segment is started. 0 disables rotation: a single file grows indefinitely.
rotate.max-backups5Number of closed segments kept. 0 keeps only the one being written; -1 never deletes.
servicesemptyPer-service log level overrides
tagsemptyUser-defined labels added to every log entry — useful for Grafana/Loki filtering

Rotation

When output: file and rotate.max-size-mb is greater than zero, file is a naming base, not a file. Hortval writes dated segments beside it and never renames them:

file: C:\ProgramData\hortval\logs\hortval.log

C:\ProgramData\hortval\logs\
hortval.20260726T091702Z.ms123.log <- closed
hortval.20260726T104417Z.ms008.log <- being written

The suffix is a UTC timestamp, so the alphabetical order of the file names is their chronological order. A closed segment is never reopened, which is what makes it safe to copy or archive.

Configure your log collector with the folder and a *.log pattern, not with the path in file. To follow the live file interactively:

Get-Content -Wait (Get-ChildItem C:\ProgramData\hortval\logs\hortval.*.log |
Sort-Object Name | Select-Object -Last 1)

If a rotation cannot complete — an antivirus holding the file, a full disk, a permissions problem — Hortval keeps writing to the current segment, reports the reason on stderr (captured by the Windows service manager), and retries later. Nothing is lost.

max-backups bounds disk usage at roughly max-backups × max-size-mb. It defaults to 5; before v0.9.4 it defaulted to 0, which meant "keep nothing", so enabling rotation silently discarded the log at each rotation.

Permissions

On Windows, access to the log files is governed by the NTFS permissions inherited from the containing folder — set that folder's ACL at install time. On Linux and macOS, files are created 0600 and the directory 0700, so a collector running under its own account cannot read them without being granted access out of band.

Per-Service Log Levels

You can set a different log level for each internal service. This is useful for debugging a specific component without flooding logs with debug output from everything else.

logs:
level: info
services:
acme-server: debug
Async-Acme-Challenges: debug

Use off (or none) to fully silence a service — for example when a chatty driver is generating noise during dev or staging captures:

logs:
services:
DB-Driver: off
acme-server: warn

Registered Service Names

An unrecognised name stops startup

Only the names below are accepted. Anything else — a typo, or a name from a release before 0.9.5 — is refused at startup and by hortval validate, with the accepted list in the message.

Until 0.9.5 it was silently ignored: the level fell back to the global default and nothing said so, which is the worst way for a diagnostic setting to fail. Two names changed in 0.9.5 and have no aliasCerteasy-acme-server became acme-server, cert-easy-main became main.

Service NameDescription
DB-DriverDatabase driver and query logs
acme-serverACME HTTP request handling
Async-Acme-Pki-HandlerAsync PKI job processing
Async-Acme-ChallengesAsync challenge validation
JWKSJWS key validation
workerJob engine (lease, dispatch, backoff)
http-serverHTTP server lifecycle
adcs-nativeADCS authority operations — native in-process connector (default)
adcs-cliADCS authority operations — certreq.exe connector (adcs-cli)
licenseLicense lifecycle (install, refresh, enforcement)
nodeNode identity (server_id, servers table)
mainStartup and shutdown: configuration, working directory, schema gate
auditTamper-evident audit log (open, rotate, chain recovery)
fakepkiBuilt-in test PKI — CA generation and issuance

The list is exhaustive as of 0.9.5, and it has to be: a name absent from it is refused. It is kept in step with the code by a test that scans every service registration in the codebase, so it cannot quietly fall behind.

Tags (Grafana/Loki labels)

logs.tags is a free-form map of key: value pairs added to every log entry. Use it to attach environment metadata that your log aggregator (Grafana/Loki, Splunk, Elastic…) can filter on.

logs:
tags:
instance: cert-srv-01
region: eu-west
role: production

Each entry shows up as a top-level field in the JSON output, alongside time, level, msg, etc. There is no fixed list of allowed keys — pick whatever your stack expects.

note

The previous automatic env field is no longer added to log entries; it conflicted with the env= shown inside license-related log messages (license environment, e.g. env=dev / env=prod). If you want an environment label, set it explicitly under tags.

Log Rotation

Log rotation is supported when output: file. Configure rotate to limit disk usage:

logs:
output: file
file: "C:\\ProgramData\\hortval\\hortval.log"
rotate:
max-size-mb: 100
max-backups: 5

This keeps up to 5 rotated files of 100 MB each (500 MB total).

Production Recommendations

  • Use format: json for structured log ingestion (Splunk, Elastic, Loki…)
  • Use output: file with rotation to avoid filling disk
  • Keep global level at info and only set debug on specific services when troubleshooting
  • Route logs to your SIEM — the audit log entries contain account IDs, order IDs, and operation details