পাইথনে মাল্টিপ্রসেসিং ব্যবহার করার সময় আমার কীভাবে লগ ইন করা উচিত?


238

এই মুহূর্তে আমার কাছে একটি ফ্রেমওয়ার্কে একটি কেন্দ্রীয় মডিউল রয়েছে যা পাইথন ২.6 multiprocessingমডিউলটি ব্যবহার করে একাধিক প্রক্রিয়া তৈরি করে । যেহেতু এটি ব্যবহার করে multiprocessing, সেখানে মডিউল স্তরের মাল্টিপ্রসেসিং-সচেতন লগ রয়েছে LOG = multiprocessing.get_logger()। প্রতি ডক্স , এই এটির প্রক্রিয়া-ভাগ কেশ যাতে আপনি আপ অসৎচয়ন জিনিষ না হয়েছে sys.stderrএকাধিক একযোগে এটা লিখিতভাবে প্রসেস না থাকার (অথবা যাই হোক না কেন filehandle)।

আমার এখন যে বিষয়টি আছে তা হ'ল ফ্রেমওয়ার্কের অন্যান্য মডিউলগুলি মাল্টিপ্রসেসিং-সচেতন নয়। আমি এটি যেভাবে দেখছি, আমার এই কেন্দ্রীয় মডিউলটির সমস্ত নির্ভরতাগুলি মাল্টিপ্রসেসিং-সচেতন লগিং ব্যবহার করতে হবে। ফ্রেমওয়ার্কের মধ্যে এটি বিরক্তিকর , ফ্রেমওয়ার্কের সমস্ত ক্লায়েন্টদের একা ছেড়ে দিন। এমন কোন বিকল্প আছে যা আমি ভাবছি না?


10
আপনি যে ডক্সগুলির সাথে লিঙ্ক করেছেন, আপনি যা বলছেন তার ঠিক বিপরীতে লিখেছেন, লগারের কোনও প্রক্রিয়া ভাগ করা লক নেই এবং জিনিসগুলি মিশ্রিত হয় - আমারও সমস্যা ছিল।
সেবাস্তিয়ান ব্লাস্ক

3
stdlib ডক্সে উদাহরণগুলি দেখুন: একাধিক প্রক্রিয়া থেকে একটি ফাইলে লগইন করুন । রেসিপিগুলির জন্য অন্যান্য মডিউলগুলি মাল্টিপ্রসেসিং-সচেতন হওয়ার প্রয়োজন হয় না।
jfs

তো, ব্যবহারের ক্ষেত্রে multiprocessing.get_logger()কী? মনে হয় লগিং করার এই অন্যান্য পদ্ধতির উপর ভিত্তি করে হ'ল লগিং কার্যকারিতা multiprocessingঅল্প মূল্য।
টিম লুডুইনস্কি

4
get_logger()multiprocessingমডিউল নিজেই ব্যবহৃত লগার । আপনি কোনও multiprocessingসমস্যা ডিবাগ করতে চাইলে এটি কার্যকর ।
jfs

উত্তর:


69

অ-অনুপ্রবেশজনকভাবে মোকাবেলা করার একমাত্র উপায় হ'ল:

  1. প্রতিটি কর্মী প্রক্রিয়া এমনভাবে ছড়িয়ে দিন যে এর লগটি একটি ভিন্ন ফাইল বর্ণনাকারী (ডিস্কে বা পাইপে যান।) আদর্শভাবে, সমস্ত লগ এন্ট্রি টাইমস্ট্যাম্প করা উচিত।
  2. আপনার নিয়ামক প্রক্রিয়া তারপরে নিম্নলিখিতগুলির মধ্যে একটি করতে পারে :
    • যদি ডিস্ক ফাইলগুলি ব্যবহার করে: রানস্টোর শেষে লগ ফাইলগুলি Coalesce করুন, টাইমস্ট্যাম্প অনুসারে বাছাই করুন
    • পাইপ ব্যবহার করা হলে (প্রস্তাবিত): Coalesce লগ সমস্ত পাইপ থেকে অন ফ্লাইট একটি কেন্দ্রীয় লগ ফাইলে প্রবেশ করুন। (উদাহরণস্বরূপ, selectপাইপগুলির ফাইল বর্ণনাকারী থেকে পর্যায়ক্রমে উপলভ্য লগ এন্ট্রিগুলিতে মার্জ-বাছাই করে এবং কেন্দ্রীভূত লগটিতে ফ্লাশ করুন Rep পুনরাবৃত্তি করুন))

