Skip to content

feat(logs): accept Docker-compatible timestamp filters#1765

Open
stephenlclarke wants to merge 14 commits into
apple:mainfrom
stephenlclarke:logs-docker-timestamp-parser
Open

feat(logs): accept Docker-compatible timestamp filters#1765
stephenlclarke wants to merge 14 commits into
apple:mainfrom
stephenlclarke:logs-docker-timestamp-parser

Conversation

@stephenlclarke

@stephenlclarke stephenlclarke commented Jun 20, 2026

Copy link
Copy Markdown

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation update

Motivation and Context

This is a draft stacked follow-up to #1592 and #1764. Once log retrieval filters are exposed through ContainerLogOptions, the CLI should accept the timestamp forms users expect from Docker-compatible log commands instead of only accepting RFC 3339 values.

Docker-compatible log workflows commonly use Unix timestamps, fractional Unix timestamps, and relative Go-style durations such as 1m30s. External orchestrators need the same parser behavior as the container logs CLI, so this draft moves timestamp parsing into ContainerResource as a small reusable helper rather than leaving ad hoc parsing in the command layer.

Draft/stacking note: this branch is based on the updated logs-tail-until-options branch from #1764, which is itself based on #1592. Because GitHub PRs to apple/container target main, this diff includes #1592 and #1764. The intended review delta for this draft is:

  • d7cc54c feat(logs): accept docker-compatible timestamps
  • eaf8fce test(logs): cover relative timestamp arguments
  • fdb387e fix(logs): parse date-only filters in local time
  • b3929ca fix(logs): accept signed relative durations

What Changed

  • Adds ContainerLogTimestampParser in ContainerResource.
  • Supports RFC 3339 timestamps with or without fractional seconds.
  • Parses date-only and no-zone timestamps using the local timezone, matching Docker-style CLI behavior.
  • Supports Unix timestamps with optional fractional seconds.
  • Supports Go-style relative durations such as 1m30s, 250ms, and 1.5h.
  • Supports signed Go-style relative durations such as +1s, -1s, and -.5h.
  • Rejects non-finite duration components instead of letting overflow produce invalid dates.
  • Updates container logs --since and --until argument parsing to use the shared parser.
  • Documents accepted timestamp formats in docs/command-reference.md.
  • Adds focused parser and CLI argument tests.

Relationship to Existing Work

Non-Goals

  • This does not add structured log records.
  • This does not change runtime log storage.
  • This does not add timestamp rendering.
  • This does not add day-based duration units; Docker's duration parser is Go-duration based, so supported units are ns, us/µs/μs, ms, s, m, and h.

Testing

  • Tested locally
  • Added/updated tests
  • Added/updated docs

Local verification:

swift format lint --strict --configuration .swift-format-nolint Sources/ContainerResource/Container/ContainerLogTimestampParser.swift Sources/ContainerCommands/Container/ContainerLogs.swift Tests/ContainerCommandsTests/ContainerLogsCommandTests.swift Tests/ContainerResourceTests/ContainerLogOptionsTests.swift Sources/Services/ContainerAPIService/Server/Containers/ContainersService.swift Tests/ContainerAPIServiceTests/ContainerLogsTests.swift
git diff --check
swift build
swift test --filter ContainerLogsCommandTests
swift test --filter ContainerLogsTests
swift test --filter ContainerLogOptionsTests

Result: focused validation passed locally. The focused tests ran 31 tests across ContainerLogsCommandTests, ContainerLogsTests, and ContainerLogOptionsTests.

chrisgeo and others added 2 commits May 23, 2026 13:14
…ient.logs

Adds an additive overload to ContainerClient.logs that accepts a
ContainerLogOptions { since: Date?, timestamps: Bool } and plumbs the
two parameters through XPC into the daemon's existing log-handle path.

Motivation
----------

