2013-07-19 32 views
25

好吧,這裏的代碼,我設置的一切:瓶日誌 - 無法得到它寫入一個文件

if __name__ == '__main__': 
    app.debug = False 

    applogger = app.logger 

    file_handler = FileHandler("error.log") 
    file_handler.setLevel(logging.DEBUG) 

    applogger.setLevel(logging.DEBUG) 
    applogger.addHandler(file_handler) 

    app.run(host='0.0.0.0') 

會發生什麼事是

  1. error.log中被創建
  2. 沒有什麼永遠寫到它
  3. 儘管沒有添加StreamHandler並將調試設置爲false我仍然得到了一切標準輸出(這可能是正確的,但似乎很奇怪)

我完全離開這裏或發生了什麼?

+0

當你設置'app.debug = True'時會發生什麼? – dAnjou

+1

沒有什麼變化 – fleshgolem

回答

50

爲什麼不去做這樣的:

if __name__ == '__main__': 
    init_db() # or whatever you need to do 

    import logging 
    logging.basicConfig(filename='error.log',level=logging.DEBUG) 

    app.run(host="0.0.0.0") 

如果你現在就開始你的應用程序,你會看到error.log中包含:

INFO:werkzeug: * Running on http://0.0.0.0:5000/ 

欲瞭解更多信息,請訪問:http://docs.python.org/2/howto/logging.html

好的,因爲你堅持用我向你展示的方法不能擁有兩個處理程序,所以我會添加一個使其非常清晰的示例。首先,這個日誌代碼添加到您的主:

import logging, logging.config, yaml 
logging.config.dictConfig(yaml.load(open('logging.conf'))) 

現在還添加一些調試代碼,讓我們看到,我們的安裝方法的效果:

logfile = logging.getLogger('file') 
logconsole = logging.getLogger('console') 
logfile.debug("Debug FILE") 
logconsole.debug("Debug CONSOLE") 

所有剩下的是「logging.conf 「計劃。讓我們使用它:

version: 1 
formatters: 
    hiformat: 
    format: 'HI %(asctime)s - %(name)s - %(levelname)s - %(message)s' 
    simple: 
    format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s' 
handlers: 
    console: 
    class: logging.StreamHandler 
    level: DEBUG 
    formatter: hiformat 
    stream: ext://sys.stdout 
    file: 
    class: logging.FileHandler 
    level: DEBUG 
    formatter: simple 
    filename: errors.log 
loggers: 
    console: 
    level: DEBUG 
    handlers: [console] 
    propagate: no 
    file: 
    level: DEBUG 
    handlers: [file] 
    propagate: no 
root: 
    level: DEBUG 
    handlers: [console,file] 

該配置比需要更復雜,但它也顯示了日誌模塊的一些功能。

現在,當我們運行應用程序,我們看到以下輸出(werkzeug-和控制檯記錄器):

HI 2013-07-22 16:36:13,475 - console - DEBUG - Debug CONSOLE 
HI 2013-07-22 16:36:13,477 - werkzeug - INFO - * Running on http://0.0.0.0:5000/ 

也要注意,使用使用了「HI」自定義格式。

現在看看「errors.log」文件。它包含:

1)瓶顯然會忽略所有的自定義日誌記錄,除非它是在生產模式下運行

2)調試=假是:

2013-07-22 16:36:13,475 - file - DEBUG - Debug FILE 
2013-07-22 16:36:13,477 - werkzeug - INFO - * Running on http://0.0.0.0:5000/ 
+1

因爲我實際上的目標是實現幾個在不同級別登錄的處理程序,並且這種方法不會讓我感到驚訝 – fleshgolem

+0

@fleshgolem:您仍然可以創建多個處理程序。請直接使用Python的日誌記錄來做到這一點。 – HolgerSchurig

+0

@fleshgolem:查看我擁有的URL並搜索logging.conf。你會看到如何創建第二個記錄器。例如,您有記錄器的「root」和「simpleExample」,它們都可以具有不同的日誌級別。 – HolgerSchurig

12

好了,我的失敗從兩個誤解朵朵不足以讓它在生產模式下運行。你必須包裝在任何類型的WSGI服務器的應用程序這樣做

