2009-10-21 179 views
83

我還沒有找到一種方法來設置Django的Python日誌記錄,我很滿意。我的要求是相當簡單:Django中Python日誌的優雅設置

  • 爲不同的事件不同的日誌處理 - 也就是說,我希望能夠登錄到不同的文件
  • 到記錄器輕鬆訪問我的模塊。該模塊應該能夠毫不費力地找到它的記錄器。
  • 應該很容易適用於命令行模塊。系統的一部分是獨立的命令行或守護進程。記錄應該很容易與這些模塊一起使用。

我目前的設置是在我登錄的每個模塊中使用logging.conf文件和安裝日誌記錄。它感覺不對。

你有喜歡的日誌記錄設置嗎?請詳細說明:您如何設置配置(您是使用logging.conf還是將其設置爲代碼),您何時/何時啓動記錄器,以及如何在模塊中訪問它們等。

+1

您可能會發現以下屏幕視頻有用 - http://ericholscher.com/blog/2008/aug/29/screencast-2-logging-fun-and-profit/。另外,Simon Willison提出了更好的登錄Django的支持(參見http://simonwillison.net/2009/Sep/28/ponies/)。 – 2009-10-21 05:20:00

+0

@Dominic Rodger - 您可以在Django中靈活地記錄應用程序,Simon的建議主要是爲了便於在Django內部進行日誌記錄。Python正在開展工作,將基於字典的配置添加到Python日誌記錄中,Django可能從中受益。 – 2009-10-21 06:05:43

回答

49

迄今爲止發現的最佳方式是在settings.py中初始化日誌記錄設置 - 無處不在。您可以使用配置文件,也可以通過編程方式逐步執行 - 這取決於您的要求。關鍵是我通常將我想要的處理程序添加到根記錄器,使用級別並有時記錄。篩選器將我想要的事件添加到適當的文件,控制檯,系統日誌等。您當然可以將處理程序添加到任何其他記錄器根據我的經驗,通常不需要這樣做。

在每個模塊中,我定義使用

logger = logging.getLogger(__name__) 

,並用這些模塊中記錄事件記錄器(如果我想區分進一步)使用記錄儀是創造了記錄器的孩子以上。

如果我的應用程序是要在不settings.py中配置日誌記錄的網站可能被使用,我某處定義NullHandler如下:

#someutils.py 

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

null_handler = NullHandler() 

,並確保它的一個實例是添加到我的應用程序中使用日誌記錄模塊中創建的所有記錄器。 (注:NullHandler已經在日誌包爲Python 3.1,並會在Python 2.7)。所以:

logger = logging.getLogger(__name__) 
logger.addHandler(someutils.null_handler) 

這樣做是爲了確保你的模塊在不配置日誌網站發揮很好在settings.py中,並且你不會感到討厭「記錄器XYZ沒有找到處理程序」消息(這是關於可能錯誤配置的日誌記錄的警告)。

這樣做,這樣滿足你們所要求的:

  • 您可以設置爲不同的事件不同的日誌處理程序,因爲你目前做的。
  • 輕鬆訪問模塊中的記錄器 - 使用getLogger(__name__)
  • 很容易適用於命令行模塊 - 它們也導入settings.py

更新:注意,從1.3版本,Django的現在合併support for logging

+0

這不會要求每個模塊都有一個在配置中定義的處理程序(您不能使用foo的處理程序來處理foo.bar)?請參閱我們多年前在http://groups.google.com/group/comp.lang.python/browse_thread/thread/6a199393bcee6c1b/2ddf482a44bc4bb1 – 2010-10-04 00:04:22

+1

@andrew cooke上的對話:您*可以*使用'foo'的處理程序來處理處理記錄到'foo.bar'的事件。回覆。該線程 - fileConfig和dictConfig現在都有選項來防止禁用舊的記錄器。看到這個問題:http://bugs.python.org/issue3136,在你的問題http://bugs.python.org/issue2697發佈幾個月後 - 無論如何,它已被整理出2008年6月以來。 – 2010-10-04 12:01:16

+0

wouldn 'logger = someutils.getLogger(__ name __)'是更好的方法嗎?其中'someutils.getLogger'從已經添加了null_handler的'logging.getLogger'返回記錄器? – 7yl4r 2016-10-11 05:12:35

6

我目前正在使用我自己創建的日誌記錄系統。它使用CSV格式進行記錄。

django-csvlog

該項目還沒有完整的文檔,但我的工作就可以了。

6

我們使用logging.ini文件初始化登錄頂級urls.py中的日誌。

logging.ini的位置在settings.py中提供,但僅此而已。

每個模塊,然後做

logger = logging.getLogger(__name__) 

爲了區分測試,開發和生產的情況下,我們有不同的logging.ini文件。大多數情況下,我們有一個「控制檯日誌」,只有錯誤纔會發送到stderr。我們有一個「應用程序日誌」,它使用轉到日誌目錄的常規滾動日誌文件。

+0

我最終使用這個,除了在settings.py中初始化而不是urls.py – Parand 2009-10-21 22:41:15

+0

如何使用logging.ini文件中settings.py的設置?例如,我需要BASE_DIR設置,所以我可以告訴它在哪裏存儲我的日誌文件。 – slypete 2010-06-03 00:02:29

+0

@slypete:我們不使用logging.ini中的設置。由於日誌記錄很大程度上是獨立的,因此我們不使用任何Django設置。是的,有可能重複一些事情。不,它沒有太大的實際區別。 – 2010-06-03 00:58:23

114

我知道這已經是一個解決的答案,但根據django> = 1.3,有一個新的日誌記錄設置。

從舊到新不是自動的,所以我想我會在這裏寫下來。

當然結賬the django doc一些更多。