ভাল, আমি এটা চিন্তা করার আগে 35s ছিল (ভেবেছিলাম আমি ব্যবহার করব atexit:-)। সমস্যা হ'ল এটি আপনাকে রিয়েলটাইম রিডআউট দেয় না। এটি মাল্টিথ্রেডিংয়ের বিপরীতে মাল্টিপ্রসেসিংয়ের দামের অংশ হতে পারে।
সিডলারি

@ সিডলারি, পাইপযুক্ত পদ্ধতির ব্যবহার করে এটি যেমন পাওয়া যায় তত কাছাকাছি রিয়েলটাইম হবে (বিশেষত
স্টাডার যদি

1
ঘটনাচক্রে, এখানে বড় অনুমান: উইন্ডোজ নয়। আপনি কি উইন্ডোজে আছেন?
ভ্লাদার

22
পরিবর্তে মূল প্রক্রিয়াটিতে কেবল একটি মাল্টিপ্রসেসিং.কুই এবং একটি লগিং থ্রেড ব্যবহার করবেন না কেন? সহজ মনে হচ্ছে।
ব্র্যান্ডন রোডস

1
@ ব্র্যান্ডনরোডস - যেমনটি আমি বলেছি, অ-প্রবেশমূলকভাবে । ব্যবহার multiprocessing.Queueসহজ হবে না যদি সেখানে ব্যবহারের বৈদ্যুতিক তার লাগানো এ কোডের একটি অনেক আছে multiprocessing.Queue, এবং / অথবা যদি কর্মক্ষমতা একটি বিষয়
vladr

122

আমি এখনই আমার নিজের একটি লগ হ্যান্ডলার লিখেছি যা কেবল পাইপের মাধ্যমে পিতামাতার প্রক্রিয়াতে সমস্ত কিছু ফিড করে। আমি কেবল এটি দশ মিনিটের জন্য পরীক্ষা করে দেখছি তবে এটি বেশ ভালভাবে কাজ করছে বলে মনে হচ্ছে।

( দ্রষ্টব্য: এটি হার্ডকোডযুক্ত RotatingFileHandler, এটি আমার নিজের ব্যবহারের ক্ষেত্রে))


আপডেট: @ জাভিয়ার এখন পাইপিতে উপলব্ধ প্যাকেজ হিসাবে এই পদ্ধতির রক্ষণাবেক্ষণ করেছেন - https://github.com/jruere/m মাল্টিপ্রসেসিং- লগিং এ গিথুব, পাইপিতে মাল্টিপ্রসেসিং-লগিং দেখুন


আপডেট: বাস্তবায়ন!

এটি এখন একযোগে সঠিক পরিচালনা করার জন্য একটি সারি ব্যবহার করে এবং ত্রুটিগুলি থেকেও সঠিকভাবে পুনরুদ্ধার করে। আমি এখন বেশ কয়েক মাস ধরে এটি উত্পাদনে ব্যবহার করছি এবং নীচের বর্তমান সংস্করণটি ইস্যু ছাড়াই কাজ করে।

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)

4
উপরের হ্যান্ডলারটি পিতামাতার প্রক্রিয়া থেকে সমস্ত ফাইল লিখন করে এবং শিশু প্রক্রিয়াগুলি থেকে পাস করা বার্তাগুলি গ্রহণ করতে কেবল একটি থ্রেড ব্যবহার করে। যদি আপনি কোনও উদ্দীপনাযুক্ত শিশু প্রক্রিয়া থেকে হ্যান্ডলারটি নিজেই প্রার্থনা করেন তবে এটি এটিকে ভুলভাবে ব্যবহার করছে এবং আপনি RotatingFileHandler এর মতো সমস্ত সমস্যা পাবেন get আমি উপরের কোডটি বছরের পর বছর ধরে কোনও সমস্যা ছাড়াই ব্যবহার করেছি।
zzzeek

9
দুর্ভাগ্যক্রমে এই পদ্ধতিটি উইন্ডোজে কাজ করে না। " Docs.python.org/library/m মাল্টিপ্রোসেসিং html 16.6.2.12 থেকে " নোট করুন যে উইন্ডোজ চাইল্ড প্রসেসগুলি কেবল প্যারেন্ট প্রসেসের লগারের মাত্রায় উত্তরাধিকারী হবে - লগারের অন্য কোনও স্বনির্ধারণ উত্তরাধিকার সূত্রে প্রাপ্ত হবে না। " উপ-প্রক্রিয়াগুলি হ্যান্ডলারের উত্তরাধিকারী হবে না এবং আপনি এটি পরিষ্কারভাবে পাস করতে পারবেন না কারণ এটি বাছাইযোগ্য নয়।
নোহ ইয়েটার

