Metric monitoring can help you make sure your applications keep running and your network stays up. According to Global IT 2017 Study “ Over $1.7 Trillion is Lost Per Year from Data Loss and Downtime”. Don’t be one of the companies that is losing money for no good reason, when you can run metrics on nearly any application. Its for this reason you need to be monitoring your python scripts.

In the next nine minutes I will be going over how to monitor your python scripts on a flask application, the example I will be using is a to-do list. In this to do list we will use SQLALCHAMEY as our database of choice. The lessons you learn here can easily apply any lessons learned here to your own applications.

If you want to download our code, go to this github:

If you want to skip over our flask application and just learn how to monitor a python script click here.

Prerequisites to Run Our Flask Application:

1st You will need python installed.

2n You will need pip, a common command tool

3rd You will need statsd, this is the backend tool that allows you to send metrics

4rd: You will need flask, this is a python library

5th You will need Flask-statsd, this will allow you to collect flask related metrics automatically

6th You will need virtualenv- a tool to create isolated python environments

7th You will need SQLALCHEMY- a database, you can use another if you so choose but your code will need to be modified accordingly

8th connect to BeeInstant — the best metric monitoring service out there

If you already have these you can skip to here:

How to Install Your Prerequisites:

1st: Go to https://www.python.org/downloads/ and install the newest version of python for your machine.

Windows:

2nd: Open command prompt and type python -m pip install -U pip.

Mac:

2nd: open terminal and type sudo easy_install pip

Linux:

2nd: open terminal and type: apt install python3 pip or apt install python2 pip depending on the version of python you are using

All:

3rd:Type pip install statsd

4th:Type pip install flask

5th: Type pip install Flask-StatsD.

6th: Type pip install virtualenv.

7th type pip install Flask-SQLAlchemy.

8th: If you are not already connected to Beeinstant to do this follow this tutorial

Writing our Flask application template:

For those of you who aren’t familiar with flask it interacts between databases, and websites via a python script. To properly use flask you’ll want to start by creating a folder for your application(I recommend on your desktop). Create a subfolder inside of the one you just made called templates. Inside of this folder we will create a html template for our flask application. . Our template is simple HTML code that you are more than welcome to modify to your choosing. For our basic example, however, this is the code we are using:


<ul>
{% for task in tasks %} 

<li>
        {% if task.done %} <strike> {% endif %}{{ task.content }} {% if task.done %} </strike>{% endif %}
        <a href="/done/{{ task.id }}">X</a>
        <a href="/delete/{{ task.id }}">Delete</a>
    </li>


{% endfor %}
</ul>


<form action="/task" method="post">  
<input type="text" name="content">
    <input type="submit" value="Add task">
</form>


Writing our flak application:

To start our python application we will first import everything we need, and then we need to configure our flask application, our Statsd and our database. After doing this your code should look something like this:

from flask import Flask, request
from flask import render_template
from flask import redirect
from flask_sqlalchemy import SQLAlchemy
import time
import statsd
c = statsd.StatsClient('localhost',8125)

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
db = SQLAlchemy(app)

Line 1–6: are simple import statements.

Line 7: We are defining c as our statsd client. We are connecting this client to our localhost and sending data out of port 8125. We can also use port 8094 but we prefer to use 8125 because the statsd client for python works best with udp. It can work with tcp but if it has a connection error it will not be as good.

Line 9: We are defining our flask application to be named app. On line 10 and 11 we are configuring our database to our application. We then define our database to be an SQLAlchemy database running from our flask app.

Next, we will create our first class. Ours shall be called Task. The code to do this is as follows:

