2013-06-24 33 views
24

Mục đích của tôi là để làm một khai thác gỗ đa mô-đun với bộ lọc thứ bậc

cách thức mà nó được đề xuất bởi khai thác gỗ tác giả Vinay Sajip, ít nhất là như xa như tôi đoán ;-)Cách chính xác để lọc các trình ghi nhật ký khác nhau bằng cách sử dụng nhật ký python là gì?

Bạn có thể bỏ qua để "Làm thế nào tôi muốn nó làm việc"

thật không may, tôi đã học rất nhanh đến mức làm việc với các cơ sở khai thác gỗ là phức tạp hơn nhiều so với hầu hết các kinh nghiệm khác của tôi với ngôn ngữ và tôi đã làm rất nhiều chung (thiết kế) sai lầm, ví dụ cố gắng để đạt được một logger lớp tập trung duy nhất đăng nhập cho nhiều mô-đun hoặc thậm chí các chương trình như (using Python logger class to generate multiple logs for different log levels). Nhưng dường như có chỗ cho thiết kế tốt hơn, và nó có thể tồi tệ hơn là dành thời gian tìm và học nó. Vì vậy, ngay bây giờ tôi hy vọng tôi đang đi đúng hướng. Nếu không Vinaj sẽ phải làm rõ phần còn lại ;-)

tôi sắp xếp đăng nhập của tôi như thế này:

  • Mỗi module python có của nó own logger
  • Mỗi logger có một cái tên tương tự như các mô-đun nó ở đâu được định nghĩa, ví dụ logger = logging.getLogger(__name__)
  • Như thế này, mã bên trong mỗi mô-đun có thể sử dụng riêng logger (được xác định tại địa phương) của mình để gửi tin nhắn đăng nhập (logging.LogRecord) để xử lý (logging.Handler)
  • Sử dụng logging.config để đạt được sự linh hoạt đầy đủ trong cấu hình của khai thác gỗ (Lưu ý: trong mã bên dưới, tôi chỉ bắt đầu bằng basicConfig)

Cách tiếp cận này là phương pháp được khuyến nghị và tôi đồng ý với những lợi thế có thể có của nó. Ví dụ, tôi có thể bật/tắt DEBUG của các thư viện bên ngoài bằng cách sử dụng các tên module đủ điều kiện (hệ thống phân cấp đặt tên đã tồn tại trong mã).

Bây giờ để có mức kiểm soát cao hơn, tôi muốn sử dụng đăng nhập. Lớp học, để có thể lọc (chỉ cho phép) một cây con được chọn trong hệ thống phân cấp của nhật ký.

này là tất cả tốt, nhưng tính năng lọc như mô tả ở đây

Filter instances are used to perform arbitrary filtering of LogRecords. 

Loggers and Handlers can optionally use Filter instances to filter 
records as desired. The base filter class only allows events which are 
below a certain point in the logger hierarchy. For example, a filter 
initialized with "A.B" will allow events logged by loggers "A.B", 
"A.B.C", "A.B.C.D", "A.B.D" etc. but not "A.BB", "B.A.B" etc. If 
initialized with the empty string, all events are passed. 

vẫn không làm việc cho tôi.

Tôi đoán là sự thiếu hiểu biết của tôi về các chi tiết đằng sau việc truyền bá LogRecords là nguồn gốc của vấn đề. Trước khi nhảy vào mã tôi muốn thể hiện ở đây một biểu đồ dòng chảy (từ cookbook tutorial mà lúc đầu tôi bằng cách nào đó thất bại trong việc ngay lập tức khám phá): logging flow-chart

Ví dụ mã

Tôi bắt đầu với hai mô-đun dụ, mỗi sử dụng nó sở hữu tên logger:

bar.py:

import logging 


logger = logging.getLogger(__name__) 


def bar(): 
    logger.info('hello from ' + __name__) 

foo.py:

import logging 
from bar import bar, logger as bar_logger 


logger = logging.getLogger('foo') 


def foo(): 
    logger.info('hello from foo') 


if __name__ == '__main__': 
    # Trivial logging setup. 
    logging.basicConfig(
     level=logging.INFO, 
     format='%(asctime)s %(name)-20s %(levelname)-8s %(message)s', 
     datefmt='%m-%d %H:%M' 
    ) 
    # Do some work. 
    foo() 
    bar() 

Logging là lần đầu tiên được xây dựng với logging.basicConfig (logger gốc, được tạo ra sau khi import logging bởi __main__ được một handler dòng gắn liền với nó, để chúng ta có một giao diện điều khiển), được kích hoạt (Logger tương ứng .disabled = False) và cả hai logger mô-đun thanhfoo truyền tới trình ghi nhật ký gốc (vì vậy chúng tôi có tổng cộng ba logger).

print logger 
print bar_logger 
print logging.root 
# Prints 
#<logging.Logger object at 0x7f0cfd520790> 
#<logging.Logger object at 0x7f0cfd55d710> 
#<logging.RootLogger object at 0x7f0cfd520550> 

Cách sử dụng thực tế là khi thanh là thư viện bên ngoài mà tôi muốn tắt tiếng (lọc ra).

Làm thế nào nó hoạt động, nhưng "tôi" không thích nó

# Don't like it 
bar_logger.addFilter(logging.Filter('foo')) 
# Do some work. 
foo() 
bar() 

in chỉ

06-24 14:08 foo     INFO  hello from foo 

Làm thế nào tôi muốn nó làm việc