2
এটি লক্ষণীয় যে এতে multiprocessing.Queueএকটি থ্রেড ব্যবহার করে put()। সুতরাং সমস্ত উপ-প্রসেসগুলি তৈরির আগে প্রার্থনা করবেন না put(যেমন MultiProcessingLogহ্যান্ডলার ব্যবহার করে একটি লগইন করুন ) do অন্যথায় থ্রেডটি শিশু প্রক্রিয়াতে মারা যাবে। একটি সমাধান হ'ল Queue._after_fork()প্রতিটি শিশু প্রক্রিয়া শুরু করার সময় কল করা বা তার multiprocessing.queues.SimpleQueueপরিবর্তে ব্যবহার করা, এতে থ্রেড জড়িত না তবে অবরুদ্ধ হয়।
দানকি ওয়াং

5
আপনি কি এমন একটি সাধারণ উদাহরণ জুড়তে পারবেন যা প্রাথমিকভাবে দেখায়, পাশাপাশি একটি অনুমান শিশু প্রক্রিয়া থেকেও ব্যবহার করে? আমি আপনার সন্তানের প্রক্রিয়াটি কীভাবে আপনার ক্লাসের অন্য কোনও উদাহরণকে তাত্ক্ষণিক না করে সারিটিতে অ্যাক্সেস পাওয়ার কথা বলে সে সম্পর্কে আমি নিশ্চিত না।
জেসিবুস্কিং

11
@ জাজেক, এই সমাধানটি ভাল তবে আমি এর সাথে কোনও প্যাকেজ বা তেমন কিছু খুঁজে পাইনি যার ফলে আমি একটি তৈরি করেছি multiprocessing-logging
জাভিয়ের

30

QueueHandlerপাইথন ৩.২++ এ স্থানীয়, এবং ঠিক এটি করে। এটি পূর্ববর্তী সংস্করণগুলিতে সহজেই প্রতিলিপি করা হয়।

পাইথন ডক্সের দুটি সম্পূর্ণ উদাহরণ রয়েছে: একাধিক প্রক্রিয়া থেকে একটি ফাইলে লগইন করা

পাইথন <3.2 ব্যবহার করছেন তাদের জন্য কেবল QueueHandlerনিজের কোড থেকে অনুলিপি করুন : https://gist.github.com/vsajip/591589 বা বিকল্পভাবে লোগুলিগুলি আমদানি করুন

প্রতিটি প্রক্রিয়া (প্যারেন্ট প্রসেস সহ) এটির লগইন রাখে Queueএবং তারপরে একটি listenerথ্রেড বা প্রক্রিয়া (প্রত্যেকটির জন্য একটি উদাহরণ সরবরাহ করা হয়) সেগুলি তুলে ধরে সেগুলিকে একটি ফাইলে লিখে দেয় - দুর্নীতি বা জড়িত হওয়ার কোনও ঝুঁকি নেই।


21

গুগল থেকে এখানে আসা যে কেউ (আমার মতো) এর সরলতার উপর ফোকাস সহ নীচের আরেকটি সমাধান রয়েছে। লগিং সহজ হওয়া উচিত! শুধুমাত্র ৩.২ বা ততোধিকের জন্য।

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()

2
QueueHandlerএবং QueueListenerক্লাসের পাশাপাশি পাইথন 2.7 ব্যবহার করা যেতে পারে, উপলব্ধ logutilsপ্যাকেজ।
লেভ লেভিটস্কি

5
মূল প্রক্রিয়াটির লগারে একটি কুইহ্যান্ডলার ব্যবহার করা উচিত। আপনার বর্তমান কোডে, প্রধান প্রক্রিয়াটি কাতাকে বাইপাস করছে তাই মূল প্রক্রিয়া এবং শ্রমিকদের মধ্যে জাতিদের অবস্থা হতে পারে। প্রত্যেকেরই কাতারে লাউইন করা উচিত (একটি কুই হ্যান্ডলারের মাধ্যমে) এবং কেবলমাত্র কুইলিস্টনারকে স্ট্রিমহ্যান্ডলারটিতে লগ করার অনুমতি দেওয়া উচিত।
ইসমাইল ইএল এটিআইপিআই 16