後,我開始從GEVENT的WSGI服務器的應用程序(和移動記錄初始化到一個更合適的地方),一切似乎都做工精細

+2

你的結論是錯誤的。我建議給你的代碼運行a)使用內置服務器,b)使用測試和/或調試模式,c)使用多個處理程序。另外,你的問題最初是「無法寫入文件」,這個問題已經由我解決了。不幸的是,你中途放大了你的問題,並想要不同的東西......下一次,我建議你更準確地描述你的問題。 – HolgerSchurig

+1

我並不完全同意。我問,爲什麼燒瓶的內部記錄器不會寫入文件,您提供了一個避開該記錄器的解決方案。是的,它的工作原理,但我不認爲這是一個確切的答案。我同意,但是我說我的問題很糟糕。我會讓這兩個開放,所以任何人發現這可能會在任何兩個尋求幫助 – fleshgolem

+2

沒有「燒瓶內部記錄器」。 Flask在Python的日誌記錄模塊中使用(並配置)記錄器。實際上,我發現Flask文檔記錄混亂,直到我在flask/logging.py中看到「從logging logging getLogger,...」的語句。從此很容易,因爲Python的日誌記錄模塊非常好記錄。所以不,我沒有規避Flask的記錄器。如果我願意,Flask的日誌輸出怎麼可能以errors.log結尾呢? – HolgerSchurig

6

輸出您在應用程序的控制檯中看到的內容來自可通過logging.getLogger('werkzeug')訪問的基礎Werkzeug記錄器。

您的日誌記錄可以在開發和發行版中同時添加處理程序到記錄器以及Flask之一。

更多信息和示例代碼:Write Flask Requests to an Access Log

+2

花了我一段時間來發現這個我自己的,'werkzeug'記錄器是所有實際的http layed記錄發生的地方。不幸的是,它不會格式化日誌,而是將''%s - - [%s]%s \ n'%'預先格式化到記錄器中。所以如果你想要相同的風格日誌,你需要在本地重新創建格式,或者調用'werkzeug._internal._log'方法。 – Pyrce

+0

這是正確的答案。 –

+0

對我來說,'logging.Logger.manager.loggerDict'沒有包含'werkzeug'日誌,並且我的'top-of-the-module'配置都沒有工作,直到我用'@ app.before_first_request' @大衛的回答。 – xenoclast

1

這工作:

if __name__ == '__main__': 
    import logging 
    logFormatStr = '[%(asctime)s] p%(process)s {%(pathname)s:%(lineno)d} %(levelname)s - %(message)s' 
    logging.basicConfig(format = logFormatStr, filename = "global.log", level=logging.DEBUG) 
    formatter = logging.Formatter(logFormatStr,'%m-%d %H:%M:%S') 
    fileHandler = logging.FileHandler("summary.log") 
    fileHandler.setLevel(logging.DEBUG) 
    fileHandler.setFormatter(formatter) 
    streamHandler = logging.StreamHandler() 
    streamHandler.setLevel(logging.DEBUG) 
    streamHandler.setFormatter(formatter) 
    app.logger.addHandler(fileHandler) 
    app.logger.addHandler(streamHandler) 
    app.logger.info("Logging is set up.") 
    app.run(host='0.0.0.0', port=8000, threaded=True) 
2

我不喜歡其他的答案,所以我堅持了下來,它好像我不得不讓我的日誌記錄配置後瓶做它自己的設置。

@app.before_first_request 
def initialize(): 

    logger = logging.getLogger("your_package_name") 
    logger.setLevel(logging.DEBUG) 
    ch = logging.StreamHandler() 
    ch.setLevel(logging.DEBUG) 
    formatter = logging.Formatter(
    """%(levelname)s in %(module)s [%(pathname)s:%(lineno)d]:\n%(message)s""" 
    ) 
    ch.setFormatter(formatter) 
    logger.addHandler(ch) 

我的應用程序的結構類似於

/package_name 
    __main__.py <- where I put my logging configuration 
    __init__.py <- conveniance for myself, not necessary 
    /tests 
    /package_name <- Actual flask app 
    __init__.py 
    /views 
    /static 
    /templates 
    /lib 

下列指示http://flask.pocoo.org/docs/0.10/patterns/packages/

