Skip to content

Refactor logging initialization to avoid module-level side effects #10

@coderabbitai

Description

@coderabbitai

Context

Currently, llmcord/logging_utils_.py executes setup_logging() and creates a RequestLogger() instance at module import time. This can interfere with test fixtures that need different logging configurations.

Related PR: #9
Related Comment: #9 (comment)


Refactoring Plan

Step 1: Remove immediate initialization

Remove these lines from the bottom of llmcord/logging_utils_.py:

# Remove these lines:
setup_logging()
request_logger = RequestLogger()

Step 2: Add a module-level variable to track initialization

_initialized = False
_request_logger: RequestLogger | None = None

Step 3: Create a lazy initialization function

def initialize_logging(*, force: bool = False) -> RequestLogger:
    """Initialize logging configuration and request logger.
    
    :param force: Force re-initialization even if already initialized.
    :return: The initialized RequestLogger instance.
    """
    global _initialized, _request_logger
    
    if _initialized and not force:
        return _request_logger  # type: ignore[return-value]
    
    setup_logging()
    _request_logger = RequestLogger()
    _initialized = True
    
    return _request_logger

Step 4: Add a getter function for request_logger

def get_request_logger() -> RequestLogger:
    """Get the request logger, initializing if necessary.
    
    :return: The RequestLogger instance.
    """
    if _request_logger is None:
        initialize_logging()
    return _request_logger

Step 5: Update usage sites

In your main entry point (llmcord/__main__.py or llmcord/bot.py), explicitly call:

from llmcord.logging_utils_ import initialize_logging

# Early in your main() or bot startup:
initialize_logging()

Then replace direct request_logger usage with:

from llmcord.logging_utils_ import get_request_logger

# When you need to log:
get_request_logger().log(payload)

Step 6: In tests, skip or mock initialization

# In your test setup:
from llmcord import logging_utils_

# Option A: Skip initialization entirely
logging_utils_._initialized = True

# Option B: Use a mock logger
logging_utils_._request_logger = Mock(spec=RequestLogger)
logging_utils_._initialized = True

# Option C: Initialize with test-specific settings
with patch('llmcord.logging_utils_.LOG_DIR', tmp_path):
    initialize_logging(force=True)

Benefits

  • Tests can control logging initialization
  • No side effects at import time
  • Better testability and flexibility
  • Maintains convenience for production code

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions