add log mode and log level, support for logging to files in .ra-aid/logs/
This commit is contained in:
parent
f89d40527d
commit
09bd7cbf4b
12
README.md
12
README.md
|
|
@ -182,8 +182,11 @@ ra-aid -m "Your task or query here"
|
|||
# Research-only mode (no implementation)
|
||||
ra-aid -m "Explain the authentication flow" --research-only
|
||||
|
||||
# Enable verbose logging for detailed execution information
|
||||
ra-aid -m "Add new feature" --verbose
|
||||
# File logging with console warnings (default mode)
|
||||
ra-aid -m "Add new feature" --log-mode file
|
||||
|
||||
# Console-only logging with detailed output
|
||||
ra-aid -m "Add new feature" --log-mode console
|
||||
```
|
||||
|
||||
More information is available in our [Usage Examples](https://docs.ra-aid.ai/category/usage).
|
||||
|
|
@ -203,7 +206,10 @@ More information is available in our [Usage Examples](https://docs.ra-aid.ai/cat
|
|||
- `--expert-model`: The model name to use for expert knowledge queries (required for non-OpenAI providers)
|
||||
- `--hil, -H`: Enable human-in-the-loop mode for interactive assistance during task execution
|
||||
- `--chat`: Enable chat mode with direct human interaction (implies --hil)
|
||||
- `--verbose`: Enable verbose logging output
|
||||
- `--log-mode`: Logging mode (choices: file, console)
|
||||
- `file`: Logs to both file and console (only warnings+ to console)
|
||||
- `console`: Logs to console only at the specified log level
|
||||
- `--log-level`: Set specific logging level (debug, info, warning, error, critical)
|
||||
- `--experimental-fallback-handler`: Enable experimental fallback handler to attempt to fix too calls when the same tool fails 3 times consecutively. (OPENAI_API_KEY recommended as openai has the top 5 tool calling models.) See `ra_aid/tool_leaderboard.py` for more info.
|
||||
- `--pretty-logger`: Enables panel markdown formatted logger messages for debugging purposes.
|
||||
- `--temperature`: LLM temperature (0.0-2.0) to control randomness in responses
|
||||
|
|
|
|||
|
|
@ -1,58 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Create initial database migration script.
|
||||
|
||||
This script creates a baseline migration representing the current database schema.
|
||||
It serves as the foundation for future schema changes.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Add the project root to the Python path
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
from ra_aid.database import DatabaseManager, create_new_migration
|
||||
from ra_aid.logging_config import get_logger, setup_logging
|
||||
|
||||
# Set up logging
|
||||
setup_logging(verbose=True)
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
def create_initial_migration():
|
||||
"""
|
||||
Create the initial migration for the current database schema.
|
||||
|
||||
Returns:
|
||||
bool: True if migration was created successfully, False otherwise
|
||||
"""
|
||||
try:
|
||||
with DatabaseManager() as db:
|
||||
# Create a descriptive name for the initial migration
|
||||
migration_name = "initial_schema"
|
||||
|
||||
# Create the migration
|
||||
logger.info(f"Creating initial migration '{migration_name}'...")
|
||||
result = create_new_migration(migration_name, auto=True)
|
||||
|
||||
if result:
|
||||
logger.info(f"Successfully created initial migration: {result}")
|
||||
print(f"✅ Initial migration created successfully: {result}")
|
||||
return True
|
||||
else:
|
||||
logger.error("Failed to create initial migration")
|
||||
print("❌ Failed to create initial migration")
|
||||
return False
|
||||
except Exception as e:
|
||||
logger.error(f"Error creating initial migration: {str(e)}")
|
||||
print(f"❌ Error creating initial migration: {str(e)}")
|
||||
return False
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Creating initial database migration...")
|
||||
success = create_initial_migration()
|
||||
|
||||
# Exit with appropriate code
|
||||
sys.exit(0 if success else 1)
|
||||
|
|
@ -1,9 +1,28 @@
|
|||
import argparse
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
|
||||
# Add litellm import
|
||||
import litellm
|
||||
|
||||
# Configure litellm to suppress debug logs
|
||||
os.environ["LITELLM_LOG"] = "ERROR"
|
||||
litellm.suppress_debug_info = True
|
||||
litellm.set_verbose = False
|
||||
|
||||
# Explicitly configure LiteLLM's loggers
|
||||
for logger_name in ["litellm", "LiteLLM"]:
|
||||
litellm_logger = logging.getLogger(logger_name)
|
||||
litellm_logger.setLevel(logging.WARNING)
|
||||
litellm_logger.propagate = True
|
||||
|
||||
# Use litellm's internal method to disable debugging
|
||||
if hasattr(litellm, "_logging") and hasattr(litellm._logging, "_disable_debugging"):
|
||||
litellm._logging._disable_debugging()
|
||||
|
||||
from langgraph.checkpoint.memory import MemorySaver
|
||||
from rich.console import Console
|
||||
from rich.panel import Panel
|
||||
|
|
@ -152,7 +171,8 @@ Examples:
|
|||
help="Enable chat mode with direct human interaction (implies --hil)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--verbose", action="store_true", help="Enable verbose logging output"
|
||||
"--log-mode", choices=["console", "file"], default="file",
|
||||
help="Logging mode: 'console' shows all logs in console, 'file' logs to file with only warnings+ in console"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--pretty-logger", action="store_true", help="Enable pretty logging output"
|
||||
|
|
@ -161,7 +181,7 @@ Examples:
|
|||
"--log-level",
|
||||
type=log_level_type,
|
||||
default="warning",
|
||||
help="Set specific logging level (case-insensitive, overrides --verbose)",
|
||||
help="Set specific logging level (case-insensitive, affects file and console logging based on --log-mode)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--temperature",
|
||||
|
|
@ -348,7 +368,7 @@ def build_status(
|
|||
def main():
|
||||
"""Main entry point for the ra-aid command line tool."""
|
||||
args = parse_arguments()
|
||||
setup_logging(args.verbose, args.pretty_logger, args.log_level)
|
||||
setup_logging(args.log_mode, args.pretty_logger, args.log_level)
|
||||
logger.debug("Starting RA.Aid with arguments: %s", args)
|
||||
|
||||
# Launch web interface if requested
|
||||
|
|
@ -598,4 +618,4 @@ def main():
|
|||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
main()
|
||||
|
|
@ -41,49 +41,74 @@ class PrettyHandler(logging.Handler):
|
|||
self.handleError(record)
|
||||
|
||||
|
||||
def setup_logging(verbose: bool = False, pretty: bool = False, log_level: Optional[str] = None) -> None:
|
||||
def setup_logging(log_mode: str = "file", pretty: bool = False, log_level: Optional[str] = None) -> None:
|
||||
"""
|
||||
Configure logging for ra_aid.
|
||||
|
||||
Args:
|
||||
verbose: Set to True to enable verbose logging (implies DEBUG level if log_level not specified)
|
||||
pretty: Set to True to enable pretty console logging
|
||||
log_level: Optional explicit log level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
|
||||
Takes precedence over verbose flag if specified
|
||||
log_mode: Determines where logs are output. Options:
|
||||
- "file": Log to both file and console, with console showing only warnings+
|
||||
- "console": Log to console at the specified log_level (no file logging)
|
||||
pretty: Set to True to enable pretty console logging.
|
||||
log_level: Optional explicit log level (DEBUG, INFO, WARNING, ERROR, CRITICAL).
|
||||
When log_mode="file": Only affects file logging level, console shows warnings+
|
||||
When log_mode="console": Controls the console logging level
|
||||
|
||||
Console logging behavior:
|
||||
- With log_mode="file": Only WARNING and higher level messages are shown in console
|
||||
- With log_mode="console": Console shows messages at the requested log_level
|
||||
|
||||
File logging behavior:
|
||||
- Only active when log_mode="file"
|
||||
- Uses the requested log_level
|
||||
- When log_level=debug is used with log_mode="file", debug logs only go to the file, not to the console
|
||||
"""
|
||||
# Create .ra-aid/logs directory if it doesn't exist
|
||||
cwd = os.getcwd()
|
||||
ra_aid_dir_str = os.path.join(cwd, ".ra-aid")
|
||||
logs_dir_str = os.path.join(ra_aid_dir_str, "logs")
|
||||
|
||||
# Create directory structure
|
||||
for directory in [ra_aid_dir_str, logs_dir_str]:
|
||||
path = Path(directory)
|
||||
if not path.exists():
|
||||
try:
|
||||
path.mkdir(mode=0o755, parents=True, exist_ok=True)
|
||||
except Exception as e:
|
||||
print(f"Warning: Failed to create log directory {directory}: {str(e)}")
|
||||
# Create directory structure if log_mode is "file"
|
||||
if log_mode == "file":
|
||||
for directory in [ra_aid_dir_str, logs_dir_str]:
|
||||
path = Path(directory)
|
||||
if not path.exists():
|
||||
try:
|
||||
path.mkdir(mode=0o755, parents=True, exist_ok=True)
|
||||
except Exception as e:
|
||||
print(f"Warning: Failed to create log directory {directory}: {str(e)}")
|
||||
|
||||
# Determine log level
|
||||
if log_level is not None:
|
||||
# Use provided log level if specified (case-insensitive)
|
||||
numeric_level = getattr(logging, log_level.upper(), None)
|
||||
if not isinstance(numeric_level, int):
|
||||
specified_log_level = getattr(logging, log_level.upper(), None)
|
||||
if not isinstance(specified_log_level, int):
|
||||
# If invalid log level is provided, fall back to default
|
||||
print(f"Invalid log level: {log_level}")
|
||||
numeric_level = logging.DEBUG if verbose else logging.WARNING
|
||||
specified_log_level = logging.WARNING
|
||||
else:
|
||||
# Use verbose flag to determine log level
|
||||
numeric_level = logging.DEBUG if verbose else logging.WARNING
|
||||
# No log_level specified, use WARNING as default
|
||||
specified_log_level = logging.WARNING
|
||||
|
||||
# Configure root logger
|
||||
logger = logging.getLogger("ra_aid")
|
||||
logger.setLevel(numeric_level)
|
||||
# Determine console log level based on log_mode
|
||||
if log_mode == "console":
|
||||
# When log_mode="console", use the specified log level for console
|
||||
console_log_level = specified_log_level
|
||||
else:
|
||||
# When log_mode="file", console only shows warnings and errors
|
||||
console_log_level = logging.WARNING
|
||||
|
||||
# Clear existing handlers to avoid duplicates
|
||||
if logger.handlers:
|
||||
logger.handlers.clear()
|
||||
# Configure the root logger
|
||||
root_logger = logging.getLogger()
|
||||
|
||||
# Always set the root logger to DEBUG level
|
||||
# This ensures all messages flow through to their respective handlers
|
||||
# Best practice is to set root logger to lowest level and let handlers control message filtering
|
||||
root_logger.setLevel(logging.DEBUG)
|
||||
|
||||
# Clear existing handlers from root logger to avoid duplicates
|
||||
if root_logger.handlers:
|
||||
root_logger.handlers.clear()
|
||||
|
||||
# Create console handler
|
||||
if pretty:
|
||||
|
|
@ -95,34 +120,50 @@ def setup_logging(verbose: bool = False, pretty: bool = False, log_level: Option
|
|||
)
|
||||
console_handler.setFormatter(formatter)
|
||||
|
||||
# Add console handler to logger
|
||||
logger.addHandler(console_handler)
|
||||
# Set console handler log level based on log_mode and log_level
|
||||
console_handler.setLevel(console_log_level)
|
||||
|
||||
# Create file handler with rotation
|
||||
try:
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
log_filename = os.path.join(logs_dir_str, f"ra_aid_{timestamp}.log")
|
||||
|
||||
# RotatingFileHandler with 5MB max size and 100 backup files
|
||||
file_handler = RotatingFileHandler(
|
||||
log_filename,
|
||||
maxBytes=5 * 1024 * 1024, # 5MB
|
||||
backupCount=100,
|
||||
encoding="utf-8"
|
||||
)
|
||||
|
||||
file_formatter = logging.Formatter(
|
||||
"%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
||||
)
|
||||
file_handler.setFormatter(file_formatter)
|
||||
file_handler.setLevel(numeric_level)
|
||||
|
||||
# Add file handler to logger
|
||||
logger.addHandler(file_handler)
|
||||
|
||||
logger.info(f"Log file created: {log_filename}")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to set up file logging: {str(e)}")
|
||||
# Add console handler to root logger
|
||||
root_logger.addHandler(console_handler)
|
||||
|
||||
# Create file handler with rotation - only when log_mode is "file"
|
||||
if log_mode == "file":
|
||||
try:
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
log_filename = os.path.join(logs_dir_str, f"ra_aid_{timestamp}.log")
|
||||
|
||||
# RotatingFileHandler with 5MB max size and 100 backup files
|
||||
file_handler = RotatingFileHandler(
|
||||
log_filename,
|
||||
maxBytes=5 * 1024 * 1024, # 5MB
|
||||
backupCount=100,
|
||||
encoding="utf-8"
|
||||
)
|
||||
|
||||
file_formatter = logging.Formatter(
|
||||
"%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
||||
)
|
||||
file_handler.setFormatter(file_formatter)
|
||||
# File handler always uses the specified log level
|
||||
file_handler.setLevel(specified_log_level)
|
||||
|
||||
# Add file handler to root logger
|
||||
root_logger.addHandler(file_handler)
|
||||
|
||||
# Create an ra_aid logger for compatibility
|
||||
logger = logging.getLogger("ra_aid")
|
||||
logger.setLevel(logging.DEBUG)
|
||||
logger.propagate = True # Let messages propagate to root handlers
|
||||
|
||||
# Log configuration details for debugging
|
||||
logger.debug(f"Logging configuration: log_mode={log_mode}, log_level={log_level}, "
|
||||
f"root_level={root_logger.level}, logger_level={logger.level}, "
|
||||
f"console_level={console_handler.level}, file_level={file_handler.level}, "
|
||||
f"propagate={logger.propagate}")
|
||||
|
||||
logger.info(f"Log file created: {log_filename}")
|
||||
except Exception as e:
|
||||
root_logger.error(f"Failed to set up file logging: {str(e)}")
|
||||
|
||||
|
||||
def get_logger(name: Optional[str] = None) -> logging.Logger:
|
||||
|
|
|
|||
|
|
@ -9,15 +9,17 @@ import traceback
|
|||
from pathlib import Path
|
||||
from typing import List
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(
|
||||
level=logging.WARNING,
|
||||
format="%(asctime)s - %(levelname)s - %(message)s",
|
||||
handlers=[
|
||||
logging.StreamHandler(sys.__stderr__) # Use the real stderr
|
||||
],
|
||||
)
|
||||
# Configure module-specific logging without affecting root logger
|
||||
logger = logging.getLogger(__name__)
|
||||
# Only configure this specific logger, not the root logger
|
||||
if not logger.handlers: # Avoid adding handlers multiple times
|
||||
logger.setLevel(logging.WARNING)
|
||||
handler = logging.StreamHandler(sys.__stderr__) # Use the real stderr
|
||||
formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
|
||||
handler.setFormatter(formatter)
|
||||
logger.addHandler(handler)
|
||||
# Prevent propagation to avoid affecting the root logger configuration
|
||||
logger.propagate = False
|
||||
|
||||
# Add project root to Python path
|
||||
project_root = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
|
||||
|
|
|
|||
Loading…
Reference in New Issue