Bagaimana cara menonaktifkan dan mengaktifkan kembali logging konsol di Python?

154

Saya menggunakan modul logging Python dan saya ingin menonaktifkan konsol logging untuk beberapa waktu tetapi tidak berhasil.

#!/usr/bin/python
import logging

logger = logging.getLogger() # this gets the root logger
# ... here I add my own handlers 
#logger.removeHandler(sys.stdout)
#logger.removeHandler(sys.stderr)

print logger.handlers 
# this will print [<logging.StreamHandler instance at ...>]
# but I may have other handlers there that I want to keep

logger.debug("bla bla")

Kode di atas menampilkan bla blaon stdout dan saya tidak tahu bagaimana cara menonaktifkan konsol handler dengan aman. Bagaimana saya bisa yakin bahwa saya sementara menghapus StreamHandler konsol dan bukan yang lain?

Sorin
sumber
Bagi mereka yang bertanya-tanya mengapa ada orang yang ingin menonaktifkan pencatatan: Anda tidak ingin mencatat data pribadi seperti kata sandi atau kunci API.
Stevoisiak
4
@StevenVascellaro. Mengapa mereka dikirim ke logger? Kedengarannya tidak benar ...
Fisikawan Gila
1
@MadPhysicist Saya memiliki aplikasi yang mengirimkan permintaan XML ke API eksternal. Secara default, permintaan ini dicatat ke file. Namun, login awal memerlukan otentikasi dengan nama pengguna dan kata sandi, yang saya tidak ingin login.
Stevoisiak
@StevenVascellaro. Saya melihat. Terima kasih untuk penjelasannya.
Fisikawan Gila
Anda tidak menunjukkan bagaimana / di mana Anda menambahkan penangan Anda. Jika mereka ditambahkan ke root logger ini akan mencegah penebangan dari menambahkan StreamHandler default seperti yang dijelaskan di docs.python.org/3/library/logging.html#logging.basicConfig Juga, per deskripsi yang ditautkan, StreamHandler default hanya ditambahkan pada saat pertama panggilan memancarkan pesan log sehingga ketika Anda mencetaknya logger.handlersharus kosong (karena mendahului logger.debug()panggilan). Kode yang dipermasalahkan hanya menampilkan [](daftar penangan kosong). Diverifikasi dengan Python 2.7.15 dan Python 3.6.6.
Piotr Dobrogost

Jawaban:

197

Saya menemukan solusi untuk ini:

logger = logging.getLogger('my-logger')
logger.propagate = False
# now if you use logger it will not log to console.

Ini akan mencegah logging dari dikirim ke logger atas yang mencakup logging konsol.

Sorin
sumber
8
Saya rasa ini bukan solusi yang baik. Tidak menyebar ke penebang yang lebih tinggi dapat memiliki konsekuensi lain yang tidak diinginkan.
lfk
2
Jika Anda hanya ingin memfilter pesan di bawah level log tertentu (katakanlah, semua INFOpesan), Anda dapat mengubah baris kedua menjadi sesuatu sepertilogger.setLevel(logging.WARNING)
Hartley Brody
2
Bagaimana Anda mengaktifkan kembali log setelahnya?
Stevoisiak
4
Bukan jawaban karena memblokir propagasi secara efektif menonaktifkan semua penangan root logger dan pertanyaannya dengan jelas menyatakan (...) tapi saya mungkin memiliki penangan lain di sana yang ingin saya pertahankan yang menyarankan tujuannya adalah untuk menonaktifkan StreamHandler default dari logger root saja .
Piotr Dobrogost
Menghentikan propagasi pesan tidak cukup. Sejak Python 3.2 , logging.lastResortpawang akan tetap mencatat pesan-pesan dengan tingkat keparahan logging.WARNINGdan lebih besar sys.stderrkarena tidak adanya penangan lain. Lihat jawaban saya .
Maggyero
106

Saya menggunakan:

logger = logging.getLogger()
logger.disabled = True
... whatever you want ...
logger.disabled = False
infinito
sumber
9
ini juga berfungsi pada logginglevel modul untuk menonaktifkan logging sepenuhnya , misalnya import logging; logging.disable(logging.CRITICAL);:: docs.python.org/2/library/logging.html#logging.disable
lsh
1
Ini jauh lebih baik daripada menonaktifkan propagasi.
Mátray Márk
6
Bukan jawaban - pertanyaannya menanyakan bagaimana cara menonaktifkan StreamHandler default saja .
Piotr Dobrogost
1
The disabledatribut bukan bagian dari API publik. Lihat bugs.python.org/issue36318 .
Maggyero
69