এছাড়াও, আপনাকে প্রতিটি সন্তানের লগারটি আরম্ভ করতে হবে না। প্যারেন্ট প্রসেসে কেবল লগারটি শুরু করুন এবং প্রতিটি শিশু প্রক্রিয়াতে লগারটি পান।
ওকেপ করুন

20

তবুও অন্য বিকল্প হতে পারে loggingপ্যাকেজের বিভিন্ন নন-ফাইল-ভিত্তিক লগিং হ্যান্ডলারগুলি :

  • SocketHandler
  • DatagramHandler
  • SyslogHandler

(এবং অন্যদের)

এইভাবে, আপনি সহজেই কোথাও একটি লগিং ডেমন থাকতে পারে যা আপনি নিরাপদে লিখতে পারেন এবং ফলাফলগুলি সঠিকভাবে পরিচালনা করতে পারেন। (উদাহরণস্বরূপ, একটি সাধারণ সকেট সার্ভার যা বার্তাটি কেবল পিকআপ করে না এবং এটিকে তার নিজস্ব ঘোরানো ফাইল হ্যান্ডলারের সাথে নির্গত করে))

SyslogHandlerখুব আপনার জন্য এই যত্ন নিতে হবে। অবশ্যই, আপনি নিজের উদাহরণটি ব্যবহার করতে পারেন syslog, সিস্টেমটি নয়।


13

অন্যদের একটি বৈকল্পিক যা লগিং এবং সারি থ্রেডকে পৃথক রাখে।

