Skip to content

Instantly share code, notes, and snippets.

@apotterri
Last active December 10, 2020 15:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save apotterri/5d4014f47e3dbd4c000ec4920835280a to your computer and use it in GitHub Desktop.
Save apotterri/5d4014f47e3dbd4c000ec4920835280a to your computer and use it in GitHub Desktop.
Querying AppMaps

This simple example demonstrates loading data generated by an AppMap recording client (for Ruby or Java) into the graph database neo4j.

A full description of this example is available at https://appland.org/code-gallery/querying-appmap-data

Interesting files in this gist:

  • Dockerfile creates an image to make it easier to record the example
  • load.js is the source for the example
  • package.json is a file used by NPM to describe the dependencies needed to run the example.
/node_modules
FROM ubuntu:18.04
RUN apt-get update && apt-get install -y curl gnupg
RUN curl -s -o- https://debian.neo4j.com/neotechnology.gpg.key | apt-key add -
RUN echo 'deb https://debian.neo4j.com stable latest' >> /etc/apt/sources.list.d/neo4j.list
RUN apt-get update && apt-get install -y neo4j=1:4.1.3
RUN curl -s -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.0/install.sh | bash
RUN echo 'export NVM_DIR="/root/.nvm"; [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"' > /root/.bash_profile
RUN bash -c 'source /root/.bash_profile; nvm install 14'
import url from 'url';
import fs from 'fs';
import minimist from 'minimist';
import path from 'path';
import neo4j from 'neo4j-driver';
import {CallTree} from '@applandinc/appmap-models';
// import buildCallTree from '../d3-appmap/src/model/callTree.js';
async function loadAppMap(session, fname) {
const data = fs.readFileSync(fname, 'utf8');
const appmap = JSON.parse(data);
const calls = new CallTree(appmap.events).rootNode.toArray();
console.log(`${path.basename(fname)}: ${calls.length} call(s)`);
const txc = session.beginTransaction();
try {
for (let call of calls) {
if (!call.input.defined_class) {
continue;
}
// Add the class (if it's not already there).
await txc.run('MERGE (f:Class {defined_class: $from})', {from: call.input.defined_class});
for (let child of call.children) {
let calleeClass = child.input.defined_class;
if (!calleeClass) {
if (child.input.sql_query) {
calleeClass = "SQL";
}
else if (child.input.http_server_request) {
calleeClass = "HTTP";
}
else {
throw new Error(`Unhandled callee type ${child.input}`);
}
}
// Show the "CALLS" relationship from the caller to the
// callee.
await txc.run('MERGE (f:Class {defined_class: $from}) MERGE(t:Class {defined_class: $to}) MERGE (f)-[:CALLS]->(t)', {
from: call.input.defined_class,
to: calleeClass
});
}
}
await txc.commit();
console.log(" ok");
} catch (err) {
txc.rollback();
console.error('rolled back');
console.error(err);
}
}
try {
const args = minimist(process.argv);
if (args._.length < 3) {
console.error('Usage: load.js files...');
process.exit(1);
}
const d = neo4j.driver(process.env.NEO4J_ADDRESS, neo4j.auth.basic(process.env.NEO4J_USERNAME, process.env.NEO4J_PASSWORD));
const session = d.session({database: process.env.NEO4J_DATABASE, defaultAccessMode: neo4j.session.WRITE});
for (let fname of args._.slice(2)) {
await loadAppMap(session, fname);
}
await session.close();
await d.close();
} catch(err) {
console.error(err);
}
{
"name": "appmap-query",
"requires": true,
"lockfileVersion": 1,
"dependencies": {
"@applandinc/appmap-models": {
"version": "github:applandinc/appmap-models#4fb15ef567f5f1cf7cf44ffc1bf2b4514be1668c",
"from": "github:applandinc/appmap-models"
},
"@babel/runtime": {
"version": "7.12.1",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.1.tgz",
"integrity": "sha512-J5AIf3vPj3UwXaAzb5j1xM4WAQDX3EMgemF8rjCP3SoW09LfRKAXQKt6CoVYl230P6iWdRcBbnLDDdnqWxZSCA==",
"requires": {
"regenerator-runtime": "^0.13.4"
}
},
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
},
"neo4j-driver": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/neo4j-driver/-/neo4j-driver-4.1.2.tgz",
"integrity": "sha512-hD13lwOuWi53IorgxL0/7HdBrR74VsQTMzZbp45a5e+vhciGtkqsfPH4oy33g+UOeORmxQ1y/lLnKpYe0Ll6xg==",
"requires": {
"@babel/runtime": "^7.5.5",
"rxjs": "^6.5.2",
"text-encoding-utf-8": "^1.0.2",
"uri-js": "^4.2.2"
}
},
"punycode": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
},
"regenerator-runtime": {
"version": "0.13.7",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz",
"integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew=="
},
"rxjs": {
"version": "6.6.3",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz",
"integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==",
"requires": {
"tslib": "^1.9.0"
}
},
"text-encoding-utf-8": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/text-encoding-utf-8/-/text-encoding-utf-8-1.0.2.tgz",
"integrity": "sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg=="
},
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
},
"uri-js": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz",
"integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==",
"requires": {
"punycode": "^2.1.0"
}
}
}
}
{
"name": "appmap-query",
"type": "module",
"dependencies": {
"@applandinc/appmap-models": "github:applandinc/appmap-models",
"minimist": "^1.2.5",
"neo4j-driver": "^4.1.2"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment