vlambda博客
学习文章列表

Flask 工厂模式注册初始化logger

工厂模式的意义

引用官方解释:

If you are already using packages and blueprints for your application (Modular Applications with Blueprints) there are a couple of really nice ways to further improve the experience. A common pattern is creating the application object when the blueprint is imported. But if you move the creation of this object into a function, you can then create multiple instances of this app later.

So why would you want to do this?

  1. Testing. You can have instances of the application with different settings to test every case.

  2. Multiple instances. Imagine you want to run different versions of the same application. Of course you could have multiple instances with different configs set up in your webserver, but if you use factories, you can have multiple instances of the same application running in the same application process which can be handy.

大概意思是:

  1. 为了方便测试,你可以随心所欲的切换不同的配置实例来测试你的用例。

  2. 相同的应用,你可以利用不同的配置实例来进行初始化,达到运行不同版本应用的目的。

官方实例的应用(autoapp.py):

   
     
     
   

  1. def create_app(config_filename):


  2.    app = Flask(__name__)


  3.    app.config.from_pyfile(config_filename)


  4.    from yourapplication.model import db


  5.    db.init_app(app)


  6.    from yourapplication.views.admin import admin


  7.    from yourapplication.views.frontend import frontend


  8.    app.register_blueprint(admin)


  9.    app.register_blueprint(frontend)


  10.    return app


初始化

为了能让我们的日志初始化添加如下代码(autoapp.py):

   
     
     
   

  1. def register_logging(app):


  2.    app.config.setdefault("LOG_PATH", "application.log")


  3.    log_formatter = "%(asctime)s [%(thread)d:%(threadName)s] %(filename)s:%(module)s:%(funcName)s in %(lineno)d] [%(levelname)s]: %(message)s"


  4.    app.config.setdefault("LOG_FORMATTER", log_formatter)


  5.    app.config.setdefault("LOG_MAX_BYTES", 50 * 1024 * 1024)


  6.    app.config.setdefault("LOG_BACKUP_COUNT", 10)


  7.    app.config.setdefault("LOG_INTERVAL", 1)


  8.    app.config.setdefault("LOG_WHEN", "D")


  9.    app.config.setdefault("LOG_LEVEL", "INFO")


  10.    formatter = logging.Formatter(app.config["LOG_FORMATTER"])


  11.    # 将日志输出到文件


  12.    # 指定间隔时间自动生成文件的处理器


  13.    # 实例化TimedRotatingFileHandler


  14.    # interval是时间间隔,


  15.    # backupCount是备份文件的个数,如果超过这个个数,就会自动删除


  16.    # when是间隔的时间单位,单位有以下几种:


  17.    # S 秒


  18.    # M 分


  19.    # H 小时、


  20.    # D 天、


  21.    # W 每星期(interval==0时代表星期一)


  22.    # midnight 每天凌晨


  23.    timed_rotating_file_handler = TimedRotatingFileHandler(


  24.        filename=app.config["LOG_PATH"],


  25.        interval=app.config["LOG_INTERVAL"],


  26.        when=app.config["LOG_WHEN"],


  27.        backupCount=app.config["LOG_BACKUP_COUNT"],


  28.        encoding="utf-8",


  29.    )


  30.    timed_rotating_file_handler.setFormatter(formatter)  # 设置文件里写入的格式


  31.    timed_rotating_file_handler.setLevel(app.config["LOG_LEVEL"])


  32.    # StreamHandler


  33.    stream_handler = StreamHandler()


  34.    stream_handler.setFormatter(formatter)


  35.    stream_handler.setLevel(app.config["LOG_LEVEL"])


  36.    # SMTPHandler


  37.    mail_handler = DelaySMTPHandler(


  38.        mailhost=app.config["MAILHOST"],


  39.        credentials=app.config["CREDENTIALS"],


  40.        fromaddr=app.config["FROMADDR"],


  41.        toaddrs=app.config["TOADDRS"],


  42.        subject=app.config["SUBJECT"],


  43.    )


  44.    mail_handler.setLevel(logging.ERROR)


  45.    mail_handler.setFormatter(formatter)


  46.    # 删除默认的handler


  47.    # app.logger.removeHandler(default_handler)


  48.    # 设置logger


  49.    for logger in (


  50.        app.logger,


  51.        logging.getLogger("sqlalchemy"),


  52.        logging.getLogger("werkzeug"),


  53.    ):


  54.        logger.addHandler(stream_handler)


  55.        logger.addHandler(timed_rotating_file_handler)


  56.        if os.getenv("FLASK_ENV") == "production":


  57.            logger.addHandler(mail_handler)


  58.    # set logger for elk


  59.    # stash_handler = logstash.LogstashHandler(


  60.    #     app.config.get('ELK_HOST'),


  61.    #     app.config.get('ELK_PORT')


  62.    # )


  63.    # root_logger.addHandler(stashHandler)


使用log

   
     
     
   

  1. from flask import current_app


  2. current_app.logger.info("hello world")


这样做的好处

  1. 可以多环境随意切换配置,而不用更改代码

  2. 日志配置在 setting.py统一配置管理

完整代码