Kamu bisa memakai:

logging.basicConfig(level=your_level)

di mana your_level adalah salah satunya:

      'debug': logging.DEBUG,
      'info': logging.INFO,
      'warning': logging.WARNING,
      'error': logging.ERROR,
      'critical': logging.CRITICAL

Jadi, jika Anda mengatur tingkat_Anda ke logging.CRITICAL , Anda hanya akan menerima pesan-pesan penting yang dikirim oleh:

logging.critical('This is a critical error message')

Mengatur tingkat_mu ke logging . Debug akan menunjukkan semua tingkat logging.

Untuk detail lebih lanjut, silakan lihat contoh logging.

Dengan cara yang sama untuk mengubah level untuk setiap Handler gunakan fungsi Handler.setLevel () .

import logging
import logging.handlers

LOG_FILENAME = '/tmp/logging_rotatingfile_example.out'

# Set up a specific logger with our desired output level
my_logger = logging.getLogger('MyLogger')
my_logger.setLevel(logging.DEBUG)

# Add the log message handler to the logger
handler = logging.handlers.RotatingFileHandler(
          LOG_FILENAME, maxBytes=20, backupCount=5)

handler.setLevel(logging.CRITICAL)

my_logger.addHandler(handler)
Vadikus
sumber
6
Ini umumnya info yang berguna, tetapi pertanyaannya adalah bagaimana cara menonaktifkan konsol logging, bukan bagaimana cara menambahkan handler tambahan. jika Anda memeriksa my_logger.handlers dengan kode di atas diterapkan pada contoh asli, Anda akan melihat dua penangan - penangan file baru dan penangan aliran asli.
Joe
KRITIS adalah kata yang saya cari. Terima kasih.
Nishant
Saya ingin melihat tingkat debug MATI. Tidak ambigu dan sederhana.
Bukan mesin
46

(pertanyaan lama, tetapi untuk pencari masa depan)

Lebih dekat ke kode / maksud poster asli, ini bekerja untuk saya di bawah python 2.6

#!/usr/bin/python
import logging

logger = logging.getLogger() # this gets the root logger

lhStdout = logger.handlers[0]  # stdout is the only handler initially

# ... here I add my own handlers 
f = open("/tmp/debug","w")          # example handler
lh = logging.StreamHandler(f)
logger.addHandler(lh)

logger.removeHandler(lhStdout)

logger.debug("bla bla")

Gotcha yang harus saya kerjakan adalah menghapus stdout handler setelah menambahkan yang baru; kode logger muncul untuk secara otomatis menambahkan kembali stdout jika tidak ada penangan yang hadir.

lkyky
sumber
2
Urutannya logger = logging.getLogger(); lhStdout = logger.handlers[0]salah karena root logger awalnya tidak memiliki penangan - python -c "import logging; assert not logging.getLogger().handlers". Diverifikasi dengan Python 2.7.15 dan Python 3.6.6.
Piotr Dobrogost
42

Manajer konteks

import logging 
class DisableLogger():
    def __enter__(self):
       logging.disable(logging.CRITICAL)
    def __exit__(self, a, b, c):
       logging.disable(logging.NOTSET)

Contoh penggunaan:

with DisableLogger():
    do_something()
pymen
sumber
Saya sangat suka idiom ini, tetapi saya lebih suka dapat menonaktifkan namespace tertentu. Sebagai contoh, saya hanya ingin logger root dinonaktifkan sementara. Meskipun menggunakan idiom ini, kita seharusnya dapat secara sementara menambah / menghapus handler dan yang lainnya.
Chris
1
Pertanyaannya menanyakan bagaimana cara menonaktifkan StreamHandler default saja .
Piotr Dobrogost
1
Anda tidak perlu menggulung kelas Anda sendiri, Anda dapat menggunakan @contextmanager dari contextlib dan menulis fungsi menghasilkan
KristianR
Jika Anda menyukai buah eksotis di pizza Anda. Tentu.
user3504575
34

Untuk sepenuhnya menonaktifkan pencatatan :

logging.disable(sys.maxint) # Python 2

logging.disable(sys.maxsize) # Python 3

Untuk mengaktifkan pencatatan :

