当应用程序有多个调用时,日志记录配置无法加载 value-formatter()

Logging config fails to the load the value-formatter() when the application has multiple calls

我正在使用 Bottle wsgi 框架来创建 Web 服务。我在 app.py 中配置了记录器(如 app.py 所示),它接收应用程序调用并使用方法 get_output() 将输入参数传递给 backend.py。我正在使用 backend.py 来处理应用程序的请求。在后端文件中,记录器配置使用 self.logger 为每个处理器实例设置(显示在 backend.py 文件中)

app.py

from bottle import Bottle
import logging.handlers
from backend import Processor

logger = logging.getLogger()
logger.setLevel(logging.DEBUG)

# Logging handler for files
file_handler = logging.handlers.TimedRotatingFileHandler("log.log", when="midnight", interval=1,
                                                         backupCount=10000)
file_handler.setLevel(logging.INFO)

# Logging handler for console
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)

# Formatter's for logging
formatter = logging.Formatter('%(asctime)s %(name)s %(levelname)s %(message)s')
file_handler.setFormatter(formatter)
console_handler.setFormatter(formatter)

# Add handlers to the logger
logger.addHandler(file_handler)
logger.addHandler(console_handler)


class App:

    def __init__(self, host, port, server):
        logger.info("Initializing the app")
        self.processor = Processor()
        self._app = Bottle()
        self._host = host
        self._port = port
        self._server = server

    def _route(self):
        self._app.route('/hello/<attribute>/<number>', method="POST", callback=self.get_output)

    def start(self):
        self._app.run(server=self._server, host=self._host, port=self._port)  ## Starts the service
        logger.info("Web service started.")

    def get_output(self, attribute, number):
        logger.info("Got the input attribute {}".format(attribute))
        result = self.processor.compute(attribute, number)
        return result


if __name__ == '__main__':
    server = App(server='waitress', host='0.0.0.0', port=8080)
    server.start()

backend.py

 import logging


class Processor:

    def __init__(self):
        self.logger = logging.getLogger(__name__)  # Setting the logging config
        self.attribute = None  ############ Setting this variable to None for the instance

    def set_attributes(self, input_attribute):
        self.attribute = input_attribute  ############### Setter to set the attribute

    def compute(self, attribute, number):
        self.set_attributes(attribute)
        self.logger.info("Completed processing the attribute {}".format(self.attribute))
        res = number + 5
        return res

问题是记录器会在多次调用此 app.py 文件时选择存储在共享内存中的先前请求参数(它选择蓝色代替绿色...等)。

我重新创建了日志语句,如下所示

2019-12-23 15:15:46,992 yoshi INFO Web service started.
Bottle v0.13-dev server starting up (using WaitressServer())...
Listening on http://0.0.0.0:8090/
Hit Ctrl-C to quit.

Serving on http://0.0.0.0:8090

line1: 2019-12-23 15:15:47,327 app.py INFO Got the input attribute Green
line2: 2019-12-23 15:15:47,327 app.py INFO Got the input attribute Blue
line3: 2019-12-23 15:15:47,327 backend.py INFO Completed processing the attribute Green
line4: 2019-12-23 15:15:47,327 app.py INFO Got the input attribute Black
line5: 2019-12-23 15:15:47,327 backend.py INFO Completed processing the attribute Green <<<-----This needs to be Blue, but it is Green again (Is it because self.attribute = None)
line6: 2019-12-23 15:15:47,327 backend.py INFO Completed processing the attribute Black
line7: 2019-12-23 15:15:47,327 backend.py INFO Completed processing the attribute None <<<-----This needs to be Violet, but it is None again (Is it because self.attribute = None)
line8: 2019-12-23 15:15:47,327 app.py INFO Got the input attribute Violet

我总共对上述应用程序进行了 4 次并行调用,同时调用了属性绿色、蓝色、黑色、紫色

问题:

我的记录器在第 5 行和第 7 行失败的原因是什么?使用 setter 方法将输入参数设置为整​​个对象的正确方法是? (如果没有,如何将输入属性设置为一个全新的模块)

难道是因为self.attribute使用了共享内存??怎么解决??

正在寻找创建日志配置的答案,该配置可用于我的应用程序的所有模块。我需要在日志消息中使用请求参数并且记录器配置不会因对应用程序的多次输入调用而失败

我认为您可能希望使用线程本地存储来保存您的属性。对您的代码进行一些修改:

app.py:

import logging.handlers
import threading

from bottle import Bottle

from backend import Processor
from storage import storage

logger = logging.getLogger()
logger.setLevel(logging.DEBUG)

# Logging handler for files
file_handler = logging.handlers.TimedRotatingFileHandler("log.log", when="midnight", interval=1,
                                                         backupCount=10000)
file_handler.setLevel(logging.INFO)

# Logging handler for console
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)

# Formatter's for logging
formatter = logging.Formatter('%(asctime)s %(name)s %(levelname)s %(message)s')
file_handler.setFormatter(formatter)
console_handler.setFormatter(formatter)

# Add handlers to the logger
logger.addHandler(file_handler)
logger.addHandler(console_handler)


class App:

    def __init__(self, host, port, server):
        logger.info("Initializing the app")
        self.processor = Processor()
        self._app = Bottle()
        self._host = host
        self._port = port
        self._server = server
        self._app.route('/hello/<attribute>/<number>', method="POST", callback=self.get_output)

    def start(self):
        self._app.run(server=self._server, host=self._host, port=self._port)  ## Starts the service
        logger.info("Web service started.")

    def get_output(self, attribute, number):
        logger.info("Got the input attribute %s", attribute)
        local_storage = storage()
        local_storage.attribute = attribute
        self.processor.compute()
        return f"done for {attribute}"


if __name__ == '__main__':
    server = App(server='waitress', host='0.0.0.0', port=8081)
    server.start()

backend.py:

import logging
import threading

from more_backend import do_work
from storage import storage


class Processor:

    def __init__(self):
        self.logger = logging.getLogger(__name__)  # Setting the logging config

    def compute(self):
        local_storage = storage()
        do_work()
        self.logger.info("Completed processing the attribute %s", local_storage.attribute)

more_backend.py:

import logging
import threading
import time
import random

from storage import storage

def do_work():
    local_storage = storage()
    logger = logging.getLogger(__name__)
    logger.info("Doing work with attribute %s", local_storage.attribute)
    time.sleep(random.random())

storage.py:

from functools import lru_cache
import threading

@lru_cache()
def storage():
    return threading.local()

认为它会做你想做的事:attribute来自每个请求的所有函数都可用于处理该请求的所有函数,无需手动传递,并且线程之间没有竞争条件。