autoapp.py:

   
     
     
   

  1. def create_app(config_filename):


  2.    app = Flask(__name__)


  3.    app.config.from_pyfile(config_filename)


  4.    from yourapplication.model import db


  5.    db.init_app(app)


  6.    register_logging(app)


  7.    from yourapplication.views.admin import admin


  8.    from yourapplication.views.frontend import frontend


  9.    app.register_blueprint(admin)


  10.    app.register_blueprint(frontend)


  11.    return app


  12. def register_logging(app):


  13.    app.config.setdefault("LOG_PATH", "application.log")


  14.    log_formatter = "%(asctime)s [%(thread)d:%(threadName)s] %(filename)s:%(module)s:%(funcName)s in %(lineno)d] [%(levelname)s]: %(message)s"


  15.    app.config.setdefault("LOG_FORMATTER", log_formatter)


  16.    app.config.setdefault("LOG_MAX_BYTES", 50 * 1024 * 1024)


  17.    app.config.setdefault("LOG_BACKUP_COUNT", 10)


  18.    app.config.setdefault("LOG_INTERVAL", 1)


  19.    app.config.setdefault("LOG_WHEN", "D")


  20.    app.config.setdefault("LOG_LEVEL", "INFO")


  21.    formatter = logging.Formatter(app.config["LOG_FORMATTER"])


  22.    # 将日志输出到文件


  23.    # 指定间隔时间自动生成文件的处理器


  24.    # 实例化TimedRotatingFileHandler


  25.    # interval是时间间隔,


  26.    # backupCount是备份文件的个数,如果超过这个个数,就会自动删除


  27.    # when是间隔的时间单位,单位有以下几种:


  28.    # S 秒


  29.    # M 分


  30.    # H 小时、


  31.    # D 天、


  32.    # W 每星期(interval==0时代表星期一)


  33.    # midnight 每天凌晨


  34.    timed_rotating_file_handler = TimedRotatingFileHandler(


  35.        filename=app.config["LOG_PATH"],


  36.        interval=app.config["LOG_INTERVAL"],


  37.        when=app.config["LOG_WHEN"],


  38.        backupCount=app.config["LOG_BACKUP_COUNT"],


  39.        encoding="utf-8",


  40.    )


  41.    timed_rotating_file_handler.setFormatter(formatter)  # 设置文件里写入的格式


  42.    timed_rotating_file_handler.setLevel(app.config["LOG_LEVEL"])


  43.    # StreamHandler


  44.    stream_handler = StreamHandler()


  45.    stream_handler.setFormatter(formatter)


  46.    stream_handler.setLevel(app.config["LOG_LEVEL"])


  47.    # SMTPHandler


  48.    mail_handler = DelaySMTPHandler(


  49.        mailhost=app.config["MAILHOST"],


  50.        credentials=app.config["CREDENTIALS"],


  51.        fromaddr=app.config["FROMADDR"],


  52.        toaddrs=app.config["TOADDRS"],


  53.        subject=app.config["SUBJECT"],


  54.    )


  55.    mail_handler.setLevel(logging.ERROR)


  56.    mail_handler.setFormatter(formatter)


  57.    # 删除默认的handler


  58.    # app.logger.removeHandler(default_handler)


  59.    # 设置logger


  60.    for logger in (


  61.        app.logger,


  62.        logging.getLogger("sqlalchemy"),


  63.        logging.getLogger("werkzeug"),


  64.    ):


  65.        logger.addHandler(stream_handler)


  66.        logger.addHandler(timed_rotating_file_handler)


  67.        if os.getenv("FLASK_ENV") == "production":


  68.            logger.addHandler(mail_handler)


  69.    # set logger for elk


  70.    # stash_handler = logstash.LogstashHandler(


  71.    #     app.config.get('ELK_HOST'),


  72.    #     app.config.get('ELK_PORT')


  73.    # )


  74.    # root_logger.addHandler(stashHandler)


