Skip to content

Logging

It is common practice to generate messages of different kinds while the application is running. They can be categorised according to their purpose and three levels are usually defined:

  • Information: Status information, for example, or confirmation that some operation has been completed.
  • Warning: Notification that a potentially undesirable event has occurred, but which can be ignored if appropriate.
  • Error: Notification that an error situation has arisen which needs to be resolved. This type of event usually means that processing cannot proceed.

The messages are written to a logfile so that they are preserved. During development with PyCharm, messages are typically written to the IDE console, but they are not preserved if the application is restarted. It is often useful to be able to examine historical messages in a logfile.

In a Flask application uses standard Python logging which can be customised in several ways. The steps below describe one simple way of implementing logging. For other options, please consult the documentation.

Logging is intialised in the Flask factory function and in this example, uses an environment variable that is specific to the selected environment. After that, a logger object must be initialised in each view file where you want to generate log messages. When you create a logger object, it picks up the configuration settings defined in the factory function, so effectively you are simply making a link to the same logger each time.

Defining the logging level

It is usually desirable to set different logging levels in different contexts. For example, you may want to see all messages (errors, warnings and information) while you are developing your application, but you may only want to lof errors in production. The logging level is defined by the environment variable LOGLEVEL which is defined in the root-level config.py file. The example below defines the logging level differently for each of the three configurations.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import logging


class Config(object):
    DEBUG = True
    SQLALCHEMY_TRACK_MODIFICATIONS = False


class DevelopmentConfig(Config):
    SQLALCHEMY_ECHO = True
    LOGLEVEL = logging.DEBUG
    logging.getLogger('sqlalchemy.engine').setLevel(logging.INFO)


class ProductionConfig(Config):
    DEBUG = False
    LOGLEVEL = logging.ERROR
    logging.getLogger('sqlalchemy.engine').setLevel(logging.ERROR)


class TestingConfig(Config):
    TESTING = True
    LOGLEVEL = logging.WARNING
    logging.getLogger('sqlalchemy.engine').setLevel(logging.WARNING)


app_config = {
    'development': DevelopmentConfig,
    'production': ProductionConfig,
    'testing': TestingConfig
}

Explanation

Line 1: Import the logging package

Line 5: Enable debug mode by default

Line 6: Disable tracking messages from Flask-SQLAlchemy. This saves on resources.

Line 10: Enable maximum output from SQLAlchemy during development

Line 11: Set the logging level to the lowest value (all messages) during development

Line 12: Configure the in-built SQLAlchemy logger to output all messages during development

Lines 17 & 23: Compare with line 11

Line2 18 & 24: Compare with line 12.

Initialising logging

Although the Python logging system is highly customisable, it also provides a simple way to create a basic setup. The example below uses the basciConfig() method to configure the essential parameters for simple but effective logging. This code should be inserted into the Flask factory function in app/__init__.py

1
2
3
logging.basicConfig(filename='<path to logfile>',
                  level = app.config['LOGLEVEL'],
                  format = '%(asctime)s %(levelname)s %(name)s %(threadName)s: %(message)s')

Explanation

Line 1: Call the basicConfig method. Replace <path to logfile> with the path to the destination of log messages on your system. Be sure to choose a location that is writable.

Line 2: Set the logging level by referencing the configuration variable

Line 3: Define the format for your log messages. If you want to use a format that is different from this example, please refer to the documentation.

Generating log messages

With logging configured as described above, you can now generate arbitrary messages from your code. The outline below illustrates a few examples of how this could be done. All messages will appear in the specified logfile.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import logging
logger = logging.getLogger()

...

logger.info("Starting processing")

for record in records:
   if record.name is None:
      logger.warning("Watch out - name is not set")

      ...

   try:
      processRecord(record)
   except Exception as e:
      logger.error(e.message)

Further reading

 Python logging how-to

 Flask logging reference

 Flask exception logging