It can be useful to measure the execution time of functions to ensure your code is running well. An execution time way longer than average implies an issue. Here is a lightweight context manager, implemented as a decorator, which saves some metrics on your functions' runtime.
This simple Metric
class gets implemented as a context manager, and saves the function name and execution time. All it requires is a database connection to save the metrics.
from contextlib import ContextDecorator,contextmanager
import pymysql.cursors
from time import time
class DatabaseFactory():
def __init__(self, host: str, user: str, password: str, database: str, port: int):
self.host: str = host
self.user: str = user
self.password = password
self.database: str = database
self.port: int = port
@contextmanager
def connection(self):
connection = self._connect()
try:
yield connection
except Exception as ex:
raise ex
finally:
connection.close()
def _connect(self):
return pymysql.connect(
host=self.host,
user=self.user,
password=self.password,
db=self.database,
charset="utf8mb4",
cursorclass=pymysql.cursors.DictCursor,
autocommit=True,
port=self.port,
)
db = DatabaseFactory(
host="127.0.0.1",
user="root",
password="",
database="metric",
port=3306,
)
class Metric():
def __init__(self, function: object):
self.function: object = function
def __call__(self):
t_start = time()
self.function()
t_end = time()
self._metrics(self.function.__name__, t_end-t_start)
def _metrics(self, function_name: str, execution_time: float):
with db.connection() as connection:
with connection.cursor() as curs:
curs.execute("INSERT INTO metrics (function_name, execution_time) VALUES (%s, %s)", (function_name, execution_time*1000000))
Most of the lines are dedicated to opening up a MySQL database connection and using a MySQL database called metric
.
The class Metric
will get implemented as a decorator and just invokes the function and saves the function name and execution time (in ms) into the database.
To implement this class as a decorator, all I need is the following small function.
from metric import Metric
import time
@Metric
def test():
time.sleep(5)
Now every time the test
function gets run I should see a new entry in my table. This is what it looks like:
select * from metrics;
+----+---------------+----------------+
| id | function_name | execution_time |
+----+---------------+----------------+
| 1 | test | 5005049 |
+----+---------------+----------------+
1 row in set (0.01 sec)
Perfect! Though this is only one metric, there are a whole host of other useful metrics to track - the sky is the limit.