5

I want to log, who is logging in the app that uses FLASK. I tried using @app.before_request but the problem is I need to access the username which is through flask globals and I get none if I use this decorator.

Also, using global variables like below also doesn`t work. How do I get a global variable in request context?

import logging
import time
from flask import request, flash
from flask import g
from forms import QueryForm, RequestForm, Approve_Reject_Btn_Form
from query.sqlQuery import SQLQuery
from database.oracle import Database
import datetime
from requests import Session

logger = logging.getLogger(__name__)
login_count = 0
'''
Homepage route - Displays all the tables in the homepage
'''
@app.route('/')
@app.route('/index')
def index():
  try:
    if not g.identity.is_authenticated():
      return render_template('homepage.html')
    else:
      try:
        global login_count
        if login_count == 0:
          username = g.identity.name
          user_ip = request.headers.get('IP_header')
          current_time = time.strftime('%c')
          db = Database(db_config.username, db_config.password)
          query = "INSERT INTO UserIP (username, login_time, ip_address) values ('%s', systimestamp, '%s')" % (username, user_ip)
          dml_query(query)
          logger.debug('User : %s, ip : %s, noted at time : %s, login_count : %s', username , user_ip, current_time, login_count)
          login_count = 1
ramu
  • 1,325
  • 2
  • 17
  • 25

1 Answers1

13

As far as your question "How to execute a block of code only once in flask?" goes, something like this should work fine:

app = Flask(__name__)

@app.before_first_request
def do_something_only_once():
    app.logger.setLevel(logging.INFO)
    app.logger.info("Initialized Flask logger handler")

The second part of your question is how to setup global variable, like the login counter in your example. For something like this I would recommend that you use an external cache. See example below with the werkzeug SimpleCache class. In production you should replace SimpleCache with redis or mongodb.

from werkzeug.contrib.cache import SimpleCache

class Cache(object):
    cache = SimpleCache(threshold = 1000, default_timeout = 3600)

    @classmethod
    def get(cls, key = None):
        return cls.cache.get(key)
    @classmethod
    def delete(cls, key = None):
        return cls.cache.delete(key)
    @classmethod
    def set(cls, key = None, value = None, timeout = 0):
        if timeout:
            return cls.cache.set(key, value, timeout = timeout)
        else:    
            return cls.cache.set(key, value)
    @classmethod
    def clear(cls):
        return cls.cache.clear()

You can use Cache class like this.

from mycache import Cache

@app.route('/')
@app.route('/index')
def index():
    if not g.identity.is_authenticated():
        return render_template('homepage.html')
    else:
        login_count = Cache.get("login_count")
        if login_count == 0:
            # ...
            # Your code
            login_count += 1
            Cache.set("login_count", login_count)

EDIT 1: Added after_request example

Per Flask documentation, after_request decorator is used to run registered function after each request. It can be used to invalidate cache, alter response object, or pretty much anything that would require a specific response modification.

@app.after_request
def after_request_callback(response):
    # Do something with the response (invalidate cache, alter response object, etc)
    return response
Boris
  • 2,275
  • 20
  • 21
  • Thanks for the elaborate answer. I also wanted to know if you know about how the after_request decorator works and could update the answer with a short example of that. Also should I use memcache instead of simpleCache since each gunicorn process initializes its own cache and defeats the purpose. – ramu Jul 07 '15 at 22:52
  • http://stackoverflow.com/questions/28900091/what-can-go-wrong-if-i-use-simplecache-in-my-flask-app – ramu Jul 07 '15 at 23:32
  • Yeah memcache, redis or mongodb are much better options for the production system. My example was just there to give you an idea how create a login counter. – Boris Jul 07 '15 at 23:49
  • So with memcache I won`t get this issue? – ramu Jul 07 '15 at 23:51
  • One thing to note: if you're using a worker system such as gunicorn, each worker will only run the function once during each startup. However, there will still be many executions of the function due to the Flask app being re-instantiated over and over under that model, so tread lightly if you're trying to use this for static variable setting and using something non-deterministic. – bsplosion Jul 25 '19 at 16:17