@@ -55,135 +55,39 @@ delete the deployment automatically.
5555
5656### Deployment logs SDK example
5757
58- ` fetch_logs() ` is the one way to read deployment logs: it fetches a revision's
59- stored log lines within a time window (` start_time ` /` end_time ` , epoch ms,
60- inclusive; omit either bound to leave that side unbounded) and yields them lazily
61- as chunks of up to ` chunk_size ` ` DeploymentLogEvent ` — each line at most once,
62- each event carrying its pod name, with bounded memory however large the window.
63- ` newest_first ` selects only the order chunks arrive: ` False ` (the default) walks
64- the window oldest chunk first, ` True ` newest chunk first; lines inside every chunk
65- are always in ascending ` (timestamp, id) ` order. So the bare call reads the full
66- retained history chronologically, ` newest_first=True ` starts from the newest
67- stored line and walks backward, and ` start_time ` alone catches up from a known
68- point to the present. By default every pod of the revision is merged into one
69- stream; pass ` pod= ` to read a single pod — discover names with
70- ` get_deployment_pods() ` (terminated pods still within log retention are included):
58+ ` fetch_logs() ` is the one way to read deployment logs: it fetches one pod's stored
59+ log lines within a time window (` start_time ` /` end_time ` , epoch ms, inclusive) and
60+ yields them lazily, oldest first, as chunks of up to ` chunk_size `
61+ ` DeploymentLogEvent ` — each line at most once, with bounded memory however long
62+ the stream. Discover pod names with ` get_deployment_pods() ` (terminated pods
63+ still within log retention are included). ` start_time ` defaults to the moment of
64+ the call; with ` end_time ` set the iterator terminates once the window is
65+ delivered:
7166
7267``` python
73- for chunk in cclient.fetch_logs(DEPLOYMENT_ID , REVISION , start_time = t1_ms, end_time = t2_ms):
68+ for chunk in cclient.fetch_logs(DEPLOYMENT_ID , REVISION , pod, start_time = t1_ms, end_time = t2_ms):
7469 for event in chunk:
7570 print (event.pod, event.message)
7671```
7772
78- The iterator always terminates, and its exhaustion is the only termination signal:
79- one that yields nothing means the window holds no stored lines (aged out of
80- retention, before the deployment existed, an unknown pod, or genuinely empty).
81- Reading backward, note that a line the log store receives late for a time region
82- the walk has already passed is absent from that call: everything older than
83- roughly the read's start minus the ingest lag (~ 15s) is complete, and when
84- completeness of the newest lines matters, read forward. There is no follow mode:
85- tailing is a caller loop that re-calls a forward ` fetch_logs ` with a later
86- ` start_time ` and deduplicates by ` event.id ` :
73+ Without ` end_time ` the same generator tails: it never terminates, and once caught
74+ up it yields an empty chunk each time nothing new is stored yet — the caller
75+ decides when to sleep or break:
8776
8877``` python
8978import time
9079
91- OVERLAP_MS = 30_000 # covers the server's ~15s late-arrival re-delivery span
92- POLL_SECONDS = 2.0
93-
94- seen = {} # event id -> timestamp, trimmed to the overlap window each round
95- boundary = int (time.time() * 1000 ) # newest timestamp seen so far
96- while True :
97- for chunk in cclient.fetch_logs(
98- DEPLOYMENT_ID , REVISION , start_time = max (boundary - OVERLAP_MS , 0 ), newest_first = False
99- ):
100- for event in chunk:
101- if event.id in seen:
102- continue
103- seen[event.id] = event.timestamp
104- boundary = max (boundary, event.timestamp)
105- print (event.pod, event.message)
106- cutoff = boundary - OVERLAP_MS
107- seen = {event_id: ts for event_id, ts in seen.items() if ts >= cutoff}
108- time.sleep(POLL_SECONDS )
109- ```
110-
111- Two properties of this loop matter. Consecutive windows overlap on purpose: the log
112- store may deliver a line up to ~ 15 seconds after its timestamp, so starting each call
113- ` OVERLAP_MS ` below the newest seen line is what keeps late arrivals from being
114- skipped — do not advance ` start_time ` past that span to avoid the duplicates. And the
115- dedup state is bounded: only ids inside the overlap window can come back again, so
116- ` seen ` is trimmed to that window each round and never grows with the stream.
117-
118- ` python examples/sdk/get_deployment_logs.py ` runs a newest-first peek, a full
119- chronological read and this tail loop. ` get_deployment_logs() ` , ` get_deployment_logs_range() ` and
120- ` deployment_log_session() ` still work but are deprecated in favor of ` fetch_logs() `
121- and raise a ` DeprecationWarning ` on use.
122-
123- ### Migrating deployment log reads from 0.5.x
124-
125- ` get_deployment_logs() ` kept its name but not its signature, and is now deprecated:
126- ` start_time ` , ` end_time ` , ` line_count ` , ` start_from_head ` and ` stream ` are gone, and
127- ` fetch_logs() ` is the replacement for every read. A 0.5.x call raises ` TypeError `
128- (or a validation error, if its arguments were positional) rather than returning
129- something wrong, so no call site fails silently.
130-
131- | To | 0.5.x | now |
132- | ---| ---| ---|
133- | Read a time window | ` get_deployment_logs(id, rev, start_time=, end_time=) ` | ` fetch_logs(id, rev, start_time=, end_time=) ` |
134- | Stream a window lazily | the same call with ` stream=True ` | ` fetch_logs(...) ` — chunks are yielded as they are fetched |
135- | Take the newest lines first | ` start_from_head=False ` | ` fetch_logs(id, rev, newest_first=True) ` |
136- | Read from the beginning | ` start_from_head=True ` | ` fetch_logs(id, rev) ` — oldest first is the default |
137- | Cap what one iteration hands you | ` line_count=n ` | ` chunk_size=n ` |
138- | Tell which pod a line came from | parse ` kubernetes.pod_name ` out of ` message ` | ` event.pod ` |
139- | Keep tailing past the window | not supported | re-call ` fetch_logs ` with overlapping windows (the tail loop above) |
140-
141- A whole-window read loses its envelope parsing, because ` message ` is now the log line
142- itself rather than a JSON record wrapping it:
143-
144- ``` python
145- # 0.5.x
146- events = cclient.get_deployment_logs(DEPLOYMENT_ID , REVISION , start_time = t1, end_time = t2)
147- for event in events:
148- record = json.loads(event[" message" ])
149- print (record[" kubernetes" ][" pod_name" ], record[" log" ])
150-
151- # now
152- for chunk in cclient.fetch_logs(DEPLOYMENT_ID , REVISION , start_time = t1, end_time = t2):
80+ for chunk in cclient.fetch_logs(DEPLOYMENT_ID , REVISION , pod):
81+ if not chunk:
82+ time.sleep(2 )
83+ continue
15384 for event in chunk:
15485 print (event.pod, event.message)
15586```
15687
157- A ` stream=True ` loop becomes a ` fetch_logs() ` loop, which yields each chunk as it
158- arrives just as the old generator yielded pages:
159-
160- ``` python
161- # 0.5.x
162- for event in cclient.get_deployment_logs(
163- DEPLOYMENT_ID , REVISION , start_time = t1, end_time = t2, stream = True
164- ):
165- print (json.loads(event[" message" ])[" log" ])
166-
167- # now
168- for chunk in cclient.fetch_logs(DEPLOYMENT_ID , REVISION , start_time = t1, end_time = t2):
169- for event in chunk:
170- print (event.message)
171- ```
172-
173- One contract change to check error handling against: a revision that does not exist
174- now answers 404 where the old endpoint answered 400.
175-
176- The 0.6.0 readers — ` get_deployment_logs() ` page anchoring, ` get_deployment_logs_range() `
177- and ` deployment_log_session() ` — still work but are deprecated and warn on use; the
178- look-behind anchoring they exposed is handled inside ` fetch_logs() ` :
179-
180- | 0.6.0 | now |
181- | ---| ---|
182- | ` get_deployment_logs(id, rev, pod) ` — newest page of one pod | ` fetch_logs(id, rev, pod=pod, newest_first=True) ` and take the first chunk |
183- | ` get_deployment_logs(id, rev, pod, after=events) ` — page newer than held events | ` fetch_logs(id, rev, pod=pod, start_time=boundary_ms) ` (the tail loop above for repeated polling) |
184- | ` get_deployment_logs_range(id, rev, start_time=, end_time=) ` | ` fetch_logs(id, rev, start_time=, end_time=) ` — chunked and lazy instead of one list |
185- | ` deployment_log_session(...).fetch_older() ` loop | ` fetch_logs(id, rev, pod=pod, newest_first=True) ` — one iterator walks back to the start |
186- | ` session.fetch_newer() ` polling | the tail loop above |
88+ ` python examples/sdk/get_deployment_logs.py ` runs both. ` get_deployment_logs() ` ,
89+ ` get_deployment_logs_range() ` and ` deployment_log_session() ` still work but are
90+ deprecated in favor of ` fetch_logs() ` and raise a ` DeprecationWarning ` on use.
18791
18892### Un-installation
18993
0 commit comments