Observability

Structured output

Use --json for machine-readable sync reports:

ocync sync -c config.yaml --json

Reports include per-image results and aggregate statistics: blobs transferred, bytes moved, mounts performed, cache hits, and errors.

A mapping that could not be resolved at all — unreachable registry, denied tag listing, bad repository name — never reaches the engine and so produces no per-image entry. Those mappings are listed separately under unresolved_mappings, which is omitted entirely when every mapping resolved. The rest of the run still proceeds; one failing mapping does not cancel the others.

A mapping that resolved but lost one of several targets is not unresolved: it synced to the targets that worked. The ones it could not use appear under dropped_targets, also omitted when empty, and each is summarised in the cycle tail as | N targets dropped. A dropped target moves the exit code off success, because images did not reach a registry the config named.

Abbreviated example:

{
  "run_id": "019713a2-...",
  "images": [
    {
      "image_id": "019713a2-...",
      "source": "cgr.dev/chainguard/nginx:latest",
      "target": "123456789012.dkr.ecr.us-east-1.amazonaws.com/nginx:latest",
      "status": "synced",
      "bytes_transferred": 31457280,
      "blob_stats": { "transferred": 3, "skipped": 1, "mounted": 2 },
      "duration": { "secs": 4, "nanos": 210000000 }
    }
  ],
  "stats": {
    "images_synced": 1,
    "images_skipped": 0,
    "images_failed": 0,
    "blobs_transferred": 3,
    "blobs_skipped": 1,
    "blobs_mounted": 2,
    "bytes_transferred": 31457280,
    "discovery_cache_hits": 0,
    "discovery_cache_misses": 1,
    "discovery_head_failures": 0,
    "discovery_target_stale": 0
  },
  "duration": { "secs": 4, "nanos": 210000000 },
  "unresolved_mappings": [
    {
      "from": "cgr.dev/chainguard/private",
      "error": "mapping 'cgr.dev/chainguard/private': registry error: 403 Forbidden"
    }
  ],
  "dropped_targets": [
    {
      "from": "cgr.dev/chainguard/nginx",
      "registry": "backup-ecr",
      "error": "mapping 'cgr.dev/chainguard/nginx': target registry 'backup-ecr' is unavailable: ECR auth setup for '...': 403 Forbidden"
    }
  ]
}

analyze

ocync analyze --json reports images_analyzed, images_partial (recorded, but missing at least one platform of a multi-arch image), images_failed (could not be read at all), plus unresolved_mappings and dropped_targets in the same shape sync uses, so one consumer reads both. An analysis that could not read everything exits non-zero, because the totals are short by whatever it missed.

Progress indicators

ocync auto-detects the output environment:

  • TTY: real-time progress bars with per-image and aggregate stats
  • Non-TTY / CI: periodic heartbeat lines with summary counts

At the default verbosity, per-image lines are suppressed, so a long run would otherwise be silent from start to finish. Two periodic lines fill that gap:

  • preparing sync every five seconds before the engine starts, carrying phase (registries, batch checkers, or mappings), done, total, and elapsed_secs. This window is sequential network work: a token mint per registry, then a tag listing per mapping.
  • sync in progress every 30 seconds while discovery or transfers are still in flight, carrying in_discovery, pending, in_flight, completed, and elapsed_secs

Both fire on wall-clock rather than on item boundaries, so a single slow mapping still reports, and a run that finishes inside one interval emits neither.

Disable all progress output with --quiet.

Logging

Control verbosity with -v flags:

LevelFlagOutput
Info(default)Sync progress and results
Debug-vAuth events, cache decisions, per-image detail
Trace-vvHTTP requests, detailed internals
Error-q / --quietErrors only

-v also uncaps the per-reason sample list in --dry-run output (default cap: 5 tags per drop reason and 5 names in the literal include path).

Log format

# Human-readable (default everywhere, including Kubernetes)
ocync sync -c config.yaml -vv

# JSON for log-aggregation pipelines that parse structured fields
ocync sync -c config.yaml -vv --log-format json

When deployed via the chart, set logging.format: json in helm values to opt into JSON output.

Override with the RUST_LOG environment variable for fine-grained filter directives.

Health endpoints

In watch mode, ocync exposes HTTP health endpoints:

EndpointPurposeHealthy
/healthzLiveness probeProcess is running
/readyzReadiness probeAt least one successful sync completed

Configure the port via --health-port (default 8080) or in Helm values:

mode: watch
watch:
  healthPort: 8080

See CLI reference for all watch flags including --interval and --json.