logging.disable(logging.NOTSET)

Jawaban lain memberikan solusi untuk masalah yang tidak sepenuhnya menyelesaikan masalah, seperti

logging.getLogger().disabled = True

dan, untuk beberapa yang nlebih besar dari 50,

logging.disable(n)

Masalah dengan solusi pertama adalah hanya berfungsi untuk root logger. Penebang lain yang dibuat menggunakan, katakanlah, logging.getLogger(__name__)tidak dinonaktifkan oleh metode ini.

Solusi kedua tidak mempengaruhi semua log. Tapi itu membatasi output ke level di atas yang diberikan, jadi orang bisa menimpanya dengan login dengan level lebih besar dari 50.

Itu bisa dicegah dengan

logging.disable(sys.maxint)

yang sejauh yang saya tahu (setelah meninjau sumber ) adalah satu-satunya cara untuk menonaktifkan logging sepenuhnya.

starfry
sumber
1
Mengundurkan diri saat pertanyaan menanyakan bagaimana cara menonaktifkan standar StreamHandler saja
Piotr Dobrogost
27

Ada beberapa jawaban yang sangat bagus di sini, tetapi tampaknya yang paling sederhana tidak terlalu dipertimbangkan (hanya dari infinito).

root_logger = logging.getLogger()
root_logger.disabled = True

Ini menonaktifkan logger root, dan dengan demikian semua logger lainnya. Saya belum benar-benar diuji tetapi harus juga yang tercepat.

Dari kode logging di python 2.7 saya melihat ini

def handle(self, record):
    """
    Call the handlers for the specified record.

    This method is used for unpickled records received from a socket, as
    well as those created locally. Logger-level filtering is applied.
    """
    if (not self.disabled) and self.filter(record):
        self.callHandlers(record)

Yang berarti bahwa ketika dinonaktifkan tidak ada penangan yang dipanggil, dan itu harus lebih efisien bahwa penyaringan ke nilai yang sangat tinggi atau menetapkan penangan yang tidak ada op misalnya.

andrea_crotti
sumber
1
Kecuali saya melakukan sesuatu yang salah, ini hanya menonaktifkan root logger dan tidak dibuat sepertilog = logging.getLogger(__name__)
starfry
2
Ini bisa menjadi masalah jika Anda berurusan dengan banyak penebang atau banyak penangan. Jika, misalnya, Anda masih ingin masuk ke file tetapi ingin menonaktifkan stream handler dalam kasus tertentu.
Joe
1
Ini menonaktifkan logger root, dan dengan demikian semua logger lain - tegasnya menonaktifkan logger root tidak menonaktifkan logger lain. Selain itu pertanyaan yang diajukan tentang menonaktifkan StreamHandler default saja .
Piotr Dobrogost
The disabledatribut bukan bagian dari API publik. Lihat bugs.python.org/issue36318 .
Maggyero
10

Tidak perlu mengalihkan stdout. Ini cara yang lebih baik untuk melakukannya:

import logging
class MyLogHandler(logging.Handler):
    def emit(self, record):
        pass

logging.getLogger().addHandler(MyLogHandler())

Cara yang lebih sederhana adalah:

logging.getLogger().setLevel(100)
Ehsan Foroughi
sumber
4
Dalam Python 2.7+ ini tersedia sebagai NullHandler ()
Pierre
1
Alasan mengapa ini berfungsi (menonaktifkan StreamHandler default) dapat dilihat ketika membaca deskripsi logging.basicConfig()fungsi (penekanan tambang): Apakah konfigurasi dasar untuk sistem logging dengan membuat StreamHandler dengan Formatter default dan menambahkannya ke logger root. Fungsi debug (), info (), warning (), error () dan critical () akan memanggil basicConfig () secara otomatis jika tidak ada penangan yang didefinisikan untuk logger root . - docs.python.org/3/library/logging.html#logging.basicConfig
Piotr Dobrogost
2

Saya tidak tahu modul logging dengan sangat baik, tetapi saya menggunakannya dengan cara yang biasanya saya ingin menonaktifkan hanya pesan debug (atau info). Kamu bisa memakaiHandler.setLevel() untuk mengatur level logging ke KRITIS atau lebih tinggi.

Anda juga dapat mengganti sys.stderr dan sys.stdout dengan file yang terbuka untuk ditulis. Lihat http://docs.python.org/library/sys.html#sys. stdout . Tapi saya tidak akan merekomendasikan itu.

