Обработчик выполняет всю запись файла из родительского процесса и использует только один поток для получения сообщений, переданных от дочерних процессов.

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

Но я сделал несколько тестов, кажется, нет проблем с использованием входа в многопроцессорную

import time
import logging
from multiprocessing import Process, current_process, pool


# setup log
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.DEBUG,
                    format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',
                    datefmt='%a, %d %b %Y %H:%M:%S',
                    filename='/tmp/test.log',
                    filemode='w')


def func(the_time, logger):
    proc = current_process()
    while True:
        if time.time() >= the_time:
            logger.info('proc name %s id %s' % (proc.name, proc.pid))
            return



if __name__ == '__main__':

    the_time = time.time() + 5

    for x in xrange(1, 10):
        proc = Process(target=func, name=x, args=(the_time, logger))
        proc.start()

Как видно из кода.

Я намеренно позволил подпроцессу записывать журнал в тот же момент (5 секунд после запуска), чтобы увеличить вероятность конфликта. Но конфликта нет вообще.

Итак, мой вопрос: можем ли мы использовать ведение журнала в многопроцессорной среде? Почему так много постов сказать не можем?

Ответы на вопрос(3)

Согласно сhttps://docs.python.org/3/howto/logging-cookbook.html#logging-to-a-single-file-from-multiple-processes

Хотя ведение журнала является поточно-ориентированным и поддерживается запись в один файл из нескольких потоков в одном процессе, запись в один файл из нескольких процессов не поддерживается, поскольку не существует стандартного способа сериализации доступа к одному файлу между несколькими процессы в Python.

Одним из возможных решений будет запись каждого процесса в свой собственный файл. Вы можете добиться этого, написав свой собственный обработчик, который добавляет pid процесса в конец файла:

import logging.handlers
import os


class PIDFileHandler(logging.handlers.WatchedFileHandler):

    def __init__(self, filename, mode='a', encoding=None, delay=0):
        filename = self._append_pid_to_filename(filename)
        super(PIDFileHandler, self).__init__(filename, mode, encoding, delay)

    def _append_pid_to_filename(self, filename):
        pid = os.getpid()
        path, extension = os.path.splitext(filename)
        return '{0}-{1}{2}'.format(path, pid, extension)

Тогда вам просто нужно позвонитьaddHandler:

logger = logging.getLogger('foo')
fh = PIDFileHandler('bar.log')
logger.addHandler(fh)
 22degrees29 дек. 2017 г., 09:30
спасибо за урок по многопроцессорности против многопоточности ...

ения после ошибок, передавая все в родительский процесс через канал.

from logging.handlers import RotatingFileHandler
import multiprocessing, threading, logging, sys, traceback

class MultiProcessingLog(logging.Handler):
    def __init__(self, name, mode, maxsize, rotate):
        logging.Handler.__init__(self)

        self._handler = RotatingFileHandler(name, mode, maxsize, rotate)
        self.queue = multiprocessing.Queue(-1)

        t = threading.Thread(target=self.receive)
        t.daemon = True
        t.start()

    def setFormatter(self, fmt):
        logging.Handler.setFormatter(self, fmt)
        self._handler.setFormatter(fmt)

    def receive(self):
        while True:
            try:
                record = self.queue.get()
                self._handler.emit(record)
            except (KeyboardInterrupt, SystemExit):
                raise
            except EOFError:
                break
            except:
                traceback.print_exc(file=sys.stderr)

    def send(self, s):
        self.queue.put_nowait(s)

    def _format_record(self, record):
         # ensure that exc_info and args
         # have been stringified.  Removes any chance of
         # unpickleable things inside and possibly reduces
         # message size sent over the pipe
        if record.args:
            record.msg = record.msg % record.args
            record.args = None
        if record.exc_info:
            dummy = self.format(record)
            record.exc_info = None

        return record

    def emit(self, record):
        try:
            s = self._format_record(record)
            self.send(s)
        except (KeyboardInterrupt, SystemExit):
            raise
        except:
            self.handleError(record)

    def close(self):
        self._handler.close()
        logging.Handler.close(self)

Обработчик выполняет всю запись файла из родительского процесса и использует только один поток для получения сообщений, переданных от дочерних процессов.

Решение Вопроса

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

Теперь происходит то, что каждый процесс содержит дескриптор открытого файла и выполняет «добавочную запись» в этот файл. Вопрос в том, при каких обстоятельствах запись добавления является «атомарной» (то есть не может быть прервана, например, другим процессом, записывающим в тот же файл и смешивающим его выходные данные). Эта проблема относится ко всем языкам программирования, так как в конце они будут выполнять системный вызов ядра.Этот ответ ответы, при каких обстоятельствах общий файл журнала в порядке.

Все сводится к проверке размера буфера канала в Linux, который определен в/usr/include/linux/limits.h и составляет 4096 байт. Для других ОС вы найдетеВот хороший список

Это означает: если ваша строка журнала меньше 4'096 байт (если в Linux), то добавление безопасно, если диск подключен напрямую (т.е. между сетями нет). Но для более подробной информации, пожалуйста, проверьте первую ссылку в моем ответе. Чтобы проверить это вы можете сделатьlogger.info('proc name %s id %s %s' % (proc.name, proc.pid, str(proc.name)*5000)) с разными длинами. С 5000 например я уже перепутал строки журнала в/tmp/test.log.

Вэтот вопрос уже есть довольно много решений для этого, поэтому я не буду добавлять свое собственное решение здесь.

Обновление: колба и многопроцессорность

Веб-фреймворки, такие как flask, будут работать на нескольких рабочих, если они размещены в uwsgi или nginx. В этом случае несколько процессов могут записывать в один файл журнала. У него будут проблемы?

Обработка ошибок в флешке выполняется через stdout / stderr, который затем перехватывается веб-сервером (uwsgi, nginx и т. Д.), Который должен следить за тем, чтобы логи записывались правильно (см., Например, [этот пример фляги + nginx]) (http://flaviusim.com/blog/Deploying-Flask-with-nginx-uWSGI-and-Supervisor/), возможно, также добавляя информацию о процессах, чтобы можно было связать строки ошибок с процессами. Изфляги док:

По умолчанию, начиная с Flask 0.11, ошибки регистрируются в журнале вашего веб-сервера автоматически. Предупреждений однако нет.

Таким образом, у вас все еще будет проблема смешанных файлов журнала, если вы используетеwarn и сообщение превышает размер буфера канала.

 hansaplast04 янв. 2018 г., 14:33
@KramerLi: я ответил на ваш вопрос в новом разделе моего ответа
 Kramer Li04 янв. 2018 г., 09:43
Еще один вопрос. Веб-фреймворки, такие как flask, будут работать на нескольких рабочих, если они размещены в uwsgi или nginx. В этом случае несколько процессов могут записывать в один файл журнала. Будет ли это иметь проблемы?

Ваш ответ на вопрос