feat: add a native logs API to ops.pebble#2660
Draft
tonyandrewmeyer wants to merge 1 commit into
Draft
Conversation
Pebble has had a /v1/logs endpoint from the start, but ops.pebble.Client never exposed it, so tools built on ops (for example borescope, see tonyandrewmeyer/borescope#85) had to shell out to a pebble binary or write their own socket client to read service logs. Add LogEntry (a frozen dataclass with the parsed RFC 3339 time, service name, and message) and two Client methods mirroring the Go client's Logs/FollowLogs split: get_logs() eagerly returns the buffered entries (n=-1 for the whole buffer), and follow_logs() returns a generator that streams new entries. The endpoint streams newline-delimited JSON, so both go through _request_raw and iterate the response line by line; following has no natural deadline, so _request_raw gains a timeout override and the request is made with no socket timeout. Container gains get_logs() only: blocking a hook indefinitely is a footgun, so following is reached via container.pebble. In Scenario, model the buffer as Container.service_logs, holding scenario.state.LogEntry entries (time defaults to now, so tests can write LogEntry('redis', 'ready')). The mock client sorts them by time (stably, keeping the state's order for equal times), filters by service, and applies Pebble's n semantics; its follow_logs ends after the buffered entries, since nothing in a test writes new ones. Harness gets NotImplementedError stubs. Tested with MockClient unit tests, a streaming /v1/logs route in the fake_pebble unix-socket server, and Scenario e2e tests; also verified by hand against a real Pebble 1.32 daemon (default n, filtering, n=-1, n=0, APIError on bad n, and live streaming via follow_logs). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
tonyandrewmeyer
commented
Jul 22, 2026
| service_names: Optional service names to get logs for. If no | ||
| service names are specified, get logs for all services. | ||
| n: Maximum number of log entries to return, across all requested | ||
| services combined. Use -1 to get everything in the buffer. |
Collaborator
Author
There was a problem hiding this comment.
I think this probably should be None, and probably entries or count or similar rather than n.
tonyandrewmeyer
commented
Jul 22, 2026
| services: Names of the services to get logs for. If not specified, | ||
| get logs for all services. | ||
| n: Maximum number of log entries to return, across all requested | ||
| services combined. Use -1 to get everything in the buffer. |
Collaborator
Author
There was a problem hiding this comment.
Also here.
tonyandrewmeyer
commented
Jul 22, 2026
| services: Names of the services to get logs for. If not specified, | ||
| get logs for all services. | ||
| n: Number of buffered log entries to yield before following. This | ||
| defaults to 0 (only new entries); use -1 to first get |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Pebble has a
/v1/logsendpoint, butops.pebble.Clientdoesn't expose it. This PR changes adds support, including to Scenario.The PR adds
LogEntry(a frozen dataclass with the parsed RFC 3339 time, service name, and message) and twoClientmethods mirroring the Go client's Logs/FollowLogs split:get_logs()eagerly returns the buffered entries (n=-1 for the whole buffer), andfollow_logs()returns a generator that streams new entries.Containergainsget_logs()only, because you probably don't want to block in a hook.In Scenario, the buffer is modelled as
Container.service_logs, holdingstate.LogEntryentries (time defaults to now, so tests can writeLogEntry('redis', 'ready')). The mock client sorts them by time (stably, keeping the state's order for equal times), filters by service, and applies Pebble's n semantics.Verified by hand against a real Pebble 1.32 daemon (default n, filtering, n=-1, n=0, APIError on bad n, and live streaming via follow_logs).