I was able to embed a bokeh server graph in Flask. This is the output on localhost:
Problem is I am not being able to make this work on Heroku. All I get displayed is the text "Here is your great site".
So, the app is made of three files.
There's the script containing the graph:
#bokeh_plot.py
import pandas as pd
from bokeh.layouts import row, widgetbox
from bokeh.models import Select
from bokeh.palettes import Spectral5
from bokeh.plotting import curdoc, figure
from bokeh.sampledata.autompg import autompg
df = autompg.copy()
SIZES = list(range(6, 22, 3))
COLORS = Spectral5
ORIGINS = ['North America', 'Europe', 'Asia']
# data cleanup
df.cyl = [str(x) for x in df.cyl]
df.origin = [ORIGINS[x-1] for x in df.origin]
df['year'] = [str(x) for x in df.yr]
del df['yr']
df['mfr'] = [x.split()[0] for x in df.name]
df.loc[df.mfr=='chevy', 'mfr'] = 'chevrolet'
df.loc[df.mfr=='chevroelt', 'mfr'] = 'chevrolet'
df.loc[df.mfr=='maxda', 'mfr'] = 'mazda'
df.loc[df.mfr=='mercedes-benz', 'mfr'] = 'mercedes'
df.loc[df.mfr=='toyouta', 'mfr'] = 'toyota'
df.loc[df.mfr=='vokswagen', 'mfr'] = 'volkswagen'
df.loc[df.mfr=='vw', 'mfr'] = 'volkswagen'
del df['name']
columns = sorted(df.columns)
discrete = [x for x in columns if df[x].dtype == object]
continuous = [x for x in columns if x not in discrete]
quantileable = [x for x in continuous if len(df[x].unique()) > 20]
def create_figure():
xs = df[x.value].values
ys = df[y.value].values
x_title = x.value.title()
y_title = y.value.title()
kw = dict()
if x.value in discrete:
kw['x_range'] = sorted(set(xs))
if y.value in discrete:
kw['y_range'] = sorted(set(ys))
kw['title'] = "%s vs %s" % (x_title, y_title)
p = figure(plot_height=600, plot_width=800, tools='pan,box_zoom,reset', **kw)
p.xaxis.axis_label = x_title
p.yaxis.axis_label = y_title
if x.value in discrete:
p.xaxis.major_label_orientation = pd.np.pi / 4
sz = 9
if size.value != 'None':
groups = pd.qcut(df[size.value].values, len(SIZES))
sz = [SIZES[xx] for xx in groups.codes]
c = "#31AADE"
if color.value != 'None':
groups = pd.qcut(df[color.value].values, len(COLORS))
c = [COLORS[xx] for xx in groups.codes]
p.circle(x=xs, y=ys, color=c, size=sz, line_color="white", alpha=0.6, hover_color='white', hover_alpha=0.5)
return p
def update(attr, old, new):
layout.children[1] = create_figure()
x = Select(title='X-Axis', value='mpg', options=columns)
x.on_change('value', update)
y = Select(title='Y-Axis', value='hp', options=columns)
y.on_change('value', update)
size = Select(title='Size', value='None', options=['None'] + quantileable)
size.on_change('value', update)
color = Select(title='Color', value='None', options=['None'] + quantileable)
color.on_change('value', update)
controls = widgetbox([x, y, color, size], width=200)
layout = row(controls, create_figure())
curdoc().add_root(layout)
curdoc().title = "Crossfilter"
The Flask script:
#app.py
import subprocess
import atexit
from flask import render_template, render_template_string, Flask
from bokeh.embed import autoload_server
from bokeh.client import pull_session
app = Flask(__name__)
bokeh_process = subprocess.Popen(
['bokeh', 'serve','--allow-websocket-origin=localhost:5000','bokeh_plot.py'], stdout=subprocess.PIPE)
@atexit.register
def kill_server():
bokeh_process.kill()
@app.route("/")
def index():
session=pull_session(app_path="/bokeh_plot")
bokeh_script=autoload_server(None,app_path="/bokeh_plot",session_id=session.id)
return render_template("index.html", bokeh_script=bokeh_script)
if __name__ == "__main__":
print("STARTED")
app.run(debug=True)
And the index.html template:
<!DOCTYPE html>
<html lang="en">
<head>
<style>
@import url(https://fonts.googleapis.com/css?family=Noto+Sans);
body {
font-family: 'Noto Sans', sans-serif;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
color: #fff;
background: #2F2F2F;
}
</style>
<meta charset="utf-8">
<title>Bokeh Crossfilter Example</title>
</head>
<body>
<p> This is your great site<p>
<div class="bk-root">
{{ bokeh_script|safe }}
</div>
</body>
</html>
And this is what I get from the heroku logs. Note there's an Cannot start Bokeh server, port 5006 is already in use"
line there.
heroku[web.1]: State changed from down to starting
heroku[web.1]: Unidling
heroku[web.1]: Starting process with command `gunicorn app:app`
app[web.1]: [2016-07-25 09:32:40 +0000] [3] [INFO] Starting gunicorn 19.6.0
app[web.1]: [2016-07-25 09:32:40 +0000] [3] [INFO] Listening at: http://0.0.0.0:48905 (3)
app[web.1]: [2016-07-25 09:32:40 +0000] [3] [INFO] Using worker: sync
app[web.1]: [2016-07-25 09:32:40 +0000] [7] [INFO] Booting worker with pid: 7
app[web.1]: [2016-07-25 09:32:40 +0000] [8] [INFO] Booting worker with pid: 8
heroku[web.1]: State changed from starting to up
"app[web.1]: File ""/app/.heroku/python/lib/python3.5/site-packages/flask/app.py"", line 1641, in full_dispatch_request"
app[web.1]: Traceback (most recent call last):
"app[web.1]: File ""/app/.heroku/python/lib/python3.5/site-packages/flask/app.py"", line 1988, in wsgi_app"
"app[web.1]: File ""/app/.heroku/python/lib/python3.5/site-packages/flask/app.py"", line 1544, in handle_user_exception"
app[web.1]: rv = self.handle_user_exception(e)
"app[web.1]: [2016-07-25 09:32:44,050] ERROR in app: Exception on / [GET]"
"app[web.1]: File ""/app/.heroku/python/lib/python3.5/site-packages/flask/_compat.py"", line 33, in reraise"
app[web.1]: response = self.full_dispatch_request()
"app[web.1]: reraise(exc_type, exc_value, tb)"
app[web.1]: raise value
"app[web.1]: OSError: Cannot pull session document because we failed to connect to the server (to start the server, try the 'bokeh serve' command)"
"app[web.1]: File ""/app/.heroku/python/lib/python3.5/site-packages/flask/app.py"", line 1639, in full_dispatch_request"
app[web.1]: rv = self.dispatch_request()
"app[web.1]: File ""/app/.heroku/python/lib/python3.5/site-packages/flask/app.py"", line 1625, in dispatch_request"
app[web.1]: return self.view_functions[rule.endpoint](**req.view_args)
"app[web.1]: File ""/app/app.py"", line 37, in index"
"app[web.1]: session=pull_session(app_path=""/bokeh_plot"")"
"app[web.1]: File ""/app/.heroku/python/lib/python3.5/site-packages/bokeh/client/session.py"", line 112, in pull_session"
app[web.1]: session.pull()
"app[web.1]: File ""/app/.heroku/python/lib/python3.5/site-packages/bokeh/client/session.py"", line 231, in pull"
app[web.1]: rv = self.handle_user_exception(e)
"app[web.1]: raise IOError(""Cannot pull session document because we failed to connect to the server (to start the server, try the 'bokeh serve' command)"")"
app[web.1]: ERROR:app:Exception on / [GET]
app[web.1]: Traceback (most recent call last):
"app[web.1]: File ""/app/.heroku/python/lib/python3.5/site-packages/flask/app.py"", line 1988, in wsgi_app"
app[web.1]: response = self.full_dispatch_request()
"app[web.1]: File ""/app/.heroku/python/lib/python3.5/site-packages/flask/app.py"", line 1641, in full_dispatch_request"
"app[web.1]: File ""/app/.heroku/python/lib/python3.5/site-packages/flask/_compat.py"", line 33, in reraise"
"app[web.1]: File ""/app/.heroku/python/lib/python3.5/site-packages/flask/app.py"", line 1625, in dispatch_request"
app[web.1]: raise value
app[web.1]: rv = self.dispatch_request()
"app[web.1]: File ""/app/.heroku/python/lib/python3.5/site-packages/flask/app.py"", line 1639, in full_dispatch_request"
app[web.1]: return self.view_functions[rule.endpoint](**req.view_args)
"app[web.1]: File ""/app/app.py"", line 37, in index"
"app[web.1]: File ""/app/.heroku/python/lib/python3.5/site-packages/flask/app.py"", line 1544, in handle_user_exception"
"app[web.1]: reraise(exc_type, exc_value, tb)"
"app[web.1]: session=pull_session(app_path=""/bokeh_plot"")"
"app[web.1]: File ""/app/.heroku/python/lib/python3.5/site-packages/bokeh/client/session.py"", line 112, in pull_session"
app[web.1]: session.pull()
"app[web.1]: File ""/app/.heroku/python/lib/python3.5/site-packages/bokeh/client/session.py"", line 231, in pull"
"app[web.1]: raise IOError(""Cannot pull session document because we failed to connect to the server (to start the server, try the 'bokeh serve' command)"")"
"app[web.1]: OSError: Cannot pull session document because we failed to connect to the server (to start the server, try the 'bokeh serve' command)"
"heroku[router]: at=info method=GET path=""/"" host=bokehapp.herokuapp.com request_id=f60be826-3fba-4eab-b18f-aa0eb822ebdd fwd=""217.73.139.64"" dyno=web.1 connect=2ms service=77ms status=500 bytes=456"
"app[web.1]: 2016-07-25 09:32:44,637 Starting Bokeh server on port 5006 with applications at paths ['/bokeh_plot']"
**"app[web.1]: 2016-07-25 09:32:44,683 Cannot start Bokeh server, port 5006 is already in use"**
"app[web.1]: 2016-07-25 09:32:44,681 Starting Bokeh server version 0.12.0"
"app[web.1]: 2016-07-25 09:32:44,637 Starting Bokeh server with process id: 26"
"app[web.1]: 2016-07-25 09:32:44,620 Starting Bokeh server version 0.12.0"
"app[web.1]: 2016-07-25 09:32:55,074 WebSocket connection opened"
"app[web.1]: 2016-07-25 09:32:55,187 ServerConnection created"
"heroku[router]: at=info method=GET path=""/"" host=bokehapp.herokuapp.com request_id=41493fd5-aed9-4198-b063-652f93cee49e fwd=""217.73.139.64"" dyno=web.1 connect=0ms service=550ms status=200 bytes=1066"
I have desperately searched for an answer on the web, but haven't found anything on how to deploy a flask app that contains a bokeh server app inside. So, this post could be a good resource if anyone is able to provide an answer.
I believe the key is in the subprocess line in the app.py file where the bokeh server is called, but my knowledge in web services is limited so, can't figure out how to make it work. Any idea?
Edit 1: bigreddot suggested to change the value of --allow-websocket-origin to the url of the app. So, I did change localhost:5000 to bokehapp.herokuapp.com. That seems to fix the IOError: Cannot put session document. However, the graph still doesn't show. Here are the new logs I get from Heroku:
heroku[slug-compiler]: Slug compilation finished
heroku[slug-compiler]: Slug compilation started
heroku[web.1]: State changed from down to starting
heroku[web.1]: Starting process with command `gunicorn app:app`
app[web.1]: [2016-07-25 15:02:50 +0000] [3] [INFO] Using worker: sync
app[web.1]: [2016-07-25 15:02:50 +0000] [7] [INFO] Booting worker with pid: 7
app[web.1]: [2016-07-25 15:02:50 +0000] [3] [INFO] Starting gunicorn 19.6.0
app[web.1]: [2016-07-25 15:02:50 +0000] [3] [INFO] Listening at: http://0.0.0.0:23620 (3)
app[web.1]: [2016-07-25 15:02:50 +0000] [8] [INFO] Booting worker with pid: 8
heroku[api]: Release v7 created by adiadi@gmail.com
heroku[api]: Deploy be12fcc by adiadi@gmail.com
heroku[web.1]: State changed from starting to up
"app[web.1]: 2016-07-25 15:02:55,278 Starting Bokeh server with process id: 25"
"app[web.1]: 2016-07-25 15:02:55,261 Starting Bokeh server version 0.12.0"
"app[web.1]: 2016-07-25 15:02:55,276 Starting Bokeh server version 0.12.0"
"app[web.1]: 2016-07-25 15:02:55,284 Cannot start Bokeh server, port 5006 is already in use"
"app[web.1]: 2016-07-25 15:02:55,278 Starting Bokeh server on port 5006 with applications at paths ['/bokeh_plot']"
"app[web.1]: 2016-07-25 15:03:14,375 WebSocket connection opened"
"app[web.1]: 2016-07-25 15:03:15,029 ServerConnection created"
"heroku[router]: at=info method=GET path=""/"" host=bokehapp.herokuapp.com request_id=5e7a06ad-4526-4304-b00e-0c37979ab751 fwd=""109.236.33.234"" dyno=web.1 connect=1ms service=3778ms status=200 bytes=1066"
"app[web.1]: 2016-07-25 15:03:59,253 ServerConnection created"
"app[web.1]: 2016-07-25 15:03:59,133 WebSocket connection opened"
Edit 2: Some even more logs after passing --log-level=debug to subprocess:
heroku[slug-compiler]: Slug compilation started
heroku[slug-compiler]: Slug compilation finished
heroku[api]: Deploy 9c2fefa by adiadi@gmail.com
heroku[api]: Release v12 created by adiadi@gmail.com
heroku[web.1]: Restarting
heroku[web.1]: State changed from up to starting
heroku[web.1]: Stopping all processes with SIGTERM
app[web.1]: [2016-07-25 21:53:37 +0000] [3] [INFO] Handling signal: term
app[web.1]: [2016-07-25 21:53:37 +0000] [7] [INFO] Worker exiting (pid: 7)
app[web.1]: [2016-07-25 21:53:38 +0000] [3] [INFO] Shutting down: Master
app[web.1]: [2016-07-25 21:53:37 +0000] [8] [INFO] Worker exiting (pid: 8)
heroku[web.1]: Process exited with status 0
heroku[web.1]: Starting process with command `gunicorn app:app`
app[web.1]: [2016-07-25 21:53:53 +0000] [3] [INFO] Starting gunicorn 19.6.0
app[web.1]: [2016-07-25 21:53:53 +0000] [3] [INFO] Using worker: sync
app[web.1]: [2016-07-25 21:53:53 +0000] [8] [INFO] Booting worker with pid: 8
app[web.1]: [2016-07-25 21:53:53 +0000] [7] [INFO] Booting worker with pid: 7
app[web.1]: [2016-07-25 21:53:53 +0000] [3] [INFO] Listening at: http://0.0.0.0:41934 (3)
heroku[web.1]: State changed from starting to up
"app[web.1]: 2016-07-25 21:53:58,821 Allowed Host headers: ['localhost:5006']"
"app[web.1]: 2016-07-25 21:53:58,821 These host origins can connect to the websocket: ['bokehapp.herokuapp.com:80', 'localhost:5006']"
"app[web.1]: 2016-07-25 21:53:58,821 Patterns are:"
"app[web.1]: 2016-07-25 21:53:58,821 Starting Bokeh server version 0.12.0"
"app[web.1]: 2016-07-25 21:53:58,823 {'application_context': <bokeh.server.application_context.ApplicationContext object at 0x7f7bf7dd51d0>,"
"app[web.1]: 2016-07-25 21:53:58,832 <class 'bokeh.server.views.ws.WSHandler'>,"
"app[web.1]: 2016-07-25 21:53:58,832 ('/bokeh_plot/autoload.js',"
"app[web.1]: 2016-07-25 21:53:58,831 ('/bokeh_plot/ws',"
"app[web.1]: 2016-07-25 21:53:58,832 <class 'bokeh.server.views.autoload_js_handler.AutoloadJsHandler'>,"
"app[web.1]: 2016-07-25 21:53:58,832 {'application_context': <bokeh.server.application_context.ApplicationContext object at 0x7f7bf7dd51d0>,"
"app[web.1]: 2016-07-25 21:53:58,832 'bokeh_websocket_path': '/bokeh_plot/ws'}),"
"app[web.1]: 2016-07-25 21:53:58,833 {'applications': {'/bokeh_plot': <bokeh.server.application_context.ApplicationContext object at 0x7f7bf7dd51d0>},"
"app[web.1]: 2016-07-25 21:53:58,823 [('/bokeh_plot/?',"
"app[web.1]: 2016-07-25 21:53:58,823 <class 'bokeh.server.views.doc_handler.DocHandler'>,"
"app[web.1]: 2016-07-25 21:53:58,823 'bokeh_websocket_path': '/bokeh_plot/ws'}),"
"app[web.1]: 2016-07-25 21:53:58,832 {'application_context': <bokeh.server.application_context.ApplicationContext object at 0x7f7bf7dd51d0>,"
"app[web.1]: 2016-07-25 21:53:58,833 'prefix': '',"
"app[web.1]: 2016-07-25 21:53:58,833 'use_redirect': True}),"
"app[web.1]: 2016-07-25 21:53:58,833 ('/static/(.*)',"
"app[web.1]: 2016-07-25 21:53:58,833 <class 'bokeh.server.views.static_handler.StaticHandler'>)]"
"app[web.1]: 2016-07-25 21:53:58,832 'bokeh_websocket_path': '/bokeh_plot/ws'}),"
"app[web.1]: 2016-07-25 21:53:58,861 Starting Bokeh server on port 5006 with applications at paths ['/bokeh_plot']"
"app[web.1]: 2016-07-25 21:53:58,832 ('/?',"
"app[web.1]: 2016-07-25 21:53:58,833 <class 'bokeh.server.views.root_handler.RootHandler'>,"
"app[web.1]: 2016-07-25 21:53:58,896 These host origins can connect to the websocket: ['bokehapp.herokuapp.com:80', 'localhost:5006']"
"app[web.1]: 2016-07-25 21:53:58,898 [('/bokeh_plot/?',"
"app[web.1]: 2016-07-25 21:53:58,899 {'application_context': <bokeh.server.application_context.ApplicationContext object at 0x7f8a84cb4b70>,"
"app[web.1]: 2016-07-25 21:53:58,896 Starting Bokeh server version 0.12.0"
"app[web.1]: 2016-07-25 21:53:58,897 Patterns are:"
"app[web.1]: 2016-07-25 21:53:58,899 <class 'bokeh.server.views.doc_handler.DocHandler'>,"
"app[web.1]: 2016-07-25 21:53:58,896 Allowed Host headers: ['localhost:5006']"
"app[web.1]: 2016-07-25 21:53:58,900 <class 'bokeh.server.views.autoload_js_handler.AutoloadJsHandler'>,"
"app[web.1]: 2016-07-25 21:53:58,899 ('/bokeh_plot/ws',"
"app[web.1]: 2016-07-25 21:53:58,899 <class 'bokeh.server.views.ws.WSHandler'>,"
"app[web.1]: 2016-07-25 21:53:58,899 'bokeh_websocket_path': '/bokeh_plot/ws'}),"
"app[web.1]: 2016-07-25 21:53:58,899 {'application_context': <bokeh.server.application_context.ApplicationContext object at 0x7f8a84cb4b70>,"
"app[web.1]: 2016-07-25 21:53:58,900 'bokeh_websocket_path': '/bokeh_plot/ws'}),"
"app[web.1]: 2016-07-25 21:53:58,900 ('/bokeh_plot/autoload.js',"
"app[web.1]: 2016-07-25 21:53:58,861 Starting Bokeh server with process id: 25"
"app[web.1]: 2016-07-25 21:53:58,900 {'application_context': <bokeh.server.application_context.ApplicationContext object at 0x7f8a84cb4b70>,"
"app[web.1]: 2016-07-25 21:53:58,900 'bokeh_websocket_path': '/bokeh_plot/ws'}),"
"app[web.1]: 2016-07-25 21:53:58,901 'use_redirect': True}),"
"app[web.1]: 2016-07-25 21:53:58,900 ('/?',"
"app[web.1]: 2016-07-25 21:53:58,901 ('/static/(.*)',"
"app[web.1]: 2016-07-25 21:53:58,901 <class 'bokeh.server.views.static_handler.StaticHandler'>)]"
"app[web.1]: 2016-07-25 21:53:58,904 Cannot start Bokeh server, port 5006 is already in use"
"app[web.1]: 2016-07-25 21:53:58,900 <class 'bokeh.server.views.root_handler.RootHandler'>,"
"app[web.1]: 2016-07-25 21:53:58,901 {'applications': {'/bokeh_plot': <bokeh.server.application_context.ApplicationContext object at 0x7f8a84cb4b70>},"
"app[web.1]: 2016-07-25 21:53:58,901 'prefix': '',"
"app[web.1]: 2016-07-25 21:54:13,880 [pid 25] 0 clients connected"
"app[web.1]: 2016-07-25 21:54:13,880 [pid 25] /bokeh_plot has 0 sessions with 0 unused"
"app[web.1]: 2016-07-25 21:54:43,870 [pid 25] /bokeh_plot has 0 sessions with 0 unused"
"app[web.1]: 2016-07-25 21:54:43,869 [pid 25] 0 clients connected"
"app[web.1]: 2016-07-25 21:54:58,870 [pid 25] /bokeh_plot has 0 sessions with 0 unused"
"app[web.1]: 2016-07-25 21:54:58,869 [pid 25] 0 clients connected"
"app[web.1]: 2016-07-25 21:55:13,862 [pid 25] /bokeh_plot has 0 sessions with 0 unused"
"app[web.1]: 2016-07-25 21:55:13,862 [pid 25] 0 clients connected"
"app[web.1]: 2016-07-25 21:55:28,864 [pid 25] /bokeh_plot has 0 sessions with 0 unused"
"app[web.1]: 2016-07-25 21:55:28,863 [pid 25] 0 clients connected"
"app[web.1]: 2016-07-25 21:55:43,864 [pid 25] 0 clients connected"
"app[web.1]: 2016-07-25 21:55:43,865 [pid 25] /bokeh_plot has 0 sessions with 0 unused"
"app[web.1]: 2016-07-25 21:55:58,862 [pid 25] /bokeh_plot has 0 sessions with 0 unused"
"app[web.1]: 2016-07-25 21:55:58,862 [pid 25] 0 clients connected"
"app[web.1]: 2016-07-25 21:56:13,876 [pid 25] 0 clients connected"
"app[web.1]: 2016-07-25 21:56:13,877 [pid 25] /bokeh_plot has 0 sessions with 0 unused"
"app[web.1]: 2016-07-25 21:56:28,864 [pid 25] 0 clients connected"
"app[web.1]: 2016-07-25 21:56:28,865 [pid 25] /bokeh_plot has 0 sessions with 0 unused"
"app[web.1]: 2016-07-25 21:56:43,875 [pid 25] 0 clients connected"
"app[web.1]: 2016-07-25 21:56:43,876 [pid 25] /bokeh_plot has 0 sessions with 0 unused"
"app[web.1]: 2016-07-25 21:56:58,872 [pid 25] 0 clients connected"
"app[web.1]: 2016-07-25 21:56:58,872 [pid 25] /bokeh_plot has 0 sessions with 0 unused"
"app[web.1]: 2016-07-25 21:57:13,863 [pid 25] /bokeh_plot has 0 sessions with 0 unused"
"app[web.1]: 2016-07-25 21:57:13,862 [pid 25] 0 clients connected"
"app[web.1]: 2016-07-25 21:57:28,872 [pid 25] 0 clients connected"
"app[web.1]: 2016-07-25 21:57:28,872 [pid 25] /bokeh_plot has 0 sessions with 0 unused"
"app[web.1]: 2016-07-25 21:57:43,868 [pid 25] /bokeh_plot has 0 sessions with 0 unused"
"app[web.1]: 2016-07-25 21:57:43,867 [pid 25] 0 clients connected"
"app[web.1]: 2016-07-25 21:57:58,863 [pid 25] 0 clients connected"
"app[web.1]: 2016-07-25 21:57:58,864 [pid 25] /bokeh_plot has 0 sessions with 0 unused"
"app[web.1]: 2016-07-25 21:58:13,879 [pid 25] 0 clients connected"
"app[web.1]: 2016-07-25 21:58:13,880 [pid 25] /bokeh_plot has 0 sessions with 0 unused"
"app[web.1]: 2016-07-25 21:58:28,873 [pid 25] 0 clients connected"
"app[web.1]: 2016-07-25 21:58:28,874 [pid 25] /bokeh_plot has 0 sessions with 0 unused"
"app[web.1]: 2016-07-25 21:58:43,872 [pid 25] /bokeh_plot has 0 sessions with 0 unused"
"app[web.1]: 2016-07-25 21:58:43,871 [pid 25] 0 clients connected"
"app[web.1]: 2016-07-25 21:58:49,718 WebSocket connection opened"
"app[web.1]: 2016-07-25 21:58:50,223 Sending pull-doc-reply from session 'Q4LGpWidke5G85sboBiRPMazzPfwOwTDMirNrDsTY3OL'"
"app[web.1]: 2016-07-25 21:58:50,177 Receiver created for Protocol('1.0')"
"app[web.1]: 2016-07-25 21:58:50,178 ServerHandler created for Protocol('1.0')"
"app[web.1]: 2016-07-25 21:58:50,178 ServerConnection created"
"heroku[router]: at=info method=GET path=""/"" host=bokehapp.herokuapp.com request_id=15fc6908-1031-4d88-86cc-8048d6195a6f fwd=""109.236.47.207"" dyno=web.1 connect=1ms service=1341ms status=200 bytes=1066"
"app[web.1]: 2016-07-25 21:58:58,864 [pid 25] /bokeh_plot has 1 sessions with 0 unused"
"app[web.1]: 2016-07-25 21:58:58,863 [pid 25] 1 clients connected"