Running A Process In the Background In Flask
Question: I am making a webapp in python and flask which will email & tweet to the
webmaster when the moment his website goes down. I am thinking of running an
infinite while loop which waits for 10 minutes and then send a request to the
website to be checked and checks if the returned response is 200. The problem
is that were in the script can I insert this loop? Any suggestions about how I
can achieve this?


Answer: # Soldier, tell me when you are dead

Trying to report a problem from dying app is not the most reliable method.

# Use external process to watch the app

You shall have some external independent app, monitoring your app.

If you live in pure Python environment, you may write a script, which will
check if accessing some app url succeeds and if not, it would alert someone.
For alerting, you may try e.g. `logbook` with log records being sent by e-mail
(see
[MailHandler](http://pythonhosted.org/Logbook/api/handlers.html#logbook.MailHandler)
or
[GMailHandler](http://pythonhosted.org/Logbook/api/handlers.html#logbook.GMailHandler)).

In production environment it would be best to run some monitoring app like
[Nagios](http://www.nagios.org/about) and simply check by
[`check_http`](https://www.monitoring-plugins.org/doc/man/check_http.html)

# Sample checking script using Logbook

For better readibility, the content is split to more parts, real script is in
one file called `monitor_url.py`

## `monitor_url.py`: Docstring, imports and MAIL_TEMPLATE

Docstring explains usage and is finally used by command line parser `docopt`

Imports are mostly `logbook` related

    
    
    """monitor_url.py - check GET access to a url, notify by GMail about problems
    Usage:
        monitor_url.py   [options] <url> <from> <pswd> <to>...
        monitor_url.py -h
    
    Options:
      -L, --logfile <logfile>    Name of logfile to write to [default: monitor_url.log].
      -N, --archives <archives>  Number of daily logs to keep, use 0 for unlimited [default: 0]
    
    The check is performed once a minute and does HTTP GET request to <url>.
    If there is a problem, it sends an e-mail using GMail account.
    There is a limit of 6 e-mails, which can be sent per hour.
    """
    import time
    from logbook import Logger, GMailHandler, StderrHandler, NestedSetup, TimedRotatingFileHandler, Processor
    from logbook.more import JinjaFormatter
    from datetime import timedelta
    import requests
    from requests.exceptions import ConnectionError
    
    MAIL_TEMPL = """Subject: {{ record.level_name }} on {{ record.extra.url }}
    
    {{ record.message }}
    
    Url: {{ record.extra.url }}
    {% if record.exc_info %}
    Exception: {{ record.formatted_exception }}
    {% else %}
    Status: {{ record.extra.req.status_code }}
    Reason: {{ record.extra.req.reason }}
    {% endif %}
    """
    

## `monitor_url.py`: `main` function performing the checks

This script is looping in `while` and performs once a minute a check. If a
problem is detected, or status code has changed, GMailHandler is configured to
send e-mail.

    
    
    def main(url, e_from, pswd, e_to, logfile, archives):
        log = Logger("httpWatcher")
        def inject_req(record):
            record.extra.url = url
            record.extra.req = req
        processor = Processor(inject_req)
        gmail_handler = GMailHandler(e_from, pswd, e_to,
            level="WARNING", record_limit=6, record_delta=timedelta(hours=1), bubble=True)
        gmail_handler.formatter = JinjaFormatter(MAIL_TEMPL)
    
        setup = NestedSetup([StderrHandler(),
            TimedRotatingFileHandler(logfile, bubble=True),
            gmail_handler,
            processor])
    
        with setup.applicationbound():
            last_status = 200
            while True:
                try:
                    req = requests.get(url)
                    if req.status_code != last_status:
                        log.warn("url was reached, status has changed")
                    if req.ok:
                        log.info("url was reached, status OK")
                    else:
                        log.error("Problem to reach the url")
                    last_status = req.status_code
                except ConnectionError:
                    req = None
                    log.exception("Unable to connect")
                    last_status = 0
                time.sleep(6)
    

## `montior_url.py` final `if __name__ ...`

This part actually parses command line parameters and calls the `main`

    
    
    if __name__ == "__main__":
        from docopt import docopt
        args = docopt(__doc__)
        print args
        main(args["<url>"], args["<from>"], args["<pswd>"], args["<to>"], args["--logfile"],
                int(args["--archives"]))
    

## Call it

Try to call it without parameters:

    
    
    $ python monitor_url.py 
    

Usage: monitor_url.py [options] ... monitor_url.py -h

To get full help, use `-h`

    
    
    $ python monitor_url.py -h
    ...the help is the same as script docstring...
    

Use it for real monitoring, here used for monitoring `http://localhost:8000`

    
    
    $ python monitor_url.py -L mylog.log -N 2 http://localhost:8000 <yourmail>@gmail.com xxxpasswordxxx notified.person@example.com
    {'--archives': '2',
     '--logfile': 'mylog.log',
     '-h': False,
     '<from>': 'your.mail@gmail.com',
     '<pswd>': 'xxxxxxx',
     '<to>': ['notified.person@example.com'],
     '<url>': 'http://localhost:8000'}
    [2014-06-09 19:41] INFO: httpWatcher: url was reached, status OK
    [2014-06-09 19:41] ERROR: httpWatcher: Unable to connect
    Traceback (most recent call last):
    ....(shortened)...
        raise ConnectionError(e, request=request)
    ConnectionError: HTTPConnectionPool(host='localhost', port=8000): Max retries exceeded with url: / (Caused by <class 'socket.error'>: [Errno 111] Connection refused)
    [2014-06-09 19:41] WARNING: httpWatcher: url was reached, status has changed
    [2014-06-09 19:41] INFO: httpWatcher: url was reached, status OK
    

Check logfile created in local folder.

Check your gmail inbox (if there is nothing, you have to play with the
password).

# Conclusions

Twitter notification is also possible with `logbook`, but is not shown here.

To run the script, Python 2.7 is expected and you shall install some packages:

    
    
    $ pip install logbook jinja2 requests
    

Managing notification from such a script is not easy. Consider this script of
beta quality in this regard.

Usage of solutions like `Nagios` seems to be more appropriate for this
purpose.

