feat: add logging reset support#808
Conversation
Derive logging state from managed handlers and remove the interface runtime latch so automatic configuration is evaluated per instance. Add reset_logging(), regression coverage, and user documentation. Closes #805 Signed-off-by: Nabin Mulepati <nmulepati@nvidia.com>
Close Data Designer-managed handlers during reset so file resources are released. Inline idempotent runtime setup in DataDesigner and verify behavior through the public constructor. Signed-off-by: Nabin Mulepati <nmulepati@nvidia.com>
|
Fern preview: https://nvidia-preview-pr-808.docs.buildwithfern.com/nemo/datadesigner
|
Code Review: PR #808 — feat: add logging reset supportAuthor: nabinchha (Nabin Mulepati) · Base: SummaryThis PR replaces process-global boolean latches (
This is a clean, well-motivated design. Deriving state from observable reality FindingsCorrectness
Conventions / Style
Test Coverage
Documentation
Minor / Optional
Structural Impact(graphify, 2.4s) Risk: MEDIUM (high-connectivity entity (DataDesigner, 86 deps))
High-Connectivity Changes
Cross-Package Dependencies
Reviewer note on blast radius: The MEDIUM risk stems from touching VerdictApprove with minor, non-blocking suggestions. This is a solid refactor that Note: |
Greptile SummaryThis PR adds resettable Data Designer logging. The main changes are:
|
| Filename | Overview |
|---|---|
| packages/data-designer-config/src/data_designer/logging.py | Adds managed handler classes, observable configured-state detection, and reset logic for Data Designer-owned handlers. |
| packages/data-designer/src/data_designer/interface/data_designer.py | Moves logging and default-settings setup into each DataDesigner construction, which enables reset support but loses a prior opt-out when a later default construction runs. |
| packages/data-designer-config/tests/test_logging.py | Adds coverage for managed handler detection, reset behavior, foreign handler preservation after configuration, and file handler closure. |
| packages/data-designer/tests/interface/test_data_designer.py | Updates interface tests for per-instance logging setup, opt-out behavior, preconfigured logging, and repeated construction. |
| fern/versions/latest/pages/concepts/logging.mdx | Documents automatic logging, custom application logging, and reset behavior. |
| fern/versions/latest.yml | Adds the Logging concept page to the latest documentation navigation. |
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 1
packages/data-designer/src/data_designer/interface/data_designer.py:151-152
**Opt-Out State Is Lost**
When an embedding app installs its own root handler, constructs `DataDesigner(auto_configure_logging=False)`, and later constructs a default `DataDesigner()`, `is_logging_configured()` is still false because app handlers are not managed handlers. This path then calls `configure_logging()`, which clears the root handlers and redirects logging away from the application's handler.
Reviews (1): Last reviewed commit: "fix: finalize logging reset cleanup" | Re-trigger Greptile
| if auto_configure_logging and not is_logging_configured(): | ||
| configure_logging() |
There was a problem hiding this comment.
When an embedding app installs its own root handler, constructs DataDesigner(auto_configure_logging=False), and later constructs a default DataDesigner(), is_logging_configured() is still false because app handlers are not managed handlers. This path then calls configure_logging(), which clears the root handlers and redirects logging away from the application's handler.
Context Used: Do not suggest defensive coding patterns such as a... (source)
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/data-designer/src/data_designer/interface/data_designer.py
Line: 151-152
Comment:
**Opt-Out State Is Lost**
When an embedding app installs its own root handler, constructs `DataDesigner(auto_configure_logging=False)`, and later constructs a default `DataDesigner()`, `is_logging_configured()` is still false because app handlers are not managed handlers. This path then calls `configure_logging()`, which clears the root handlers and redirects logging away from the application's handler.
**Context Used:** Do not suggest defensive coding patterns such as a... ([source](greptile.json))
How can I resolve this? If you propose a fix, please make it concise.There was a problem hiding this comment.
Thanks for checking this. This behavior is intentional and is the first-construction-wins footgun tracked by #805: auto_configure_logging is per instance, not a sticky process-wide opt-out. The first construction with False leaves application handlers untouched; a later default construction explicitly opts back into Data Designer logging. Applications that own logging should pass False to each DataDesigner instance. The Fern page already documents this, and the PR description now calls it out explicitly. Keeping a sticky opt-out would recreate the behavior this issue removes.
| if isinstance(handler, DataDesignerManagedHandler): | ||
| root_logger.removeHandler(handler) | ||
| handler.close() | ||
| root_logger.setLevel(logging.WARNING) |
There was a problem hiding this comment.
I think reset_logging() might surprise apps that own their logging: if there are no managed handlers (e.g. only a basicConfig handler), it still forces the root level to WARNING, so an app at INFO silently loses its INFO logs even though its handlers survive. Since the managed-handler check is right there, maybe early-return when nothing was removed? All the new tests still pass with that guard. Both Claude Code and Codex flagged this one independently.
| @@ -1599,23 +1564,37 @@ def test_init_auto_configure_logging_false_preserves_root_handlers( | |||
|
|
|||
|
|
|||
| def test_init_preserves_preconfigured_logging( | |||
There was a problem hiding this comment.
the old version of this test restored the logger level in a finally; now it exits with a real DEBUG-config handler on root and the next tests inherit it (works today only because the next test happens to start with reset_logging()). wdyt about an autouse fixture that snapshots/restores root handlers + levels? that would also let the other tests drop their leading reset_logging() calls.
| model_providers=stub_model_providers, | ||
| secret_resolver=PlaintextResolver(), | ||
| managed_assets_path=stub_managed_assets_path, | ||
| auto_configure_logging=False, |
There was a problem hiding this comment.
nit: this flag is load-bearing (without it configure_logging() clears root handlers and detaches caplog's capture handler when the test runs alone) - might be nice to leave a one-line comment so the next caplog test doesn't trip on the same thing
now that |
tiny doc nit: "skipped when |
📋 Summary
Replace process-global runtime initialization latches with observable logging state and idempotent per-construction setup. Add a public
reset_logging()API so embedding applications can surgically undo Data Designer logging without disturbing foreign handlers; missing default model settings are re-resolved safely on each construction.🔗 Related Issue
Closes #805
🔄 Changes
is_logging_configured()from the handlers currently attached to the root logger.reset_logging()to remove and close managed handlers, preserve foreign handlers, and restore default root anddata_designerlevels.auto_configure_loggingis evaluated for eachDataDesignerconstruction. An explicitFalseapplies only to that instance; a later default construction intentionally opts back into Data Designer logging.resolve_seed_default_model_settings()on each construction; the operation remains idempotent and writes only missing defaults.🔍 Attention Areas
data_designer.logging— public managed-handler and reset semantics.DataDesigner.__init__— per-construction logging and default-settings initialization.🧪 Testing
make testequivalent passes: config (627), engine (2,172), interface (1,037 passed, 1 skipped)✅ Checklist
Description updated with AI