Как вывести журнал в тот же файл - PullRequest
0 голосов
/ 16 октября 2018

Я использовал tornado для создания своего веб-сервиса, и я использую logging, чтобы получить регистратор tornado, и все кажется успешным.Но так как сервис multiprocessing, то сегодня, когда я проверяю журнал, я обнаружил, что часть информации потеряна.Поэтому я хочу спросить, можно ли решить эту проблему, если я открою другой журнал для другого процесса?

или если любое другое решение может быть применено на сервере multi-processes при выводе журнала.

1 Ответ

0 голосов
/ 16 октября 2018

Вот код, который я использую в своем решении:

https://gist.github.com/hcl14/259432dd648180bf2af672c26d9df9fc

Одновременно запускает серверы торнадо с приложениями-флягами, осуществляет вход на экран в общий журнал (stdout.log)и специфичные для процесса журналы.Просто посмотрите комментарии в коде, как все должно быть импортировано и организовано.

Чтобы запустить все, соедините все три файла, создайте подпапку logs, запустите mainprogram.py и проверьте отправку различных запросов POST.Затем вы можете проверить файлы в папке журнала, чтобы убедиться, что все записано правильно.

mainprogram.py - это основной файл, где регистратор инициализируется и помещается в модуль global_vars.Этот модуль должен быть импортирован повсеместно, а оттуда получен логгер для конкретного процесса.Причина в том, что это просто удобный способ хранения переменных в глобальной области видимости между модулями.Когда новый процесс разветвляется, он перезаписывает там регистратор с помощью регистратора, специфичного для процесса, поэтому все модули, используемые процессом, будут записывать в соответствующий регистратор:

separate_logging.py (для удобства переименован в gist в mainprogram.py):

import logging
import os
import multiprocessing

from tornado.wsgi import WSGIContainer
from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop
from tornado.options import define, options

# a way to pass variables into separate modules, 
# process-specific because of 'fork' mode
# (each process will have its own version of this module)
import global_vars



logPath = os.environ.get('LOGFOLDER','logs')
fileName = os.environ.get('LOGFILE', "stdout.log")

address = os.environ.get('ADDRESS','0.0.0.0')
port = os.environ.get('PORT','8888')

NUM_PROCESSES = os.cpu_count()

# initializes the main logger to be used across all modules

logFormatter = logging.Formatter("%(asctime)s [%(processName)-12.12s] [%(threadName)-12.12s] [%(levelname)-5.5s] [%(filename)s:%(lineno)d] %(message)s")
rootLogger = logging.getLogger(__name__)

# first handler is general log
fileHandler = logging.FileHandler("{0}/{1}".format(logPath, fileName))
fileHandler.setFormatter(logFormatter)
rootLogger.addHandler(fileHandler)
# second handler is logging to console
consoleHandler = logging.StreamHandler()
consoleHandler.setFormatter(logFormatter)
rootLogger.addHandler(consoleHandler) 

rootLogger.setLevel("DEBUG") # log everything
rootLogger.propagate = False

# until process branches, it uses rootLogger
global_vars.multiprocess_globals["logger"] = rootLogger



# third handler is process-specific log, initialized in processes
def init_logger2(secondary_logfile, rootLogger):
    fileHandler1 = logging.FileHandler("{0}/{1}".format(logPath, 'process_'+str(secondary_logfile)+'.log'))
    fileHandler1.setFormatter(logFormatter)
    rootLogger.addHandler(fileHandler1)

    return rootLogger



# external modules import goes here!
# otherwise they will not find any logger!
from flask_app import create_app




# ---------------

# process function
def run(process_id):

    # initialize process-specific logger
    processLogger = init_logger2(process_id, rootLogger)
    global_vars.multiprocess_globals["logger"] = processLogger

    # here you can run tornado app:

    try:
        app = create_app()  # pass interests to flask app        
        ioloop = IOLoop()
        http_server_api = HTTPServer(WSGIContainer(app))

        # reuse_port allows multiple servers co-exist
        # as separate processes
        http_server_api.bind(address=address, port=port, reuse_port=True) 
        http_server_api.start()

        processLogger.info("Process %s started %s:%s" % (process_id, address,
                                            port))

        ioloop.start()
    except Exception as e:
        processLogger.error(e)





# start processes (tornado servers)
if __name__ == '__main__':

    processes = []
    for i in range(1,NUM_PROCESSES):
        p = multiprocessing.Process(target=run, args=(str(i),))
        p.daemon = False # if we want to spawn child processes
        #p.daemon = True # if we want to gracefully stop program
        processes.append(p)


    # Run processes:

    for p in processes:
        p.start()

    # block program from exiting
    for p in processes:
        p.join() 

global_vars.py:

# global variables to be used across processes.
# Separate file is needed to make globals accessible from different submodules.

# Global variables which need to exist in the scope of each process.
# Variables are added into this dictionary during process initialization.
multiprocess_globals = {}

flask_app.py: просто приложение в колбе, которое может использовать более глубокие модули.В этом случае эти модули также должны импортировать регистратор, как это делает flask_app:

# create flask app to be run by tornado process

# process-specific globals
import global_vars 

from flask import Flask, request, Response, json, abort, jsonify
import json as json2

# from deeper_module import do_something

app = Flask(__name__)
app.config.from_object(__name__)

# get process-specific logger
# do such import in any submodule !
# as global_vars is changed on process fork!
rootLogger = global_vars.multiprocess_globals["logger"]
logger = rootLogger.getChild(__name__)


def create_app():

    app = Flask(__name__)

    @app.route('/my_url1', methods=['POST'])
    def my_url1():

        body = json.loads(request.data)

        # debug to process-specific logger
        logger.debug(body)

        # do_something()

        response = app.response_class(
            response=json.dumps({'response':'good'}),
            status=200,
            mimetype='application/json'
        )
        return response

    # another functions

    return app

Все модули, использующие определенный регистратор, должны быть импортированы после инициализации global_vars в основной программе.После запуска программы вы увидите:

2018-10-16 12:50:20,988 [Process-1   ] [MainThread  ] [INFO ] [separate_logging.py:86] Process 1 started 0.0.0.0:8888
2018-10-16 12:50:20,989 [Process-2   ] [MainThread  ] [INFO ] [separate_logging.py:86] Process 2 started 0.0.0.0:8888
2018-10-16 12:50:20,990 [Process-3   ] [MainThread  ] [INFO ] [separate_logging.py:86] Process 3 started 0.0.0.0:8888
2018-10-16 12:50:20,991 [Process-4   ] [MainThread  ] [INFO ] [separate_logging.py:86] Process 4 started 0.0.0.0:8888
2018-10-16 12:50:20,991 [Process-6   ] [MainThread  ] [INFO ] [separate_logging.py:86] Process 6 started 0.0.0.0:8888
2018-10-16 12:50:20,992 [Process-7   ] [MainThread  ] [INFO ] [separate_logging.py:86] Process 7 started 0.0.0.0:8888
2018-10-16 12:50:20,993 [Process-5   ] [MainThread  ] [INFO ] [separate_logging.py:86] Process 5 started 0.0.0.0:8888

Затем вы можете запустить POST-запрос, который будет обработан одним из запущенных процессов:

$ curl -H "Content-Type: application/json" -X POST -d '{"bla-bla":"bla"}' 127.0.0.1:8888/my_url1

Результат:

2018-10-16 12:51:40,040 [Process-5 ] [MainThread ] [DEBUG] [flask_app.py:31] {'bla-bla': 'bla'}

Вы можете убедиться, что эта строка появится в общем журнале logs/stdout.log, а также в logs/process_5.log.

...