Tôi muốn lọc nó ra trực thuộc Trung ương , tức là tại logger gốc của tôi w/o sự cần thiết phải nhập khẩu tất cả các logger của tất cả các module bên ngoài ra khỏi đó.

logging.root.addFilter(logging.Filter('foo')) 

in

06-24 14:17 foo     INFO  hello from foo 
06-24 14:17 bar     INFO  hello from bar 

Phải có một số rõ ràng/sai lầm ngu ngốc mà tôi bỏ lỡ: Tôi không muốn bất kỳ tin nhắn từ thanh logger. Hey, nhưng cách nào tốt hơn để tìm nó hơn là tóm tắt tất cả trên SO, folks? ;-)

Tôi sẽ cố gắng tìm ra cách để bar_logger đợi quyết định từ trình ghi nhật ký gốc, trước khi phát ra bất kỳ thứ gì. Tôi chỉ hy vọng rằng đây thực sự là cách nó được cho là làm việc ngay từ đầu.

+0

Cũng kiểm http://stackoverflow.com/questions/7507825/python-complete-example-of-dict-for -logging-config-dictconfig –

+0

Bạn cũng có thể tìm thấy *** logging_tree *** introspection rất sâu sắc đối với các trường hợp đăng nhập phức tạp https://pypi.python.org/pypi/logging_tree –

+0

Mẹo tuyệt vời từ một đồng nghiệp của tôi về việc đăng nhập http://pythonsweetness.tumblr.com/post/67394619015/use-of-logging-package-from-within-a-library –

Trả lời

30

Giải pháp

Thêm bộ lọc để xử lý chứ không phải là logger:

handler.addFilter(logging.Filter('foo')) 

Giải thích

Trong biểu đồ biểu đồ dòng chảy bạn đăng, nhận thấy có hai viên kim cương:

  • Bộ lọc có được gắn với logger từ chối bản ghi không?
  • Bộ lọc có được đính kèm với hander từ chối bản ghi không?

Vì vậy, bạn nhận được hai thay đổi khi từ chối một LogRecord. Nếu bạn đính kèm bộ lọc vào trình ghi nhật ký, nhưng bắt đầu LogRecord thông qua, ví dụ như trình ghi nhật ký foo hoặc thanh, thì LogRecord không bị lọc vì LogRecord chuyển qua foo hoặc thanh ghi tự do và bộ lọc logger gốc không bao giờ đi vào chơi. (Nhìn vào biểu đồ dòng chảy một lần nữa.)

Ngược lại, StreamHandler được xác định bởi basicConfig có khả năng lọc bất kỳ LogRecord nào đi qua nó.

Vì vậy: thêm bộ lọc để xử lý chứ không phải là logger:

# foo.py 
import logging 
import bar 

logger = logging.getLogger('foo') 

def foo(): 
    logger.info('hello from foo') 

if __name__ == '__main__': 
    # Trivial logging setup. 
    logging.basicConfig(
     level=logging.INFO, 
     format='%(asctime)s %(name)-20s %(levelname)-8s %(message)s', 
     datefmt='%m-%d %H:%M') 
    for handler in logging.root.handlers: 
     handler.addFilter(logging.Filter('foo')) 

    foo() 
    bar.bar() 

sản lượng

06-24 09:17 foo     INFO  hello from foo 

Nếu bạn muốn cho phép đăng nhập từ khai thác gỗ có tên bắt đầu bằng foo hoặc bar , nhưng không phải từ bất kỳ trình ghi nhật ký nào khác, bạn có thể tạo bộ lọc danh sách trắng như sau:

import logging 
foo_logger = logging.getLogger('foo') 
bar_logger = logging.getLogger('bar') 
baz_logger = logging.getLogger('baz') 

class Whitelist(logging.Filter): 
    def __init__(self, *whitelist): 
     self.whitelist = [logging.Filter(name) for name in whitelist] 

    def filter(self, record): 
     return any(f.filter(record) for f in self.whitelist) 

logging.basicConfig(
    level=logging.INFO, 
    format='%(asctime)s %(name)-20s %(levelname)-8s %(message)s', 
    datefmt='%m-%d %H:%M') 
for handler in logging.root.handlers: 
    handler.addFilter(Whitelist('foo', 'bar')) 

foo_logger.info('hello from foo') 
# 06-24 09:41 foo     INFO  hello from foo 
bar_logger.info('hello from bar') 
# 06-24 09:41 bar     INFO  hello from bar 
baz_logger.info('hello from baz') 
# No output since Whitelist filters if record.name not begin with 'foo' or 'bar' 

Và tương tự, bạn có thể danh sách đen tên logger với điều này:

class Blacklist(Whitelist): 
    def filter(self, record): 
     return not Whitelist.filter(self, record) 
+0

hoạt động và cho phép linh hoạt hơn! rất tốt. –

+0

Tôi đoán cuối cùng nếu ứng dụng đủ tinh vi (như django), bạn nên có một ví dụ với logging.config.dictConfig (cho mặc định) và logging.config.fileConfig (để tùy chỉnh tệp văn bản của người dùng), để tất cả đều rất phù hợp với ý tưởng của tôi về việc sử dụng ghi nhật ký. Người ta có thể tiêm Whitelist ở đó bằng ký hiệu '()' cho các nhà máy tùy chỉnh. Thnx lần nữa! –

+1

@YauhenYakimovich: Cảm ơn bạn đã chỉnh sửa. – unutbu

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