Skip to content

fix: preserve preconfigured logging#755

Merged
nabinchha merged 4 commits into
NVIDIA-NeMo:mainfrom
schultzjack:codex/388-preserve-configured-logging
Jul 7, 2026
Merged

fix: preserve preconfigured logging#755
nabinchha merged 4 commits into
NVIDIA-NeMo:mainfrom
schultzjack:codex/388-preserve-configured-logging

Conversation

@schultzjack

@schultzjack schultzjack commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Summary

  • track successful configure_logging() calls in the logging module
  • skip interface default logging setup when logging was already configured
  • add an auto_configure_logging keyword to DataDesigner.__init__ so applications that manage their own logging (e.g. via logging.basicConfig() or custom root handlers) can opt out of Data Designer's default logging setup entirely
  • document that is_logging_configured() only tracks Data Designer's own configure_logging(), not stdlib logging configuration
  • add regression coverage for: DataDesigner() preserving a prior debug logging config, default construction still configuring logging, and auto_configure_logging=False preserving existing root handlers

Testing

Closes #388

Signed-off-by: schultzjack <schultzjack@users.noreply.github.com>
@schultzjack schultzjack requested a review from a team as a code owner June 17, 2026 00:17
@github-actions

github-actions Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

All contributors have signed the DCO ✍️ ✅
Posted by the DCO Assistant Lite bot.

@greptile-apps

greptile-apps Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes issue #388 by preventing DataDesigner.__init__ from overwriting previously configured logging. It introduces a _logging_configured module-level flag that configure_logging() sets on success, and checks that flag (plus a new auto_configure_logging constructor parameter) before running default logging setup.

  • Adds _logging_configured flag and is_logging_configured() to data_designer/logging.py; configure_logging() sets the flag at the end of a successful run.
  • _initialize_interface_runtime() gains an auto_configure_logging keyword argument; logging is skipped when either auto_configure_logging=False or the flag is already set.
  • New regression tests cover: default construction still configures logging, pre-configured debug level is preserved, and auto_configure_logging=False leaves existing root handlers untouched.

Confidence Score: 5/5

Safe to merge. The change is minimal and well-scoped: a module-level boolean flag is set at the end of a successful configure_logging() run, and checked before any automatic logging setup in _initialize_interface_runtime. Existing behavior is unchanged for the common path; the opt-out paths are all covered by new regression tests.

The flag is only set after all side-effects in configure_logging() succeed, so a mid-run exception leaves the state clean for a retry. The _interface_runtime_initialized one-time-run guard is preserved correctly, and all new tests use monkeypatch to isolate module-level state. No logic errors or correctness issues were found.

No files require special attention.

Important Files Changed

Filename Overview
packages/data-designer-config/src/data_designer/logging.py Adds module-level _logging_configured flag, sets it at the end of configure_logging(), and exposes is_logging_configured(). Logic is correct; the flag is set only after all side-effects succeed.
packages/data-designer/src/data_designer/interface/data_designer.py Adds auto_configure_logging keyword-only parameter to DataDesigner.__init__ and threads it through _initialize_interface_runtime; guard condition correctly combines both signals before calling configure_logging().
packages/data-designer-config/tests/test_logging.py Adds one test verifying that configure_logging() sets the _logging_configured flag; properly uses monkeypatch to reset the flag before and after.
packages/data-designer/tests/interface/test_data_designer.py Adds five new regression tests covering all combinations of pre-configured logging, auto_configure_logging=False, and default construction; existing test patched to also reset _logging_configured for correct isolation.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["DataDesigner.__init__(auto_configure_logging=...)"] --> B["_initialize_interface_runtime(auto_configure_logging=...)"]
    B --> C{"_interface_runtime_initialized?"}
    C -- "Yes" --> Z["return (no-op)"]
    C -- "No" --> D{"auto_configure_logging AND NOT is_logging_configured()"}
    D -- "True" --> E["configure_logging()"]
    E --> F["_logging_configured = True"]
    F --> G["resolve_seed_default_model_settings()"]
    D -- "False" --> G
    G --> H["_interface_runtime_initialized = True"]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A["DataDesigner.__init__(auto_configure_logging=...)"] --> B["_initialize_interface_runtime(auto_configure_logging=...)"]
    B --> C{"_interface_runtime_initialized?"}
    C -- "Yes" --> Z["return (no-op)"]
    C -- "No" --> D{"auto_configure_logging AND NOT is_logging_configured()"}
    D -- "True" --> E["configure_logging()"]
    E --> F["_logging_configured = True"]
    F --> G["resolve_seed_default_model_settings()"]
    D -- "False" --> G
    G --> H["_interface_runtime_initialized = True"]
Loading

Reviews (3): Last reviewed commit: "Merge branch 'main' into codex/388-prese..." | Re-trigger Greptile

@schultzjack

Copy link
Copy Markdown
Contributor Author

recheck

@github-actions

Copy link
Copy Markdown
Contributor

Stale PR reminder

This PR has had failing checks for 7 days without activity.

Failing checks: check

