Flask 備註一(單元測試,Debugger, Logger)

Flask 備註一(單元測試,Debugger, Logger)

Flask是一個使用python開發Web程序的框架。依賴於Werkzeug提供完整的WSGI支持,以及Jinja2提供templates支持。Flask的設計理念是提供Micro以及方便的框架。"Micro"是由於除了提供基本特性功能的實現外,其餘的功能(例如數據庫訪問)都是經過extension來完成。方便的特色是由於提供了簡單易用的必要特性和功能,提供的功能包含:python

  1. 內置的開發服務器和調試工具。
  2. 集成單元測試支持。
  3. 支持Templates(依賴JinJa2提供)
  4. 完整的WSGI支持(依賴Werkzeug提供)
  5. 安全Cookie和Sessions
  6. Thread-Locals,例如在每一個Request裏面的對象只在線程裏面有意義。

Templates

Flask使用jinja2做爲templates引擎。jinjia2的默認的語法設定以下:sql

  • {%. .. %} 包含statements(邏輯處理代碼塊)
  • {{ ... }} 包含expressions (輸出到tempalte的語句)
  • {# ... #} 包含comments (註釋,不會輸出到template的語句)
  • # ... ## 包含單行statements (單行處理代碼塊)

單元測試

Flask經過test_client提供了很是簡單的方式來建立測試的上下文環境(Local Context)。這樣能夠經過各類方式來完成單元測試,最基本的方式是經過python自帶的unittest來完成集成的測試用例。數據庫

unittest

使用unittest的Flask單元測試的框架以下:express

import os
import xxxx
import unittest
import tempfile

class XxxxTestCase(unittest.TestCase):

    def setUp(self):
        self.db_fd, xxxx.app.config['DATABASE'] = tempfile.mkstemp()
        xxxx.app.config['TESTING'] = True
        self.app = xxxx.app.test_client()
        xxxx.init_db()

    def tearDown(self):
        os.close(self.db_fd)
        os.unlink(xxxx.app.config['DATABASE'])

if __name__ == '__main__':
    unittest.main()

在上述例子裏面:flask

  1. xxxx是一個自定義的簡單的flask程序,xxxx.app是一個Flask類的對象,對應一個Web應用。
  2. 在setUp函數裏面,將Flask的Web應用的TESTING參數配置爲True,並建立了一個對應的測試客戶端(test_client),包含測試須要的上下文環境。
  3. 在setUp函數裏面,建立了xxxx的數據庫文件並進行了初始化。
  4. 在tearDown函數裏面,關閉了數據庫文件並刪除。
    在unittest中測試用例的函數必須喲以test單詞開始,例如testFunc1,這樣測試框架能夠將此函數做爲測試用例執行。執行測試用例以前先執行setUp函數進行初始化,以後執行tearDown進行資源釋放。基本的測試用例以下:安全

    def testemptydb(self):
    rv = self.app.get('/')
    assert 'No entries here so far' in rv.data服務器

Local Context

unittest框架經過setUp建立了testclient以及須要的上下文環境,而且在tearDown函數裏面進行銷燬。一樣可使用testclient在任意代碼中完成驗證測試。由於test_client建立了一個臨時的上下文環境,在任何區域中均可以經過with語句是此context在範圍內有效,所以在這個範圍內能夠驗證request以及session的信息。
驗證request信息的簡單例子:網絡

with app.test_client() as c:
    rv = c.get('/?number=42')
    assert request.args['number'] == '42'

驗證session信息的簡單例子:session

with app.test_client() as c:
    rv = c.get('/')
    assert flask.session['foo'] == 42

若是須要修改testclient建立的臨時context中的session信息,能夠經過sessiontranscation來獲取session對象進行修改。以下簡單實例:app

with app.test_client() as c:
    with c.session_transcation() as sess:
        sess['a_key'] = 'a value'

Debug&Logging

在程序中,錯誤永遠沒法避免,錯誤有多是代碼邏輯問題,服務器問題,網絡問題或者是硬件問題,環境問題等等。Flask提供了兩種方式定位問題,其一能夠打開程序的調試模式,經過調試器(debugger)跟蹤程序的執行信息;另外就是Flask提供了完善的日誌系統,記錄程序的運行信息。

Debug

Flask經過必要的參數設置,來肯定是否使用Debug模式以及是否使用自帶的調試器。這些參數包含:

  • debug。True: 設置Debug模式; False: 非Debug模式
  • use_debugger。True: 使用內部調試器; Flase: 不實用內部調試器
  • use_reloader。True: 在Excpetion時,是否reload和fork當前進程進行調試; False: Nothing.

若是使用第三方相似於Aptana/Eclipse等調試器,須要設置debug爲True,usedebugger和usereloader爲False。

經過內置的debugger,當程序出現exception時,在錯誤界面提供一個交互式的界面,並且在這個界面裏面能夠執行任意的代碼進行程序調試。這樣存在着巨大的安全隱患,所以永遠不要在產品服務器上開啓調試模式

Logging

Flask附帶的Logger依賴於Python內置的日誌系統,經過默認Logging庫設置日誌的處理Handler,日誌Format以及處理的Level。程序中經過Flask的Logger所寫的Log經過系統自帶的日誌系統進行過濾和格式化,而後輸出到所設置的Handler中。

handler能夠是文件也能夠是郵件,通常狀況下的應用場景是,將大部分的日誌信息保存到文件中,將重點須要關注的日誌信息發送到郵件中。

  • 文件Handler。文件Handler包含四種:

    • FileHandler,對應文件系統的一個文件。
    • RotatingFileHandler, 對應文件系統的一個文件,在輸出必定數量的信息以後,重頭開始。
    • NTEventLogHandler,對應Windows操做系統的日誌系統。
    • SysLogHandler,對應Unix Syslog系統。
  • 郵件Handler。一般使用smtphandler進行郵件發送。

  • Handler的簡單示例以下:

    if not app.debug:
    import logging
    from logging.handler import SMTPHandler, FileHandler

    file_handler = FileHandler(/var/test/flask.log)
    file_handler.setLevel(logging.WARNING)
    
    email_handler = SMTPHandler('127.0.0.1',
                                'server-error@example.com'
                                ADMINS,
                                'Your Application Failed!!!')
    app.logger.addHandler(file_handler)
    app.logger.addHandler(email_handler)

Formatter是Python自帶的日誌系統提供的,能夠對Handler進行設置須要保存的信息格式。通常狀況下,在郵件Handler中保存詳細的多行文本信息;在文件Handler裏面保存單行重要信息。Log的格式參數以下:

  • %(levelname)s || 日誌級別(包含:'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL') ||
  • %(pathname)s || Log產生的源文件路徑 ||
  • %(filename)s || Log產生的源文件名稱 ||
  • %(module)s || Log產生的模塊 ||
  • %(funcName)s || Log產生的函數名稱 ||
  • %(lineno)d || Log產生的所在文件行號 ||
  • %(asctime)s || Log產生的時間格式是:YYYY-MM-DD hh-mm-ss, mmm(,以後是毫秒數)
  • %(message)s || Log的信息 ||

Email日誌格式設置的簡單示例:

from logging import Formatter
mail_handler.setFormatter(Formatter('''
    Message type:       %(levelname)s
    Location:           %(pathname)s:%(lineno)d
    Module:             %(module)s
    Function:           %(funcName)s
    Time:               %(asctime)s
    Message:            %(message)s
'''))

文件日誌格式設置的簡單示例:

from logging import Formatter
file_handler.setFormatter(Formatter(
    '%(asctime)s %(levelname)s: %(message)s '
    '[in %(pathname)s:%(lineno)d]'
    ))

Flask中使用的第三庫都有本身的日誌策略,一樣可使用getlogger獲取每一個依賴庫的logger設置統一的Handler。以下示例:

from logging import getLogger
loggers = [app.logger, getLogger('sqlalchemy'),
           getLogger('otherlibrary')]
for logger in loggers:
    logger.addHandler(mail_handler)
    logger.addHandler(file_handler)
相關文章
相關標籤/搜索