Làm cho trình ghi nhật ký Python xuất tất cả các thông báo ra thiết bị xuất chuẩn ngoài tệp nhật ký


450

Có cách nào để ghi nhật ký Python bằng loggingmô-đun tự động xuất mọi thứ ra thiết bị xuất chuẩn ngoài tệp nhật ký mà chúng được cho là sẽ đi không? Ví dụ, tôi muốn tất cả các cuộc gọi đến logger.warning, logger.critical, logger.errorđi đến những nơi dự định của họ nhưng ngoài luôn được sao chép vào stdout. Điều này là để tránh trùng lặp các tin nhắn như:

mylogger.critical("something failed")
print "something failed"

1
Vui lòng kiểm tra câu trả lời này stackoverflow.com/questions/9321741/
Ấn

Câu trả lời:


635

Tất cả đầu ra đăng nhập được xử lý bởi các xử lý; chỉ cần thêm một logging.StreamHandler()vào logger gốc.

Dưới đây là một ví dụ cấu hình trình xử lý luồng (sử dụng stdoutthay vì mặc định stderr) và thêm nó vào trình ghi nhật ký gốc:

import logging
import sys

root = logging.getLogger()
root.setLevel(logging.DEBUG)

handler = logging.StreamHandler(sys.stdout)
handler.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
root.addHandler(handler)

4
Điều đó tốt nhưng nếu nó đã được chuyển hướng đến một tập tin, làm thế nào tôi có thể in nó stdoutra?

54
@ user248237: Bằng cách thêm một trình xử lý mới như minh họa. Trình xử lý mới không thay thế trình xử lý hiện có, họ cũng có thể xử lý các mục nhật ký.
Martijn Pieters

@MartijnPieters có cách nào để thêm một chuỗi vào mỗi câu lệnh log được in ra không?
Prakhar Mohan Srivastava

7
@PrakharMohanSrivastava Tôi đoán bạn chỉ cần thêm nó vào chuỗi được truyền vào logging.Formatter.
A.Wan

3
@ himanshu219: trường hợp sử dụng là ngay khi bạn bắt đầu thêm nhiều trình xử lý, bạn thường muốn phân biệt. DEBUG vào bảng điều khiển, CẢNH BÁO và lên đến một tệp, v.v.
Martijn Pieters

505

Cách đơn giản nhất để đăng nhập vào thiết bị xuất chuẩn:

import logging
import sys
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)

57
Hừm, nhưng cái này không đăng nhập vào một tập tin, phải không? Câu hỏi là làm thế nào để đăng nhập vào tập tin bàn điều khiển.
Weidenrinde


Trong Python 3 ít nhất, có vẻ như bỏ qua stream=sys.stdoutvẫn hoạt động để đăng nhập vào bảng điều khiển cho tôi.
Taylor Edmiston

3
@TaylorEdmiston Vâng, nhưng đó là luồng stderr AFAIK. Hãy thử chuyển hướng đầu ra từ vỏ.
Sorin

1
ĐỒNG Ý. Điều này không trả lời cả hai: đăng nhập vào tập tin và vào bàn điều khiển, nhưng thật tuyệt khi tìm thấy những gì tôi cần trong 3 dòng hoặc ít hơn.
Steve3p0

67

Có thể sử dụng nhiều trình xử lý.

import logging
import auxiliary_module

# create logger with 'spam_application'
log = logging.getLogger('spam_application')
log.setLevel(logging.DEBUG)

# create formatter and add it to the handlers
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

# create file handler which logs even debug messages
fh = logging.FileHandler('spam.log')
fh.setLevel(logging.DEBUG)
fh.setFormatter(formatter)
log.addHandler(fh)

# create console handler with a higher log level
ch = logging.StreamHandler()
ch.setLevel(logging.ERROR)
ch.setFormatter(formatter)
log.addHandler(ch)

log.info('creating an instance of auxiliary_module.Auxiliary')
a = auxiliary_module.Auxiliary()
log.info('created an instance of auxiliary_module.Auxiliary')

log.info('calling auxiliary_module.Auxiliary.do_something')
a.do_something()
log.info('finished auxiliary_module.Auxiliary.do_something')

log.info('calling auxiliary_module.some_function()')
auxiliary_module.some_function()
log.info('done with auxiliary_module.some_function()')

# remember to close the handlers
for handler in log.handlers:
    handler.close()
    log.removeFilter(handler)

Vui lòng xem: https://docs.python.org/2/howto/logging-cookbook.html


4
Câu trả lời tuyệt vời, mặc dù một chút lộn xộn. Yêu cách bạn chỉ ra cách sử dụng các cấp độ và định dạng khác nhau cho các luồng và tệp. +1, nhưng +2 về tinh thần.
Mèo Unun

