Skip to content

Instantly share code, notes, and snippets.

@benosteen
Last active August 30, 2018 21:17
Show Gist options
  • Save benosteen/7cbcb920d69dbc1bc480d254679e3ea0 to your computer and use it in GitHub Desktop.
Save benosteen/7cbcb920d69dbc1bc480d254679e3ea0 to your computer and use it in GitHub Desktop.
This injects a PyCallGraph middleware into the normal flow of the Arches project.

When any resource is requested with a "?graph=true" parameter, it engages the pycallgraph object to start tracking, stopping at the end of the processing and then exports a .png of the callgraph to the CALLGRAPH_DIR (or "ROOT_DIR/callgraphs" if CALLGRAPH_DIR is not set).

It adds the requisite dependencies to the Dockerfile: apt-get install graphviz pip install pycallgraph

diff --git a/Dockerfile b/Dockerfile
index 5ebdd83f5..b6bff2f63 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -32,7 +32,8 @@ RUN apt-get update -y &&\
zlib1g-dev \
clang \
make \
- pkg-config &&\
+ pkg-config \
+ graphviz &&\
curl -sL https://deb.nodesource.com/setup_6.x | bash - &&\
apt-get install nodejs &&\
npm install -g yarn &&\
@@ -47,7 +48,8 @@ RUN pip install virtualenv==15.1.0 &&\
. ENV/bin/activate &&\
pip install -U pip \
setuptools &&\
- pip install requests
+ pip install requests &&\
+ pip install pycallgraph
## Install Postgresql client
diff --git a/arches/app/utils/middleware.py b/arches/app/utils/middleware.py
index 893909515..9c68b3ebb 100644
--- a/arches/app/utils/middleware.py
+++ b/arches/app/utils/middleware.py
@@ -13,6 +13,38 @@ from jose import jwt, jws, JWSError
HTTP_HEADER_ENCODING = 'iso-8859-1'
+## PYCALLGRAPH ---------------------------------------------------------- ##
+from pycallgraph import Config
+from pycallgraph import PyCallGraph
+from pycallgraph.globbing_filter import GlobbingFilter
+from pycallgraph.output import GraphvizOutput
+import os
+
+class PyCallGraphMiddleware(MiddlewareMixin):
+
+ def process_view(self, request, callback, callback_args, callback_kwargs):
+ if 'graph' in request.GET:
+ config = Config()
+ CALLGRAPH_DIR = settings.CALLGRAPH_DIR or os.path.join(settings.ROOT_DIR, "callgraphs")
+ # you might want to remove the django exclusion from here to probe further
+ # Note that there is a lot of abstraction and the callgraph for it is huge
+ config.trace_filter = GlobbingFilter(exclude=['django.*', 'pycallgraph.*', 'PyCallGraph.*'])
+ path_called = request.get_full_path().replace("/?", "_withparams_").replace("/", "_")
+ if not os.path.isdir(CALLGRAPH_DIR):
+ os.mkdir(CALLGRAPH_DIR)
+ graphviz = GraphvizOutput(output_file=os.path.join(CALLGRAPH_DIR, "{0}-{1}.png".format(path_called, time.time())))
+ pycallgraph = PyCallGraph(output=graphviz, config=config)
+ pycallgraph.start()
+
+ self.pycallgraph = pycallgraph
+
+ def process_response(self, request, response):
+ if 'graph' in request.GET:
+ self.pycallgraph.done()
+
+ return response
+## PYCALLGRAPH END------------------------------------------------------- ##
+
class SetAnonymousUser(MiddlewareMixin):
def process_request(self, request):
# for OAuth authentication to work, we can't automatically assign
diff --git a/arches/settings.py b/arches/settings.py
index 2d4b47292..6a0d11521 100644
--- a/arches/settings.py
+++ b/arches/settings.py
@@ -281,6 +281,7 @@ MIDDLEWARE = [
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'arches.app.utils.middleware.SetAnonymousUser',
+ 'arches.app.utils.middleware.PyCallGraphMiddleware',
]
ROOT_URLCONF = 'arches.urls'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment