Utilities¶
Structured logging configuration and filesystem path validation helpers.
Logging¶
Structured logging utilities for the heterodyne package.
Provides a lightweight but flexible logging system that matches the CMC reimplementation requirements: contextual log prefixes, configurable console and rotating file handlers, and helpers for performance monitoring.
- class heterodyne.utils.logging.LogConfiguration[source]
Bases:
objectProgrammatic logging configuration.
Alternative to
configure_logging()for programmatic control over logging settings.- console_level
Console log level (default “INFO”).
- console_format
Console format (“simple” or “detailed”).
- console_colors
Enable ANSI colors in console (default False).
- file_enabled
Enable file logging (default True).
- file_path
Log file path (None = auto-generate).
- file_level
File log level (default “DEBUG”).
- file_format
File format (“simple” or “detailed”).
- file_rotation_mb
Max file size before rotation (default 10).
- file_backup_count
Number of backup files to keep (default 5).
- module_overrides
Per-module log level overrides.
Example
>>> config = LogConfiguration.from_cli_args(verbose=True, log_file="analysis.log") >>> config.apply()
>>> config = LogConfiguration( ... console_level="INFO", ... file_level="DEBUG", ... module_overrides={"jax": "WARNING", "heterodyne.optimization": "DEBUG"} ... ) >>> config.apply()
- console_level: str = 'INFO'
- console_format: str = 'simple'
- console_colors: bool = False
- file_enabled: bool = True
- file_level: str = 'DEBUG'
- file_format: str = 'detailed'
- file_rotation_mb: int = 10
- file_backup_count: int = 5
- apply()[source]
Apply this configuration to the logging system.
- classmethod from_dict(config)[source]
Create configuration from dictionary.
- classmethod from_cli_args(verbose=False, quiet=False, log_file=None)[source]
Create configuration from CLI flags.
- __init__(console_level='INFO', console_format='simple', console_colors=False, file_enabled=True, file_path=None, file_level='DEBUG', file_format='detailed', file_rotation_mb=10, file_backup_count=5, module_overrides=<factory>)
- class heterodyne.utils.logging.AnalysisSummaryLogger[source]
Bases:
objectStructured logging for analysis completion summaries.
Tracks phase timings, metrics, output files, and convergence status for logging a structured summary at analysis completion.
Example
>>> summary = AnalysisSummaryLogger(run_id="analysis_001", analysis_mode="two_component") >>> summary.start_phase("loading") >>> data = load_data(config) >>> summary.end_phase("loading", memory_peak_gb=2.1) >>> summary.record_metric("chi_squared", result.chi_squared) >>> summary.set_convergence_status("converged") >>> summary.log_summary(logger)
- __init__(run_id, analysis_mode)[source]
Initialize summary logger for an analysis run.
- start_phase(name)[source]
Mark phase start for timing.
- end_phase(name, memory_peak_gb=None)[source]
Mark phase completion.
- record_metric(name, value)[source]
Record a named metric (e.g., chi_squared).
- add_output_file(path)[source]
Record an output file path.
- set_convergence_status(status)[source]
Set final convergence status (failure is sticky).
Once a failure status (“failed”, “not_converged”, “max_iter”) has been recorded, subsequent calls with a non-failure status are ignored. Failure-to-failure transitions and success-to-failure transitions are always allowed.
- set_config_summary(optimizer=None, n_params=None, n_data_points=None, n_phi_angles=None, data_file=None, **kwargs)[source]
Set configuration summary for logging.
- log_summary(logger)[source]
Log the complete analysis summary.
- Parameters:
logger (
Logger|LoggerAdapter[Any]) – Logger to use for output.- Return type:
- class heterodyne.utils.logging.MinimalLogger[source]
Bases:
objectConfigurable logger manager for the heterodyne package.
Thread-safe singleton for managing heterodyne logging configuration.
- static __new__(cls)[source]
- Return type:
MinimalLogger
- __init__()[source]
- configure(level='INFO', *, console_level=None, console_format='detailed', console_colors=False, file_path=None, file_level=None, max_size_mb=10, backup_count=5, module_levels=None, force=False)[source]
Configure heterodyne logging.
Thread-safe configuration of the logging system.
- Parameters:
console_level (
str|int|None) – Console handler level. PassFalseto suppress the console handler entirely.console_format (
str) – “simple” or “detailed”.console_colors (
bool) – Enable ANSI color output on the console.file_path (
str|Path|None) – Path to log file.Nonedisables file logging.file_level (
str|int|None) – File handler level. PassFalseto suppress the file handler even when file_path is set.max_size_mb (
int) – Rotating log file maximum size in MB.backup_count (
int) – Number of backup files to keep.module_levels (
Mapping[str,str|int] |None) – Per-module level overrides.force (
bool) – Remove existing managed handlers before reconfiguring.
- Return type:
- Returns:
Path to the log file if a file handler was created, else
None.
- configure_from_dict(logging_config, *, verbose=False, quiet=False, output_dir=None, run_id=None)[source]
Configure logging from a
logging:config section.- Parameters:
logging_config (
Mapping[str,Any] |None) – Mapping with logging configuration (may beNoneor containenabled: falseto skip configuration).verbose (
bool) – Override console level to DEBUG.quiet (
bool) – Override console level to ERROR.output_dir (
Path|str|None) – Base directory for auto-generated log file paths.run_id (
str|None) – Run identifier used for auto-generated log filenames.
- Return type:
- Returns:
Path to the log file if a file handler was created, else
None.
- heterodyne.utils.logging.get_logger(name=None, *, context=None)[source]
Get a logger instance with automatic naming and optional context.
Delegates to
logging.getLoggerviaMinimalLogger, which caches logger instances by name. Callers that previously used:get_logger("heterodyne")
or:
get_logger(__name__)
continue to work unchanged. The optional context keyword adds structured key-value prefixes to every message emitted through the returned adapter.
- Parameters:
- Return type:
- Returns:
A plain
logging.Loggerwhen context isNone, or a_ContextAdapterthat prepends[key=value ...]to every message when context is provided.
- heterodyne.utils.logging.configure_logging(level='INFO', log_file=None, format_string=None)[source]
Configure logging for the heterodyne package.
Simple interface for configuring console (and optionally file) logging. For richer control — rotating files, per-module overrides, CLI flag integration — use
LogConfigurationinstead.
- heterodyne.utils.logging.with_context(logger, **context)[source]
Create a contextual logger with key-value prefixes.
Context is formatted as
[key=value key2=value2] message. Nested calls merge contexts (inner overrides outer on key conflicts). Thread-safe for use in multiprocessing.- Parameters:
logger (
Logger|LoggerAdapter[Logger]) – Base logger or existing contextual adapter to wrap.**context (
Any) – Key-value pairs to include as prefix.
- Return type:
- Returns:
A logger adapter that prefixes all messages with context.
Example
>>> logger = get_logger(__name__) >>> ctx_logger = with_context(logger, run_id="abc123", mode="two_component") >>> ctx_logger.info("Starting analysis") # Output: [run_id=abc123 mode=two_component] Starting analysis
>>> # Nested context >>> shard_logger = with_context(ctx_logger, q_bin=5) >>> shard_logger.info("Processing bin") # Output: [run_id=abc123 mode=two_component q_bin=5] Processing bin
- class heterodyne.utils.logging.PhaseContext[source]
Bases:
objectContext object returned by
log_phase()with timing and memory info.- name: str
- duration: float = 0.0
- __init__(name, duration=0.0, memory_peak_gb=None, memory_delta_gb=None)
- heterodyne.utils.logging.log_phase(name, logger=None, level=20, track_memory=False, threshold_s=0.0)[source]
Context manager for phase-level timing with optional memory tracking.
- Parameters:
name (
str) – Phase name for logging.logger (
Logger|LoggerAdapter[Logger] |None) – Logger to use. IfNone, uses the caller’s module logger.level (
int) – Log level for phase messages.track_memory (
bool) – Track memory usage during phase.threshold_s (
float) – Only log if duration > threshold (0 = always log).
- Yields:
PhaseContextwithname,duration,memory_peak_gb, andmemory_delta_gb. Duration and memory values are populated after the context exits.
Example
>>> with log_phase("optimization", track_memory=True) as phase: ... result = run_optimization(data) >>> print(f"Took {phase.duration:.1f}s") # Logs: Phase 'optimization' completed in 45.3s (peak memory: 12.4 GB)
- heterodyne.utils.logging.log_exception(logger, exc, context=None, level=logging.ERROR, include_traceback=True)[source]
Log an exception with full context for debugging.
Extracts module, function, and line number from exception traceback. Formats context as key-value pairs in the message.
- Parameters:
logger (
Logger|LoggerAdapter[Logger]) – Logger to use.exc (
BaseException) – Exception to log.context (
dict[str,Any] |None) – Additional context (e.g., parameter values).level (
int) – Log level (default ERROR).include_traceback (
bool) – Include full traceback (default True).
- Return type:
Example
>>> try: ... result = compute_jacobian(params) ... except ValueError as e: ... log_exception(logger, e, context={ ... "iteration": 45, ... "params": params.tolist()[:5] ... }) ... raise # Logs: # ERROR | heterodyne.optimization.nlsq.core | Exception in compute_jacobian: # ValueError: invalid value # Context: iteration=45, params=[1.2e-11, 0.85, ...] # Traceback (most recent call last): # ...
- heterodyne.utils.logging.log_calls(logger=None, level=logging.DEBUG, include_args=False, include_result=False)[source]
Decorator to log function calls.
- heterodyne.utils.logging.log_performance(logger=None, level=logging.INFO, threshold=0.1)[source]
Decorator to log function performance.
Only emits a log record when the function’s wall-clock time meets or exceeds threshold seconds.
- heterodyne.utils.logging.log_operation(operation_name, logger=None, level=20)[source]
Context manager for logging named operations with timing.
- Parameters:
- Yields:
The resolved logger so callers can emit additional messages inside the
withblock without holding a separate reference.
Example
>>> with log_operation("jacobian computation") as log: ... J = compute_jacobian(params) ... log.debug("Jacobian shape: %s", J.shape) # Logs: Starting operation: jacobian computation # Logs: Completed operation: jacobian computation in 0.042s
- class heterodyne.utils.logging.ConvergenceLogger[source]
Bases:
objectStructured logger for optimization convergence diagnostics.
- __init__(logger=None)[source]
- log_iteration(iteration, loss, gradient_norm=None, step_size=None)[source]
Log optimization iteration metrics.
- Return type:
- log_diagnostic(metric_name, value, threshold, higher_is_better=True)[source]
Log diagnostic metric with pass/fail status.
Path Validation¶
Path validation and filesystem utilities.
- exception heterodyne.utils.path_validation.PathValidationError[source]
Bases:
ExceptionRaised when path validation fails.
- heterodyne.utils.path_validation.resolve_path(path)[source]
Resolve path to absolute, expanding user and symlinks.
- heterodyne.utils.path_validation.validate_file_exists(path, description='File')[source]
Validate that a file exists and is readable.
- heterodyne.utils.path_validation.validate_output_path(path, create_parents=True)[source]
Validate and prepare output path.