class Task(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    content = db.Column(db.Text)
    done = db.Column(db.Boolean, default=False)

    def __init__(self, content):
        self.content = content
        self.done = False

    def __repr__(self):
        return '&amp;amp;lt;Content %s&amp;amp;gt;' % self.content
db.create_all()

Line 1: We create our class named Task and define it as a database model.

Line 2: We create a variable id and set it equal to a database column that holds intergeries, we set it to be our primary key, meaning that no two id’s can have the same value.

Line 3: We create a content column that will hold text.

Line 4: We create a done column that will hold a boolean set to false automatically, we do this because whenever we create a task later on our list we want to assume it has not been completed yet.

Lines 5–8: We initiate our content and our done column.

Lines 9- 11 We create a way to return our database in a printable representation.

Line 12: Outside of our class we initiate our database by creating it.

@app.route('/')
def tasks_list():
    tasks = Task.query.all()
    return render_template('list.html', tasks=tasks)

Line 1: We create our home page. This is our first app route. Each app route is its own webpage inside, set from your flask application.

Line 2–3: We define our tasks list and create our tasks = to our tasks class querying the database

Line 4: We load the html template importing our tasks

We must be able to add tasks. To do this we must create a new app route. It will look as follows (the commented out sections are for monitoring feel free to implement them as you go or you can do them all later. They will be explained later).

@app.route('/task', methods=['POST'])
def add_task():
	#start=time.time()
    content = request.form['content']
    if not content:
    	#dur = (time.time() - start) *1000
    	#c.timing("errortime",dur)
    	#c.incr("errorcount")
        return 'Error'
    task = Task(content)
    db.session.add(task)
    db.session.commit()
   # dur = (time.time() - start) *1000
   # c.timing("tasktime",dur)
   # c.incr("taskcount")
    return redirect('/')

Line 1–9: We start by defining our content as a request. If we are not able to fulfill this request, we return an error

Line 10–16: We create the task equal to the content the user wants to add to the list. We then add the task to our database, committing directly afterwards. We then redirect back to the home page, which will now load with the task written.

Now that we can add tasks we need to be able to delete them. To do this we will create a delete route.

@app.route('/delete/&amp;lt;int:task_id&amp;gt;')
def delete_task(task_id):
    task = Task.query.get(task_id)
    if not task:
    	#c.incr("errorcount")
        return redirect('/')

    db.session.delete(task)
    db.session.commit()
    #c.incr("deletecount")
    return redirect('/')

Line 1: Upon someone clicking the delete button

Line 2-3: Define the delete task set to the task id. Then Get the task id.

Line 4–6: If there is not a task to delete it will instantly redirect back to the homepage,

Line 7–11: Delete the task from the database and commit, then redirect back to the home page.

Now we can add and delete a task, but we what if we want to “resolve tasks” too, so we can keep track of how many things we did today. You guessed it to do this we will create yet another app route. Our code for this will look as follows:

@app.route('/done/&amp;lt;int:task_id&amp;gt;')
def resolve_task(task_id):
    task = Task.query.get(task_id)
    if not task:
        return redirect('/')
    if task.done:
        task.done = False
    else:
        task.done = True

    db.session.commit()
    return redirect('/')


if __name__ == '__main__':
    app.run()

Line 1–3: Upon someone clicking the done button define the resolve_task set to the task id. Then Get the task id.

Line 4-5: If the task does not exist redirect home

Line 6-9: If the task is done then set the task.done variable to false else set it to true

Line 11–12: commit the database to the changes we have made then redirect to home.

Line 13–16: If the name of the page we are on is main we will run the app

How to launch your flask application:

If you are on a PC :

Launch a command prompt and type the following:

cd Desktop/flask-apps

set FLASK_APP=app.py

set FLASK_DEBUG=1

flask run

If you are on a mac:

Launch terminal and type the following:

cd Desktop/flask-apps

export FLASK_APP=app.py

export FLASK_DEBUG=1

flask run

If you are on a Linux machine :

Launch terminal and type the following:

cd ~/Desktop/flask-apps

FLASK_APP=app.py flask run

Now that you have launched your flask application go to localhost:5000 in your browser and you should see your to do list running!

Implementing custom metrics:

To see how often something is used each minute:

All you have to do is create a simple increment variable. For example, say you wanted to know how many times your page was loaded all you would need to do is put this line of code

c.incr("testincreament")

Inside of the web page section of your flask code. Whatever it is you have in your “”is what your metric will be called when you send it to BeeInstant.

To monitor how long it takes to run a function:

First import time into your function, if you have not already. Then create a start time. At the point at which you want to end your timer create a new variable to represent duration, and set it equal to time.time-start *1000(so it is in ms). Like so:


start=time.time()

#task here

dur = (time.time() - start ) *1000 #time 1000 to get in milliseconds

Then use a timing variable to report your metric like so:


c.timing("errortime",dur)

So you now understand how to send you custom metrics for your python script via Flask!

What Metrics do I Make?

If you look at the above code, you will notice many metrics that I have created that are commented out. Go back through your flask application and insert those lines of code if you have not already. Now you will have several custom metrics, an error counter, a task counter, a deletion counter, and a timer to see how long it takes to create a new task. You can add any other custom metrics you may want to your program, just make sure you give them a unique name. After setting up your metrics run your flask application again and add and delete some tasks.

Understanding your python metrics:

To view your metrics go to app.beeinstant.com and login. When looking at your metrics, you will notice that there are many automatically collected by the flask. These are infrastructure metrics, and you get as many of theses as you want with BeeInstant for free. Now go to your metrics explorer, search and plot any of the metrics you care about.

NOTE: it is possible to plot the same metric more than once, this is useful say if you want to look at the p95, p99 and maximum all at once. This is commonly used when looking at latency. To learn more about properly measuring latency checkout this blog post.

Properly Setting your Metrics:

When you plot your metrics they will automatically be set to averages; you will want to change this for the large majority of your metrics. For your increment metrics, you will want to change them to sum so that way you can see the total amount of tasks or deletes done in a minute. (note: if you delete the same amount you add in the same minute one of your data points will hide the other).

Averages v.s Global Percentiles

Your graph right now should thus look something like this:

You will notice that the first data point for task time is 20 milliseconds when your view is set to average. However, as anyone well versed in statistics will know your average case is not actually what is happening. You can see this when you ho ahead and set your task time to p95 instead of average now. Your graph will now look something like this:

You will notice that the first point on-task time is no longer 20mil seconds but 27! P 95 represents what you are expiring 95 % of the time. As you can now see the average was misleading you the whole time as that isn’t the true reality of what is happening. It is for this reason that BeeInstant provides nine global statistics out of the box so that you know what is happening. For more information on this checkout our other blog post here.