Add log level control for docker agent

Related to #742
This commit is contained in:
rcourtman 2025-11-22 07:43:48 +00:00
parent ee3f1e0cdb
commit 19a2cac355
3 changed files with 55 additions and 16 deletions

View file

@ -50,7 +50,9 @@ func main() {
cfg := loadConfig() cfg := loadConfig()
logger := zerolog.New(os.Stdout).With().Timestamp().Logger() zerolog.SetGlobalLevel(cfg.LogLevel)
logger := zerolog.New(os.Stdout).Level(cfg.LogLevel).With().Timestamp().Logger()
cfg.Logger = &logger cfg.Logger = &logger
agent, err := dockeragent.New(cfg) agent, err := dockeragent.New(cfg)
@ -87,6 +89,7 @@ func loadConfig() dockeragent.Config {
envSwarmTasks := utils.GetenvTrim("PULSE_SWARM_TASKS") envSwarmTasks := utils.GetenvTrim("PULSE_SWARM_TASKS")
envIncludeContainers := utils.GetenvTrim("PULSE_INCLUDE_CONTAINERS") envIncludeContainers := utils.GetenvTrim("PULSE_INCLUDE_CONTAINERS")
envCollectDisk := utils.GetenvTrim("PULSE_COLLECT_DISK") envCollectDisk := utils.GetenvTrim("PULSE_COLLECT_DISK")
envLogLevel := utils.GetenvTrim("LOG_LEVEL")
defaultInterval := 30 * time.Second defaultInterval := 30 * time.Second
if envInterval != "" { if envInterval != "" {
@ -120,6 +123,11 @@ func loadConfig() dockeragent.Config {
collectDiskDefault = utils.ParseBool(envCollectDisk) collectDiskDefault = utils.ParseBool(envCollectDisk)
} }
logLevelDefault := "info"
if envLogLevel != "" {
logLevelDefault = envLogLevel
}
urlFlag := flag.String("url", envURL, "Pulse server URL (e.g. http://pulse:7655)") urlFlag := flag.String("url", envURL, "Pulse server URL (e.g. http://pulse:7655)")
tokenFlag := flag.String("token", envToken, "Pulse API token (required)") tokenFlag := flag.String("token", envToken, "Pulse API token (required)")
intervalFlag := flag.Duration("interval", defaultInterval, "Reporting interval (e.g. 30s)") intervalFlag := flag.Duration("interval", defaultInterval, "Reporting interval (e.g. 30s)")
@ -128,6 +136,7 @@ func loadConfig() dockeragent.Config {
insecureFlag := flag.Bool("insecure", utils.ParseBool(envInsecure), "Skip TLS certificate verification") insecureFlag := flag.Bool("insecure", utils.ParseBool(envInsecure), "Skip TLS certificate verification")
noAutoUpdateFlag := flag.Bool("no-auto-update", utils.ParseBool(envNoAutoUpdate), "Disable automatic agent updates") noAutoUpdateFlag := flag.Bool("no-auto-update", utils.ParseBool(envNoAutoUpdate), "Disable automatic agent updates")
runtimeFlag := flag.String("runtime", envRuntime, "Container runtime to expect (auto, docker, podman)") runtimeFlag := flag.String("runtime", envRuntime, "Container runtime to expect (auto, docker, podman)")
logLevelFlag := flag.String("log-level", logLevelDefault, "Log level: debug, info, warn, error")
var targetFlags stringFlagList var targetFlags stringFlagList
flag.Var(&targetFlags, "target", "Pulse target in url|token[|insecure] format. Repeat to send to multiple Pulse instances") flag.Var(&targetFlags, "target", "Pulse target in url|token[|insecure] format. Repeat to send to multiple Pulse instances")
var containerStateFlags stringFlagList var containerStateFlags stringFlagList
@ -211,6 +220,12 @@ func loadConfig() dockeragent.Config {
interval = 30 * time.Second interval = 30 * time.Second
} }
logLevel, err := parseLogLevel(*logLevelFlag)
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
containerStates := make([]string, 0) containerStates := make([]string, 0)
if len(containerStateFlags) > 0 { if len(containerStateFlags) > 0 {
containerStates = append(containerStates, containerStateFlags.Values()...) containerStates = append(containerStates, containerStateFlags.Values()...)
@ -235,9 +250,24 @@ func loadConfig() dockeragent.Config {
IncludeTasks: *includeTasksFlag, IncludeTasks: *includeTasksFlag,
IncludeContainers: *includeContainersFlag, IncludeContainers: *includeContainersFlag,
CollectDiskMetrics: *collectDiskFlag, CollectDiskMetrics: *collectDiskFlag,
LogLevel: logLevel,
} }
} }
func parseLogLevel(value string) (zerolog.Level, error) {
normalized := strings.ToLower(strings.TrimSpace(value))
if normalized == "" {
return zerolog.InfoLevel, nil
}
level, err := zerolog.ParseLevel(normalized)
if err != nil {
return zerolog.InfoLevel, fmt.Errorf("invalid log level %q: must be debug, info, warn, or error", value)
}
return level, nil
}
func parseTargetSpecs(specs []string) ([]dockeragent.TargetConfig, error) { func parseTargetSpecs(specs []string) ([]dockeragent.TargetConfig, error) {
targets := make([]dockeragent.TargetConfig, 0, len(specs)) targets := make([]dockeragent.TargetConfig, 0, len(specs))
for _, spec := range specs { for _, spec := range specs {

View file

@ -220,6 +220,8 @@ docker run -d \
ghcr.io/rcourtman/pulse-docker-agent:latest ghcr.io/rcourtman/pulse-docker-agent:latest
``` ```
Add `-e LOG_LEVEL=warn` when your container logs feed into journald or another centralized collector and you want quieter steady-state logging.
> **Note**: Official images for `linux/amd64` and `linux/arm64` are published to `ghcr.io/rcourtman/pulse-docker-agent`. To test local changes, run `docker build --target agent_runtime -t pulse-docker-agent:test .` from the repository root. > **Note**: Official images for `linux/amd64` and `linux/arm64` are published to `ghcr.io/rcourtman/pulse-docker-agent`. To test local changes, run `docker build --target agent_runtime -t pulse-docker-agent:test .` from the repository root.
`--pid=host`, `--uts=host`, and the `/etc/machine-id` bind keep host metadata stable so Pulse doesnt think the container itself is the Docker host. Auto-update is disabled in the image by default; rebuild or override `PULSE_NO_AUTO_UPDATE=false` only if you manage upgrades outside of your orchestrator. Expect to grant the container the same level of Docker socket access as the systemd service—running inside Docker doesnt sandbox the agent from the host. `--pid=host`, `--uts=host`, and the `/etc/machine-id` bind keep host metadata stable so Pulse doesnt think the container itself is the Docker host. Auto-update is disabled in the image by default; rebuild or override `PULSE_NO_AUTO_UPDATE=false` only if you manage upgrades outside of your orchestrator. Expect to grant the container the same level of Docker socket access as the systemd service—running inside Docker doesnt sandbox the agent from the host.
@ -232,6 +234,7 @@ docker run -d \
| `--token`, `PULSE_TOKEN`| Pulse API token with `docker:report` scope (required). | — | | `--token`, `PULSE_TOKEN`| Pulse API token with `docker:report` scope (required). | — |
| `--target`, `PULSE_TARGETS` | One or more `url|token[|insecure]` entries to fan-out reports to multiple Pulse servers. Separate entries with `;` or repeat the flag. | — | | `--target`, `PULSE_TARGETS` | One or more `url|token[|insecure]` entries to fan-out reports to multiple Pulse servers. Separate entries with `;` or repeat the flag. | — |
| `--interval`, `PULSE_INTERVAL` | Reporting cadence (supports `30s`, `1m`, etc.). | `30s` | | `--interval`, `PULSE_INTERVAL` | Reporting cadence (supports `30s`, `1m`, etc.). | `30s` |
| `--log-level`, `LOG_LEVEL` | Log verbosity (`debug`, `info`, `warn`, `error`). Use `debug` only while actively troubleshooting to avoid noisy journals. | `info` |
| `--runtime`, `PULSE_RUNTIME` | Container runtime to target (`docker`, `podman`, `auto`). | `docker` | | `--runtime`, `PULSE_RUNTIME` | Container runtime to target (`docker`, `podman`, `auto`). | `docker` |
| `--container-socket`, `PULSE_CONTAINER_SOCKET` / `CONTAINER_HOST` | Explicit runtime socket path or `unix://` URI. | Runtime default | | `--container-socket`, `PULSE_CONTAINER_SOCKET` / `CONTAINER_HOST` | Explicit runtime socket path or `unix://` URI. | Runtime default |
| `--rootless`, `PULSE_RUNTIME_ROOTLESS` | Install/manage the agent as a user service (Podman). | Auto (rootful) | | `--rootless`, `PULSE_RUNTIME_ROOTLESS` | Install/manage the agent as a user service (Podman). | Auto (rootful) |
@ -260,6 +263,7 @@ Need the alerts but at a different tone? The same Containers tab exposes global
## Testing and troubleshooting ## Testing and troubleshooting
- Run with `--interval 15s --insecure` in a terminal to see log output while testing. - Run with `--interval 15s --insecure` in a terminal to see log output while testing.
- Adjust verbosity with `--log-level` or `LOG_LEVEL` (`info` by default). Drop to `warn` for quiet steady-state logs; use `debug` only when actively debugging.
- Ensure the Pulse API token has not expired or been regenerated. - Ensure the Pulse API token has not expired or been regenerated.
- If `pulse-docker-agent` reports `Cannot connect to the Docker daemon`, verify the socket path and permissions. - If `pulse-docker-agent` reports `Cannot connect to the Docker daemon`, verify the socket path and permissions.
- Check Pulse (`/containers` tab) for the latest heartbeat time. Hosts are marked offline if they stop reporting for >4× the configured interval. - Check Pulse (`/containers` tab) for the latest heartbeat time. Hosts are marked offline if they stop reporting for >4× the configured interval.

View file

@ -57,6 +57,7 @@ type Config struct {
IncludeTasks bool IncludeTasks bool
IncludeContainers bool IncludeContainers bool
CollectDiskMetrics bool CollectDiskMetrics bool
LogLevel zerolog.Level
Logger *zerolog.Logger Logger *zerolog.Logger
} }
@ -81,20 +82,20 @@ const (
// Agent collects Docker metrics and posts them to Pulse. // Agent collects Docker metrics and posts them to Pulse.
type Agent struct { type Agent struct {
cfg Config cfg Config
docker *client.Client docker *client.Client
daemonHost string daemonHost string
runtime RuntimeKind runtime RuntimeKind
runtimeVer string runtimeVer string
supportsSwarm bool supportsSwarm bool
httpClients map[bool]*http.Client httpClients map[bool]*http.Client
logger zerolog.Logger logger zerolog.Logger
machineID string machineID string
hostName string hostName string
cpuCount int cpuCount int
targets []TargetConfig targets []TargetConfig
allowedStates map[string]struct{} allowedStates map[string]struct{}
stateFilters []string stateFilters []string
hostID string hostID string
prevContainerCPU map[string]cpuSample prevContainerCPU map[string]cpuSample
preCPUStatsFailures int preCPUStatsFailures int
@ -158,8 +159,12 @@ func New(cfg Config) (*Agent, error) {
} }
logger := cfg.Logger logger := cfg.Logger
if zerolog.GlobalLevel() == zerolog.DebugLevel && cfg.LogLevel != zerolog.DebugLevel {
zerolog.SetGlobalLevel(cfg.LogLevel)
}
if logger == nil { if logger == nil {
defaultLogger := zerolog.New(os.Stdout).With().Timestamp().Str("component", "pulse-docker-agent").Logger() defaultLogger := zerolog.New(os.Stdout).Level(cfg.LogLevel).With().Timestamp().Str("component", "pulse-docker-agent").Logger()
logger = &defaultLogger logger = &defaultLogger
} else { } else {
scoped := logger.With().Str("component", "pulse-docker-agent").Logger() scoped := logger.With().Str("component", "pulse-docker-agent").Logger()