+0

我的問題不是關於登錄到文件,而是將模塊日誌與Docker中的Flask日誌結合使用進行過濾,儘管我無法讓它工作的原因似乎是一樣的(Flask篡奪記錄器)。這個答案給了我我需要的東西! – xenoclast

1

爲什麼不把代碼中的潛水,看到...

我們的土地上是模塊flask.logging.py,它定義了一個名爲create_logger(app)的函數。在解決Flask的日誌記錄問題時,檢查該功能將爲潛在的肇事者提供一些線索。

在該功能衝突的第一個可能的原因是該行:

logger = getLogger(app.logger_name) 

讓我們來看看爲什麼:

變量app.logger_nameFlask.__init__()方法設置爲import_name價值,這本身就是接收參數Flask(__name__)。即app.logger_name被分配了值__name__,這可能是您的主包的名稱,讓我們對這個例子稱之爲'awesomeapp'。

現在,假設您決定手動配置並創建自己的記錄器。你認爲如果你的項目命名爲「awesomeapp」,你也可以用這個名字來配置你的記錄器,我認爲這很有可能。

my_logger = logging.getLogger('awesomeapp') # doesn't seem like a bad idea 
fh = logging.FileHandler('/tmp/my_own_log.log') 
my_logger.setLevel(logging.DEBUG) 
my_logger.addHandler(fh) 

這樣做是有道理的,除了幾個問題。

Flask.logger屬性調用首次將依次調用函數flask.logging.create_logger(),並執行以下操作將接踵而至:

logger = getLogger(app.logger_name) 

還記得你的項目的名字命名的記錄器,以及如何app.logger_name股份表示名字呢?在上面的代碼行中發生的是,功能logging.getLogger()現在已經檢索到您以前創建的記錄器,並且以下說明將以一種會讓您在以後搔癢的方式混淆它。例如,

del logger.handlers[:] 

噗,你剛剛失去了所有你以前可能註冊過的處理程序。

在函數內部發生的其他事情,沒有深入細節。它會創建並註冊兩個可以吐出到sys.stderr和/或Response對象的對象。一個用於日誌級別「調試」,另一個用於「生產」。

class DebugLogger(Logger): 
    def getEffectiveLevel(self): 
     if self.level == 0 and app.debug: 
      return DEBUG 
     return Logger.getEffectiveLevel(self) 

class DebugHandler(StreamHandler): 
    def emit(self, record): 
     if app.debug and _should_log_for(app, 'debug'): 
      StreamHandler.emit(self, record) 

class ProductionHandler(StreamHandler): 
    def emit(self, record): 
     if not app.debug and _should_log_for(app, 'production'): 
      StreamHandler.emit(self, record) 

debug_handler = DebugHandler() 
debug_handler.setLevel(DEBUG) 
debug_handler.setFormatter(Formatter(DEBUG_LOG_FORMAT)) 

prod_handler = ProductionHandler(_proxy_stream) 
prod_handler.setLevel(ERROR) 
prod_handler.setFormatter(Formatter(PROD_LOG_FORMAT)) 

logger.__class__ = DebugLogger 
logger.addHandler(debug_handler) 
logger.addHandler(prod_handler) 

對於上述細節,以點燃它應該變得更加清晰,爲什麼我們的手動配置的記錄似乎在瓶介入到不正常的行爲。新的信息爲我們提供了新的選擇。如果您仍想保留單獨的記錄器,最簡單的方法是將其命名爲與項目不同的項目(例如my_logger = getLogger('awesomeapp_logger'))。如果你想與Flask中的日誌協議保持一致,另一個選擇是使用類似於Flask的方法在Flask.logger上註冊一個logging.FileHandler對象。

def enable_file_logging(app): 
    import logging 
    from flask.logging import _should_log_for 

    logging_path = app.config['LOGGING_PATH'] 
    class DebugFileHandler(logging.FileHandler): 
     def emit(self, record): 
      if app.debug and _should_log_for(app, 'debug'): 
       logging.FileHandler.emit(self, record) 

    debug_file_handler = DebugFileHandler('/tmp/my_own_log.log') 
    app.logger.addHandler(debug_file_handler) 

app = Flask(__name__) 
enable_file_logging(app)