Jak należy logować podczas korzystania z wieloprocesora w Pythonie?
W tej chwili mam centralny moduł w frameworku, który generuje wiele procesów za pomocą Pythona 2.6 multiprocessing
moduł . Ponieważ używa multiprocessing
, istnieje dziennik świadomy wieloprocesorowego przetwarzania na poziomie modułów, LOG = multiprocessing.get_logger()
. Na docs, Ten rejestrator ma blokady współdzielone przez proces, dzięki czemu nie można przeciągać rzeczy w sys.stderr
(lub jakimkolwiek filehandle), mając wiele procesów zapisujących do niego jednocześnie.
Problem jaki mam teraz polega na tym, że pozostałe moduły w frameworku nie są multiprocessing-aware. Z tego, co widzę, muszę sprawić, by wszystkie zależności od tego modułu centralnego wykorzystywały rejestrowanie wieloprocesorowe. To denerwujące wewnątrz frameworka, nie mówiąc już o wszystkich klientach frameworka. Czy są jakieś alternatywy, o których nie myślę?
19 answers
Jedynym sposobem radzenia sobie z tym nieinwazyjnie jest:
- odtwarza każdy proces roboczy tak, że jego dziennik trafia do innego deskryptora pliku (na dysk lub do rury.) Najlepiej, aby wszystkie wpisy dziennika były oznaczone czasem.
- twój proces kontrolera może wykonać jedną z następujących czynności:
- Jeśli używasz plików na dysku: Połącz pliki dziennika na końcu biegu, posortowane według znacznika czasu
-
Jeśli używasz rur (zalecane): coalesce log wpisy w locie ze wszystkich rur, do centralnego pliku dziennika. (Np. okresowo
select
z deskryptorów plików pipes, wykonaj sortowanie scalające na dostępnych wpisach dziennika i przepuść do scentralizowanego dziennika. Powtarzam.)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-20 13:28:38
Właśnie napisałem własny program obsługi dziennika, który przekazuje wszystko do procesu nadrzędnego za pomocą rury. Testowałem go dopiero od 10 minut, ale wygląda na to, że działa całkiem dobrze.
(Uwaga: jest to zakodowane na twardo do RotatingFileHandler
, co jest moim własnym przypadkiem użycia.)
Update: @ javier utrzymuje teraz to podejście jako pakiet dostępny na Pypi-Zobacz multiprocessing-logging na Pypi, github na https://github.com/jruere/multiprocessing-logging
Aktualizacja: Wdrożenie!
To teraz używa kolejki do poprawnej obsługi współbieżności, a także poprawnie odzyskuje po błędach. Używam tego w produkcji od kilku miesięcy, a obecna wersja poniżej działa bez problemu.
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)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-24 15:06:26
Jeszcze inną alternatywą mogą być różne procedury rejestrowania nie oparte na plikach w logging
pakiet :
SocketHandler
DatagramHandler
SyslogHandler
(i inni)
W ten sposób możesz łatwo mieć gdzieś demona logowania, do którego możesz bezpiecznie pisać i obsługiwać wyniki poprawnie. (Np. prosty serwer gniazd, który po prostu rozpakowuje wiadomość i emituje ją do własnej obracającej się obsługi plików.)
SyslogHandler
będzie dbać tego też dla Ciebie. Oczywiście możesz użyć własnej instancji syslog
, a nie systemowej.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-20 13:29:55
Wariant pozostałych, który utrzymuje wątek logowania i kolejki oddzielnie.
"""sample code for logging in subprocesses using multiprocessing
* Little handler magic - The main process uses loggers and handlers as normal.
* Only a simple handler is needed in the subprocess that feeds the queue.
* Original logger name from subprocess is preserved when logged in main
process.
* As in the other implementations, a thread reads the queue and calls the
handlers. Except in this implementation, the thread is defined outside of a
handler, which makes the logger definitions simpler.
* Works with multiple handlers. If the logger in the main process defines
multiple handlers, they will all be fed records generated by the
subprocesses loggers.
tested with Python 2.5 and 2.6 on Linux and Windows
"""
import os
import sys
import time
import traceback
import multiprocessing, threading, logging, sys
DEFAULT_LEVEL = logging.DEBUG
formatter = logging.Formatter("%(levelname)s: %(asctime)s - %(name)s - %(process)s - %(message)s")
class SubProcessLogHandler(logging.Handler):
"""handler used by subprocesses
It simply puts items on a Queue for the main process to log.
"""
def __init__(self, queue):
logging.Handler.__init__(self)
self.queue = queue
def emit(self, record):
self.queue.put(record)
class LogQueueReader(threading.Thread):
"""thread to write subprocesses log records to main process log
This thread reads the records written by subprocesses and writes them to
the handlers defined in the main process's handlers.
"""
def __init__(self, queue):
threading.Thread.__init__(self)
self.queue = queue
self.daemon = True
def run(self):
"""read from the queue and write to the log handlers
The logging documentation says logging is thread safe, so there
shouldn't be contention between normal logging (from the main
process) and this thread.
Note that we're using the name of the original logger.
"""
# Thanks Mike for the error checking code.
while True:
try:
record = self.queue.get()
# get the logger for this record
logger = logging.getLogger(record.name)
logger.callHandlers(record)
except (KeyboardInterrupt, SystemExit):
raise
except EOFError:
break
except:
traceback.print_exc(file=sys.stderr)
class LoggingProcess(multiprocessing.Process):
def __init__(self, queue):
multiprocessing.Process.__init__(self)
self.queue = queue
def _setupLogger(self):
# create the logger to use.
logger = logging.getLogger('test.subprocess')
# The only handler desired is the SubProcessLogHandler. If any others
# exist, remove them. In this case, on Unix and Linux the StreamHandler
# will be inherited.
for handler in logger.handlers:
# just a check for my sanity
assert not isinstance(handler, SubProcessLogHandler)
logger.removeHandler(handler)
# add the handler
handler = SubProcessLogHandler(self.queue)
handler.setFormatter(formatter)
logger.addHandler(handler)
# On Windows, the level will not be inherited. Also, we could just
# set the level to log everything here and filter it in the main
# process handlers. For now, just set it from the global default.
logger.setLevel(DEFAULT_LEVEL)
self.logger = logger
def run(self):
self._setupLogger()
logger = self.logger
# and here goes the logging
p = multiprocessing.current_process()
logger.info('hello from process %s with pid %s' % (p.name, p.pid))
if __name__ == '__main__':
# queue used by the subprocess loggers
queue = multiprocessing.Queue()
# Just a normal logger
logger = logging.getLogger('test')
handler = logging.StreamHandler()
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.setLevel(DEFAULT_LEVEL)
logger.info('hello from the main process')
# This thread will read from the subprocesses and write to the main log's
# handlers.
log_queue_reader = LogQueueReader(queue)
log_queue_reader.start()
# create the processes.
for i in range(10):
p = LoggingProcess(queue)
p.start()
# The way I read the multiprocessing warning about Queue, joining a
# process before it has finished feeding the Queue can cause a deadlock.
# Also, Queue.empty() is not realiable, so just make sure all processes
# are finished.
# active_children joins subprocesses when they're finished.
while multiprocessing.active_children():
time.sleep(.1)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-15 07:41:56
Książka kucharska z logowaniem Pythona zawiera dwa pełne przykłady: https://docs.python.org/3/howto/logging-cookbook.html#logging-to-a-single-file-from-multiple-processes
Używa QueueHandler
, który jest nowy w Pythonie 3.2, ale łatwy do skopiowania do własnego kodu (tak jak sam zrobiłem w Pythonie 2.7) z: https://gist.github.com/vsajip/591589
Każdy proces umieszcza swoje logowanie na Queue
, a następnie listener
wątek lub proces (dla każdego podano jeden przykład) odbiera te i zapisuje je wszystkie do pliku-bez ryzyka uszkodzenia lub zniekształcenia.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-18 06:47:17
Wszystkie obecne rozwiązania są zbyt połączone z konfiguracją logowania za pomocą funkcji obsługi. Moje rozwiązanie ma następującą architekturę i funkcje:
- możesz użyć dowolnej konfiguracji logowania, którą chcesz
- logowanie odbywa się w wątku demona
- Bezpieczne zamknięcie demona za pomocą Menedżera kontekstu
- Komunikacja do wątku logowania odbywa się za pomocą
multiprocessing.Queue
- w podprocesach,
logging.Logger
(i już zdefiniowane instancje) są łatane, aby wysłać Wszystkie zapisy do kolejki - New : sformatuj traceback i wiadomość przed wysłaniem do kolejki, aby uniknąć błędów wytrawiania
Kod z przykładem użycia i wyjściem można znaleźć w poniższym Gist: https://gist.github.com/schlamar/7003737
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-16 08:04:10
Poniżej znajduje się kolejne rozwiązanie z naciskiem na prostotę dla każdego (jak ja), którzy dostają się tutaj z Google. Logowanie powinno być łatwe! Tylko dla 3.2 lub wyższych.
import multiprocessing
import logging
from logging.handlers import QueueHandler, QueueListener
import time
import random
def f(i):
time.sleep(random.uniform(.01, .05))
logging.info('function called with {} in worker thread.'.format(i))
time.sleep(random.uniform(.01, .05))
return i
def worker_init(q):
# all records from worker processes go to qh and then into q
qh = QueueHandler(q)
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
logger.addHandler(qh)
def logger_init():
q = multiprocessing.Queue()
# this is the handler for all log records
handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter("%(levelname)s: %(asctime)s - %(process)s - %(message)s"))
# ql gets records from the queue and sends them to the handler
ql = QueueListener(q, handler)
ql.start()
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
# add the handler to the logger so records from this process are handled
logger.addHandler(handler)
return ql, q
def main():
q_listener, q = logger_init()
logging.info('hello from main thread')
pool = multiprocessing.Pool(4, worker_init, [q])
for result in pool.map(f, range(10)):
pass
pool.close()
pool.join()
q_listener.stop()
if __name__ == '__main__':
main()
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-23 20:13:33
Też lubię odpowiedź zzzeeka, ale Andre ma rację, że kolejka jest wymagana, aby zapobiec garbowaniu. Miałem trochę szczęścia z rurą, ale widziałem garbowanie, które jest nieco oczekiwane. Zaimplementowanie go okazało się trudniejsze niż myślałem, szczególnie ze względu na działanie w systemie Windows, gdzie istnieją dodatkowe ograniczenia dotyczące zmiennych globalnych i takie tam (zobacz: Jak zaimplementowano Wieloprocesor Pythona w systemie Windows?)
Ale w końcu to zadziałało. Ten przykład prawdopodobnie nie jest idealnie, więc komentarze i sugestie są mile widziane. Nie obsługuje również ustawiania formatera ani niczego innego niż root logger. Zasadniczo musisz ponownie zainstalować logger w każdym z procesów puli za pomocą kolejki i ustawić inne atrybuty na loggerze.Ponownie, wszelkie sugestie, jak ulepszyć Kod, są mile widziane. Na pewno nie znam jeszcze wszystkich sztuczek Pythona: -)
import multiprocessing, logging, sys, re, os, StringIO, threading, time, Queue
class MultiProcessingLogHandler(logging.Handler):
def __init__(self, handler, queue, child=False):
logging.Handler.__init__(self)
self._handler = handler
self.queue = queue
# we only want one of the loggers to be pulling from the queue.
# If there is a way to do this without needing to be passed this
# information, that would be great!
if child == False:
self.shutdown = False
self.polltime = 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):
#print "receive on"
while (self.shutdown == False) or (self.queue.empty() == False):
# so we block for a short period of time so that we can
# check for the shutdown cases.
try:
record = self.queue.get(True, self.polltime)
self._handler.emit(record)
except Queue.Empty, e:
pass
def send(self, s):
# send just puts it in the queue for the server to retrieve
self.queue.put(s)
def _format_record(self, record):
ei = record.exc_info
if ei:
dummy = self.format(record) # just to get traceback text into record.exc_text
record.exc_info = None # to avoid Unpickleable error
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):
time.sleep(self.polltime+1) # give some time for messages to enter the queue.
self.shutdown = True
time.sleep(self.polltime+1) # give some time for the server to time out and see the shutdown
def __del__(self):
self.close() # hopefully this aids in orderly shutdown when things are going poorly.
def f(x):
# just a logging command...
logging.critical('function number: ' + str(x))
# to make some calls take longer than others, so the output is "jumbled" as real MP programs are.
time.sleep(x % 3)
def initPool(queue, level):
"""
This causes the logging module to be initialized with the necessary info
in pool threads to work correctly.
"""
logging.getLogger('').addHandler(MultiProcessingLogHandler(logging.StreamHandler(), queue, child=True))
logging.getLogger('').setLevel(level)
if __name__ == '__main__':
stream = StringIO.StringIO()
logQueue = multiprocessing.Queue(100)
handler= MultiProcessingLogHandler(logging.StreamHandler(stream), logQueue)
logging.getLogger('').addHandler(handler)
logging.getLogger('').setLevel(logging.DEBUG)
logging.debug('starting main')
# when bulding the pool on a Windows machine we also have to init the logger in all the instances with the queue and the level of logging.
pool = multiprocessing.Pool(processes=10, initializer=initPool, initargs=[logQueue, logging.getLogger('').getEffectiveLevel()] ) # start worker processes
pool.map(f, range(0,50))
pool.close()
logging.debug('done')
logging.shutdown()
print "stream output is:"
print stream.getvalue()
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:54:36
Ponieważ możemy reprezentować rejestrowanie wieloprocesowe tylu wydawców i jednego abonenta( słuchacza), użycie ZeroMQ do implementacji wiadomości pub-SUB jest rzeczywiście opcją.
Ponadto, pyzmq moduł, wiązania Pythona dla ZMQ, implementujePUBHandler , który jest obiektem do publikowania komunikatów logowania przez zmq.Gniazdo PUB.
Istnieje rozwiązanie w sieci , do scentralizowanego logowania z rozproszonej aplikacji za pomocą PyZMQ i PUBHandler, które może być łatwo przystosowane do pracy lokalnie z wieloma procesami publikowania.
formatters = {
logging.DEBUG: logging.Formatter("[%(name)s] %(message)s"),
logging.INFO: logging.Formatter("[%(name)s] %(message)s"),
logging.WARN: logging.Formatter("[%(name)s] %(message)s"),
logging.ERROR: logging.Formatter("[%(name)s] %(message)s"),
logging.CRITICAL: logging.Formatter("[%(name)s] %(message)s")
}
# This one will be used by publishing processes
class PUBLogger:
def __init__(self, host, port=config.PUBSUB_LOGGER_PORT):
self._logger = logging.getLogger(__name__)
self._logger.setLevel(logging.DEBUG)
self.ctx = zmq.Context()
self.pub = self.ctx.socket(zmq.PUB)
self.pub.connect('tcp://{0}:{1}'.format(socket.gethostbyname(host), port))
self._handler = PUBHandler(self.pub)
self._handler.formatters = formatters
self._logger.addHandler(self._handler)
@property
def logger(self):
return self._logger
# This one will be used by listener process
class SUBLogger:
def __init__(self, ip, output_dir="", port=config.PUBSUB_LOGGER_PORT):
self.output_dir = output_dir
self._logger = logging.getLogger()
self._logger.setLevel(logging.DEBUG)
self.ctx = zmq.Context()
self._sub = self.ctx.socket(zmq.SUB)
self._sub.bind('tcp://*:{1}'.format(ip, port))
self._sub.setsockopt(zmq.SUBSCRIBE, "")
handler = handlers.RotatingFileHandler(os.path.join(output_dir, "client_debug.log"), "w", 100 * 1024 * 1024, 10)
handler.setLevel(logging.DEBUG)
formatter = logging.Formatter("%(asctime)s;%(levelname)s - %(message)s")
handler.setFormatter(formatter)
self._logger.addHandler(handler)
@property
def sub(self):
return self._sub
@property
def logger(self):
return self._logger
# And that's the way we actually run things:
# Listener process will forever listen on SUB socket for incoming messages
def run_sub_logger(ip, event):
sub_logger = SUBLogger(ip)
while not event.is_set():
try:
topic, message = sub_logger.sub.recv_multipart(flags=zmq.NOBLOCK)
log_msg = getattr(logging, topic.lower())
log_msg(message)
except zmq.ZMQError as zmq_error:
if zmq_error.errno == zmq.EAGAIN:
pass
# Publisher processes loggers should be initialized as follows:
class Publisher:
def __init__(self, stop_event, proc_id):
self.stop_event = stop_event
self.proc_id = proc_id
self._logger = pub_logger.PUBLogger('127.0.0.1').logger
def run(self):
self._logger.info("{0} - Sending message".format(proc_id))
def run_worker(event, proc_id):
worker = Publisher(event, proc_id)
worker.run()
# Starting subscriber process so we won't loose publisher's messages
sub_logger_process = Process(target=run_sub_logger,
args=('127.0.0.1'), stop_event,))
sub_logger_process.start()
#Starting publisher processes
for i in range(MAX_WORKERS_PER_CLIENT):
processes.append(Process(target=run_worker,
args=(stop_event, i,)))
for p in processes:
p.start()
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-08 08:27:22
Po prostu Opublikuj gdzieś swoją instancję rejestratora. w ten sposób pozostałe moduły i klienci mogą korzystać z twojego API, aby uzyskać rejestrator bez konieczności import multiprocessing
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-13 04:40:00
Podobała mi się odpowiedź zzzeeka. Po prostu zamieniłbym Potok na kolejkę, ponieważ jeśli wiele wątków / procesów używa tego samego końca potoku do generowania wiadomości logów, zostaną one zniekształcone.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-06 13:59:52
Może delegowanie wszystkich logowań do innego procesu, który odczytuje wszystkie wpisy z kolejki?
LOG_QUEUE = multiprocessing.JoinableQueue()
class CentralLogger(multiprocessing.Process):
def __init__(self, queue):
multiprocessing.Process.__init__(self)
self.queue = queue
self.log = logger.getLogger('some_config')
self.log.info("Started Central Logging process")
def run(self):
while True:
log_level, message = self.queue.get()
if log_level is None:
self.log.info("Shutting down Central Logging process")
break
else:
self.log.log(log_level, message)
central_logger_process = CentralLogger(LOG_QUEUE)
central_logger_process.start()
Po prostu udostępnij LOG_QUEUE za pomocą jednego z mechanizmów wieloprocesowych lub nawet dziedziczenia i wszystko działa dobrze!
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-12 23:13:44
Mam rozwiązanie podobne do ironhackera z tym, że używam logowania.wyjątek w niektórych moich kodzie i okazało się, że musiałem sformatować wyjątek przed przekazaniem go z powrotem nad kolejką, ponieważ tracebacki nie są pickle'able: {]}
class QueueHandler(logging.Handler):
def __init__(self, queue):
logging.Handler.__init__(self)
self.queue = queue
def emit(self, record):
if record.exc_info:
# can't pass exc_info across processes so just format now
record.exc_text = self.formatException(record.exc_info)
record.exc_info = None
self.queue.put(record)
def formatException(self, ei):
sio = cStringIO.StringIO()
traceback.print_exception(ei[0], ei[1], ei[2], None, sio)
s = sio.getvalue()
sio.close()
if s[-1] == "\n":
s = s[:-1]
return s
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-05 06:11:37
Poniżej znajduje się klasa, która może być używana w środowisku Windows, wymaga ActivePython. Możesz również dziedziczyć dla innych programów obsługi logowania (StreamHandler itp.)
class SyncronizedFileHandler(logging.FileHandler):
MUTEX_NAME = 'logging_mutex'
def __init__(self , *args , **kwargs):
self.mutex = win32event.CreateMutex(None , False , self.MUTEX_NAME)
return super(SyncronizedFileHandler , self ).__init__(*args , **kwargs)
def emit(self, *args , **kwargs):
try:
win32event.WaitForSingleObject(self.mutex , win32event.INFINITE)
ret = super(SyncronizedFileHandler , self ).emit(*args , **kwargs)
finally:
win32event.ReleaseMutex(self.mutex)
return ret
A oto przykład, który demonstruje użycie:
import logging
import random , time , os , sys , datetime
from string import letters
import win32api , win32event
from multiprocessing import Pool
def f(i):
time.sleep(random.randint(0,10) * 0.1)
ch = random.choice(letters)
logging.info( ch * 30)
def init_logging():
'''
initilize the loggers
'''
formatter = logging.Formatter("%(levelname)s - %(process)d - %(asctime)s - %(filename)s - %(lineno)d - %(message)s")
logger = logging.getLogger()
logger.setLevel(logging.INFO)
file_handler = SyncronizedFileHandler(sys.argv[1])
file_handler.setLevel(logging.INFO)
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
#must be called in the parent and in every worker process
init_logging()
if __name__ == '__main__':
#multiprocessing stuff
pool = Pool(processes=10)
imap_result = pool.imap(f , range(30))
for i , _ in enumerate(imap_result):
pass
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-01 06:57:48
Oto mój prosty hack / obejście... Nie najbardziej wyczerpujące, ale łatwo modyfikowalne i prostsze do przeczytania i zrozumienia, jak sądzę, niż jakiekolwiek inne odpowiedzi, które znalazłem przed napisaniem tego:
import logging
import multiprocessing
class FakeLogger(object):
def __init__(self, q):
self.q = q
def info(self, item):
self.q.put('INFO - {}'.format(item))
def debug(self, item):
self.q.put('DEBUG - {}'.format(item))
def critical(self, item):
self.q.put('CRITICAL - {}'.format(item))
def warning(self, item):
self.q.put('WARNING - {}'.format(item))
def some_other_func_that_gets_logger_and_logs(num):
# notice the name get's discarded
# of course you can easily add this to your FakeLogger class
local_logger = logging.getLogger('local')
local_logger.info('Hey I am logging this: {} and working on it to make this {}!'.format(num, num*2))
local_logger.debug('hmm, something may need debugging here')
return num*2
def func_to_parallelize(data_chunk):
# unpack our args
the_num, logger_q = data_chunk
# since we're now in a new process, let's monkeypatch the logging module
logging.getLogger = lambda name=None: FakeLogger(logger_q)
# now do the actual work that happens to log stuff too
new_num = some_other_func_that_gets_logger_and_logs(the_num)
return (the_num, new_num)
if __name__ == '__main__':
multiprocessing.freeze_support()
m = multiprocessing.Manager()
logger_q = m.Queue()
# we have to pass our data to be parallel-processed
# we also need to pass the Queue object so we can retrieve the logs
parallelable_data = [(1, logger_q), (2, logger_q)]
# set up a pool of processes so we can take advantage of multiple CPU cores
pool_size = multiprocessing.cpu_count() * 2
pool = multiprocessing.Pool(processes=pool_size, maxtasksperchild=4)
worker_output = pool.map(func_to_parallelize, parallelable_data)
pool.close() # no more tasks
pool.join() # wrap up current tasks
# get the contents of our FakeLogger object
while not logger_q.empty():
print logger_q.get()
print 'worker output contained: {}'.format(worker_output)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-13 16:55:19
Jedną z alternatyw jest zapisanie logowania mutliprocessingu do znanego pliku i zarejestrowanie atexit
Handlera, który połączy się z tymi procesami, odczyta go z powrotem na stderr; jednak nie otrzymasz w ten sposób przepływu w czasie rzeczywistym do wiadomości wyjściowych na stderr.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-13 04:40:17
Jeśli masz blokady występujące w kombinacji blokad, wątków i widełek w module logging
, który jest zgłoszony w Zgłoś błąd 6721 (Zobacz także powiązane pytanie).
Jest małe rozwiązanie fixup opublikowane tutaj .
To jednak naprawi wszelkie potencjalne impasy w logging
. To nie naprawi tego, że rzeczy mogą być zniekształcone. Zobacz inne odpowiedzi przedstawione tutaj.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:31:33
There is this great package
Pakiet: https://pypi.python.org/pypi/multiprocessing-logging/
Kod: https://github.com/jruere/multiprocessing-logging
Zainstaluj:
pip install multiprocessing-logging
Następnie dodaj:
import multiprocessing_logging
# This enables logs inside process
multiprocessing_logging.install_mp_handler()
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-07 02:55:58
Moim dzieciom, które od dziesięcioleci spotykają się z tym samym problemem i znalazły to pytanie na tej stronie, zostawiam tę odpowiedź.
Prostota a nadmierna komplikacja. Użyj innych narzędzi. Python jest niesamowity, ale nie został zaprojektowany do robienia pewnych rzeczy.
Poniższy fragment dla logrotate daemon działa dla mnie i nie komplikuje zbytnio rzeczy. Zaplanuj jego działanie co godzinę i
/var/log/mylogfile.log {
size 1
copytruncate
create
rotate 10
missingok
postrotate
timeext=`date -d '1 hour ago' "+%Y-%m-%d_%H"`
mv /var/log/mylogfile.log.1 /var/log/mylogfile-$timeext.log
endscript
}
Tak to instaluję (dowiązania symboliczne nie działają dla logrotate):
sudo cp /directpath/config/logrotate/myconfigname /etc/logrotate.d/myconfigname
sudo cp /etc/cron.daily/logrotate /etc/cron.hourly/logrotate
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-07 16:32:32