Krab
sumber
Ini bisa berfungsi jika logger.handlers akan mengandung sesuatu, saat ini sedang [].
sorin
2

Anda juga bisa:

handlers = app.logger.handlers
# detach console handler
app.logger.handlers = []
# attach
app.logger.handlers = handlers
Italo Maia
sumber
Mengapa Anda menggunakan app.loggeryang bahkan tidak Anda tentukan alih-alih logger root yang secara eksplisit disebutkan dalam pertanyaan ( logging.getLogger()) dan sebagian besar jawaban? Bagaimana Anda tahu Anda bisa memodifikasi handlersproperti dengan aman alih-alih Logger.addHandlermetode menelepon ?
Piotr Dobrogost
2
import logging

log_file = 'test.log'
info_format = '%(asctime)s - %(levelname)s - %(message)s'
logging.config.dictConfig({
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'info_format': {
            'format': info_format
        },
    },
    'handlers': {
        'console': {
            'level': 'INFO',
            'class': 'logging.StreamHandler',
            'formatter': 'info_format'
        },
        'info_log_file': {
            'class': 'logging.handlers.RotatingFileHandler',
            'level': 'INFO',
            'filename': log_file,
            'formatter': 'info_format'
        }
    },
    'loggers': {
        '': {
            'handlers': [
                'console',
                'info_log_file'
            ],
            'level': 'INFO'
        }
    }
})


class A:

    def __init__(self):
        logging.info('object created of class A')

        self.logger = logging.getLogger()
        self.console_handler = None

    def say(self, word):
        logging.info('A object says: {}'.format(word))

    def disable_console_log(self):
        if self.console_handler is not None:
            # Console log has already been disabled
            return

        for handler in self.logger.handlers:
            if type(handler) is logging.StreamHandler:
                self.console_handler = handler
                self.logger.removeHandler(handler)

    def enable_console_log(self):
        if self.console_handler is None:
            # Console log has already been enabled
            return

        self.logger.addHandler(self.console_handler)
        self.console_handler = None


if __name__ == '__main__':
    a = A()
    a.say('111')
    a.disable_console_log()
    a.say('222')
    a.enable_console_log()
    a.say('333')

Output konsol:

2018-09-15 15:22:23,354 - INFO - object created of class A
2018-09-15 15:22:23,356 - INFO - A object says: 111
2018-09-15 15:22:23,358 - INFO - A object says: 333

konten file test.log:

2018-09-15 15:22:23,354 - INFO - object created of class A
2018-09-15 15:22:23,356 - INFO - A object says: 111
2018-09-15 15:22:23,357 - INFO - A object says: 222
2018-09-15 15:22:23,358 - INFO - A object says: 333
Shawn Hu
sumber
2
Tambahkan beberapa deskripsi tentang kode. Ini akan membantu jauh lebih baik
Mathews Sunny
2

Dengan mengubah satu level di "logging.config.dictConfig" Anda akan dapat membawa keseluruhan level logging ke level baru.

logging.config.dictConfig({
'version': 1,
'disable_existing_loggers': False,
'formatters': {
    'console': {
        'format': '%(name)-12s %(levelname)-8s %(message)s'
    },
    'file': {
        'format': '%(asctime)s %(name)-12s %(levelname)-8s %(message)s'
    }
},
'handlers': {
    'console': {
        'class': 'logging.StreamHandler',
        'formatter': 'console'
    },
#CHANGE below level from DEBUG to THE_LEVEL_YOU_WANT_TO_SWITCH_FOR
#if we jump from DEBUG to INFO
# we won't be able to see the DEBUG logs in our logging.log file
    'file': {
        'level': 'DEBUG',
        'class': 'logging.FileHandler',
        'formatter': 'file',
        'filename': 'logging.log'
    },
},
'loggers': {
    '': {
        'level': 'DEBUG',
        'handlers': ['console', 'file'],
        'propagate': False,
    },
}

})

Ganesh Kalidas
sumber
1

Menemukan solusi elegan menggunakan dekorator , yang mengatasi masalah berikut: bagaimana jika Anda menulis modul dengan beberapa fungsi, masing-masing dengan beberapa pesan debug, dan Anda ingin menonaktifkan login di semua fungsi tapi yang sedang Anda fokuskan?

Anda dapat melakukannya menggunakan dekorator:

import logging, sys
logger = logging.getLogger()
logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)