Please push an update or leave a comment if you're still working on this.
Otherwise, this PR will be automatically closed in 7 days.

To prevent auto-close, add the keep-open label.

@github-actions

Copy link
Copy Markdown
Contributor

Issue #388 has been triaged. The linked issue check is being re-evaluated.

@andreatgretel

Copy link
Copy Markdown
Contributor

Thanks for jumping on this, this fixes a pretty easy-to-miss logging footgun. I reviewed the runtime init path and the new logging state tracking. The core behavior looks good, but I'd like one small clarification before merge.

is_logging_configured() only tracks calls through data_designer.logging.configure_logging(). It won't detect users who configured logging through the standard Python APIs, like logging.basicConfig() or manually attached root handlers, so DataDesigner() would still call configure_logging() and replace those handlers.

Please add a short docstring/comment making that scope explicit, or broaden the check if you want this to cover general Python logging setup too. The current fix is fine for the linked issue, but the helper name reads a bit broader than what it actually guarantees.

Focused tests and a smoke check passed locally. Once that expectation is tightened up, this looks good to merge from my side.

@andreatgretel

Copy link
Copy Markdown
Contributor

Following up on my comment above: the current fix handles the narrow repro from #388 where the caller uses data_designer.logging.configure_logging() before constructing DataDesigner.

There is a related case discussed later in #388 that this does not cover yet: applications that configure logging through stdlib APIs before using Data Designer. For example, if a caller uses logging.basicConfig() or attaches custom root handlers, DataDesigner() can still call DD's default configure_logging(), clear those root handlers, and replace the application's logging setup.

I think the lowest-risk incremental direction is to make DD's automatic runtime logging setup opt-out explicitly, which also matches the direction Mike suggested on #388. Something like:

  • add an auto_configure_logging: bool = True keyword to DataDesigner.__init__
  • pass it into _initialize_interface_runtime(...)
  • only call DD's default configure_logging() when both auto_configure_logging is True and DD logging has not already been configured
  • add tests for:
    • default behavior still configures DD logging
    • DataDesigner(..., auto_configure_logging=False) preserves existing root handlers
    • prior data_designer.logging.configure_logging(...) is still preserved

That should keep standalone behavior unchanged while giving embedded/library users a reliable way to prevent DD from taking over process logging.

schultzjack and others added 2 commits July 4, 2026 13:03
Address review feedback on NVIDIA-NeMo#755 and the embedding case from NVIDIA-NeMo#388:

- document that is_logging_configured() only tracks Data Designer's own
  configure_logging(), not stdlib logging configuration
- add auto_configure_logging keyword to DataDesigner.__init__ so
  applications that manage their own logging (e.g. via
  logging.basicConfig()) can opt out of Data Designer's default
  logging setup
- add tests for the default behavior, the opt-out preserving existing
  root handlers, and prior configure_logging() still being preserved

Signed-off-by: schultzjack <schultzjack@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@schultzjack

Copy link
Copy Markdown
Contributor Author

Thanks for the careful pass, and for spelling out the direction in the follow-up — having the acceptance criteria written out made this straightforward. Both points addressed in 4b5a6b0.

  • is_logging_configured() now has a docstring making the scope explicit: it only tracks DD's own configure_logging(), not stdlib configuration like logging.basicConfig() or manually attached root handlers.
  • DataDesigner.__init__ now accepts auto_configure_logging: bool = True, forwarded to _initialize_interface_runtime(), which only applies the default logging setup when the flag is set and DD logging hasn't already been configured — the shape you outlined, which also matches what Mike suggested on configure_logging() called before DataDesigner() is silently overwritten #388.

Tests cover the three cases you listed: default construction still configures DD logging, auto_configure_logging=False preserves existing root handlers, and a prior configure_logging() call is still preserved.

Also verified both repros from #388 locally: the original MRE (DEBUG level survives DataDesigner() construction) and the stdlib case (logging.basicConfig() formatter and handlers survive DataDesigner(auto_configure_logging=False)).

Branch is synced with main.

@andreatgretel andreatgretel left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the follow-up. The logging configuration scope is now explicit, auto_configure_logging=False provides the requested opt-out, and the default, preconfigured, and opt-out paths are covered. I don't see any remaining blockers. Approving.

@nabinchha nabinchha left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for tackling this, @schultzjack — really clean fix for an annoying footgun, and the coverage (default, preconfigured, and the auto_configure_logging=False opt-out preserving real root handlers) is thorough. The explicit docstring about is_logging_configured() only tracking Data Designer's own configure_logging() is a nice touch.

Approving to unblock merge.

I've filed #805 to track a follow-up cleanup: replacing the _logging_configured / _interface_runtime_initialized module globals with observable state, adding a public reset_logging(), and dropping the private-global patching from these tests (which also makes auto_configure_logging take effect per-instance rather than only on the first construction). That's intentionally scoped out of this PR so we can land the fix now.

@nabinchha nabinchha merged commit 4c2e026 into NVIDIA-NeMo:main Jul 7, 2026
61 checks passed
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.

configure_logging() called before DataDesigner() is silently overwritten

3 participants