การทำให้ Python loggers ส่งข้อความทั้งหมดไปที่ stdout นอกเหนือจากไฟล์บันทึก


450

มีวิธีที่จะทำให้การบันทึก Python โดยใช้loggingโมดูลส่งออกสิ่งที่จะ stdout โดยอัตโนมัตินอกเหนือจากไฟล์บันทึกที่พวกเขาควรจะไป? ตัวอย่างเช่นผมต้องการโทรหาlogger.warning, logger.critical, logger.errorที่จะไปยังสถานที่ที่ตั้งใจของพวกเขา stdoutแต่ในนอกจากนี้มักจะถูกคัดลอกไป นี่คือเพื่อหลีกเลี่ยงการทำซ้ำข้อความเช่น:

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

1
โปรดตรวจสอบคำตอบนี้stackoverflow.com/questions/9321741/…
SeF

คำตอบ:


636

เอาต์พุตการบันทึกทั้งหมดถูกจัดการโดยตัวจัดการ เพียงเพิ่ม a logging.StreamHandler()ลงใน root logger

นี่คือตัวอย่างการกำหนดค่าตัวจัดการสตรีม (ใช้stdoutแทนค่าเริ่มต้นstderr) และเพิ่มลงในตัวบันทึกราก:

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
ไม่เป็นไร แต่ถ้ามันถูกเปลี่ยนเส้นทางไปยังไฟล์ฉันจะพิมพ์มันได้stdoutอย่างไร?

54
@ user248237: โดยเพิ่มตัวจัดการใหม่ตามที่แสดง ตัวจัดการใหม่ไม่ได้แทนที่ตัวจัดการที่มีอยู่พวกเขายังได้รับการประมวลผลรายการบันทึก
Martijn Pieters

@MartijnPieters มีวิธีการเพิ่มสตริงในทุกคำสั่งบันทึกพิมพ์ออกมา?
Prakhar Mohan Srivastava

7
@ PrakharMohanSrivastava ฉันเดาว่าคุณสามารถเพิ่มลงในสตริงที่ส่งผ่านเข้าไปlogging.Formatterได้
59

3
@ himanshu219: กรณีการใช้งานคือทันทีที่คุณเริ่มเพิ่มตัวจัดการหลายตัวคุณมักต้องการแยกแยะ แก้ปัญหาที่คอนโซลคำเตือนและมากถึงไฟล์ ฯลฯ
Martijn Pieters

505

วิธีที่ง่ายที่สุดในการเข้าสู่ stdout:

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

57
หืม แต่นี่ไม่ได้ถูกบันทึกไว้ในไฟล์ใช่มั้ย คำถามคือจะบันทึกไฟล์และคอนโซลได้อย่างไร
Weidenrinde

ลิงก์อ้างอิง: Python3 เอกสาร: Logging.basicConfig
Czechnology

อย่างน้อยใน Python 3 ดูเหมือนว่าการข้ามstream=sys.stdoutยังคงใช้ได้กับการเข้าสู่คอนโซลสำหรับฉัน
Taylor Edmiston

3
@TaylorEdmiston ใช่ แต่มันเป็นกระแส stderr AFAIK ลองเปลี่ยนเส้นทางเอาต์พุตจากเชลล์
Sorin

1
ตกลง. สิ่งนี้ไม่ตอบทั้ง: การเข้าสู่ไฟล์และคอนโซล แต่มันก็ดีที่ได้พบสิ่งที่ฉันต้องการใน 3 บรรทัดหรือน้อยกว่า
Steve3p0

67

เป็นไปได้โดยใช้ตัวจัดการหลายตัว

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)

โปรดดู: https://docs.python.org/2/howto/logging-cookbook.html


4
คำตอบที่ยอดเยี่ยมแม้ว่าจะยุ่งเล็กน้อย รักวิธีที่คุณแสดงวิธีใช้ระดับและรูปแบบที่แตกต่างกันสำหรับสตรีมและไฟล์ +1 แต่ +2 ด้วยจิตวิญญาณ
Cat Unfun

สำหรับฉันสิ่งนี้ไม่ทำงานหากไม่มีsys.stdoutพารามิเตอร์ในch = logging.StreamHandler()
veuncent

64

คุณสามารถสร้างสองรถยกสำหรับแฟ้มและ stdout แล้วสร้างหนึ่งคนตัดไม้กับอาร์กิวเมนต์handlers basicConfigมันอาจมีประโยชน์หากคุณมี log_level และรูปแบบเอาต์พุตเดียวกันสำหรับตัวจัดการทั้งสอง:

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

วิธีที่ง่ายที่สุดในการเข้าสู่ไฟล์และ stderr:

import logging

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

สิ่งนี้จะไม่แสดงป้ายกำกับ INFO, DEBUG และ ERROR ก่อนที่ข้อความการบันทึกในคอนโซล มันจะแสดงป้ายกำกับเหล่านั้นในไฟล์ มีแนวคิดใดที่จะแสดงป้ายกำกับในคอนโซลหรือไม่
JahMyst

1
ขอบคุณ @JahMyst ฉันเพิ่ม Formatter น่าเสียดายที่มันไม่สั้นอีกต่อไป แต่ยังคงเป็นวิธีที่ง่ายที่สุด :-)
Weidenrinde

12

นี่เป็นวิธีการแก้ปัญหาบนพื้นฐานของการมีประสิทธิภาพ แต่เอกสารไม่ดีวิธีlogging.config.dictConfig แทนการส่งข้อความเข้าสู่ระบบทุกstdoutก็จะส่งข้อความที่มีระดับการบันทึกERRORและสูงกว่าการและทุกอย่างอื่นไปstderr stdoutนี้จะมีประโยชน์ถ้าชิ้นส่วนอื่น ๆ ของระบบจะฟังหรือstderrstdout

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)

ต้องเปลี่ยนชื่อ logger เป็นสตริงว่างเปล่าเพื่อรับ root logger เป็นอย่างอื่นมีประโยชน์มากขอบคุณ!
Newtopian

8

เนื่องจากไม่มีใครแชร์ซับสองอย่างเรียบร้อยฉันจะแชร์ตัวเอง:

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

2

นี่เป็นตัวอย่างที่ง่ายมาก:

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")

ผลลัพธ์จะแสดง "test msg" บน stdout และในไฟล์

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.