def disable_debug_messages(func):
    def wrapper(*args, **kwargs):
        prev_state = logger.disabled
        logger.disabled = True
        result = func(*args, **kwargs)
        logger.disabled = prev_state
        return result
    return wrapper

Kemudian, Anda dapat melakukan:

@disable_debug_messages
def function_already_debugged():
    ...
    logger.debug("This message won't be showed because of the decorator")
    ...

def function_being_focused():
    ...
    logger.debug("This message will be showed")
    ...

Bahkan jika Anda menelepon function_already_debuggeddari dalam function_being_focused, debug pesan darifunction_already_debugged tidak akan ditampilkan. Ini memastikan Anda hanya akan melihat pesan debug dari fungsi yang Anda fokuskan.

Semoga ini bisa membantu!

Carlos Souza
sumber
0

Jika Anda ingin menonaktifkan logger tertentu sementara di sini adalah apa yang dilakukan.

Contoh Log

2019-10-02 21:28:45,663 django.request PID: 8  Internal Server Error: /service_portal/get_all_sites

Kode

django_request_logger = logging.getLogger('django.request')
django_request_logger.disabled = True
django_request_logger.disabled = False
sonance207
sumber
0

Di pustaka Logging Python, seseorang dapat sepenuhnya menonaktifkan logging (untuk semua level) untuk logger tertentu dengan melakukan salah satu dari berikut ini:

  1. Menambahkan ke logging.NullHandler()penangan pengendali (untuk mencegah logging.lastResortpenangan dari penebangan peristiwa keparahan logging.WARNINGdan lebih besar ke sys.stderr) dan mengatur propagateatribut pencatat itu untukFalse (untuk mencegah penebang meneruskan peristiwa ke penangan penebang leluhurnya).

    • Menggunakan API utama:

      import logging
      
      logging.getLogger("foo").addHandler(logging.NullHandler())
      logging.getLogger("foo").propagate = False
    • Menggunakan API konfigurasi:

      import logging.config
      
      logging.config.dictConfig({
          "version": 1,
          "handlers": {
              "null": {
                  "class": "logging.NullHandler"
              }
          },
          "loggers": {
              "foo": {
                  "handlers": ["null"],
                  "propagate": False
              }
          }
      })
  2. Menambahkan lambda record: Falsefilter ke logger .

    • Menggunakan API utama:

      import logging
      
      logging.getLogger("foo").addFilter(lambda record: False)
    • Menggunakan API konfigurasi:

      import logging.config
      
      logging.config.dictConfig({
          "version": 1,
          "filters": {
              "all": {
                  "()": lambda: (lambda record: False)
              }
          },
          "loggers": {
              "foo": {
                  "filters": ["all"]
              }
          }
      })

Peringatan.- Bertentangan dengan solusi 1, solusi 2 tidak menonaktifkan logging dari penebang anak (misalnyalogging.getLogger("foo.bar") ), jadi orang harus menggunakannya hanya untuk menonaktifkan logging untuk logger tunggal.

Catatan. - Mengatur disabledatribut logger Truebukanlah solusi ke-3, karena ini bukan bagian dari API publik. Lihat https://bugs.python.org/issue36318 :

import logging

logging.getLogger("foo").disabled = True  # DO NOT DO THAT
Maggyero
sumber
-1

subkelas pawang yang ingin Anda nonaktifkan sementara:

class ToggledHandler(logging.StreamHandler):
"""A handler one can turn on and off"""

def __init__(self, args, kwargs):
    super(ToggledHandler, self).__init__(*args, **kwargs)
    self.enabled = True  # enabled by default

def enable(self):
    """enables"""
    self.enabled = True

def disable(self):
    """disables"""
    self.enabled = False

def emit(self, record):
    """emits, if enabled"""
    if self.enabled:
        # this is taken from the super's emit, implement your own
        try:
            msg = self.format(record)
            stream = self.stream
            stream.write(msg)
            stream.write(self.terminator)
            self.flush()
        except Exception:
            self.handleError(record)

menemukan pawang dengan nama cukup mudah:

_handler = [x for x in logging.getLogger('').handlers if x.name == your_handler_name]
if len(_handler) == 1:
    _handler = _handler[0]
else:
    raise Exception('Expected one handler but found {}'.format(len(_handler))

pernah ditemukan:

_handler.disable()
doStuff()
_handler.enable()
jake77
sumber