Skip to main content
All mySpellChecker modules log under the myspellchecker namespace. Call configure_logging() once at application startup to control log level, output format (human-readable or JSON), and destination.

Overview

The logging system provides:
  • Centralized configuration for all myspellchecker modules
  • Consistent logger naming under the myspellchecker namespace
  • Support for development (verbose) and production (JSON) modes
  • Thread-safe logger caching
  • Mixin class for easy integration

Basic Configuration

Configure logging once at the start of your application:
from myspellchecker.utils.logging_utils import configure_logging

# Simple setup (outputs to stderr)
configure_logging(level="INFO")

Configuration Options

configure_logging supports several parameters:
ParameterTypeDefaultDescription
levelstr/intINFOLog level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
json_outputboolFalseOutput as JSON for log aggregators
debug_modeboolFalseVerbose format with timestamps and line numbers
format_stringstrNoneCustom format string (overrides other format options)
streamTextIOstderrOutput stream

Format Strings

The logging system uses different formats based on configuration:
# Default format
"%(levelname)s: %(name)s: %(message)s"

# Debug format (debug_mode=True)
"%(asctime)s - %(levelname)s - %(name)s:%(lineno)d - %(message)s"

# JSON format (json_output=True)
'{"time": "%(asctime)s", "level": "%(levelname)s", "logger": "%(name)s", "message": "%(message)s"}'

# Simple format (custom)
"%(message)s"

Examples

Development Mode:
configure_logging(level="DEBUG", debug_mode=True)
# Output: 2023-10-27 10:00:00 - DEBUG - myspellchecker.core.spellchecker:45 - Loaded 5000 words
Production Mode:
configure_logging(level="INFO", json_output=True)
# Output: {"time": "...", "level": "INFO", "logger": "myspellchecker.core", "message": "Ready"}
Simple Build Output:
configure_logging(level="INFO", format_string="%(message)s")
# Output: Building dictionary...

Using Loggers in Your Code

If you are extending mySpellChecker or writing custom components, use the logging utility:
from myspellchecker.utils.logging_utils import get_logger

logger = get_logger(__name__)

def my_custom_function():
    logger.debug("Starting processing")
    try:
        # ...
        logger.info("Success")
    except Exception as e:
        logger.error(f"Failed: {e}")

Logger Naming

get_logger() ensures consistent naming under the myspellchecker namespace:
# In src/myspellchecker/algorithms/symspell.py
logger = get_logger(__name__)
# Returns logger named "myspellchecker.algorithms.symspell"

# With short name
logger = get_logger("custom")
# Returns logger named "myspellchecker.custom"

Logger Caching

Loggers are cached for efficiency (LRU cache with 256 entries):
# These return the same logger instance
logger1 = get_logger(__name__)
logger2 = get_logger(__name__)
assert logger1 is logger2  # True

LoggerMixin

For classes, use LoggerMixin to automatically get a correctly named logger:
from myspellchecker.utils.logging_utils import LoggerMixin

class MyCustomProvider(LoggerMixin):
    def connect(self):
        # Logger name: "myspellchecker.module.MyCustomProvider"
        self.logger.info("Connecting to database...")

    def query(self, text):
        self.logger.debug(f"Querying: {text}")
The logger is lazily initialized on first access and named based on the class’s module and name.

Setting Log Levels

Package-Wide Level

from myspellchecker.utils.logging_utils import set_log_level

# Set package-wide level
set_log_level("DEBUG")

Module-Specific Level

# Silence a noisy module
set_log_level("WARNING", "myspellchecker.algorithms.symspell")

# Enable debug for specific module
set_log_level("DEBUG", "myspellchecker.core.validators")

Package Logger Access

Get the root logger for advanced configuration:
from myspellchecker.utils.logging_utils import get_package_logger

# Get root myspellchecker logger
root_logger = get_package_logger()

# Add custom handler
import logging
file_handler = logging.FileHandler("spellchecker.log")
root_logger.addHandler(file_handler)

# Add filter
class SensitiveFilter(logging.Filter):
    def filter(self, record):
        # Filter out sensitive info
        return "password" not in record.getMessage()

root_logger.addFilter(SensitiveFilter())

Integration with Standard Logging

The logging utilities are compatible with Python’s standard logging:
import logging

# Standard pattern still works
logger = logging.getLogger("myspellchecker.mymodule")
logger.info("This works too")

# Configure via standard logging
logging.getLogger("myspellchecker").setLevel(logging.DEBUG)

CLI Logging

The CLI automatically configures logging:
# Normal output
myspellchecker check input.txt

# Verbose output
myspellchecker check input.txt --verbose

# Debug output (same as verbose)
myspellchecker check input.txt -v

Best Practices

  1. Configure once: Call configure_logging() once at application startup
  2. Use get_logger: Always use get_logger(__name__) for consistent naming
  3. Log levels: Use appropriate levels (DEBUG for development, INFO+ for production)
  4. Structured logging: Use JSON output in production for log aggregators
  5. Don’t log sensitive data: Be careful with user input in log messages

Troubleshooting

Duplicate Logs

If you see duplicate logs, ensure:
  1. configure_logging() is called only once
  2. You’re not adding handlers manually without removing existing ones
# Reset handlers
import logging
logger = logging.getLogger("myspellchecker")
logger.handlers.clear()
configure_logging(level="INFO")

No Output

If logs aren’t appearing:
  1. Check the log level
  2. Verify configure_logging() was called
  3. Check the output stream
import sys
configure_logging(level="DEBUG", stream=sys.stdout)

See Also