Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
1 contributor

Users who have contributed to this file

174 lines (139 sloc) 6.1 KB
import os
import sys
import datetime
import libhoney
from flask_sqlalchemy import SQLAlchemy
from flask import Flask, jsonify, g, request
import logging
app = Flask(__name__)
app.logger.addHandler(logging.StreamHandler(sys.stderr))
app.logger.setLevel(logging.DEBUG)
db_user = "root"
db_pass = ""
db_host = os.getenv("DB_HOST")
db_name = "example-python-api"
app.config["SQLALCHEMY_DATABASE_URI"] = "mysql://{}:{}@{}:3306/{}".format(db_user, db_pass, db_host, db_name)
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
db = SQLAlchemy(app)
libhoney.init(writekey=os.getenv("HONEYCOMB_WRITEKEY"), dataset="examples.python-api")
libhoney_builder = libhoney.Builder()
class Todo(db.Model):
id = db.Column(db.Integer, primary_key=True)
description = db.Column(db.String(200), nullable=False)
completed = db.Column(db.Boolean(False), nullable=False)
due = db.Column(db.DateTime())
def serialize(self):
return {
"id": self.id,
"description": self.description,
"completed": self.completed,
"due": self.due,
}
def __repr__(self):
return "<Todo {}>".format(self.id)
db.create_all()
class InvalidUsage(Exception):
status_code = 400
def __init__(self, message, status_code=None, payload=None):
Exception.__init__(self)
self.message = message
if status_code is not None:
self.status_code = status_code
self.payload = payload
def to_dict(self):
rv = dict(self.payload or ())
rv['message'] = self.message
return rv
def milliseconds_since(start):
delta = (datetime.datetime.now() - start).total_seconds()
return delta*1000
@app.errorhandler(InvalidUsage)
def handle_invalid_usage(error):
g.ev.add_field("errors.message", error.message)
response = jsonify(error.to_dict())
response.status_code = error.status_code
return response
@app.before_request
def before():
# g is the thread-local / request-local variable, we will use it to store
# information that will be used when we eventually send the event to
# Honeycomb, including a timer for the whole request duration.
g.req_start = datetime.datetime.now()
g.ev = libhoney_builder.new_event()
g.ev.add_field("request.path", request.path)
g.ev.add_field("request.method", request.method)
g.ev.add_field("request.user_agent.browser", request.user_agent.browser)
g.ev.add_field("request.user_agent.platform", request.user_agent.platform)
g.ev.add_field("request.user_agent.language", request.user_agent.language)
g.ev.add_field("request.user_agent.string", request.user_agent.string)
g.ev.add_field("request.user_agent.version", request.user_agent.version)
g.ev.add_field("request.python_function", request.endpoint)
g.ev.add_field("request.url_pattern", str(request.url_rule))
@app.after_request
def after(response):
g.ev.add_field("response.status_code", response.status_code)
# Note that this isn"t the total time to serve the request, i.e., how long
# the end user is waiting. It accounts for the time spent in the Flask
# handlers but not Werkzeug, etc. Ingesting edge data from ELB or
# nginx etc. is usually much better for that kind of (total request time) info.
g.ev.add_field("timers.flask_time_ms", milliseconds_since(g.req_start))
app.logger.debug(g.ev)
g.ev.send()
return response
@app.route("/")
def index():
return jsonify(up=True)
@app.route("/todos/", defaults={"todo_id": None}, methods=["GET", "POST"])
@app.route("/todos/<int:todo_id>/", methods=["GET", "DELETE", "PUT"])
def todo(todo_id):
if todo_id is None:
if request.method == "POST":
json = request.get_json()
todo = Todo(
description=json.get("description", ""),
completed=json.get("completed", False),
due=datetime.datetime.fromtimestamp(json.get("due", None)),
)
insert_todo_start = datetime.datetime.now()
db.session.add(todo)
db.session.commit()
g.ev.add_field("timers.db.insert_todo_ms", milliseconds_since(insert_todo_start))
# Augment events with high cardinality information!
g.ev.add_field("todo.id", todo.id)
return jsonify(todo.serialize())
if request.method == "GET":
select_all_todos_start = datetime.datetime.now()
todos = [todo.serialize() for todo in Todo.query.all()]
g.ev.add_field("timers.db.select_all_todos", milliseconds_since(select_all_todos_start))
return jsonify(todos)
raise InvalidUsage("Method not allowed", status_code=405)
else:
# Augment events with high cardinality information!
g.ev.add_field("todo.id", todo_id)
select_todo_start = datetime.datetime.now()
todo = Todo.query.get(todo_id)
g.ev.add_field("timers.db.select_todo", milliseconds_since(select_todo_start))
if todo is None:
err = "Todo not found"
g.ev.add_field("errors.message", err)
return jsonify({"error": err}), 404
if request.method == "GET":
return jsonify(todo.serialize())
elif request.method == "DELETE":
delete_todo_start = datetime.datetime.now()
db.session.delete(todo)
db.session.commit()
g.ev.add_field("timers.db.delete_todo", milliseconds_since(delete_todo_start))
return jsonify({"success": True, "id": todo_id})
elif request.method == "PUT":
json = request.get_json()
todo.description = json.get("description", todo.description)
todo.completed = json.get("completed", todo.completed)
app.logger.debug(json.get("completed", todo.completed))
todo.due = json.get("due", todo.due),
update_todo_start = datetime.datetime.now()
db.session.add(todo)
db.session.commit()
g.ev.add_field("timers.db.update_todo", milliseconds_since(update_todo_start))
return jsonify(todo.serialize())
raise InvalidUsage("Method not allowed", status_code=405)
You can’t perform that action at this time.