External orchestrators that drive the API server (the canonical use
case is a Compose-spec orchestrator implementing 'compose logs --since
<timestamp>' and '--timestamps') need to retrieve only recent log
output and optionally annotate lines with timestamps. Today
ContainerClient.logs(id:) returns raw containerLog + bootlog file
handles unconditionally; consumers either replay the entire log buffer
or implement their own line-by-line filter on the client. Surfacing
the parameters at the API boundary keeps the line scanning where the
file lives — server-side — and matches the docker-compose UX users
expect.

What this PR changes
--------------------

- Sources/ContainerResource/Container/ContainerLogOptions.swift (new):
  the ContainerLogOptions struct, Sendable + Codable, with a static
  '.default' equivalent to the original logs(id:) zero-config call.
- Sources/Services/ContainerAPIService/Client/XPC+.swift: two new
  XPCKeys cases ('logSince', 'logTimestamps') for the optional
  parameters.
- Sources/Services/ContainerAPIService/Client/ContainerClient.swift:
  new logs(id:options:) overload. The existing logs(id:) is retained
  as a thin wrapper over .default for source compatibility.
- Sources/Services/ContainerAPIService/Server/Containers/ContainersService.swift:
  matching logs(id:options:) overload; when options.since is provided,
  applies a private 'filterFileHandleSince' that parses ISO-8601
  timestamps from line starts and drops older lines (lines without a
  parseable timestamp pass through unchanged).
- Sources/Services/ContainerAPIService/Server/Containers/ContainersHarness.swift:
  parses the two optional XPC keys, builds ContainerLogOptions, and
  forwards to the new service overload.

Wire compatibility
------------------

XPC calls without the new keys decode as 'sinceRaw.timeIntervalSince1970
== 0' (the harness treats this as 'no since filter') and 'timestamps =
false', so older clients hitting a newer server see unchanged behavior.
Newer clients hitting an older server send the keys; an old harness
will silently drop them and run the original codepath.

Known limitations (intentional, follow-up work)
-----------------------------------------------

- The 'options.timestamps' parameter is plumbed end-to-end but the
  daemon does not currently decorate raw log lines that lack a
  timestamp prefix. Line decoration is a deliberate follow-up; keeping
  the parameter on the API surface today avoids a second wire-format
  break later.
- 'filterFileHandleSince' currently slurps the file into memory then
  returns a Pipe-backed FileHandle. For the typical container log
  size this is fine; for very large logs a streaming line iterator
  would be a worthwhile optimization. Filed as a known-issue for the
  follow-up timestamp-decoration PR.

Verification
------------

Full 'swift build' clean on macOS 26 / Apple silicon (release config,
all targets including downstream consumers of ContainerClient.logs).
@stephenlclarke stephenlclarke force-pushed the logs-docker-timestamp-parser branch from c4e1989 to 9c5df9d Compare June 20, 2026 08:18
@stephenlclarke stephenlclarke force-pushed the logs-docker-timestamp-parser branch from 6f0dd14 to 7ab07bd Compare June 20, 2026 10:21
@stephenlclarke stephenlclarke force-pushed the logs-docker-timestamp-parser branch from 7ab07bd to 6d09d2d Compare June 20, 2026 10:24
@stephenlclarke stephenlclarke force-pushed the logs-docker-timestamp-parser branch from 6d09d2d to 3273286 Compare June 20, 2026 10:33
@stephenlclarke stephenlclarke force-pushed the logs-docker-timestamp-parser branch from 3273286 to d61e4a3 Compare June 20, 2026 10:35
@stephenlclarke stephenlclarke force-pushed the logs-docker-timestamp-parser branch from d61e4a3 to f0673cb Compare June 20, 2026 11:17
@stephenlclarke stephenlclarke force-pushed the logs-docker-timestamp-parser branch from f0673cb to b3929ca Compare June 20, 2026 11:21
@stephenlclarke stephenlclarke marked this pull request as ready for review June 20, 2026 12:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants