Created on 25 Jan 2021 ; Modified on 25 Jan 2021
This is the fifth part of an article about Flask, as follows:
This is the fifth part of a Flask project to show a single page in two different versions:
Here we speak how create an instance folder and how to log messages.
Instance folder is useful to write and to lay files that change in different instances of the same application. E.g. configuration files, data files, log files, ...
Instance folder and its contents must not be stored in version control system used to develop the application.
First we modify our application factory function to instruct Flask to use instance folder.
After that, we are going to configure and to start the Python logging library.
We modify the factory function create_app in single_page/__init__.py: calling Flask, we use the parameter instance_relative_config to tell it to refer to instance folder and the folder is relative to project (alias instance) folder.
1 import os # + 2 3 from flask import Flask 4 from flask_babel import Babel 5 from flask_sitemap import Sitemap 6 7 babel = Babel() 8 sitemap = Sitemap() 9 10 def create_app(): 11 '''create and configure the app''' 12 app = Flask(__name__, instance_relative_config=True) # +- 13 14 # ensure the instance folder exists 15 if not os.path.exists(app.instance_path): # + 16 os.makedirs(app.instance_path) 17 18 app.config.from_mapping( 19 SECRET_KEY='leave-hope-to-enter', 20 LANGUAGES = {'en': 'english', 'it': 'italiano',}, 21 ) 22 23 from .oneel import views as views1 24 app.register_blueprint(views1.oneel) 25 26 from .twoels import views as views2 27 app.register_blueprint(views2.twoels) 28 29 babel.init_app(app) 30 sitemap.init_app(app) 31 32 @app.route('/sitemap') 33 def ep_sitemap(): # endpoint for sitemap 34 return sitemap.sitemap(), 200, {'Content-Type': 'text/xml', } 35 36 return app
Please note we check for folder existence and we create it if it isn't: no magic here!
And now, in single_page/__init__.py we add:
1 import os 2 import logging # + 3 from logging.handlers import RotatingFileHandler # + 4 5 # 3rd parties libs import 6 from flask import Flask 7 from flask_babel import Babel 8 from flask_sitemap import Sitemap 9 10 babel = Babel() 11 sitemap = Sitemap() 12 13 def create_app(): 14 '''create and configure the app''' 15 app = Flask(__name__, instance_relative_config=True) 16 17 # ensure the instance folder exists 18 if not os.path.exists(app.instance_path): 19 os.makedirs(app.instance_path) 20 21 set_config(app) # + 22 set_logger(app) # + 23 24 from .oneel import views as views1 25 app.register_blueprint(views1.oneel) 26 27 from .twoels import views as views2 28 app.register_blueprint(views2.twoels) 29 30 babel.init_app(app) 31 sitemap.init_app(app) 32 33 @app.route('/sitemap') 34 def ep_sitemap(): # endpoint for sitemap 35 return sitemap.sitemap(), 200, {'Content-Type': 'text/xml', } 36 37 return app 38 39 40 def set_config(app): # + 41 '''setting configuration ''' 42 app.config.from_mapping( 43 SECRET_KEY='leave-hope-to-enter', 44 LANGUAGES = {'en': 'english', 'it': 'italiano',}, 45 LOG = { 46 'DIR' : os.path.join(app.instance_path, 'logs'), 47 'FILE': os.path.join(app.instance_path, 'logs/covid.log'), 48 'BUFSIZE': 102400, 49 'FILE_HANDLER_LEVEL': logging.DEBUG, 50 'APP_LOGGER_LEVEL' : logging.DEBUG, 51 } 52 ) 53 54 55 def set_logger(app): # + 56 '''setting logger''' 57 # ensure the log folder exists 58 if not os.path.exists(app.config['LOG']['DIR']): 59 os.mkdir(app.config['LOG']['DIR']) 60 file_handler = RotatingFileHandler(app.config['LOG']['FILE'], 61 maxBytes=app.config['LOG']['BUFSIZE'], 62 backupCount=10) 63 file_handler.setFormatter(logging.Formatter( 64 '%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]')) 65 file_handler.setLevel(app.config['LOG']['FILE_HANDLER_LEVEL']) 66 app.logger.addHandler(file_handler) 67 68 app.logger.setLevel(app.config['LOG']['APP_LOGGER_LEVEL']) 69 app.logger.debug('Covid application starts')
We can see:
So our directory structure become:
▼ flask_single_page run.py babel.cfg ▼ instance # + instance folder ▼ log # + next step: to store log files ▼ single_page __init__.py ▼ oneel __init__.py views.py ▼ static ▼ css style.css ▼ templates base.html index.html ▼ twoels __init__.py views.py ▼ static ▼ css style.css ▼ templates 2lbase.html 2lindex.html ▼ docs ▼ tests ▼ venv
To log we need a call to our application logger. E.g. if we wish track the enter in views we can modify them as follow:
1 from datetime import datetime 2 from flask import Blueprint, render_template, current_app # +- 3 from single_page import sitemap 4 5 # this app will respond to srv/1l/... URLs 6 oneel = Blueprint('oneel', 7 __name__, 8 static_folder='static', 9 template_folder='templates', 10 url_prefix='/1l') 11 12 @oneel.route('/') # index URLs 13 @oneel.route('/index') 14 @oneel.route('/index.html') 15 def index(): 16 current_app.logger.debug('> index') # + 17 return render_template('index.html', title='single language title') 18 19 20 @sitemap.register_generator 21 def index(): 22 '''generate URLs using language codes 23 24 Note. used by flask-sitemap 25 ''' 26 yield 'oneel.index', {}, datetime.now(), 'monthly', 0.7
The call to current_app.logger.debug in function index will log our entrance in the function body.
An example of log file follows:
1 2021-01-25 11:34:12,645 DEBUG: Covid application starts [in C:\Dati\Studio\Sviluppi\flask_single_page\single_page\__init__.py:69] 2 2021-01-25 11:42:51,267 DEBUG: Covid application starts [in C:\Dati\Studio\Sviluppi\flask_single_page\single_page\__init__.py:69] 3 2021-01-25 11:45:23,935 DEBUG: > index [in C:\Dati\Studio\Sviluppi\flask_single_page\single_page\oneel\views.py:23] 4 2021-01-25 11:45:38,915 DEBUG: > index [in C:\Dati\Studio\Sviluppi\flask_single_page\single_page\oneel\views.py:23] 5 2021-01-25 11:46:08,656 DEBUG: > index [in C:\Dati\Studio\Sviluppi\flask_single_page\single_page\twoels\views.py:38] 6 2021-01-25 11:46:15,718 ERROR: Exception on /de/2l/ [GET] [in C:\Dati\Studio\Sviluppi\flask_single_page\venv\lib\site-packages\flask\app.py:1891] 7 Traceback (most recent call last): 8 File "C:\Dati\Studio\Sviluppi\flask_single_page\venv\lib\site-packages\flask\app.py", line 2447, in wsgi_app 9 response = self.full_dispatch_request() 10 File "C:\Dati\Studio\Sviluppi\flask_single_page\venv\lib\site-packages\flask\app.py", line 1952, in full_dispatch_request 11 rv = self.handle_user_exception(e) 12 File "C:\Dati\Studio\Sviluppi\flask_single_page\venv\lib\site-packages\flask\app.py", line 1821, in handle_user_exception 13 reraise(exc_type, exc_value, tb) 14 File "C:\Dati\Studio\Sviluppi\flask_single_page\venv\lib\site-packages\flask\_compat.py", line 39, in reraise 15 raise value 16 File "C:\Dati\Studio\Sviluppi\flask_single_page\venv\lib\site-packages\flask\app.py", line 1948, in full_dispatch_request 17 rv = self.preprocess_request() 18 File "C:\Dati\Studio\Sviluppi\flask_single_page\venv\lib\site-packages\flask\app.py", line 2236, in preprocess_request 19 func(request.endpoint, request.view_args) 20 File "C:\Dati\Studio\Sviluppi\flask_single_page\single_page\twoels\views.py", line 31, in pull_lang_code 21 raise ValueError(f"language {lc} is not accepted, because not in {tuple(current_app.config['LANGUAGES'].keys())}") 22 ValueError: language de is not accepted, because not in ('en', 'it')
Lines 1 and 2 show the start of the application. Lines from 3 to 5 show use of the index view in oneel and twoels apps.
From line 6 downward we have a stack trace due to an error calling url http://localhost:5000/de/2l/ because language de is not configured in our application. Note. This trace is not called explicity from our application. We raise a ValueError signal; Flask forward it also to a log error in addition to writing it to console.
Enjoy, ldfa