2017-12-25 110 views
18

Tôi đã được thông báo rằng không thể sử dụng ghi nhật ký trong Đa xử lý. Bạn phải làm điều khiển đồng thời trong trường hợp đa xử lý messes đăng nhập.Có hỗ trợ ghi nhật ký python không?

Nhưng tôi đã làm một số thử nghiệm, nó có vẻ như không có vấn đề sử dụng đăng nhập vào đa xử

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

Như bạn có thể nhìn thấy từ mã.

Tôi cố tình để nhật ký ghi xử lý con tại cùng một thời điểm (5s sau khi bắt đầu) để tăng cơ hội xung đột. Nhưng không có xung đột gì cả.

Vì vậy, câu hỏi của tôi là chúng ta có thể sử dụng đăng nhập đa xử lý? Tại sao có quá nhiều bài viết nói rằng chúng ta có thể không?

Trả lời

8

Như Matino giải thích một cách chính xác: đăng nhập vào một thiết lập đa xử lý không an toàn, vì nhiều quy trình (giờ đây ai biết bất kỳ điều gì về các quy trình khác hiện có) đang viết vào cùng một tệp, có khả năng can thiệp với nhau.

Bây giờ điều xảy ra là mọi quy trình đều có một bộ xử lý mở và thực hiện "ghi thêm" vào tệp đó. Câu hỏi đặt ra là trong hoàn cảnh nào, phụ lục viết là "nguyên tử" (nghĩa là, không thể bị gián đoạn bởi ví dụ: một quy trình khác ghi vào cùng một tệp và xen kẽ đầu ra của nó). Vấn đề này áp dụng cho mọi ngôn ngữ lập trình, vì cuối cùng chúng sẽ làm một syscall với kernel. This answer câu trả lời trong trường hợp đó tệp nhật ký được chia sẻ là ok.

Nó đi xuống để kiểm tra kích thước bộ đệm ống của bạn, trên linux được xác định trong /usr/include/linux/limits.h và là 4096 byte. Đối với các hệ điều hành khác, bạn tìm thấy here một danh sách tốt.

Điều đó có nghĩa là: Nếu dòng nhật ký của bạn nhỏ hơn 4'096 byte (nếu trên Linux), thì phụ lục sẽ an toàn nếu đĩa được đính kèm trực tiếp (tức là không có mạng ở giữa). Nhưng để biết thêm chi tiết, vui lòng kiểm tra liên kết đầu tiên trong câu trả lời của tôi. Để kiểm tra điều này, bạn có thể thực hiện logger.info('proc name %s id %s %s' % (proc.name, proc.pid, str(proc.name)*5000)) với các lenghts khác nhau. Với 5000 ví dụ, tôi đã trộn lẫn các đường ghi trong /tmp/test.log.

Trong this question đã có một vài giải pháp cho điều này, vì vậy tôi sẽ không thêm giải pháp của riêng mình tại đây.

Cập nhật: Flask và đa xử

khung web như bình sẽ được chạy trong nhiều công nhân nếu được tổ chức bởi uwsgi hoặc nginx. Trong trường hợp đó, nhiều quy trình có thể ghi vào một tệp nhật ký

Việc xử lý lỗi trong bình được thực hiện thông qua máy tính/máy chủ sau đó được máy chủ web (uwsgi, nginx, v.v ...) cần chăm sóc log được viết bằng đúng thời trang (xem ví dụ this flask+nginx example, có lẽ cũng thêm xử lý thông tin, do đó bạn có thể kết hợp dòng lỗi quy trình Từ flasks doc:.

theo mặc định như của Flask 0.11, lỗi đăng nhập để đăng nhập của máy chủ web của bạn tự động. Tuy nhiên, cảnh báo không phải là

Vì vậy, bạn vẫn có vấn đề về tệp nhật ký xen kẽ nếu bạn sử dụng warn và thông báo vượt quá kích thước bộ đệm đường ống.

+0

Một câu hỏi nữa. Các khuôn khổ web như bình sẽ được chạy trong nhiều công nhân nếu được lưu trữ bởi uwsgi hoặc nginx. Trong trường hợp đó, nhiều quy trình có thể ghi vào một tệp nhật ký. Nó sẽ có vấn đề? –

+0

@KramerLi: Tôi đã trả lời câu hỏi của bạn trong phần mới trong câu trả lời của tôi – hansaplast

13

Không an toàn để ghi vào một tệp duy nhất từ ​​nhiều quy trình.

Theo https://docs.python.org/3/howto/logging-cookbook.html#logging-to-a-single-file-from-multiple-processes

Mặc dù khai thác gỗ là thread-an toàn, và đăng nhập vào một tập tin duy nhất từ ​​ nhiều chủ đề trong một quá trình duy nhất được hỗ trợ, đăng nhập vào một tập tin duy nhất từ ​​nhiều quy trình không được hỗ trợ, vì không có cách tiêu chuẩn để tuần tự hóa quyền truy cập vào một tệp duy nhất trên nhiều quá trình bằng Python.

Một giải pháp có thể là có mỗi quá trình ghi vào tệp riêng của mình. Bạn có thể đạt được điều này bằng cách viết handler của riêng bạn mà cho biết thêm quá trình pid đến cuối của tập tin:

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) 

Sau đó, bạn chỉ cần gọi addHandler:

logger = logging.getLogger('foo') 
fh = PIDFileHandler('bar.log') 
logger.addHandler(fh) 
+0

cảm ơn cho bài học về đa tiến vs luồng ... – 22degrees

0

Sử dụng hàng đợi để xử lý đúng đồng thời đồng thời khôi phục từ lỗi bằng cách cho mọi thứ vào quy trình gốc qua đường ống.

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) 

Việc xử lý hiện tất cả các tập tin văn bản từ quá trình cha mẹ và chỉ sử dụng một thread để nhận tin nhắn thông qua từ con xử lý

Các vấn đề liên quan