"""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)

আমি সারি রেকর্ড থেকে লগার নাম আনার একটি ধারণা পছন্দ করি। এটি fileConfig()মেইনপ্রসেসে প্রচলিত এবং পুল ওয়ার্কার্সে সবেমাত্র কনফিগার করা লগার (কেবলমাত্র setLevel(logging.NOTSET)) ব্যবহার করতে দেয় । আমি অন্য মন্তব্যে যেমন উল্লেখ করেছি, আমি পুলটি ব্যবহার করছি তাই মাল্টিপ্রসেসিংয়ের পরিবর্তে ম্যানেজারের কাছ থেকে আমার কুই (প্রক্সি) পেতে হয়েছিল যাতে এটি মিশ্রিত করা যায়। এটি আমাকে অভিধানের ভিতরে কোনও কর্মীর কাছে সারি পাস করতে দেয় (যার বেশিরভাগটি আরগস্পারেস অবজেক্ট ব্যবহার করে নেওয়া হয়েছে vars())। আমি মনে করি শেষ পর্যন্ত এমএস উইন্ডোজের পক্ষে সেরা পন্থা যা কাঁটাচামচ () অভাবযুক্ত এবং @Zzeak সমাধানের জন্য বিরতি দেয়।
mtl

@mlt আমি মনে করি আপনার কাছে পরিবর্তে Init একটি মাল্টিপ্রসেসিং সারি করা হতে পারে একটি ম্যানেজার ব্যবহার করে এর (উত্তর দেখুন stackoverflow.com/questions/25557686/... - এটা লক্স সম্বন্ধে কিন্তু আমি মনে করি ভাল হিসাবে এটি লাইনগুলি জন্য কাজ করে)
fantabolous

@ ফ্যান্টাবোলাস যা এমএস উইন্ডোজ বা অন্য কোনও প্ল্যাটফর্মের অভাবে কাজ করবে না fork। এইভাবে প্রতিটি প্রক্রিয়াটির নিজস্ব স্বতন্ত্র নিরর্থক সারি থাকবে। লিঙ্কযুক্ত কিউ / এ-এর দ্বিতীয় পদ্ধতি এ জাতীয় প্ল্যাটফর্মগুলিতে কাজ করবে না। এটি বহনযোগ্য কোডের একটি উপায় to
mlt

আকর্ষণীয় আমি উইন্ডোজটি ব্যবহার করছি এবং এটি আমার পক্ষে ঠিক কাজ করবে বলে মনে হচ্ছে - শেষ বারের মত মন্তব্য করার পরেও আমি multiprocessing.Queueমূল প্রক্রিয়াটির সাথে ভাগ করে নেওয়ার প্রক্রিয়াগুলির একটি পুল স্থাপন করেছি এবং সেই থেকে আমি ক্রমাগত এটি ব্যবহার করে চলেছি। এটি কেন কাজ করে তা বোঝার দাবি করবেন না।
চমত্কার

10

সমস্ত বর্তমান সমাধান হ্যান্ডলার ব্যবহার করে লগিং কনফিগারেশনে খুব মিলিত। আমার সমাধানে নিম্নলিখিত স্থাপত্য এবং বৈশিষ্ট্য রয়েছে:

  • আপনি যে কোনও লগিং কনফিগারেশন ব্যবহার করতে পারেন
  • ডেমন থ্রেডে লগিং করা হয়
  • প্রসঙ্গ ব্যবস্থাপক ব্যবহার করে ডেমনটি নিরাপদ বন্ধ করা
  • লগিং থ্রেডে যোগাযোগ দ্বারা সম্পন্ন হয় multiprocessing.Queue
  • সাব-প্রসেসগুলিতে, logging.Logger(এবং ইতিমধ্যে সংজ্ঞায়িত দৃষ্টান্তগুলি) সারিতে সমস্ত রেকর্ড প্রেরণ করার জন্য প্যাচ করা হয়
  • নতুন : বাছাইয়ের ত্রুটিগুলি রোধ করতে কাতারে পাঠানোর আগে ফর্ম্যাট ট্রেসব্যাক এবং বার্তা

ব্যবহারের উদাহরণ এবং আউটপুট সহ কোড নিম্নলিখিত গিস্টটিতে পাওয়া যাবে: https://gist.github.com/schlamar/7003737


যদি না আমি কিছু অনুপস্থিত করছি, এই আসলে একটি ডেমন থ্রেড, যেহেতু আপনার সেট করা না হয় daemon_thread.daemonথেকে True। আমার পাইথন প্রোগ্রামটি যথাযথভাবে প্রস্থান করার জন্য আমার প্রাসঙ্গিক ব্যবস্থাপকের মধ্যে যখন কোনও ব্যতিক্রম ঘটে তখন তা করার দরকার ছিল।
blah238

আমিও লক্ষ্য দ্বারা নিক্ষিপ্ত ধরা, লগ এবং গেলা ব্যতিক্রম করা প্রয়োজন funcমধ্যে logged_call, অন্যথায় ব্যতিক্রম অন্যান্য লগ আউটপুট বিকৃত পেতে হবে। এটির
blah238

8

যেহেতু আমরা বহু প্রকাশক এবং একজন গ্রাহক (শ্রোতা) হিসাবে মাল্টিপ্রসেস লগিং উপস্থাপন করতে পারি, তাই পিইউবি -এসইউবি ম্যাসেজিং বাস্তবায়নের জন্য জিরোএমকিউ ব্যবহার করা আসলেই একটি বিকল্প।

তদুপরি, পাইজএমকিউ মডিউল, জেডএমকিউর জন্য পাইথন বাইন্ডিংগুলি, পুবহ্যান্ডলার প্রয়োগ করে , এটি একটি zmq.PUB সকেটের মাধ্যমে লগিং বার্তা প্রকাশের জন্য অবজেক্ট।

ওয়েবে একটি সমাধান রয়েছে , পাইজএমকিউ এবং পিইউবিএইচডিএলআর ব্যবহার করে বিতরণ অ্যাপ্লিকেশন থেকে সেন্ট্রালাইজড লগিংয়ের জন্য যা একাধিক প্রকাশের প্রক্রিয়াগুলির সাথে স্থানীয়ভাবে কাজ করার জন্য সহজেই গৃহীত হতে পারে।

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()

6

আমি 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()

1
আমি ভাবছি যদি if 'MainProcess' == multiprocessing.current_process().name:পাসের জায়গায় ব্যবহার করা যায় child?
mlt

যদি অন্য কেউ উইন্ডোজটিতে পৃথক প্রক্রিয়া সামগ্রীর পরিবর্তে প্রক্রিয়া পুলটি ব্যবহার করার চেষ্টা করে থাকে তবে এটি উল্লেখযোগ্য যে ম্যানেজারটি সরাসরি পিক করতে না পারায় সাব-প্রসেসগুলিতে সারি পাস করতে ব্যবহৃত হবে।
mlt 6

এই বাস্তবায়ন আমার জন্য ভাল কাজ করেছে। আমি এটিকে সংশোধন করে একটি স্বেচ্ছাসেবী সংখ্যক হ্যান্ডলারের সাথে কাজ করি। এইভাবে আপনি একটি নন-মাল্টিপ্রসেসিং ফ্যাশনে আপনাকে রুট হ্যান্ডলারটি কনফিগার করতে পারেন, তারপরে যেখানে সারি তৈরি করা নিরাপদ হবে, এটিতে রুট হ্যান্ডলারগুলি পাস করুন, তাদের মুছুন, এবং এটিই একমাত্র হ্যান্ডলার হিসাবে তৈরি করুন।
Jaxor24

3

আপনার লগারের উদাহরণটি কোথাও প্রকাশ করুন। এইভাবে, অন্যান্য মডিউল এবং ক্লায়েন্টগুলি আপনার API ব্যবহার করে লगरটি ছাড়াই ব্যবহার করতে পারে import multiprocessing


1
এটির সাথে সমস্যাটি হ'ল মাল্টিপ্রসেসিং লগারগুলি নামবিহীন প্রদর্শিত হয়, তাই আপনি বার্তা প্রবাহটি সহজেই ব্যাখ্যা করতে সক্ষম হবেন না। সম্ভবত এটি সৃষ্টির পরে তাদের নামকরণ করা সম্ভব হবে, যা এটি দেখার পক্ষে আরও যুক্তিসঙ্গত করে তোলে।
সিডলারি

ভাল, প্রতিটি মডিউলের জন্য একটি লগার প্রকাশ করুন, বা আরও ভাল, রফতানি ডাইভারেন্ট ক্লোজারগুলি যা মডিউলটির সাথে লগার ব্যবহার করে। মোডটি হ'ল অন্য মডিউলগুলি আপনার এপিআই
জাভিয়ের

অবশ্যই যুক্তিসঙ্গত (এবং আমার কাছ থেকে +1!) তবে আমি যে import logging; logging.basicConfig(level=logging.DEBUG); logging.debug('spam!')কোনও জায়গা থেকে সক্ষম হতে পেরেছি এবং এটি সঠিকভাবে কাজ করতে চাই।
সিডলারি

3
এটি একটি আকর্ষণীয় ঘটনা যা আমি যখন পাইথনটি ব্যবহার করি তখন দেখি যে আমরা 1 বা 2 সাধারণ লাইনে যা করতে চাই তা করতে সক্ষম হয়ে অভ্যস্ত যা অন্যান্য ভাষায় সরল এবং যৌক্তিক পদ্ধতির (যেমন, মাল্টিপ্রসেসিং লগার বা মোড়কে প্রকাশ করার জন্য) এটি একটি অ্যাক্সেসরে থাকা) এখনও বোঝার মতো অনুভব করে। :)
কাইলোটন

3

আমি জাজেকের উত্তরটি পছন্দ করেছি। আমি কেবল একটি সারিটির জন্য পাইপটি প্রতিস্থাপন করব যেহেতু যদি একাধিক থ্রেড / প্রক্রিয়াগুলি লগ বার্তা উত্পন্ন করতে একই পাইপ প্রান্তটি ব্যবহার করে তবে তারা শেষ হয়ে যাবে।


হ্যান্ডলারের সাথে আমার কিছু সমস্যা ছিল, যদিও এটি যে বার্তাগুলি ছড়িয়ে দেওয়া ছিল না, কেবল তার পুরো বিষয়টি কাজ করা বন্ধ করে দেবে। আমি পাইপকে কাতারে পরিবর্তন করেছি যেহেতু এটি আরও উপযুক্ত। তবে আমি যে ত্রুটিগুলি পাচ্ছিলাম সেগুলি এর দ্বারা সমাধান করা হয়নি - অবশেষে আমি একটি চেষ্টা যোগ করেছি / প্রাপ্ত () পদ্ধতিটি বাদ দিয়ে - খুব কমই, ব্যতিক্রম লগ করার চেষ্টা ব্যর্থ হবে এবং সেখানে ধরা পড়বে। একবার আমি চেষ্টাটি যোগ / বাদ দিয়ে, এটি কোনও সমস্যা ছাড়াই কয়েক সপ্তাহ ধরে চলে এবং একটি স্ট্যান্ডার্ডার ফাইল প্রতি সপ্তাহে প্রায় দু'টি ভুল ব্যতিক্রম গ্রহণ করবে।
zzzeek

2

কোনও সারি থেকে সমস্ত লগ এন্ট্রি পড়তে থাকা অন্য প্রক্রিয়াতে সমস্ত লগিং অর্পণ সম্পর্কে কীভাবে?

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()

মাল্টিপ্রসেস যেকোন প্রক্রিয়া বা এমনকি উত্তরাধিকারের মাধ্যমে কেবল LOG_QUEUE ভাগ করুন এবং এটি সমস্ত কার্যকর কাজ করে!


1

আমার একটি সমাধান রয়েছে যা লোহাখাতারের অনুরূপ ব্যতীত আমি আমার কিছু কোডে লগিং.এক্সেপশন ব্যবহার করি এবং দেখতে পেয়েছি যে ট্রেসব্যাকগুলি পিকেলযোগ্য নয়, তাই ক্যুতে আবার পাস করার আগে আমার ব্যতিক্রমটি ফর্ম্যাট করা দরকার:

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

আমি এই লাইন বরাবর একটি সম্পূর্ণ উদাহরণস্বরূপ পাওয়া এখানে
আরেহ লাইব বৃষ

1

নীচে একটি ক্লাস যা উইন্ডোজ পরিবেশে ব্যবহার করা যেতে পারে তার জন্য অ্যাক্টিভ পাইথন প্রয়োজন। আপনি অন্যান্য লগিং হ্যান্ডলারের (স্ট্রিমহ্যান্ডলার ইত্যাদি) এর উত্তরাধিকারীও হতে পারেন

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

এবং এখানে একটি উদাহরণ যা ব্যবহার দেখায়:

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

সম্ভবত multiprocessing.Lock()উইন্ডোজ মুটেক্সের পরিবর্তে ব্যবহার করা সমাধানটিকে বহনযোগ্য করে তুলবে।
এক্সমেডেকো

1

এখানে আমার সরল হ্যাক / কর্মপরিকল্পনা ... সর্বাধিক বিস্তৃত নয়, তবে সহজেই পরিবর্তিতযোগ্য এবং সহজেই পড়া এবং বোঝার পক্ষে এটি লেখার আগে আমার পাওয়া অন্য যে কোনও উত্তরগুলির চেয়ে আমি মনে করি:

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)

1

এই দুর্দান্ত প্যাকেজ আছে

প্যাকেজ: https://pypi.python.org/pypi/m মাল্টিপ্রসেসিং- ব্লগিং /

কোড: https://github.com/jruere/m মাল্টিপ্রসেসিং- ব্লগিং

ইনস্টল করুন:

pip install multiprocessing-logging

তারপর যোগ:

import multiprocessing_logging

# This enables logs inside process
multiprocessing_logging.install_mp_handler()

3
এই লাইব্রেরিটি আক্ষরিক অর্থে বর্তমান এসও পোস্টের আরও একটি মন্তব্যে অবলম্বন করা হয়েছে : stackoverflow.com/a/894284/1698058
ক্রিস হান্ট

উত্স: স্ট্যাকওভারফ্লো.com / a / 894284 / 1663382 আমি হোমপেজে ডকুমেন্টেশন ছাড়াও মডিউলটির উদাহরণ ব্যবহারের প্রশংসা করি।
লিকুইডনিয়াস

0

বিকল্পগুলির মধ্যে একটি হ'ল একটি পরিচিত ফাইলটিতে মাল্টিপ্রোসেসিং লগিং লিখুন এবং atexitসেই প্রক্রিয়াগুলিতে যোগদানের জন্য একটি হ্যান্ডলার নিবন্ধন করুন এটি স্ট্যাডারের উপর আবার পড়ুন; তবে আপনি স্টার্ডারের আউটপুট বার্তাগুলিতে কোনও রিয়েল-টাইম প্রবাহ পাবেন না।


পদ্ধতির আপনি আপনার মন্তব্য এখান থেকে এক অভিন্ন নিচে প্রস্তাব করা হয় stackoverflow.com/questions/641420/...
Iruvar

0

আপনার যদি loggingমডিউলে তালা, থ্রেড এবং কাঁটাচামচগুলির সংমিশ্রণে ডেডলকগুলি থাকে তবে এটি বাগ রিপোর্ট 6721 তে রিপোর্ট করা হয়েছে ( সম্পর্কিত এসও প্রশ্নটিও দেখুন )।

এখানে একটি ছোট ফিক্সআপ সমাধান পোস্ট করা আছে

যাইহোক, যে শুধু কোনো সম্ভাব্য ডেডলক ঠিক করবে logging। এটি ঠিক করবে না যে জিনিসগুলি সম্ভবত সমবেত হয়েছে। এখানে উপস্থাপন করা অন্যান্য উত্তরগুলি দেখুন।


0

সরল ধারণা হিসাবে উল্লেখ করা হয়েছে:

  • বর্তমানের প্রক্রিয়াটির ফাইল নাম এবং প্রক্রিয়া আইডি ধরুন।
  • সেট আপ a [WatchedFileHandler][1]। এই হ্যান্ডলারের কারণগুলি এখানে বিস্তারিত আলোচনা করা হয়েছে , তবে সংক্ষেপে অন্যান্য লগিং হ্যান্ডলারের সাথে রেসের কিছু খারাপ অবস্থা রয়েছে। এই একের কাছে দৌড় শর্তের জন্য সংক্ষিপ্ততম উইন্ডো রয়েছে।
    • লগগুলিকে যেমন / var / লগ / ... সংরক্ষণ করার জন্য একটি পথ বেছে নিন

0

যার জন্য এটির প্রয়োজন হতে পারে, আমি মাল্টিপ্রসেসিং_লগিং প্যাকেজের জন্য একটি ডেকরেটর লিখেছিলাম যা বর্তমান প্রক্রিয়াটির নাম লগগুলিতে যুক্ত করে, তাই এটি স্পষ্ট হয়ে যায় কে লগ করে।

এটি ইনস্টল_ এমপি_হ্যান্ডলার () চালায় তাই পুল তৈরির আগে এটি চালানো অযৌক্তিক হয়ে যায়।

এটি আমাকে দেখতে দেয় যে কোন কর্মী কোন বার্তাটি লগ করে creates

এখানে একটি উদাহরণ সহ ব্লুপ্রিন্ট:

import sys
import logging
from functools import wraps
import multiprocessing
import multiprocessing_logging

# Setup basic console logger as 'logger'
logger = logging.getLogger()
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setFormatter(logging.Formatter(u'%(asctime)s :: %(levelname)s :: %(message)s'))
logger.setLevel(logging.DEBUG)
logger.addHandler(console_handler)


# Create a decorator for functions that are called via multiprocessing pools
def logs_mp_process_names(fn):
    class MultiProcessLogFilter(logging.Filter):
        def filter(self, record):
            try:
                process_name = multiprocessing.current_process().name
            except BaseException:
                process_name = __name__
            record.msg = f'{process_name} :: {record.msg}'
            return True

    multiprocessing_logging.install_mp_handler()
    f = MultiProcessLogFilter()

    # Wraps is needed here so apply / apply_async know the function name
    @wraps(fn)
    def wrapper(*args, **kwargs):
        logger.removeFilter(f)
        logger.addFilter(f)
        return fn(*args, **kwargs)

    return wrapper


# Create a test function and decorate it
@logs_mp_process_names
def test(argument):
    logger.info(f'test function called via: {argument}')


# You can also redefine undecored functions
def undecorated_function():
    logger.info('I am not decorated')


@logs_mp_process_names
def redecorated(*args, **kwargs):
    return undecorated_function(*args, **kwargs)


# Enjoy
if __name__ == '__main__':
    with multiprocessing.Pool() as mp_pool:
        # Also works with apply_async
        mp_pool.apply(test, ('mp pool',))
        mp_pool.apply(redecorated)
        logger.info('some main logs')
        test('main program')

-5

আমার বাচ্চারা যারা দশকে একই সমস্যাটি মিলিত করে এবং এই সাইটে এই প্রশ্নটি পেয়েছে তাদের কাছে আমি এই উত্তরটি রেখেছি।

সরলতা বনাম overcomplicating। শুধু অন্যান্য সরঞ্জাম ব্যবহার করুন। পাইথন দুর্দান্ত, তবে এটি কিছু কিছু করার জন্য তৈরি করা হয়নি।

লোগ্রোটেট ডেমনের জন্য নীচের স্নিপেটটি আমার জন্য কাজ করে এবং জিনিসগুলিকে অত্যধিক জটিল করে না। ঘন্টা সময় চালানোর জন্য এটি নির্ধারণ করুন এবং

/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
}

এইভাবেই আমি এটি ইনস্টল করব (প্রতিলিঙ্কগুলি লোগ্রোটেটের জন্য কাজ করে না):

sudo cp /directpath/config/logrotate/myconfigname /etc/logrotate.d/myconfigname
sudo cp /etc/cron.daily/logrotate /etc/cron.hourly/logrotate
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.