আমি Zzzeek এর উত্তরও পছন্দ করি তবে আন্ড্রে সঠিক যে গ্যাবলিং প্রতিরোধের জন্য একটি সারি প্রয়োজন। পাইপটি নিয়ে আমার কিছু ভাগ্য ছিল, তবে কিছুটা প্রত্যাশিত গার্বলিং দেখেছি। এটি প্রয়োগ করা আমার চিন্তাভাবনার চেয়ে শক্ত হয়ে গেছে, বিশেষত উইন্ডোজে চলার কারণে, যেখানে বিশ্বব্যাপী ভেরিয়েবল এবং স্টাফ সম্পর্কে কিছু অতিরিক্ত বিধিনিষেধ রয়েছে (দেখুন: উইন্ডোজে পাইথন মাল্টিপ্রসেসিং কীভাবে প্রয়োগ করা হয়েছে? )
কিন্তু, অবশেষে আমি এটি কাজ করেছিলাম। এই উদাহরণটি সম্ভবত নিখুঁত নয়, সুতরাং মন্তব্য এবং পরামর্শ স্বাগত। এটি ফর্ম্যাটর সেট করতে বা রুট লগার ব্যতীত অন্য কোনও কিছুকে সমর্থন করে না। মূলত, আপনাকে প্রতিটি কলা দিয়ে পুলের প্রক্রিয়াতে লগার পুনরায় লাগাতে হবে এবং লগারে অন্যান্য বৈশিষ্ট্যগুলি সেট আপ করতে হবে।
আবার কোডটি কীভাবে আরও উন্নত করা যায় সে সম্পর্কে কোনও পরামর্শ স্বাগত। আমি অবশ্যই সমস্ত পাইথন ট্রিক্স এখনও জানি না :-)
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()