Async/Sync Best Practices & Shared Logger Behavior¶
This guide explains LogEverything’s intelligent sync/async compatibility system, shared logger creation, and performance best practices when mixing sync and async logging patterns.
🧠 Understanding Shared Logger Creation¶
When you use a sync Logger with async functions (or vice versa), LogEverything automatically creates shared loggers for optimal performance and compatibility.
How It Works:
At Decoration Time: The
@logdecorator is applied but no shared logger is created yetAt Execution Time: When the decorated function runs, LogEverything detects the async/sync mismatch
Automatic Creation: A shared logger is created with the name pattern
{original_name}_async_sharedor{original_name}_sync_sharedConfiguration Inheritance: The shared logger inherits all settings from the original logger
Registry Management: Both loggers are maintained in the global registry for reuse
Example of Shared Logger Creation:
import asyncio
from logeverything import Logger
from logeverything.decorators import log
from logeverything.core import _active_loggers
# Step 1: Create sync logger
sync_logger = Logger("MyApp")
sync_logger.configure(async_mode=True) # Enable async compatibility
print(f"Initial loggers: {list(_active_loggers.keys())}")
# Step 2: Define async function (no shared logger created yet)
@log(using="MyApp")
async def fetch_data(item_id):
await asyncio.sleep(0.1) # Simulate async I/O
return f"data_{item_id}"
print(f"After decoration: {list(_active_loggers.keys())}")
# Step 3: Execute async function (shared logger created now)
result = await fetch_data(123)
print(f"After execution: {list(_active_loggers.keys())}")
print(f"Result: {result}")
Output:
Initial loggers: ['MyApp']
After decoration: ['MyApp']
2026-05-01 05:38:59 | [ ℹ️ INFO ] | MyApp_async_shared | 🔵 CALL fetch_data(item_id=123) [base_events.py:1936]
2026-05-01 05:38:59 | [ ℹ️ INFO ] | MyApp_async_shared | ✅ DONE fetch_data (100.64ms) ➜ 'data_123'
After execution: ['MyApp', 'MyApp_async_shared']
Result: data_123
- Output Explanation:
Initial:
['MyApp']- Only your sync loggerAfter decoration:
['MyApp']- Still just your sync loggerAfter execution:
['MyApp', 'MyApp_async_shared']- Shared logger created on demand
🔧 Performance Characteristics¶
Understanding the performance implications of different logging patterns:
- Pure Sync Logging (Fastest for sync code):
Logger:
Logger("app")Functions: Sync functions only
Performance: No async overhead, direct logging
Use case: Traditional applications, scripts, sync APIs
- Pure Async Logging (Fastest for async code):
Logger:
AsyncLogger("app")Functions: Async functions primarily
Performance: Optimized for async from the start
Use case: FastAPI apps, async microservices, real-time systems
- Mixed Logging with Shared Creation:
Logger:
Logger("app", async_mode=True)orAsyncLogger("app")Functions: Mix of sync and async
Performance: Slight overhead for shared logger creation
Use case: Applications transitioning to async, mixed workloads
Performance Benchmark Results:
Logger Type | Creation Time | Execution Overhead
-------------------------------|---------------|-------------------
Pure Logger (sync only) | 42ms | None
Pure AsyncLogger (async only) | 5ms | None
Logger + async_mode + mixed | 12ms | ~2-3ms (first call)
Shared logger creation happens once per unique name pattern.
📋 Best Practices¶
Choose the Right Logger Type:
# ✅ For pure sync applications
from logeverything import Logger
log = Logger("sync_app")
# ✅ For pure async applications
from logeverything.asyncio import AsyncLogger
log = AsyncLogger("async_app")
# ✅ For mixed sync/async applications
from logeverything import Logger
log = Logger("mixed_app")
log.configure(async_mode=True) # Enable async compatibility
Timing Best Practices:
# ✅ GOOD: Create loggers before decorators
from logeverything import Logger
logger = Logger("my_app")
logger.configure(async_mode=True)
@log(using="my_app")
async def my_function():
pass
# ❌ AVOID: Decorators before logger creation
@log(using="not_created_yet") # May cause temporary logger creation
async def my_function():
pass
logger = Logger("not_created_yet") # Created too late
Configuration Best Practices:
# ✅ Configure async_mode when you'll use async functions
sync_logger = Logger("app")
sync_logger.configure(
async_mode=True, # Enable async compatibility
level="INFO",
profile="production"
)
# ✅ AsyncLogger is automatically async-ready
async_logger = AsyncLogger("app")
async_logger.configure(
level="INFO",
profile="production"
)
Decorator Usage Best Practices:
# ✅ Use @log for universal compatibility
from logeverything.decorators import log
@log # Automatically selects best available logger
def any_function():
pass
@log(using="specific_logger") # Use specific logger
async def async_function():
pass
🔍 Understanding Auto-Detection Messages¶
When you see messages like “AsyncLogger: Auto-detected environment: script”, here’s what’s happening:
- Why These Messages Appear:
You created a sync Logger with
async_mode=TrueAn async function with
@logdecorator executesLogEverything creates a shared AsyncLogger for async compatibility
The new AsyncLogger prints its auto-configuration messages
- These Messages Are Normal:
✅ They indicate the system is working correctly
✅ They show async compatibility was automatically enabled
✅ They help you understand when shared loggers are created
✅ They provide configuration confirmation for the new logger
Example Output:
2025-08-08 18:01:14 | INFO | logeverything | Auto-detected environment: script
2025-08-08 18:01:14 | INFO | logeverything | Applied optimized default configuration for script environment
2025-08-08 18:01:14 | INFO | logeverything | AsyncLogger: Auto-detected environment: script
2025-08-08 18:01:14 | INFO | logeverything | AsyncLogger: Applied async-optimized configuration for script environment
2025-08-08 18:01:14 | INFO | logeverything | AsyncLogger: Using high-performance async logging
- Message Explanation:
First set: Your original sync Logger auto-configuring
Second set: Shared AsyncLogger being created for your async function
Suppressing Auto-Detection Messages:
If you prefer quieter startup, configure loggers explicitly:
# Suppress auto-detection by explicit configuration
logger = Logger("app", level="INFO", profile="production")
# or
logger.configure(level="INFO", profile="production", suppress_auto_detection=True)
⚡ Migration Strategies¶
Migrating Sync Apps to Async:
# Phase 1: Enable async compatibility
logger = Logger("my_app")
logger.configure(async_mode=True)
# Phase 2: Gradually convert functions to async
@log(using="my_app")
async def new_async_function():
pass
@log(using="my_app") # Still works with same logger
def existing_sync_function():
pass
# Phase 3: Eventually migrate to AsyncLogger for best performance
# async_logger = AsyncLogger("my_app") # When fully async
Migrating from Other Loggers:
# From stdlib logging
import logging
old_logger = logging.getLogger("app")
# To LogEverything (preserves existing functionality)
from logeverything import Logger
new_logger = Logger("app", level="INFO")
# Gradual decorator adoption
@log(using="app") # Add decorators incrementally
def enhanced_function():
pass
🛠 Troubleshooting¶
Common Issues and Solutions:
Issue: “Decorator requested logger ‘xyz’ but it was not found”
# ❌ Problem: Logger created after decorator
@log(using="my_app")
def my_function():
pass
my_logger = Logger("my_app") # Too late!
# ✅ Solution: Create logger first
my_logger = Logger("my_app")
@log(using="my_app")
def my_function():
pass
Issue: Unexpected AsyncLogger messages
# ❌ You see AsyncLogger messages but only created sync Logger
# This is normal! It means shared AsyncLogger was created for async functions
# ✅ This is expected behavior when mixing sync/async
sync_logger = Logger("app", async_mode=True)
@log(using="app")
async def async_func(): # Triggers shared AsyncLogger creation
pass
Issue: Performance slower than expected
# ❌ Frequent shared logger creation
for i in range(1000):
new_logger = Logger(f"temp_{i}", async_mode=True)
# Creates many shared loggers
# ✅ Reuse loggers
app_logger = Logger("app", async_mode=True)
@log(using="app") # Single shared logger created once
async def process_item(item):
pass
📊 Monitoring Shared Logger Creation¶
You can monitor shared logger creation in your applications:
from logeverything.core import _active_loggers
# Monitor logger registry
print("Active loggers:", list(_active_loggers.keys()))
# After async function execution, you might see:
# ['my_app', 'my_app_async_shared']
Logger Registry Inspection:
from logeverything.core import _active_loggers
def inspect_loggers():
for name, logger in _active_loggers.items():
logger_type = type(logger).__name__
print(f"Logger '{name}': {logger_type}")
# Example output:
# Logger 'my_app': Logger
# Logger 'my_app_async_shared': AsyncLogger
This shared logger system ensures optimal performance while maintaining complete compatibility between sync and async code patterns.