settings.py

   
     
     
   

  1. # -*- coding: utf-8 -*-


  2. """Application configuration.


  3. """


  4. import os


  5. BASE_DIR = os.path.dirname(__file__)


  6. class Config(object):


  7.    # Base settings #################################################


  8.    DEBUG = False


  9.    TESTING = False


  10.    SECRET_KEY = ""


  11.    BUNDLE_ERRORS = True


  12.    # 日志配置 ###############################################################


  13.    LOG_PATH = os.path.join(BASE_DIR, "logs", "falling-wind-service.log")


  14.    LOG_FORMATTER = (


  15.        "%(asctime)s [%(name)s] [%(thread)d:%(threadName)s] "


  16.        "%(filename)s:%(module)s:%(funcName)s "


  17.        "in %(lineno)d] "


  18.        "[%(levelname)s]: %(message)s"


  19.    )


  20.    LOG_MAX_BYTES = 50 * 1024 * 1024  # 日志文件大小


  21.    LOG_BACKUP_COUNT = 10  # 备份文件数量


  22.    LOG_INTERVAL = 1


  23.    LOG_WHEN = "D"


  24.    # 数据库配置 ####################################################


  25.    SQLALCHEMY_ENGINE_OPTIONS = {


  26.        "pool_timeout": 10,  # 默认链接超时时长


  27.        "pool_size": 10,  # 数据库链接池大小


  28.    }


  29.    # 提供多库链接 使用其他库进行链接的时候需要使用bind指定那个库使用


  30.    SQLALCHEMY_BINDS = {}


  31.    SQLALCHEMY_TRACK_MODIFICATIONS = True


  32.    # Celery ##################################################################


  33.    enable_utc = True


  34.    timezone = "Asia/Shanghai"


  35.    # or the actual content-type (MIME)


  36.    accept_content = ["application/json"]


  37.    # or the actual content-type (MIME)


  38.    result_accept_content = ["application/json"]


  39.    include = ["app_tasks.user_tasks"]


  40.    result_expires = 3600


  41.    # JWT ####################################################################


  42.    JWT_SECRET_KEY = ""


  43.    # JWT_BLACKLIST_ENABLED = False


  44.    # JWT_BLACKLIST_TOKEN_CHECKS = ['access', 'refresh']


  45. class Pro(Config):


  46.    ENV = "product"


  47.    # 日志 ##################################################################


  48.    LOG_PATH = "your application log path"


  49.    # DB ##################################################################


  50.    DB_HOST = ""


  51.    DB_PORT = 3306


  52.    DB_DATABASE = ""


  53.    DB_USER = ""


  54.    DB_PASSWORD = ""


  55.    SQLALCHEMY_DATABASE_URI = "mysql+pymysql://{}:{}@{}:{}/{}".format(


  56.        DB_USER, DB_PASSWORD, DB_HOST, DB_PORT, DB_DATABASE


  57.    )


  58.    # Redis ####################################################


  59.    REDIS_URL = ""


  60.    # Mail ########################################################


  61.    MAIL_SERVER = "smtp.qq.com"


  62.    MAIL_PORT = 465


  63.    MAIL_USE_SSL = True


  64.    MAIL_USERNAME = ""


  65.    MAIL_PASSWORD = ""


  66.    # Celery ###########################################################


  67.    # Broker settings.


  68.    broker_url = ""


  69.    # Using the redis to store task state and results.


  70.    result_backend = ""


  71.    # JWT ###############################################################


  72.    # JWT_ACCESS_TOKEN_EXPIRES = 60 * 60


  73.    JWT_SECRET_KEY = ""


  74.    # SMTPHandler ######################################################


  75.    MAILHOST = ("smtp.qq.com", 465)


  76.    CREDENTIALS = ("", "")


  77.    FROMADDR = ""


  78.    TOADDRS = [""]


  79.    SUBJECT = ""


  80.    SECURE = ("SSL",)


  81. class Dev(Config):


  82.    DEBUG = True


  83.    ENV = "dev"


  84.    # 日志 ##################################################################


  85.    LOG_PATH = "your path"


  86.    # DB ####################################################################


  87.    DB_HOST = "localhost"


  88.    DB_PORT = 3306


  89.    DB_DATABASE = ""


  90.    DB_USER = ""


  91.    DB_PASSWORD = ""


  92.    SQLALCHEMY_DATABASE_URI = "mysql+pymysql://{}:{}@{}:{}/{}".format(


  93.        DB_USER, DB_PASSWORD, DB_HOST, DB_PORT, DB_DATABASE


  94.    )


  95.    # Redis ####################################################


  96.    REDIS_URL = ""


  97.    # Mail ########################################################


  98.    MAIL_SERVER = "smtp.qq.com"


  99.    MAIL_PORT = 25


  100.    MAIL_USE_TLS = True


  101.    MAIL_USERNAME = ""


  102.    MAIL_PASSWORD = ""


  103.    # Celery ###################################################################


  104.    # Broker settings.


  105.    broker_url = ""


  106.    # Using the redis to store task state and results.


  107.    result_backend = ""


  108.    # JWT ###############################################################


  109.    JWT_ACCESS_TOKEN_EXPIRES = 60 * 60


  110.    JWT_SECRET_KEY = ""


  111.    # SMTPHandler ######################################################


  112.    MAILHOST = ("smtp.qq.com", 465)


  113.    CREDENTIALS = ("", "")


  114.    FROMADDR = ""


  115.    TOADDRS = [""]


  116.    SUBJECT = ""


  117. class Test(Config):


  118.    TESTING = True


  119.    DEBUG = True


  120.    ENV = "test"


app.py

   
     
     
   

  1. # -*- coding: utf-8 -*-


  2. """Create an application instance."""


  3. from autoapp import create_app


  4. import os


  5. from extensions import celery


  6. if os.getenv("FLASK_ENV") == "development":


  7.    app = create_app("settings.Dev")


  8. elif os.getenv("FLASK_ENV") == "production":


  9.    app = create_app("settings.Pro")


  10. else:


  11.    raise EnvironmentError("Please set FLASK_ENV !!!")


  12. celery = celery


运行flask

windows

   
     
     
   

  1. $ set FLASK_ENV=development


  2. $ flask run


Linux

   
     
     
   

  1. $ export FLASK_ENV=development


  2. $ flask run


Enjoy your code!