cấu hình logger để đăng nhập vào tập tin và in ra thiết bị xuất chuẩn


353

Tôi đang sử dụng mô đun ghi nhật ký của Python để ghi lại một số chuỗi gỡ lỗi vào một tệp hoạt động khá tốt. Ngoài ra, tôi muốn sử dụng mô-đun này để in các chuỗi ra thành thiết bị xuất chuẩn. Làm thế nào để tôi làm điều này? Để đăng nhập chuỗi của tôi vào một tệp, tôi sử dụng mã sau đây:

import logging
import logging.handlers
logger = logging.getLogger("")
logger.setLevel(logging.DEBUG)
handler = logging.handlers.RotatingFileHandler(
    LOGFILE, maxBytes=(1048576*5), backupCount=7
)
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
handler.setFormatter(formatter)
logger.addHandler(handler)

và sau đó gọi một hàm logger như

logger.debug("I am written to the file")

Cảm ơn bạn đã giúp đỡ ở đây!

Câu trả lời:


451

Chỉ cần có một xử lý để logger gốc và thêm StreamHandler. Việc StreamHandlerviết lên stderr. Không chắc chắn nếu bạn thực sự cần thiết bị xuất chuẩn trên stderr, nhưng đây là những gì tôi sử dụng khi tôi thiết lập trình ghi nhật ký Python và tôi cũng thêm vào đó FileHandler. Sau đó, tất cả các bản ghi của tôi đi đến cả hai nơi (đó là những gì nó nghe giống như bạn muốn).

import logging
logging.getLogger().addHandler(logging.StreamHandler())

Nếu bạn muốn xuất ra stdoutthay vì stderr, bạn chỉ cần chỉ định nó cho hàm StreamHandlertạo.

import sys
# ...
logging.getLogger().addHandler(logging.StreamHandler(sys.stdout))

Bạn cũng có thể thêm Formatternó vào để tất cả các dòng nhật ký của bạn có một tiêu đề chung.

I E:

import logging
logFormatter = logging.Formatter("%(asctime)s [%(threadName)-12.12s] [%(levelname)-5.5s]  %(message)s")
rootLogger = logging.getLogger()

fileHandler = logging.FileHandler("{0}/{1}.log".format(logPath, fileName))
fileHandler.setFormatter(logFormatter)
rootLogger.addHandler(fileHandler)

consoleHandler = logging.StreamHandler()
consoleHandler.setFormatter(logFormatter)
rootLogger.addHandler(consoleHandler)

In theo định dạng của:

2012-12-05 16:58:26,618 [MainThread  ] [INFO ]  my message

19
Bạn cũng có thể chỉ khởi tạo StreamHandlervới sys.stdout, và sau đó nó sẽ đăng nhập vào đó thay vì stderr.
Silas Ray

1
@ sr2222 logger.addHandler (sys.stdout) cho tôi TênError: tên 'sys' không được xác định
stdcerr

21
Vâng vâng ... bạn phải import sysđầu tiên. Và thực sự khởi tạo trình xử lý, tức làconsoleHandler = logging.StreamHandler(sys.stdout)
Silas Ray

15
Bởi vì như tôi đã nói, đó không phải là cách bạn làm điều đó. Tạo Handler với sys.stdout, sau đó gắn trình xử lý vào logger.
Silas Ray

6
Đừng quên rootLogger.setLevel(logging.DEBUG)nếu bạn đang cố gắng xem thông tin hoặc thông báo gỡ lỗi
bão_m2138

247

logging.basicConfig()có thể lấy một đối số từ khóa handlerskể từ Python 3.3, đơn giản hóa rất nhiều thiết lập ghi nhật ký, đặc biệt là khi thiết lập nhiều trình xử lý với cùng một trình định dạng:

handlers- Nếu được chỉ định, đây sẽ là một trình lặp của trình xử lý đã được tạo để thêm vào trình ghi nhật ký gốc. Bất kỳ trình xử lý nào chưa có bộ định dạng sẽ được gán bộ định dạng mặc định được tạo trong hàm này.

Do đó, toàn bộ thiết lập có thể được thực hiện với một cuộc gọi như thế này:

import logging

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(message)s",
    handlers=[
        logging.FileHandler("debug.log"),
        logging.StreamHandler()
    ]
)

(Hoặc với import sys+ StreamHandler(sys.stdout)theo yêu cầu ban đầu của câu hỏi - mặc định cho StreamHandler là để ghi vào stderr Nhìn vào. Thuộc tính LogRecord nếu bạn muốn tùy chỉnh định dạng đăng nhập và thêm những thứ như tên tập tin / dòng, thông tin chủ đề, vv)

Việc thiết lập ở trên chỉ cần được thực hiện một lần ở gần đầu tập lệnh. Bạn có thể sử dụng nhật ký từ tất cả các vị trí khác trong cơ sở mã sau này như sau:

