2009-06-10 48 views
11

Vì vậy, ngay bây giờ chúng tôi có rất nhiều tập lệnh python và chúng tôi đang cố gắng hợp nhất chúng và sửa chữa và dự phòng. Một trong những điều chúng tôi đang cố gắng làm là đảm bảo rằng tất cả sys.stdout/sys.stderr đi vào mô-đun đăng nhập python.chuyển hướng sys.stdout sang nhật ký python

Bây giờ điều quan trọng là, chúng tôi muốn những sản phẩm sau in ra:

[<ERROR LEVEL>] | <TIME> | <WHERE> | <MSG> 

Bây giờ tất cả sys.stdout/sys.stderr thông điệp khá nhiều trong tất cả các thông báo lỗi python có định dạng của [CẤP ] - MSG, tất cả đều được viết bằng sys.stdout/sys.stderr. Tôi có thể phân tích các tiền phạt, trong wrapper sys.stdout của tôi và trong wrapper sys.stderr. Sau đó, gọi mức ghi nhật ký tương ứng, tùy thuộc vào đầu vào được phân tích cú pháp.

Vì vậy, về cơ bản chúng tôi có một gói được gọi là foo và một gói con được gọi là nhật ký. Trong __init__.py chúng ta định nghĩa như sau:

def initLogging(default_level = logging.INFO, stdout_wrapper = None, \ 
       stderr_wrapper = None): 
    """ 
     Initialize the default logging sub system 
    """ 
    root_logger = logging.getLogger('') 
    strm_out = logging.StreamHandler(sys.__stdout__) 
    strm_out.setFormatter(logging.Formatter(DEFAULT_LOG_TIME_FORMAT, \ 
              DEFAULT_LOG_TIME_FORMAT)) 
    root_logger.setLevel(default_level) 
    root_logger.addHandler(strm_out) 

    console_logger = logging.getLogger(LOGGER_CONSOLE) 
    strm_out = logging.StreamHandler(sys.__stdout__) 
    #strm_out.setFormatter(logging.Formatter(DEFAULT_LOG_MSG_FORMAT, \ 
    #          DEFAULT_LOG_TIME_FORMAT)) 
    console_logger.setLevel(logging.INFO) 
    console_logger.addHandler(strm_out) 

    if stdout_wrapper: 
     sys.stdout = stdout_wrapper 
    if stderr_wrapper: 
     sys.stderr = stderr_wrapper 


def cleanMsg(msg, is_stderr = False): 
    logy = logging.getLogger('MSG') 
    msg = msg.rstrip('\n').lstrip('\n') 
    p_level = r'^(\s+)?\[(?P<LEVEL>\w+)\](\s+)?(?P<MSG>.*)$' 
    m = re.match(p_level, msg) 
    if m: 
     msg = m.group('MSG') 
     if m.group('LEVEL') in ('WARNING'): 
      logy.warning(msg) 
      return 
     elif m.group('LEVEL') in ('ERROR'): 
      logy.error(msg) 
      return 
    if is_stderr: 
     logy.error(msg) 
    else: 
     logy.info(msg) 

class StdOutWrapper: 
    """ 
     Call wrapper for stdout 
    """ 
    def write(self, s): 
     cleanMsg(s, False) 

class StdErrWrapper: 
    """ 
     Call wrapper for stderr 
    """ 
    def write(self, s): 
     cleanMsg(s, True) 

Bây giờ chúng ta sẽ gọi đây là một trong các kịch bản của chúng tôi ví dụ:

import foo.log 
foo.log.initLogging(20, foo.log.StdOutWrapper(), foo.log.StdErrWrapper()) 
sys.stdout.write('[ERROR] Foobar blew') 

Trong đó sẽ được chuyển đổi thành một thông báo lỗi đăng nhập. Giống như:

[ERROR] | 20090610 083215 | __init__.py | Foobar Blew 

Bây giờ vấn đề là khi chúng ta làm điều đó, Module nơi thông báo lỗi được đăng nhập bây giờ là __init__ (tương ứng với foo.log.__init__.py tập tin) mà đánh bại toàn bộ mục đích.

Tôi đã cố gắng thực hiện sâuCopy/shallowCopy của các đối tượng stderr/stdout, nhưng điều đó không có gì, nó vẫn cho biết thông báo mô-đun đã xảy ra trong __init__.py. Làm thế nào tôi có thể làm cho nó để điều này không xảy ra?

Trả lời

6

Vấn đề là mô-đun ghi nhật ký đang tìm kiếm một lớp trên ngăn xếp cuộc gọi để tìm người gọi nó, nhưng bây giờ chức năng của bạn là lớp trung gian tại thời điểm đó (Mặc dù tôi đã mong đợi nó báo cáo cleanMsg, chứ không phải __init__, vì đó là nơi bạn đang gọi vào nhật ký()). Thay vào đó, bạn cần nó để đi lên hai cấp độ, hoặc người nào khác vượt qua người gọi của bạn là vào tin nhắn đăng nhập. Bạn có thể thực hiện việc này bằng cách tự kiểm tra khung ngăn xếp và lấy chức năng gọi, chèn nó vào tin nhắn.

Để tìm khung gọi, bạn có thể sử dụng các mô-đun kiểm tra:

import inspect 
f = inspect.currentframe(N) 

sẽ nhìn lên khung N, và trả lại cho bạn con trỏ khung. tức là người gọi ngay lập tức của bạn là khung hiện hành (1), nhưng bạn có thể phải đi một khung khác nếu đây là phương thức stdout.write. Khi bạn có khung gọi, bạn có thể lấy đối tượng mã thực thi và xem tên tệp và chức năng được liên kết với nó.ví dụ:

Bạn cũng có thể cần phải nhập một số mã để xử lý mã không phải python (ví dụ: hàm C hoặc nội trang) vì chúng có thể thiếu đối tượng f_code.

Hoặc, theo dõi mikej's answer, bạn có thể sử dụng cùng một cách tiếp cận trong lớp Logger tùy chỉnh kế thừa từ ghi nhật ký. Trình ghi đè ghi findCaller để điều hướng nhiều khung lên, thay vì một.

2

Tôi nghĩ vấn đề là thông điệp đăng nhập thực tế của bạn đang được tạo ra bởi các logy.errorlogy.info cuộc gọi trong cleanMsg, do đó phương pháp đó là nguồn gốc của các thông điệp log và bạn đang nhìn thấy điều này như __init__.py

Nếu bạn nhìn vào mã nguồn của lib/logging/__init__.py của Python, bạn sẽ thấy một phương thức được định nghĩa là findCaller là mô đun ghi nhật ký sử dụng để lấy được người gọi yêu cầu ghi nhật ký.
Có lẽ bạn có thể ghi đè lên đối tượng ghi nhật ký của mình để tùy chỉnh hành vi?

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