Günlük dosyasına ek olarak Python kaydedicilerinin tüm iletileri stdout'a çıkarması


450

loggingModülü kullanarak Python günlüğü otomatik olarak stdout şeyler çıktı gitmek için gereken günlük dosyasına ek yapmak için bir yolu var mı ? Örneğin, ben tüm aramaları istiyorum logger.warning, logger.critical, logger.errorhedeflenen yerlere gitmek ama ek olarak daima kopyalanabilir stdout. Bu, aşağıdaki gibi mesajların kopyalanmasını önlemek içindir:

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

1
Lütfen bu yanıtı kontrol edin stackoverflow.com/questions/9321741/…
SeF

Yanıtlar:


636

Tüm günlük çıktıları işleyiciler tarafından işlenir; sadece logging.StreamHandler()kök kaydediciye a ekleyin .

Akış işleyicisini yapılandırmak ( stdoutvarsayılan yerine kullanmak stderr) ve kök kaydediciye eklemek için bir örnek :

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
Sorun değil ama zaten bir dosyaya yönlendirildiyse, buna stdoutek olarak nasıl yazdırılabilirim ?

54
@ user248237: Gösterildiği gibi yeni bir işleyici ekleyerek . Yeni işleyiciler mevcut işleyicilerin yerini almaz, ayrıca günlük girişlerini de işleyebilirler.
Martijn Pieters

@MartijnPieters, yazdırılan her günlük deyimine dize eklemenin bir yolu var mı?
Prakhar Mohan Srivastava

7
@PrakharMohanSrivastava Sanırım sadece geçirilen dizeye ekleyebilirsiniz logging.Formatter.
A.Wan

3
@ himanshu219: kullanım durumu, birden çok işleyici eklemeye başlar başlamaz, genellikle farklılaştırmak istediğinizdir. Konsola HATA AYIKLAMA, UYARI ve bir dosyaya kadar, vb.
Martijn Pieters

505

Stdout'a giriş yapmanın en basit yolu:

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

57
Hm, ama bu bir dosyaya kaydedilmedi, değil mi? Soru, dosyaya ve konsola nasıl günlük kaydı yapılacağıydı .
Weidenrinde


En azından Python 3'te, stream=sys.stdoutbenim için konsola oturum açmak için atlamak hala işe yarıyor gibi görünüyor .
Taylor Edmiston

3
@TaylorEdmiston Evet, ama AFAIK stderr akışı. Çıkışı kabuktan yeniden yönlendirmeyi deneyin.
Sorin

1
TAMAM. Bu her ikisine de cevap vermez: dosyaya ve konsola giriş yapmak, ancak 3 veya daha az satırda ihtiyacım olanı bulmak güzeldi.
Steve3p0

67

Birden fazla işleyici kullanmak mümkündür.

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)

Lütfen bakınız: https://docs.python.org/2/howto/logging-cookbook.html


4
Harika cevap, biraz dağınık da olsa. Akışlar ve dosyalar için farklı düzeylerin ve biçimlerin nasıl kullanıldığını göstermeyi sevin. +1, ama ruh olarak +2.
Unfun Cat

Benim için bu sys.stdoutparametre olmadan çalışmadıch = logging.StreamHandler()
veuncent

64

Dosya ve stdout için iki işleyici oluşturabilir ve sonra handlersargümanıyla bir günlükçü oluşturabilirsiniz basicConfig. Her iki işleyici için aynı log_level ve format çıktısına sahipseniz yararlı olabilir:

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

Dosyaya ve stderr'e giriş yapmanın en basit yolu:

import logging

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

Bu, konsoldaki günlük iletisinden önce INFO, DEBUG ve ERROR etiketlerini göstermez. Bu etiketleri dosyada gösterir. Etiketleri konsolda gösterecek herhangi bir fikir var mı?
JahMyst

1
Teşekkürler, @JahMyst, Formatter'ı ekledim. Ne yazık ki, artık çok kısa değil, ama yine de en basit yol. :-)
Weidenrinde

12

İşte güçlü ancak zayıf belgelenmiş logging.config.dictConfigyönteme dayanan bir çözüm . Her günlük iletisini göndermek yerine, stdoutgünlük düzeyi ERRORve daha yüksek olan iletileri stderrve diğer her şeyi gönderir stdout. Sistemin diğer bölümleri dinliyorsa stderrveya bu yararlı olabilir 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)

Kök kaydediciyi almak için kaydediciyi boş bir dizeye yeniden adlandırmak zorundaydı. Aksi takdirde çok yararlı, teşekkürler!
Newtopian

8

Hiç kimse düzgün bir iki astar paylaşmadığından, kendimi paylaşacağım:

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

2

İşte son derece basit bir örnek:

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

Çıktı, stdout'ta ve dosyada "test msg" gösterecektir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.