logging.info('Useful message')
logging.error('Something bad happened')
...

Lưu ý: Nếu nó không hoạt động, một người khác có thể đã khởi tạo hệ thống ghi nhật ký khác. Ý kiến ​​đề nghị làm logging.root.handlers = []trước khi gọi đến basicConfig().


5
đừng quên thiết lập level = log.INFO hoặc cấp độ mong muốn
Andy Matteson

5
Định nghĩa cho FileHandler: logging.FileHandler(filename, mode='a', encoding=None, delay=False). Điều này có nghĩa là, khi bạn chỉ muốn đăng nhập vào cùng một thư mục, bạn chỉ có thể sử dụng FileHandler("mylog.log"). Nếu bạn muốn ghi đè nhật ký mỗi lần, hãy đặt "w" làm đối số thứ hai.
dùng136036

7
Tôi đã thử điều này, nhưng tệp đầu ra trống mặc dù bảng điều khiển đang cho đầu ra .. Có đề xuất nào không ..?
Ramesh-X

4
@ Ramesh-X, điều này cũng khiến tôi phát điên. chỉ cần thực hiện logging.root.handlers = []trước khi gọi đến basicConfig, hãy xem chức năng - thật khó chịu.
ihadanny

70

Thêm StreamHandler mà không có đối số sẽ vào stderr thay vì stdout. Nếu một số quy trình khác có sự phụ thuộc vào kết xuất xuất chuẩn (tức là khi viết plugin NRPE), thì hãy đảm bảo chỉ định rõ ràng thiết bị xuất chuẩn hoặc bạn có thể gặp phải một số rắc rối không mong muốn.

Dưới đây là một ví dụ nhanh sử dụng lại các giá trị giả định và LOGFILE từ câu hỏi:

import logging
from logging.handlers import RotatingFileHandler
from logging import handlers
import sys

log = logging.getLogger('')
log.setLevel(logging.DEBUG)
format = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")

ch = logging.StreamHandler(sys.stdout)
ch.setFormatter(format)
log.addHandler(ch)

fh = handlers.RotatingFileHandler(LOGFILE, maxBytes=(1048576*5), backupCount=7)
fh.setFormatter(format)
log.addHandler(fh)

Tôi đang thử điều này.
Ajay Kumar

19

Chạy basicConfigvới stream=sys.stdouttư cách là đối số trước khi thiết lập bất kỳ trình xử lý nào khác hoặc ghi nhật ký bất kỳ thư nào hoặc thêm thủ công StreamHandlerđẩy thông điệp đến thiết bị ghi nhật ký gốc (hoặc bất kỳ trình ghi nhật ký nào bạn muốn, cho vấn đề đó).


5

Sau khi sử dụng mã của Waterboy nhiều lần trong nhiều gói Python, cuối cùng tôi đã chuyển nó thành một gói Python độc lập nhỏ mà bạn có thể tìm thấy ở đây:

https://github.com/acschaefer/duallog

Mã này là tài liệu tốt và dễ sử dụng. Chỉ cần tải xuống .pytệp và đưa nó vào dự án của bạn hoặc cài đặt toàn bộ gói thông qua pip install duallog.


Vì một số lý do không được đăng nhập vào bảng điều khiển, không có tệp nào (trống)
JackTheKnife

5

Đăng nhập stdoutrotating filevới các cấp độ và định dạng khác nhau:

import logging
import logging.handlers
import sys

if __name__ == "__main__":

    # Change root logger level from WARNING (default) to NOTSET in order for all messages to be delegated.
    logging.getLogger().setLevel(logging.NOTSET)

    # Add stdout handler, with level INFO
    console = logging.StreamHandler(sys.stdout)
    console.setLevel(logging.INFO)
    formater = logging.Formatter('%(name)-13s: %(levelname)-8s %(message)s')
    console.setFormatter(formater)
    logging.getLogger().addHandler(console)

    # Add file rotating handler, with level DEBUG
    rotatingHandler = logging.handlers.RotatingFileHandler(filename='rotating.log', maxBytes=1000, backupCount=5)
    rotatingHandler.setLevel(logging.DEBUG)
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    rotatingHandler.setFormatter(formatter)
    logging.getLogger().addHandler(rotatingHandler)

    log = logging.getLogger("app." + __name__)

    log.debug('Debug message, should only appear in the file.')
    log.info('Info message, should appear in file and stdout.')
    log.warning('Warning message, should appear in file and stdout.')
    log.error('Error message, should appear in file and stdout.')

2