Đối với tôi điều này không làm việc mà không có sys.stdouttham số trongch = logging.StreamHandler()
veuncent

64

Bạn có thể tạo hai trình xử lý cho tệp và thiết bị xuất chuẩn và sau đó tạo một trình ghi nhật ký với handlersđối số basicConfig. Nó có thể hữu ích nếu bạn có cùng log_level và định dạng đầu ra cho cả hai trình xử lý:

import logging
import sys

file_handler = logging.FileHandler(filename='tmp.log')
stdout_handler = logging.StreamHandler(sys.stdout)
handlers = [file_handler, stdout_handler]

logging.basicConfig(
    level=logging.DEBUG, 
    format='[%(asctime)s] {%(filename)s:%(lineno)d} %(levelname)s - %(message)s',
    handlers=handlers
)

logger = logging.getLogger('LOGGER_NAME')

32

Cách đơn giản nhất để đăng nhập vào tệp và vào stderr:

import logging

logging.basicConfig(filename="logfile.txt")
stderrLogger=logging.StreamHandler()
stderrLogger.setFormatter(logging.Formatter(logging.BASIC_FORMAT))
logging.getLogger().addHandler(stderrLogger)

Điều này không hiển thị nhãn INFO, DEBUG và ERROR trước thông điệp đăng nhập trong bảng điều khiển. Nó không hiển thị các nhãn trong tập tin. Bất kỳ ý tưởng để cũng hiển thị các nhãn trong giao diện điều khiển?
JahMyst

1
Cảm ơn, @JahMyst, tôi đã thêm Trình định dạng. Thật không may, nó không quá ngắn nữa, nhưng vẫn là cách đơn giản nhất. :-)
Weidenrinde

12

Đây là một giải pháp dựa trên logging.config.dictConfigphương pháp mạnh mẽ nhưng tài liệu kém . Thay vì gửi mọi thông điệp tường trình tới stdout, nó sẽ gửi các tin nhắn với cấp độ nhật ký ERRORvà cao hơn stderrvà mọi thứ khác stdout. Điều này có thể hữu ích nếu các phần khác của hệ thống đang nghe stderrhoặc stdout.

import logging
import logging.config
import sys

class _ExcludeErrorsFilter(logging.Filter):
    def filter(self, record):
        """Filters out log messages with log level ERROR (numeric value: 40) or higher."""
        return record.levelno < 40


config = {
    'version': 1,
    'filters': {
        'exclude_errors': {
            '()': _ExcludeErrorsFilter
        }
    },
    'formatters': {
        # Modify log message format here or replace with your custom formatter class
        'my_formatter': {
            'format': '(%(process)d) %(asctime)s %(name)s (line %(lineno)s) | %(levelname)s %(message)s'
        }
    },
    'handlers': {
        'console_stderr': {
            # Sends log messages with log level ERROR or higher to stderr
            'class': 'logging.StreamHandler',
            'level': 'ERROR',
            'formatter': 'my_formatter',
            'stream': sys.stderr
        },
        'console_stdout': {
            # Sends log messages with log level lower than ERROR to stdout
            'class': 'logging.StreamHandler',
            'level': 'DEBUG',
            'formatter': 'my_formatter',
            'filters': ['exclude_errors'],
            'stream': sys.stdout
        },
        'file': {
            # Sends all log messages to a file
            'class': 'logging.FileHandler',
            'level': 'DEBUG',
            'formatter': 'my_formatter',
            'filename': 'my.log',
            'encoding': 'utf8'
        }
    },
    'root': {
        # In general, this should be kept at 'NOTSET'.
        # Otherwise it would interfere with the log levels set for each handler.
        'level': 'NOTSET',
        'handlers': ['console_stderr', 'console_stdout', 'file']
    },
}

logging.config.dictConfig(config)

đã phải đổi tên logger thành một chuỗi rỗng để thực sự có được logger gốc. Nếu không thì rất hữu ích, cảm ơn!
Newtopian

8

Vì không ai chia sẻ hai lớp lót gọn gàng, tôi sẽ chia sẻ cái riêng của mình:

logging.basicConfig(filename='logs.log', level=logging.DEBUG, format="%(asctime)s:%(levelname)s: %(message)s")
logging.getLogger().addHandler(logging.StreamHandler())

2

Đây là một ví dụ cực kỳ đơn giản:

import logging
l = logging.getLogger("test")

# Add a file logger
f = logging.FileHandler("test.log")
l.addHandler(f)

# Add a stream logger
s = logging.StreamHandler()
l.addHandler(s)

# Send a test message to both -- critical will always log
l.critical("test msg")

Đầu ra sẽ hiển thị "thông báo thử nghiệm" trên thiết bị xuất chuẩn và cả trong tệp.

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.