aboutsummaryrefslogtreecommitdiff
path: root/autogpts/autogpt/autogpt/logs
diff options
context:
space:
mode:
Diffstat (limited to 'autogpts/autogpt/autogpt/logs')
-rw-r--r--autogpts/autogpt/autogpt/logs/__init__.py15
-rw-r--r--autogpts/autogpt/autogpt/logs/config.py156
-rw-r--r--autogpts/autogpt/autogpt/logs/formatters.py16
-rw-r--r--autogpts/autogpt/autogpt/logs/helpers.py7
-rw-r--r--autogpts/autogpt/autogpt/logs/utils.py4
5 files changed, 147 insertions, 51 deletions
diff --git a/autogpts/autogpt/autogpt/logs/__init__.py b/autogpts/autogpt/autogpt/logs/__init__.py
index abcdac713..dc99649e5 100644
--- a/autogpts/autogpt/autogpt/logs/__init__.py
+++ b/autogpts/autogpt/autogpt/logs/__init__.py
@@ -1,3 +1,4 @@
+from .config import configure_chat_plugins, configure_logging
from .helpers import user_friendly_output
from .log_cycle import (
CURRENT_CONTEXT_FILE_NAME,
@@ -9,3 +10,17 @@ from .log_cycle import (
USER_INPUT_FILE_NAME,
LogCycleHandler,
)
+
+__all__ = [
+ "configure_logging",
+ "configure_chat_plugins",
+ "user_friendly_output",
+ "CURRENT_CONTEXT_FILE_NAME",
+ "NEXT_ACTION_FILE_NAME",
+ "PROMPT_SUMMARY_FILE_NAME",
+ "PROMPT_SUPERVISOR_FEEDBACK_FILE_NAME",
+ "SUMMARY_FILE_NAME",
+ "SUPERVISOR_FEEDBACK_FILE_NAME",
+ "USER_INPUT_FILE_NAME",
+ "LogCycleHandler",
+]
diff --git a/autogpts/autogpt/autogpt/logs/config.py b/autogpts/autogpt/autogpt/logs/config.py
index 7b8a043b1..ae483d0f8 100644
--- a/autogpts/autogpt/autogpt/logs/config.py
+++ b/autogpts/autogpt/autogpt/logs/config.py
@@ -1,21 +1,24 @@
"""Logging module for Auto-GPT."""
from __future__ import annotations
+import enum
import logging
+import os
import sys
from pathlib import Path
from typing import TYPE_CHECKING, Optional
from auto_gpt_plugin_template import AutoGPTPluginTemplate
-from openai.util import logger as openai_logger
+from openai._base_client import log as openai_logger
if TYPE_CHECKING:
from autogpt.config import Config
from autogpt.speech import TTSConfig
+from autogpt.core.configuration import SystemConfiguration, UserConfigurable
from autogpt.core.runner.client_lib.logging import BelowLevelFilter
-from .formatters import AutoGptFormatter
+from .formatters import AutoGptFormatter, StructuredLoggingFormatter
from .handlers import TTSHandler, TypingConsoleHandler
LOG_DIR = Path(__file__).parent.parent.parent / "logs"
@@ -34,80 +37,141 @@ USER_FRIENDLY_OUTPUT_LOGGER = "USER_FRIENDLY_OUTPUT"
_chat_plugins: list[AutoGPTPluginTemplate] = []
+class LogFormatName(str, enum.Enum):
+ SIMPLE = "simple"
+ DEBUG = "debug"
+ STRUCTURED = "structured_google_cloud"
+
+
+TEXT_LOG_FORMAT_MAP = {
+ LogFormatName.DEBUG: DEBUG_LOG_FORMAT,
+ LogFormatName.SIMPLE: SIMPLE_LOG_FORMAT,
+}
+
+
+class LoggingConfig(SystemConfiguration):
+ level: int = UserConfigurable(
+ default=logging.INFO,
+ from_env=lambda: logging.getLevelName(os.getenv("LOG_LEVEL", "INFO")),
+ )
+
+ # Console output
+ log_format: LogFormatName = UserConfigurable(
+ default=LogFormatName.SIMPLE,
+ from_env=lambda: LogFormatName(os.getenv("LOG_FORMAT", "simple")),
+ )
+ plain_console_output: bool = UserConfigurable(
+ default=False,
+ from_env=lambda: os.getenv("PLAIN_OUTPUT", "False") == "True",
+ )
+
+ # File output
+ log_dir: Path = LOG_DIR
+ log_file_format: Optional[LogFormatName] = UserConfigurable(
+ default=LogFormatName.SIMPLE,
+ from_env=lambda: LogFormatName(
+ os.getenv("LOG_FILE_FORMAT", os.getenv("LOG_FORMAT", "simple"))
+ ),
+ )
+
+
def configure_logging(
- debug_mode: bool = False,
- plain_output: bool = False,
- tts_config: Optional[TTSConfig] = None,
+ level: int = logging.INFO,
log_dir: Path = LOG_DIR,
+ log_format: Optional[LogFormatName] = None,
+ log_file_format: Optional[LogFormatName] = None,
+ plain_console_output: bool = False,
+ tts_config: Optional[TTSConfig] = None,
) -> None:
- """Configure the native logging module."""
+ """Configure the native logging module.
+
+ Should be usable as `configure_logging(**config.logging.dict())`, where
+ `config.logging` is a `LoggingConfig` object.
+ """
+
+ # Auto-adjust default log format based on log level
+ log_format = log_format or (
+ LogFormatName.SIMPLE if level != logging.DEBUG else LogFormatName.DEBUG
+ )
+ log_file_format = log_file_format or log_format
+
+ structured_logging = log_format == LogFormatName.STRUCTURED
+
+ if structured_logging:
+ plain_console_output = True
+ log_file_format = None
# create log directory if it doesn't exist
if not log_dir.exists():
log_dir.mkdir()
- log_level = logging.DEBUG if debug_mode else logging.INFO
- log_format = DEBUG_LOG_FORMAT if debug_mode else SIMPLE_LOG_FORMAT
- console_formatter = AutoGptFormatter(log_format)
+ log_handlers: list[logging.Handler] = []
+
+ if log_format in (LogFormatName.DEBUG, LogFormatName.SIMPLE):
+ console_format_template = TEXT_LOG_FORMAT_MAP[log_format]
+ console_formatter = AutoGptFormatter(console_format_template)
+ else:
+ console_formatter = StructuredLoggingFormatter()
+ console_format_template = SIMPLE_LOG_FORMAT
# Console output handlers
stdout = logging.StreamHandler(stream=sys.stdout)
- stdout.setLevel(log_level)
+ stdout.setLevel(level)
stdout.addFilter(BelowLevelFilter(logging.WARNING))
stdout.setFormatter(console_formatter)
stderr = logging.StreamHandler()
stderr.setLevel(logging.WARNING)
stderr.setFormatter(console_formatter)
-
- # INFO log file handler
- activity_log_handler = logging.FileHandler(log_dir / LOG_FILE, "a", "utf-8")
- activity_log_handler.setLevel(logging.INFO)
- activity_log_handler.setFormatter(
- AutoGptFormatter(SIMPLE_LOG_FORMAT, no_color=True)
- )
-
- if debug_mode:
- # DEBUG log file handler
- debug_log_handler = logging.FileHandler(log_dir / DEBUG_LOG_FILE, "a", "utf-8")
- debug_log_handler.setLevel(logging.DEBUG)
- debug_log_handler.setFormatter(
- AutoGptFormatter(DEBUG_LOG_FORMAT, no_color=True)
- )
-
- # ERROR log file handler
- error_log_handler = logging.FileHandler(log_dir / ERROR_LOG_FILE, "a", "utf-8")
- error_log_handler.setLevel(logging.ERROR)
- error_log_handler.setFormatter(AutoGptFormatter(DEBUG_LOG_FORMAT, no_color=True))
-
- # Configure the root logger
- logging.basicConfig(
- format=log_format,
- level=log_level,
- handlers=(
- [stdout, stderr, activity_log_handler, error_log_handler]
- + ([debug_log_handler] if debug_mode else [])
- ),
- )
-
- ## Set up user-friendly loggers
+ log_handlers += [stdout, stderr]
# Console output handler which simulates typing
typing_console_handler = TypingConsoleHandler(stream=sys.stdout)
typing_console_handler.setLevel(logging.INFO)
typing_console_handler.setFormatter(console_formatter)
+ # User friendly output logger (text + speech)
user_friendly_output_logger = logging.getLogger(USER_FRIENDLY_OUTPUT_LOGGER)
user_friendly_output_logger.setLevel(logging.INFO)
user_friendly_output_logger.addHandler(
- typing_console_handler if not plain_output else stdout
+ typing_console_handler if not plain_console_output else stdout
)
if tts_config:
user_friendly_output_logger.addHandler(TTSHandler(tts_config))
- user_friendly_output_logger.addHandler(activity_log_handler)
- user_friendly_output_logger.addHandler(error_log_handler)
user_friendly_output_logger.addHandler(stderr)
user_friendly_output_logger.propagate = False
+ # File output handlers
+ if log_file_format is not None:
+ if level < logging.ERROR:
+ file_output_format_template = TEXT_LOG_FORMAT_MAP[log_file_format]
+ file_output_formatter = AutoGptFormatter(
+ file_output_format_template, no_color=True
+ )
+
+ # INFO log file handler
+ activity_log_handler = logging.FileHandler(log_dir / LOG_FILE, "a", "utf-8")
+ activity_log_handler.setLevel(level)
+ activity_log_handler.setFormatter(file_output_formatter)
+ log_handlers += [activity_log_handler]
+ user_friendly_output_logger.addHandler(activity_log_handler)
+
+ # ERROR log file handler
+ error_log_handler = logging.FileHandler(log_dir / ERROR_LOG_FILE, "a", "utf-8")
+ error_log_handler.setLevel(logging.ERROR)
+ error_log_handler.setFormatter(
+ AutoGptFormatter(DEBUG_LOG_FORMAT, no_color=True)
+ )
+ log_handlers += [error_log_handler]
+ user_friendly_output_logger.addHandler(error_log_handler)
+
+ # Configure the root logger
+ logging.basicConfig(
+ format=console_format_template,
+ level=level,
+ handlers=log_handlers,
+ )
+
+ # Speech output
speech_output_logger = logging.getLogger(SPEECH_OUTPUT_LOGGER)
speech_output_logger.setLevel(logging.INFO)
if tts_config:
@@ -120,7 +184,7 @@ def configure_logging(
json_logger.propagate = False
# Disable debug logging from OpenAI library
- openai_logger.setLevel(logging.INFO)
+ openai_logger.setLevel(logging.WARNING)
def configure_chat_plugins(config: Config) -> None:
diff --git a/autogpts/autogpt/autogpt/logs/formatters.py b/autogpts/autogpt/autogpt/logs/formatters.py
index 4930d3c73..a51112573 100644
--- a/autogpts/autogpt/autogpt/logs/formatters.py
+++ b/autogpts/autogpt/autogpt/logs/formatters.py
@@ -1,6 +1,7 @@
import logging
from colorama import Style
+from google.cloud.logging_v2.handlers import CloudLoggingFilter, StructuredLogHandler
from autogpt.core.runner.client_lib.logging import FancyConsoleFormatter
@@ -16,7 +17,7 @@ class AutoGptFormatter(FancyConsoleFormatter):
# Make sure `msg` is a string
if not hasattr(record, "msg"):
record.msg = ""
- elif not type(record.msg) == str:
+ elif not type(record.msg) is str:
record.msg = str(record.msg)
# Strip color from the message to prevent color spoofing
@@ -37,3 +38,16 @@ class AutoGptFormatter(FancyConsoleFormatter):
return remove_color_codes(super().format(record))
else:
return super().format(record)
+
+
+class StructuredLoggingFormatter(StructuredLogHandler, logging.Formatter):
+ def __init__(self):
+ # Set up CloudLoggingFilter to add diagnostic info to the log records
+ self.cloud_logging_filter = CloudLoggingFilter()
+
+ # Init StructuredLogHandler
+ super().__init__()
+
+ def format(self, record: logging.LogRecord) -> str:
+ self.cloud_logging_filter.filter(record)
+ return super().format(record)
diff --git a/autogpts/autogpt/autogpt/logs/helpers.py b/autogpts/autogpt/autogpt/logs/helpers.py
index b19ee45b2..580e09a8a 100644
--- a/autogpts/autogpt/autogpt/logs/helpers.py
+++ b/autogpts/autogpt/autogpt/logs/helpers.py
@@ -53,10 +53,9 @@ def print_attribute(
def request_user_double_check(additionalText: Optional[str] = None) -> None:
if not additionalText:
additionalText = (
- "Please ensure you've setup and configured everything"
- " correctly. Read https://github.com/Significant-Gravitas/AutoGPT/autogpts/autogpt#readme to "
- "double check. You can also create a github issue or join the discord"
- " and ask there!"
+ "Please ensure you've setup and configured everything correctly. "
+ "Read https://docs.agpt.co/autogpt/setup/ to double check. "
+ "You can also create a github issue or join the discord and ask there!"
)
user_friendly_output(
diff --git a/autogpts/autogpt/autogpt/logs/utils.py b/autogpts/autogpt/autogpt/logs/utils.py
index 0b92e2967..d9f39af30 100644
--- a/autogpts/autogpt/autogpt/logs/utils.py
+++ b/autogpts/autogpt/autogpt/logs/utils.py
@@ -3,3 +3,7 @@ import re
def remove_color_codes(s: str) -> str:
return re.sub(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])", "", s)
+
+
+def fmt_kwargs(kwargs: dict) -> str:
+ return ", ".join(f"{n}={repr(v)}" for n, v in kwargs.items())