Operation Logger
operation_logger
Owns all console, logfile, and (verbose mode) request/response file output for the cascade_cms library.
Rendering model (design doc chain-node-and-logger-design.md Part 2 / the
pass-2 rework decision log):
- One line per chain:
(uuid_or_path, asset_type) OP1 -> fn_name: Type -> ..., built viaChainLineBuilderand produced byOperationLogger.flush_chain/flush_chain_error. - Lines are built incrementally in memory as a chain executes (append_step() per node, reflecting real step-by-step progress — a hang mid-chain could still be introspected via render_in_progress()), but are only written to the console/logfile once, at the point the chain finishes or stops. Chains run concurrently, so a true live in-place redraw of N lines is not practical in a scrolling console or an append-only logfile; write-once avoids that without losing the incremental-construction property.
[METHOD] URLrequest/response detail lines and the raw request/response JSON files are verbose (debug) mode only, and never inline a payload.- Two modes, controlled solely by whether
debug_configisNone:- Normal (debug_config=None): minimal console, simple logfile.
- Debug (debug_config=dict): quiet console, verbose logfile plus the
per-request JSON files.
There is no longer a separate "log operations vs. callbacks vs. responses"
toggle —
_is_debugis the single on/off switch for verbose behavior.
Recognized debug_config keys:
log_dir — directory for the logfile and (verbose mode)
request/response JSON files. Default "./logs".
show_network_headers — verbose mode only: also log request/response
HTTP headers.
ChainLineBuilder
Accumulates one chain's pipeline-line segments as its nodes execute.
Owns only this line's in-progress state (an identifier prefix and an
ordered list of completed segment strings) and has no dependency on
operations.py's Node/OperationChain shape — it's driven purely by
primitive arguments, so it is usable and testable standalone. What each
segment's label is (a bare operation name, fn_name: ReturnType, ...)
is entirely the caller's decision (see pass-3 wiring); this class only
joins and aligns whatever labels it's given.
Source code in cascade_cms/operation_logger.py
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | |
start(identifier)
Resolve and store the (uuid_or_path, asset_type) line prefix.
Source code in cascade_cms/operation_logger.py
87 88 89 | |
append_step(label)
Append one segment — an operation name or a fn_name: Type label.
Source code in cascade_cms/operation_logger.py
91 92 93 | |
render_in_progress()
The line as it stands, with a trailing arrow — not yet finished.
Exposed for introspection of a chain that is still executing (e.g. a
hang mid-chain); the normal success/error flush paths use
render_complete()/render_error() instead, since by the time a
chain finishes or stops it is no longer "in progress".
Source code in cascade_cms/operation_logger.py
95 96 97 98 99 100 101 102 103 104 105 | |
render_complete()
The finished (or stopped) line's text, with no trailing arrow.
Source code in cascade_cms/operation_logger.py
107 108 109 | |
render_error(failing_step_index, message, file, line)
The v marker and !ERROR: block for a failure at failing_step_index.
failing_step_index indexes into the segments already appended via
append_step() — the failing step's own (possibly unresolved) label
is expected to already be present at that index (e.g. an operation's
bare name, appended when its request was issued — see pass-3 wiring)
so the column below aligns under its first character.
The column is computed by reconstructing the line's literal text up to (not including) that label and measuring its length: each " -> " separator is 4 characters (both surrounding spaces), not 2 — the most likely source of an off-by-one here.
A multi-line message keeps every continuation line at the same
indentation as the first (no progressive indent); @{file}:{line}
is appended to the last line of the message.
Source code in cascade_cms/operation_logger.py
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | |
OperationLogger
Owns all console and logfile output for the cascade_cms library.
See the module docstring for the two modes and the write-once-per-chain rendering decision.
Source code in cascade_cms/operation_logger.py
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 | |
is_debug
property
Whether this logger is in verbose (debug) mode.
Pass-3 integration seam: callers (operations.py, driver.py) need this to decide whether it's worth doing verbose-only work (e.g. serializing a payload to reference its file) before calling into logger methods that already no-op internally outside debug mode — this just avoids paying that cost for nothing in normal mode.
log_batch_start()
Bracket the start of one submit_requests() batch.
A script may call submit_requests() multiple times in one
session, each getting its own start/end pair — this is
deliberately separate from the session-level log_init/log_exit.
Source code in cascade_cms/operation_logger.py
255 256 257 258 259 260 261 262 263 264 265 | |
log_batch_end(succeeded, total)
Close one batch's bracket and report its tally.
succeeded/total are supplied by the caller (already known from
the batch's own results) rather than tracked incrementally here, so
this is the single place both the per-batch and the running
session-total (log_exit) counts are updated.
Source code in cascade_cms/operation_logger.py
267 268 269 270 271 272 273 274 275 276 277 278 279 280 | |
flush_chain(builder)
Write a chain's finished (successful) line to the logfile.
Source code in cascade_cms/operation_logger.py
286 287 288 | |
flush_chain_error(builder, failing_step_index, message, file, line)
Write a stopped chain's line plus its v/!ERROR: block.
Writes the pipeline text (via render_complete(), since the
failing step's own label is already the last appended segment —
see ChainLineBuilder.render_error) followed by the alignment
block, all in one flush.
Source code in cascade_cms/operation_logger.py
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 | |
log_request_detail(method, url, payload_ref=None)
Write [METHOD] URL, once per server-touching operation.
Debug-mode only — normal mode's logfile is just the one pipeline
line per chain (from flush_chain/flush_chain_error) plus the
batch tally; per-request detail is reserved for verbose output.
No payload is ever inlined here, in any mode.
Source code in cascade_cms/operation_logger.py
314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 | |
log_cache_hit(method, url)
Mark a request served from the local response cache instead of
the network, written right after that request's own [METHOD] URL
line — the surrounding chain line reports success either way, which
can make a stale cache look identical to a real (or a broken)
request; this line is the only place that distinction is visible.
Source code in cascade_cms/operation_logger.py
331 332 333 334 335 336 337 338 339 340 | |
write_request_file(uuid, payload)
Write {uuid}_request.json to the logs folder. Verbose mode only.
Source code in cascade_cms/operation_logger.py
353 354 355 356 357 | |
write_response_file(uuid, response)
Write {uuid}_response.json to the logs folder. Verbose mode only.
Skipped for a trivial success/fail shape — a dict whose keys are a
subset of {"success", "message"} — since that carries no
information beyond what the chain line already shows.
Source code in cascade_cms/operation_logger.py
359 360 361 362 363 364 365 366 367 368 369 370 371 | |
log_network_headers(request_headers, response_headers)
Write network header info. Verbose mode + show_network_headers only.
Source code in cascade_cms/operation_logger.py
373 374 375 376 377 378 379 380 381 382 | |
log_cascade_error(message, identifier)
Log a CascadeError (API-level failure) outside of chain context.
Thin wrapper per the design (chain-level failures normally go
through flush_chain_error, which has step-index context this
doesn't) — kept as a standalone entry point for a request-level
failure with no enclosing chain line.
Source code in cascade_cms/operation_logger.py
388 389 390 391 392 393 394 395 396 397 398 399 400 | |
log_python_error(exc)
Log a Python exception outside of chain context. See log_cascade_error.
Source code in cascade_cms/operation_logger.py
402 403 404 405 406 407 408 409 410 411 412 413 414 | |