2011-08-24 46 views
50

Tôi đang sử dụng trình ghi nhật ký python. Sau đây là mã của tôi:Đầu ra nhật ký trùng lặp khi sử dụng mô-đun ghi nhật ký Python

import os 
import time 
import datetime 
import logging 
class Logger : 
    def myLogger(self): 
     logger = logging.getLogger('ProvisioningPython') 
     logger.setLevel(logging.DEBUG) 
     now = datetime.datetime.now() 
     handler=logging.FileHandler('/root/credentials/Logs/ProvisioningPython'+ now.strftime("%Y-%m-%d") +'.log') 
     formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') 
     handler.setFormatter(formatter) 
     logger.addHandler(handler) 
     return logger 

Vấn đề tôi có là tôi nhận được nhiều mục nhập trong tệp nhật ký cho mỗi cuộc gọi logger.info. Làm sao tôi có thể giải quyết việc này?

+0

trình cho tôi. Python 3.2 và Windows XP. – Zuljin

+1

Bạn có chắc là bạn không tạo nhiều bản ghi nhật ký không? – Gandi

+0

Có. trong tập tin khác nhau tôi đang dùng dụ mới như chúng ta đã làm trong các dự án Java. Vui lòng chỉ định cho tôi xem điều đó có tạo ra sự cố hay không. – user865438

Trả lời

54

Các logging.getLogger() đã là một singleton. (Documentation)

Vấn đề là mỗi khi bạn gọi myLogger(), nó sẽ thêm một trình xử lý khác vào cá thể, làm cho các bản ghi trùng lặp.

Có lẽ một cái gì đó như thế này?

import os 
import time 
import datetime 
import logging 

loggers = {} 

def myLogger(name): 
    global loggers 

    if loggers.get(name): 
     return loggers.get(name) 
    else: 
     logger = logging.getLogger(name) 
     logger.setLevel(logging.DEBUG) 
     now = datetime.datetime.now() 
     handler = logging.FileHandler(
      '/root/credentials/Logs/ProvisioningPython' 
      + now.strftime("%Y-%m-%d") 
      + '.log') 
     formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') 
     handler.setFormatter(formatter) 
     logger.addHandler(handler) 
     loggers.update(dict(name=logger)) 

     return logger 
+3

Tôi nghĩ bạn nên có loggers.update (dict ((name, logger))). – acrophobia

8

Bạn đang gọi số Logger.myLogger() nhiều lần. Lưu trữ bản sao trình ghi nhật ký nó trả về một nơi nào đó và sử dụng lại rằng.

Cũng lưu ý rằng nếu bạn đăng nhập trước khi xử lý bất kỳ được thêm, mặc định StreamHandler(sys.stderr) sẽ được tạo.

+0

Thực ra tôi đang cố gắng truy cập cá thể logger như chúng ta sử dụng trong java.Nhưng tôi không biết liệu nó có cần tạo một cá thể chỉ một lần cho toàn bộ dự án hay không. – user865438

+1

@ user865483: Chỉ một lần. Tất cả các logger thư viện tiêu chuẩn là những người độc thân. –

3

Trình ghi nhật ký của bạn sẽ hoạt động dưới dạng singleton. Bạn không nên tạo nó nhiều lần. Dưới đây là ví dụ làm thế nào nó có thể trông:

import os 
import time 
import datetime 
import logging 
class Logger : 
    logger = None 
    def myLogger(self): 
     if None == self.logger: 
      self.logger=logging.getLogger('ProvisioningPython') 
      self.logger.setLevel(logging.DEBUG) 
      now = datetime.datetime.now() 
      handler=logging.FileHandler('ProvisioningPython'+ now.strftime("%Y-%m-%d") +'.log') 
      formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') 
      handler.setFormatter(formatter) 
      self.logger.addHandler(handler) 
     return self.logger 

s = Logger() 
m = s.myLogger() 
m2 = s.myLogger() 
m.info("Info1") 
m2.info("info2") 
+0