Đây là một giải pháp hoàn chỉnh, được bao bọc độc đáo dựa trên câu trả lời của Waterboy và nhiều nguồn khác. Nó hỗ trợ đăng nhập vào cả bảng điều khiển và tệp nhật ký, cho phép cài đặt mức nhật ký khác nhau, cung cấp đầu ra được tô màu và có thể dễ dàng cấu hình (cũng có sẵn dưới dạng Gist ):

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# -------------------------------------------------------------------------------
#                                                                               -
#  Python dual-logging setup (console and log file),                            -
#  supporting different log levels and colorized output                         -
#                                                                               -
#  Created by Fonic <https://github.com/fonic>                                  -
#  Date: 04/05/20                                                               -
#                                                                               -
#  Based on:                                                                    -
#  https://stackoverflow.com/a/13733863/1976617                                 -
#  https://uran198.github.io/en/python/2016/07/12/colorful-python-logging.html  -
#  https://en.wikipedia.org/wiki/ANSI_escape_code#Colors                        -
#                                                                               -
# -------------------------------------------------------------------------------

# Imports
import os
import sys
import logging

# Logging formatter supporting colored output
class LogFormatter(logging.Formatter):

    COLOR_CODES = {
        logging.CRITICAL: "\033[1;35m", # bright/bold magenta
        logging.ERROR:    "\033[1;31m", # bright/bold red
        logging.WARNING:  "\033[1;33m", # bright/bold yellow
        logging.INFO:     "\033[0;37m", # white / light gray
        logging.DEBUG:    "\033[1;30m"  # bright/bold black / dark gray
    }

    RESET_CODE = "\033[0m"

    def __init__(self, color, *args, **kwargs):
        super(LogFormatter, self).__init__(*args, **kwargs)
        self.color = color

    def format(self, record, *args, **kwargs):
        if (self.color == True and record.levelno in self.COLOR_CODES):
            record.color_on  = self.COLOR_CODES[record.levelno]
            record.color_off = self.RESET_CODE
        else:
            record.color_on  = ""
            record.color_off = ""
        return super(LogFormatter, self).format(record, *args, **kwargs)

# Setup logging
def setup_logging(console_log_output, console_log_level, console_log_color, logfile_file, logfile_log_level, logfile_log_color, log_line_template):

    # Create logger
    # For simplicity, we use the root logger, i.e. call 'logging.getLogger()'
    # without name argument. This way we can simply use module methods for
    # for logging throughout the script. An alternative would be exporting
    # the logger, i.e. 'global logger; logger = logging.getLogger("<name>")'
    logger = logging.getLogger()

    # Set global log level to 'debug' (required for handler levels to work)
    logger.setLevel(logging.DEBUG)

    # Create console handler
    console_log_output = console_log_output.lower()
    if (console_log_output == "stdout"):
        console_log_output = sys.stdout
    elif (console_log_output == "stderr"):
        console_log_output = sys.stderr
    else:
        print("Failed to set console output: invalid output: '%s'" % console_log_output)
        return False
    console_handler = logging.StreamHandler(console_log_output)

    # Set console log level
    try:
        console_handler.setLevel(console_log_level.upper()) # only accepts uppercase level names
    except:
        print("Failed to set console log level: invalid level: '%s'" % console_log_level)
        return False

    # Create and set formatter, add console handler to logger
    console_formatter = LogFormatter(fmt=log_line_template, color=console_log_color)
    console_handler.setFormatter(console_formatter)
    logger.addHandler(console_handler)

    # Create log file handler
    try:
        logfile_handler = logging.FileHandler(logfile_file)
    except Exception as exception:
        print("Failed to set up log file: %s" % str(exception))
        return False

    # Set log file log level
    try:
        logfile_handler.setLevel(logfile_log_level.upper()) # only accepts uppercase level names
    except:
        print("Failed to set log file log level: invalid level: '%s'" % logfile_log_level)
        return False

    # Create and set formatter, add log file handler to logger
    logfile_formatter = LogFormatter(fmt=log_line_template, color=logfile_log_color)
    logfile_handler.setFormatter(logfile_formatter)
    logger.addHandler(logfile_handler)

    # Success
    return True

# Main function
def main():

    # Setup logging
    script_name = os.path.splitext(os.path.basename(sys.argv[0]))[0]
    if (not setup_logging(console_log_output="stdout", console_log_level="warning", console_log_color=True,
                        logfile_file=script_name + ".log", logfile_log_level="debug", logfile_log_color=False,
                        log_line_template="%(color_on)s[%(created)d] [%(threadName)s] [%(levelname)-8s] %(message)s%(color_off)s")):
        print("Failed to setup logging, aborting.")
        return 1

    # Log some messages
    logging.debug("Debug message")
    logging.info("Info message")
    logging.warning("Warning message")
    logging.error("Error message")
    logging.critical("Critical message")

# Call main function
if (__name__ == "__main__"):
    sys.exit(main())

-4

Đối với 2.7, hãy thử các cách sau:

fh = logging.handlers.RotatingFileHandler(LOGFILE, maxBytes=(1048576*5), backupCount=7)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.