Logging Tutorial¶
Configure and use ToolUniverse’s comprehensive logging system
ToolUniverse provides a robust logging system to help you monitor tool execution, debug issues, and track research workflows.
Logging Overview
ToolUniverse uses Python’s standard logging module with enhanced formatting and emoji indicators. The logging system provides:
5 Standard Log Levels: DEBUG, INFO, WARNING, ERROR, CRITICAL
1 Custom Level: PROGRESS (for workflow tracking)
Emoji Indicators: Visual cues for different message types
Flexible Configuration: Environment variables, programmatic setup, and per-instance control
Log Levels Explained
Understanding when to use each log level:
DEBUG Level¶
When to use: Detailed diagnostic information for troubleshooting
Tool loading details
API request/response data
Internal state changes
Parameter validation steps
from tooluniverse import ToolUniverse
from tooluniverse.logging_config import setup_logging
# Enable DEBUG logging
setup_logging('DEBUG')
tu = ToolUniverse()
tu.load_tools() # Will show detailed tool loading information
Example output:
🔍 DEBUG: Tool files:
🔍 DEBUG: {
"opentargets": "/path/to/opentarget_tools.json",
"pubchem": "/path/to/pubchem_tools.json"
}
INFO Level (Default)¶
When to use: General information about normal operations
Tool registration confirmations
Workflow progress updates
Configuration changes
Successful operations
# INFO is the default level
tu = ToolUniverse()
tu.register_custom_tool(MyCustomTool, "my_tool")
# Output: ℹ️ INFO: Custom tool 'my_tool' registered successfully!
Example output:
ℹ️ INFO: Loading 245 tools from 12 categories
ℹ️ INFO: Custom tool 'protein_analyzer' registered successfully!
PROGRESS Level (Custom)¶
When to use: Track progress of long-running workflows
Multi-step research pipelines
Batch processing operations
Data collection workflows
from tooluniverse.logging_config import get_logger
logger = get_logger('workflow')
def drug_discovery_pipeline(compounds):
logger.progress(f"Starting analysis of {len(compounds)} compounds")
for i, compound in enumerate(compounds):
logger.progress(f"Processing compound {i+1}/{len(compounds)}: {compound}")
# ... processing logic ...
logger.progress("Drug discovery pipeline completed")
Example output:
📈 PROGRESS: Starting analysis of 150 compounds
📈 PROGRESS: Processing compound 1/150: aspirin
📈 PROGRESS: Processing compound 2/150: ibuprofen
WARNING Level¶
When to use: Something unexpected happened but execution continues
API rate limiting
Deprecated tool usage
Missing optional parameters
Fallback operations
# Warnings appear automatically when issues occur
result = tu.run({
"name": "NonExistentTool", # This will trigger a warning
"arguments": {"query": "test"}
})
Example output:
⚠️ WARNING: Tool 'NonExistentTool' not found, similar tools: ['ExistingTool1', 'ExistingTool2']
ERROR Level¶
When to use: Serious problems that prevent specific operations
Tool execution failures
API authentication errors
Invalid parameters
Network timeouts
Example output:
❌ ERROR: Failed to execute tool 'PubChem_search': Invalid API key
❌ ERROR: OpenTargets query timeout after 30 seconds
CRITICAL Level¶
When to use: Very serious errors that might stop the program
System-level failures
Configuration corruption
Resource exhaustion
Example output:
🚨 CRITICAL: Failed to initialize ToolUniverse: Configuration file corrupted
️ Configuration Methods¶
Method 1: Environment Variable (Recommended)¶
Set the log level before starting your application:
# Linux/macOS
export TOOLUNIVERSE_LOG_LEVEL=DEBUG
python your_script.py
# Windows Command Prompt
set TOOLUNIVERSE_LOG_LEVEL=DEBUG
python your_script.py
# Windows PowerShell
$env:TOOLUNIVERSE_LOG_LEVEL="DEBUG"
python your_script.py
# Your script will automatically use the environment setting
from tooluniverse import ToolUniverse
tu = ToolUniverse() # Uses DEBUG level from environment
Method 2: Global Configuration¶
Set logging level programmatically for all ToolUniverse operations:
from tooluniverse.logging_config import setup_logging
from tooluniverse import ToolUniverse
# Configure logging before creating ToolUniverse instances
setup_logging('WARNING') # Only show warnings and errors
tu = ToolUniverse()
tu.load_tools() # Will use WARNING level
Method 3: Per-Instance Configuration¶
Set different log levels for different ToolUniverse instances:
from tooluniverse import ToolUniverse
# Create instances with different log levels
debug_tu = ToolUniverse(log_level='DEBUG') # Detailed logging
quiet_tu = ToolUniverse(log_level='ERROR') # Only errors
# Default instance uses global/environment setting
default_tu = ToolUniverse()
Method 4: Dynamic Level Changes¶
Change log level during execution:
from tooluniverse.logging_config import set_log_level
from tooluniverse import ToolUniverse
tu = ToolUniverse()
# Start with minimal logging
set_log_level('ERROR')
tu.load_tools() # Quiet loading
# Enable verbose logging for debugging
set_log_level('DEBUG')
result = tu.run({"name": "PubChem_search", "arguments": {"query": "aspirin"}})
# Return to normal logging
set_log_level('INFO')
️ Custom Logging in Your Code¶
Using ToolUniverse Logger in Your Scripts¶
from tooluniverse.logging_config import get_logger
# Get a logger for your module
logger = get_logger('my_research_script')
def analyze_compounds(compound_list):
logger.info(f"Starting analysis of {len(compound_list)} compounds")
results = []
for i, compound in enumerate(compound_list):
logger.debug(f"Processing {compound}")
try:
# Your analysis logic here
result = perform_analysis(compound)
results.append(result)
logger.progress(f"Completed {i+1}/{len(compound_list)} compounds")
except Exception as e:
logger.error(f"Failed to analyze {compound}: {e}")
continue
logger.info(f"Analysis completed. {len(results)} successful results")
return results
Convenience Functions¶
ToolUniverse provides convenience functions for quick logging:
from tooluniverse.logging_config import debug, info, warning, error, critical, progress
# Quick logging without creating logger instances
info("Starting research workflow")
debug("Loading configuration from file")
progress("Processing 25% complete")
warning("API rate limit approaching")
error("Tool execution failed")
critical("System resources exhausted")
Common Logging Patterns
Research Workflow Logging¶
from tooluniverse import ToolUniverse
from tooluniverse.logging_config import get_logger, setup_logging
# Setup logging for research workflow
setup_logging('INFO')
logger = get_logger('drug_discovery')
def drug_discovery_workflow(target_disease):
logger.info(f"🎯 Starting drug discovery for: {target_disease}")
tu = ToolUniverse()
tu.load_tools()
# Step 1: Find disease targets
logger.progress("Step 1: Identifying disease targets")
targets_query = {
"name": "OpenTargets_get_associated_targets_by_disease_name",
"arguments": {"diseaseName": target_disease, "limit": 10}
}
try:
targets = tu.run(targets_query)
logger.info(f"✅ Found {len(targets.get('data', []))} targets")
except Exception as e:
logger.error(f"❌ Target identification failed: {e}")
return None
# Step 2: Find compounds
logger.progress("Step 2: Searching for compounds")
# ... more workflow steps ...
logger.info("🎉 Drug discovery workflow completed successfully")
return results
Debugging Failed Tools¶
from tooluniverse import ToolUniverse
from tooluniverse.logging_config import setup_logging
# Enable debug logging to diagnose tool issues
setup_logging('DEBUG')
tu = ToolUniverse()
tu.load_tools()
# Debug a failing query
problematic_query = {
"name": "PubChem_get_compound_info",
"arguments": {"compound_name": "invalid_compound_name"}
}
try:
result = tu.run(problematic_query)
except Exception as e:
# Debug logs will show detailed error information
print(f"Query failed: {e}")
Batch Processing with Progress Tracking¶
from tooluniverse import ToolUniverse
from tooluniverse.logging_config import get_logger
logger = get_logger('batch_processor')
tu = ToolUniverse()
tu.load_tools()
def process_compound_batch(compounds):
logger.info(f"🚀 Starting batch processing of {len(compounds)} compounds")
results = []
errors = []
for i, compound in enumerate(compounds):
logger.progress(f"Processing {i+1}/{len(compounds)}: {compound}")
query = {
"name": "PubChem_get_compound_info",
"arguments": {"compound_name": compound}
}
try:
result = tu.run(query)
results.append(result)
logger.debug(f"✅ Successfully processed {compound}")
except Exception as e:
errors.append((compound, str(e)))
logger.warning(f"⚠️ Failed to process {compound}: {e}")
logger.info(f"📊 Batch complete: {len(results)} successful, {len(errors)} failed")
return results, errors
Best Practices
Choose Appropriate Log Levels¶
Production: Use INFO or WARNING to reduce noise
Development: Use DEBUG for detailed diagnostics
CI/CD: Use ERROR to only show failures
Demonstrations: Use PROGRESS to show workflow steps
import os
from tooluniverse.logging_config import setup_logging
# Environment-based logging configuration
if os.getenv('ENVIRONMENT') == 'production':
setup_logging('WARNING')
elif os.getenv('ENVIRONMENT') == 'development':
setup_logging('DEBUG')
else:
setup_logging('INFO') # Default
Structure Your Log Messages¶
Use consistent formatting for better readability:
logger = get_logger('workflow')
# Good: Clear, actionable messages
logger.info("🎯 Starting protein analysis workflow")
logger.progress("Step 1/5: Loading protein sequences")
logger.error("❌ PubChem API returned 404 for compound 'xyz123'")
# Avoid: Vague or uninformative messages
logger.info("Processing") # Too vague
logger.debug("Data: " + str(huge_dict)) # Too much data
Log Important Context¶
Include relevant details for debugging:
logger = get_logger('tool_executor')
def execute_tool_safely(tool_name, arguments):
logger.debug(f"Executing tool: {tool_name}")
logger.debug(f"Arguments: {arguments}")
try:
result = tu.run({"name": tool_name, "arguments": arguments})
logger.info(f"✅ Tool '{tool_name}' completed successfully")
return result
except Exception as e:
logger.error(f"❌ Tool '{tool_name}' failed: {e}")
logger.debug(f"Full traceback:", exc_info=True) # Include stack trace in debug
raise
Troubleshooting Logging
Common Issues and Solutions¶
Issue: No log messages appearing
# Solution: Check if logging is configured
from tooluniverse.logging_config import setup_logging
setup_logging('DEBUG') # Ensure logging is enabled
Issue: Too many log messages
# Solution: Increase log level to reduce verbosity
from tooluniverse.logging_config import set_log_level
set_log_level('WARNING') # Only warnings and errors
Issue: Log messages not showing emoji
# This is normal in some terminals. The logging still works,
# just without emoji indicators. No action needed.
Issue: Want to disable all logging
# Solution: Set to CRITICAL level
from tooluniverse.logging_config import setup_logging
setup_logging('CRITICAL')
Check Current Log Level¶
from tooluniverse.logging_config import get_logger
logger = get_logger()
current_level = logger.getEffectiveLevel()
print(f"Current log level: {current_level}")
# Level numbers: DEBUG=10, INFO=20, PROGRESS=25, WARNING=30, ERROR=40, CRITICAL=50
Log Output Examples
Here’s what different log levels look like in practice:
🔍 DEBUG: Tool files loaded from: /path/to/tools/
🔍 DEBUG: Validating parameters for PubChem_search
ℹ️ INFO: Loading 245 tools from 12 categories
📈 PROGRESS: Processing compound 15/100: caffeine
⚠️ WARNING: API rate limit reached, waiting 2 seconds
❌ ERROR: OpenTargets query failed: Connection timeout
🚨 CRITICAL: Failed to initialize tool registry
Next Steps
Now that you understand ToolUniverse logging:
** Workflows** → Scientific Workflows - Apply logging to scientific workflows
** Extend Tools** → Navigation - Add logging to custom tools
** Troubleshooting** → Troubleshooting Tutorial - Debug common logging issues
Tip
Pro tip: Start with INFO level for normal use, switch to DEBUG when investigating issues, and use PROGRESS for long-running workflows to track execution progress!