sau đó một lần nữa nếu tôi sẽ lấy trường hợp khác nhau trong tập tin khác nhau. Giả sử trong tệp 1 s = Trình ghi() m = s.myLogger() và trong tệp 2 s = Trình ghi() Nó sẽ hoạt động hoặc không m2 = s.myLogger() – user865438

+0

Tôi vẫn nhận được bản sao của cùng một bản ghi nhiều lần. Tôi có một nghi ngờ ở đây cho dù bên trong thread Log in nhiều hơn một hay không. Xin vui lòng giúp tôi trong này. – user865438

+1

@ user865438, chúng tôi không cần phải lo lắng về việc triển khai thực hiện một singleton (Nó đã là). Để đăng nhập vào các mô-đun phụ, hãy theo dõi Sổ tay nhật ký chính thức [link] (https://docs.python.org/2/howto/logging-cookbook.html#logging-cookbook). Về cơ bản, bạn cần phải thực hiện theo các hệ thống phân cấp đặt tên trong khi đặt tên cho các logger và nó sẽ chăm sóc phần còn lại. – narayan

30
import datetime 
import logging 
class Logger : 
    def myLogger(self): 
     logger=logging.getLogger('ProvisioningPython') 
     if not len(logger.handlers): 
      logger.setLevel(logging.DEBUG) 
      now = datetime.datetime.now() 
      handler=logging.FileHandler('/root/credentials/Logs/ProvisioningPython'+ now.strftime("%Y-%m-%d") +'.log') 
      formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') 
      handler.setFormatter(formatter) 
      logger.addHandler(handler) 
     return logger 

làm các trick cho tôi

sử dụng python 2.7

+2

+1 cho kiểm tra chiều dài logger.handlers. – kakyo

+1

Điều này hoạt động ngay cả khi mô-đun được tải lại (không phải là trường hợp của các câu trả lời khác) – yco

+1

Cảm ơn mẹo, BTW để kiểm tra xem danh sách có trống hay không bạn không cần sử dụng toán tử "len" mà bạn có thể trực tiếp sử dụng nếu my_list: .. – redobot

1

Việc thực hiện các logger đã là một singleton.

Nhiều cuộc gọi đến logging.getLogger ('someLogger') trả lại tham chiếu cho cùng một đối tượng nhật ký. Điều này đúng không chỉ trong cùng một mô-đun , mà còn trên các mô-đun, miễn là nó nằm trong cùng một quy trình phiên dịch Python . Nó là đúng cho các tham chiếu đến cùng một đối tượng; Ngoài ra, mã ứng dụng có thể xác định và định cấu hình phụ huynh trình ghi trong một mô-đun và tạo (nhưng không định cấu hình) nhật ký con trong mô-đun riêng biệt và tất cả cuộc gọi nhật ký cho trẻ sẽ chuyển đến số cấp độ gốc. Đây là một mô-đun chính

Nguồn- Using logging in multiple modules

Vì vậy, cách bạn nên sử dụng này là -

Giả sử chúng tôi đã tạo và cấu hình một logger gọi 'main_logger' trong module chính (mà chỉ đơn giản là cấu hình các logger, không trả lại bất cứ điều gì).

# get the logger instance 
logger = logging.getLogger("main_logger") 
# configuration follows 
... 

Bây giờ trong một tiểu module, nếu chúng ta tạo ra một logger con sau thứ bậc tên 'main_logger.sub_module_logger', chúng tôi không cần phải cấu hình nó trong sub-module. Chỉ cần tạo logger theo thứ bậc đặt tên là đủ.

# get the logger instance 
logger = logging.getLogger("main_logger.sub_module_logger") 
# no configuration needed 
# it inherits the configuration from the parent logger 
... 

Và nó cũng sẽ không thêm trình xử lý trùng lặp.

Xem this câu hỏi để có câu trả lời tiết kiệm hơn một chút.

+1

xác định lại trình xử lý sau khi getLogger có vẻ hoạt động với tôi: 'logger = logging.getLogger ('my_logger'); logger.handlers = [logger.handlers [0],] ' – radtek

0

Đôi (hoặc ba hoặc ..- dựa trên số lượng tải lại) đầu ra bộ ghi nhật ký cũng có thể xảy ra khi bạn tải lại mô-đun của mình qua importlib.reload (vì lý do tương tự như được giải thích trong câu trả lời được chấp nhận). Tôi thêm câu trả lời này chỉ cho một tài liệu tham khảo trong tương lai vì nó đã cho tôi một thời gian để tìm ra lý do tại sao sản lượng của tôi là dupli (triple) cated.

0

Một workaround đơn giản là như

logger.handlers[:] = [handler] 

Bằng cách này bạn tránh phụ xử lý mới vào danh sách "xử lý" bên dưới.

0
from logging.handlers import RotatingFileHandler 
import logging 
import datetime 

# stores all the existing loggers 
loggers = {} 

def get_logger(name): 

    # if a logger exists, return that logger, else create a new one 
    global loggers 
    if name in loggers.keys(): 
     return loggers[name] 
    else: 
     logger = logging.getLogger(name) 
     logger.setLevel(logging.DEBUG) 
     now = datetime.datetime.now() 
     handler = logging.FileHandler(
      'path_of_your_log_file' 
      + now.strftime("%Y-%m-%d") 
      + '.log') 
     formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') 
     handler.setFormatter(formatter) 
     logger.addHandler(handler) 
     loggers.update(dict(name=logger)) 
     return logger 
+0

Vui lòng thêm giải thích để làm cho câu trả lời này có giá trị hơn để sử dụng lâu dài. –

0

Bạn có thể để có được danh sách của tất cả các xử lý cho các logger đặc biệt, vì vậy bạn có thể làm một cái gì đó như thế này

logger = logging.getLogger(logger_name) 
handler_installed = False 
for handler in logger: 
    # Here your condition to check for handler presence 
    if isinstance(handler, logging.FileHandler) and handler.baseFilename == log_filename: 
     handler_installed = True 
     break 

if not handler_installed: 
    logger.addHandler(your_handler) 

Trong ví dụ trên chúng tôi kiểm tra nếu xử lý cho một tập tin được chỉ định đã nối với logger, nhưng có quyền truy cập vào danh sách tất cả các trình xử lý cung cấp cho bạn khả năng quyết định tiêu chí nào bạn nên thêm trình xử lý khác hoặc không.

0

Đã xảy ra sự cố này ngay hôm nay. Kể từ khi chức năng của tôi đã được @staticmethod các đề xuất trên đã được giải quyết với ngẫu nhiên().

Nhìn cái gì đó như:

import random 

logger = logging.getLogger('ProvisioningPython.{}'.format(random.random())) 
8

Kể từ Python 3.2 bạn chỉ có thể kiểm tra nếu xử lý đã xuất hiện và nếu như vậy, hãy bỏ chúng trước khi thêm bộ xử lý mới. Điều này là khá thuận tiện khi gỡ lỗi và mã bao gồm logger khởi tạo của bạn

if (logger.hasHandlers()): 
    logger.handlers.clear() 

logger.addHandler(handler) 
+0

Câu trả lời hay, Thx :)) –

0

Đây là một bổ sung cho @ rm957377 của câu trả lời nhưng với một lời giải thích lý do tại sao điều này xảy ra. Khi bạn chạy một hàm lambda trong AWS, chúng gọi hàm của bạn từ bên trong một cá thể gói mà vẫn còn sống cho nhiều cuộc gọi. Có nghĩa là, nếu bạn gọi addHandler() trong mã của hàm của bạn, nó sẽ tiếp tục thêm các trình xử lý trùng lặp vào bảng ghi nhật ký mỗi khi hàm chạy. Các singleton đăng nhập vẫn tồn tại thông qua nhiều cuộc gọi của bạn lambda chức năng.

Để giải quyết điều này, bạn có thể xóa bộ xử lý của bạn trước khi bạn đặt chúng thông qua:

logging.getLogger().handlers.clear() 
logging.getLogger().addHandler(...) 
Các vấn đề liên quan