From f2bb507ebb4cc9a5aeeca7a9084446d30019a750 Mon Sep 17 00:00:00 2001 From: Lincoln Stein Date: Thu, 8 Jun 2023 09:23:11 -0400 Subject: [PATCH] allow logger to be reconfigured after startup --- invokeai/app/api_app.py | 16 +- .../backend/install/invokeai_configure.py | 3 + invokeai/backend/util/logging.py | 166 ++++++++++++++++-- invokeai/frontend/install/model_install.py | 3 +- 4 files changed, 168 insertions(+), 20 deletions(-) diff --git a/invokeai/app/api_app.py b/invokeai/app/api_app.py index df3337cf81..fa46762d56 100644 --- a/invokeai/app/api_app.py +++ b/invokeai/app/api_app.py @@ -4,7 +4,6 @@ from inspect import signature import uvicorn -from invokeai.backend.util.logging import InvokeAILogger from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from fastapi.openapi.docs import get_redoc_html, get_swagger_ui_html @@ -15,15 +14,19 @@ from fastapi_events.middleware import EventHandlerASGIMiddleware from pathlib import Path from pydantic.schema import schema +#This should come early so that modules can log their initialization properly +from .services.config import InvokeAIAppConfig +from ..backend.util.logging import InvokeAILogger +app_config = InvokeAIAppConfig.get_config() +app_config.parse_args() +logger = InvokeAILogger.getLogger(config=app_config) + import invokeai.frontend.web as web_dir from .api.dependencies import ApiDependencies from .api.routers import sessions, models, images from .api.sockets import SocketIO from .invocations.baseinvocation import BaseInvocation -from .services.config import InvokeAIAppConfig - -logger = InvokeAILogger.getLogger() # Create the app # TODO: create this all in a method so configuration/etc. can be passed in? @@ -41,11 +44,6 @@ app.add_middleware( socket_io = SocketIO(app) -# initialize config -# this is a module global -app_config = InvokeAIAppConfig.get_config() -app_config.parse_args() - # Add startup event to load dependencies @app.on_event("startup") async def startup_event(): diff --git a/invokeai/backend/install/invokeai_configure.py b/invokeai/backend/install/invokeai_configure.py index aaa744ba09..603760c0c1 100755 --- a/invokeai/backend/install/invokeai_configure.py +++ b/invokeai/backend/install/invokeai_configure.py @@ -40,6 +40,7 @@ import invokeai.configs as configs from invokeai.app.services.config import ( InvokeAIAppConfig, ) +from invokeai.backend.util.logging import InvokeAILogger from invokeai.frontend.install.model_install import addModelsForm, process_and_execute from invokeai.frontend.install.widgets import ( CenteredButtonPress, @@ -80,6 +81,7 @@ INIT_FILE_PREAMBLE = """# InvokeAI initialization file # or renaming it and then running invokeai-configure again. """ +logger=None # -------------------------------------------- def postscript(errors: None): @@ -824,6 +826,7 @@ def main(): if opt.full_precision: invoke_args.extend(['--precision','float32']) config.parse_args(invoke_args) + logger = InvokeAILogger().getLogger(config=config) errors = set() diff --git a/invokeai/backend/util/logging.py b/invokeai/backend/util/logging.py index 1ac6ea291a..e664061bc3 100644 --- a/invokeai/backend/util/logging.py +++ b/invokeai/backend/util/logging.py @@ -1,6 +1,7 @@ # Copyright (c) 2023 Lincoln D. Stein and The InvokeAI Development Team -"""invokeai.util.logging +""" +invokeai.util.logging Logging class for InvokeAI that produces console messages @@ -11,6 +12,7 @@ from invokeai.backend.util.logging import InvokeAILogger logger = InvokeAILogger.getLogger(name='InvokeAI') // Initialization (or) logger = InvokeAILogger.getLogger(__name__) // To use the filename +logger.configure() logger.critical('this is critical') // Critical Message logger.error('this is an error') // Error Message @@ -28,6 +30,149 @@ Console messages: Alternate Method (in this case the logger name will be set to InvokeAI): import invokeai.backend.util.logging as IAILogger IAILogger.debug('this is a debugging message') + +## Configuration + +The default configuration will print to stderr on the console. To add +additional logging handlers, call getLogger with an initialized InvokeAIAppConfig +object: + + + config = InvokeAIAppConfig.get_config() + config.parse_args() + logger = InvokeAILogger.getLogger(config=config) + +### Three command-line options control logging: + +`--log_handlers ...` + +This option activates one or more log handlers. Options are "console", "file", "syslog" and "http". To specify more than one, separate them by spaces: + +``` +invokeai-web --log_handlers console syslog=/dev/log file=C:\\Users\\fred\\invokeai.log +``` + +The format of these options is described below. + +### `--log_format {plain|color|legacy|syslog}` + +This controls the format of log messages written to the console. Only the "console" log handler is currently affected by this setting. + +* "plain" provides formatted messages like this: + +```bash + +[2023-05-24 23:18:2[2023-05-24 23:18:50,352]::[InvokeAI]::DEBUG --> this is a debug message +[2023-05-24 23:18:50,352]::[InvokeAI]::INFO --> this is an informational messages +[2023-05-24 23:18:50,352]::[InvokeAI]::WARNING --> this is a warning +[2023-05-24 23:18:50,352]::[InvokeAI]::ERROR --> this is an error +[2023-05-24 23:18:50,352]::[InvokeAI]::CRITICAL --> this is a critical error +``` + +* "color" produces similar output, but the text will be color coded to indicate the severity of the message. + +* "legacy" produces output similar to InvokeAI versions 2.3 and earlier: + +``` +### this is a critical error +*** this is an error +** this is a warning +>> this is an informational messages + | this is a debug message +``` + +* "syslog" produces messages suitable for syslog entries: + +```bash +InvokeAI [2691178] this is a critical error +InvokeAI [2691178] this is an error +InvokeAI [2691178] this is a warning +InvokeAI [2691178] this is an informational messages +InvokeAI [2691178] this is a debug message +``` + +(note that the date, time and hostname will be added by the syslog system) + +### `--log_level {debug|info|warning|error|critical}` + +Providing this command-line option will cause only messages at the specified level or above to be emitted. + +## Console logging + +When "console" is provided to `--log_handlers`, messages will be written to the command line window in which InvokeAI was launched. By default, the color formatter will be used unless overridden by `--log_format`. + +## File logging + +When "file" is provided to `--log_handlers`, entries will be written to the file indicated in the path argument. By default, the "plain" format will be used: + +```bash +invokeai-web --log_handlers file=/var/log/invokeai.log +``` + +## Syslog logging + +When "syslog" is requested, entries will be sent to the syslog system. There are a variety of ways to control where the log message is sent: + +* Send to the local machine using the `/dev/log` socket: + +``` +invokeai-web --log_handlers syslog=/dev/log +``` + +* Send to the local machine using a UDP message: + +``` +invokeai-web --log_handlers syslog=localhost +``` + +* Send to the local machine using a UDP message on a nonstandard port: + +``` +invokeai-web --log_handlers syslog=localhost:512 +``` + +* Send to a remote machine named "loghost" on the local LAN using facility LOG_USER and UDP packets: + +``` +invokeai-web --log_handlers syslog=loghost,facility=LOG_USER,socktype=SOCK_DGRAM +``` + +This can be abbreviated `syslog=loghost`, as LOG_USER and SOCK_DGRAM are defaults. + +* Send to a remote machine named "loghost" using the facility LOCAL0 and using a TCP socket: + +``` +invokeai-web --log_handlers syslog=loghost,facility=LOG_LOCAL0,socktype=SOCK_STREAM +``` + +If no arguments are specified (just a bare "syslog"), then the logging system will look for a UNIX socket named `/dev/log`, and if not found try to send a UDP message to `localhost`. The Macintosh OS used to support logging to a socket named `/var/run/syslog`, but this feature has since been disabled. + +## Web logging + +If you have access to a web server that is configured to log messages when a particular URL is requested, you can log using the "http" method: + +``` +invokeai-web --log_handlers http=http://my.server/path/to/logger,method=POST +``` + +The optional [,method=] part can be used to specify whether the URL accepts GET (default) or POST messages. + +Currently password authentication and SSL are not supported. + +## Using the configuration file + +You can set and forget logging options by adding a "Logging" section to `invokeai.yaml`: + +``` +InvokeAI: + [... other settings...] + Logging: + log_handlers: + - console + - syslog=/dev/log + log_level: info + log_format: color +``` """ import logging.handlers @@ -180,16 +325,17 @@ class InvokeAILogger(object): loggers = dict() @classmethod - def getLogger(cls, name: str = 'InvokeAI', config: InvokeAIAppConfig=None) -> logging.Logger: - if not config: - config = InvokeAIAppConfig() - config.parse_args() - - if name not in cls.loggers: + def getLogger(cls, + name: str = 'InvokeAI', + config: InvokeAIAppConfig=InvokeAIAppConfig.get_config())->logging.Logger: + if name in cls.loggers: + logger = cls.loggers[name] + logger.handlers.clear() + else: logger = logging.getLogger(name) - logger.setLevel(config.log_level.upper()) # yes, strings work here - for ch in cls.getLoggers(config): - logger.addHandler(ch) + logger.setLevel(config.log_level.upper()) # yes, strings work here + for ch in cls.getLoggers(config): + logger.addHandler(ch) cls.loggers[name] = logger return cls.loggers[name] diff --git a/invokeai/frontend/install/model_install.py b/invokeai/frontend/install/model_install.py index d956299bdf..265c456e3a 100644 --- a/invokeai/frontend/install/model_install.py +++ b/invokeai/frontend/install/model_install.py @@ -28,7 +28,7 @@ import torch from npyscreen import widget from omegaconf import OmegaConf -import invokeai.backend.util.logging as logger +from invokeai.backend.util.logging import InvokeAILogger from invokeai.backend.install.model_install_backend import ( Dataset_path, @@ -939,6 +939,7 @@ def main(): if opt.full_precision: invoke_args.extend(['--precision','float32']) config.parse_args(invoke_args) + logger = InvokeAILogger().getLogger(config=config) if not (config.root_dir / config.conf_path.parent).exists(): logger.info(