這是基本的conf,默認情況下使用Django管理員的createProject V1.3創建 - 里程可能與最新的Django版本更改:

LOGGING = { 
    'version': 1, 
    'disable_existing_loggers': False, 
    'handlers': { 
     'mail_admins': { 
      'level': 'ERROR', 
      'class': 'django.utils.log.AdminEmailHandler', 
     } 
    }, 
    'loggers': { 
     'django.request': { 
      'handlers': ['mail_admins'], 
      'level': 'ERROR', 
      'propagate': True, 
     } 
    } 
} 

這種結構是基於標準Python logging dictConfig,這決定了以下塊:

  • formatters - 對應的值將是一個字典,其中每個鍵是一個格式化器ID和每個值是描述如何配置對應格式化實例的字典。
  • filters - 相應的值將是一個字典,其中每個鍵是一個過濾器ID,每個值是一個字典,描述如何配置相應的過濾器實例。
  • handlers - 相應的值將是一個字典,其中每個鍵是一個處理程序ID,每個值是一個字典,描述如何配置相應的Handler實例。每個處理器具有以下鍵:

    • class(強制性)。這是處理程序類的完全限定名稱。
    • level(可選)。處理程序的級別。
    • formatter(可選)。此處理程序的格式化程序的ID。
    • filters(可選)。此處理程序的過濾器的id列表。

我通常至少這一點:

  • 添加.log文件
  • 配置我的應用程序寫入此日誌

換算成:

LOGGING = { 
    'version': 1, 
    'disable_existing_loggers': False, 
    'formatters': { 
     'verbose': { 
      'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s' 
     }, 
     'simple': { 
      'format': '%(levelname)s %(message)s' 
     }, 
    }, 
    'filters': { 
     'require_debug_false': { 
      '()': 'django.utils.log.RequireDebugFalse' 
     } 
    }, 
    'handlers': { 
     'null': { 
      'level':'DEBUG', 
      'class':'django.utils.log.NullHandler', 
     }, 
     'console':{ 
      'level': 'DEBUG', 
      'class': 'logging.StreamHandler', 
      'formatter': 'simple' 
     }, 
     # I always add this handler to facilitate separating loggings 
     'log_file':{ 
      'level': 'DEBUG', 
      'class': 'logging.handlers.RotatingFileHandler', 
      'filename': os.path.join(VAR_ROOT, 'logs/django.log'), 
      'maxBytes': '16777216', # 16megabytes 
      'formatter': 'verbose' 
     }, 
     'mail_admins': { 
      'level': 'ERROR', 
      'filters': ['require_debug_false'], 
      'class': 'django.utils.log.AdminEmailHandler', 
      'include_html': True, 
     } 
    }, 
    'loggers': { 
     'django.request': { 
      'handlers': ['mail_admins'], 
      'level': 'ERROR', 
      'propagate': True, 
     }, 
     'apps': { # I keep all my of apps under 'apps' folder, but you can also add them one by one, and this depends on how your virtualenv/paths are set 
      'handlers': ['log_file'], 
      'level': 'INFO', 
      'propagate': True, 
     }, 
    }, 
    # you can also shortcut 'loggers' and just configure logging for EVERYTHING at once 
    'root': { 
     'handlers': ['console', 'mail_admins'], 
     'level': 'INFO' 
    }, 
} 

編輯

request exceptions are now always loggedTicket #16288

我更新了上述樣品的conf明確包括mail_admins正確的過濾器,這樣,在默認情況下,電子郵件不會在調試是真發。

你應該添加過濾器:

'filters': { 
    'require_debug_false': { 
     '()': 'django.utils.log.RequireDebugFalse' 
    } 
}, 

,並將其應用到mail_admins處理程序:

'mail_admins': { 
     'level': 'ERROR', 
     'filters': ['require_debug_false'], 
     'class': 'django.utils.log.AdminEmailHandler', 
     'include_html': True, 
    } 

否則django.core.handers.base.handle_uncaught_exception不傳遞錯誤,如果設置了 'django.request' 記錄儀.DEBUG是真的。

如果你不這樣做在Django 1.5,你會得到一個

DeprecationWarning:你有沒有關於「mail_admins」日誌處理程序定義的過濾器:添加隱調試假只能過濾

但事情仍然會正常工作在Django 1.4和Django 1.5中。

**結束編輯**

那的conf強烈由Django的文檔樣本的conf的啓發,但添加日誌文件的一部分。

我也經常做到以下幾點:

LOG_LEVEL = 'DEBUG' if DEBUG else 'INFO' 

... 
    'level': LOG_LEVEL 
... 

然後在我的Python代碼,我總是在增加的情況下沒有日誌記錄的conf是任何定義的NullHandler。這避免了沒有指定Handler的警告。對於庫特別有用的不一定只調用在Django(ref

import logging 
# Get an instance of a logger 
logger = logging.getLogger(__name__) 
class NullHandler(logging.Handler): #exists in python 3.1 
    def emit(self, record): 
     pass 
nullhandler = logger.addHandler(NullHandler()) 

# here you can also add some local logger should you want: to stdout with streamhandler, or to a local file... 

[...]

logger.warning('etc.etc.') 

希望這有助於!

+0

斯特凡諾,非常感謝您的詳細解答,非常有幫助。這一點可能會使其值升級到1.3。 – Parand 2011-04-28 00:56:22

+0

Parand,它絕對是(恕我直言!)值得加緊到Django 1.3,但有幾點要照顧平穩過渡 - 如果你遇到麻煩,打開一個新的SO問題;-) – Stefano 2011-04-28 09:55:18

+0

順便說一下:我仍然使用這種設置和文件日誌,但是我轉到[sentry](https://github.com/dcramer/sentry)進行製作! – Stefano 2012-08-01 10:01:25