diff --git a/drakcore/drakcore/frontend/package-lock.json b/drakcore/drakcore/frontend/package-lock.json
index 8714382be..e43fd686b 100644
--- a/drakcore/drakcore/frontend/package-lock.json
+++ b/drakcore/drakcore/frontend/package-lock.json
@@ -8463,6 +8463,11 @@
         "p-is-promise": "^2.0.0"
       }
     },
+    "memoize-one": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.1.1.tgz",
+      "integrity": "sha512-HKeeBpWvqiVJD57ZUAsJNm71eHTykffzcLZVYWiVfQeI1rJtuEaS7hQiEpWfVVk18donPwJEcFKIkCmPJNOhHA=="
+    },
     "memory-fs": {
       "version": "0.4.1",
       "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
@@ -11230,6 +11235,25 @@
         "warning": "^3.0.0"
       }
     },
+    "react-virtualized-auto-sizer": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.2.tgz",
+      "integrity": "sha512-MYXhTY1BZpdJFjUovvYHVBmkq79szK/k7V3MO+36gJkWGkrXKtyr4vCPtpphaTLRAdDNoYEYFZWE8LjN+PIHNg=="
+    },
+    "react-window": {
+      "version": "1.8.5",
+      "resolved": "https://registry.npmjs.org/react-window/-/react-window-1.8.5.tgz",
+      "integrity": "sha512-HeTwlNa37AFa8MDZFZOKcNEkuF2YflA0hpGPiTT9vR7OawEt+GZbfM6wqkBahD3D3pUjIabQYzsnY/BSJbgq6Q==",
+      "requires": {
+        "@babel/runtime": "^7.0.0",
+        "memoize-one": ">=3.1.1 <6"
+      }
+    },
+    "react-window-infinite-loader": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/react-window-infinite-loader/-/react-window-infinite-loader-1.0.5.tgz",
+      "integrity": "sha512-IcPIq8lADK3zsAcqoLqQGyduicqR6jWkiK2VUX5sKSI9X/rou6OWlOEexnGyujdNTG7hSG8OVBFEhLSDs4qrxg=="
+    },
     "read-pkg": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
diff --git a/drakcore/drakcore/frontend/package.json b/drakcore/drakcore/frontend/package.json
index 78fc0f991..8110f0d7c 100644
--- a/drakcore/drakcore/frontend/package.json
+++ b/drakcore/drakcore/frontend/package.json
@@ -14,7 +14,10 @@
     "react-d3-tree": "^1.16.1",
     "react-dom": "^16.13.1",
     "react-router-dom": "^5.2.0",
-    "react-scripts": "^3.4.1"
+    "react-scripts": "^3.4.1",
+    "react-virtualized-auto-sizer": "^1.0.2",
+    "react-window": "^1.8.5",
+    "react-window-infinite-loader": "^1.0.5"
   },
   "scripts": {
     "start": "react-scripts start",
diff --git a/drakcore/drakcore/frontend/src/AnalysisApicall.js b/drakcore/drakcore/frontend/src/AnalysisApicall.js
index b067f4104..55e4325d9 100644
--- a/drakcore/drakcore/frontend/src/AnalysisApicall.js
+++ b/drakcore/drakcore/frontend/src/AnalysisApicall.js
@@ -1,8 +1,10 @@
 import React from "react";
 import { Component } from "react";
+import { FixedSizeList as List } from "react-window";
+import OptionPicker from "./OptionPicker";
+import AutoSizer from "react-virtualized-auto-sizer";
 import "./App.css";
 import api from "./api";
-import OptionPicker from "./OptionPicker";
 
 class AnalysisApicall extends Component {
   constructor(props) {
@@ -13,9 +15,13 @@ class AnalysisApicall extends Component {
     this.state = {
       calls: null,
       processList: [],
+      filter: '',
+      filteredResults: [],
     };
 
     this.pidChanged = this.pidChanged.bind(this);
+    this.filterChanged = this.filterChanged.bind(this);
+    this.computeFiltered = this.computeFiltered.bind(this);
   }
 
   async pidChanged(new_pid) {
@@ -24,11 +30,33 @@ class AnalysisApicall extends Component {
       const res = await api.getApiCalls(this.analysis_id, new_pid);
       const calls = res.data.split("\n").map(JSON.parse);
       this.setState({ calls });
+      this.computeFiltered(this.state.filter);
     } catch (e) {
       this.setState({ calls: [] });
     }
   }
 
+  computeFiltered(filter) {
+    if (filter === '') {
+      this.setState({ filteredResults: this.state.calls });
+      return;
+    }
+    try {
+      let regex = new RegExp(filter, 'gi');
+      this.setState({
+        filteredResults: this.state.calls.filter((elem) =>
+          regex.test(elem.method)
+        ),
+      });
+    } catch  { }
+  }
+
+  filterChanged(event) {
+    const newFilter = event.target.value;
+    this.setState({filter: newFilter});
+    this.computeFiltered(newFilter);
+  }
+
   async componentDidMount() {
     const analysis = this.props.match.params.analysis;
     const process_tree = await api.getProcessTree(analysis);
@@ -37,14 +65,11 @@ class AnalysisApicall extends Component {
       let result = [];
 
       process_tree.forEach((proc) => {
-        result.push({
-          key: proc.pid,
-          value: `${proc.pid} – ${proc.procname || "unnamed process"}`,
-        });
+        result.push({ key: proc.pid, value: `${proc.pid} – ${proc.procname || "unnamed process"}` });
         result.push(...treeFlatten(proc.children));
       });
 
-      result.sort((a, b) => a.pid - b.pid);
+      result.sort((a, b) => a.key - b.key);
 
       return result;
     }
@@ -82,33 +107,62 @@ class AnalysisApicall extends Component {
         </div>
       );
     } else {
-      let tableContent = this.state.calls.map((entry, i) => (
-        <tr key={i}>
-          <td>{entry.timestamp}</td>
-          <td>
-            <code>{entry.method}</code>
-          </td>
-          <td>
-            {entry.arguments.map((arg, i) => (
-              <div key={i} className="badge-outline-primary badge mr-1">
-                {arg}
-              </div>
-            ))}
-          </td>
-        </tr>
-      ));
-      content = (
-        <table className="table table-centered apicallTable">
-          <thead>
-            <tr>
-              <th>Timestamp</th>
-              <th>Method</th>
-              <th>Arguments</th>
-            </tr>
-          </thead>
-          <tbody>{tableContent}</tbody>
-        </table>
+      // let tableContent = this.state.calls.map((entry, i) => (
+      //   <tr key={i}>
+      //     <td>{entry.timestamp}</td>
+      //     <td>
+      //       <code>{entry.method}</code>
+      //     </td>
+      //     <td>
+      //       {entry.arguments.map((arg, i) => (
+      //         <div key={i} className="badge-outline-primary badge mr-1">
+      //           {arg}
+      //         </div>
+      //       ))}
+      //     </td>
+      //   </tr>
+      // ));
+
+      const Row = ({ data, index, style }) => {
+        const entry = data[index];
+        const args = entry.arguments.join(", ");
+        return (
+          <div style={style} className="d-flex flex-row align-content-stretch">
+            <div className="p-1 d-none d-md-block">{entry.timestamp}</div>
+            <div className="p-1">
+              <code>
+                {entry.method}({args}) = ?
+              </code>
+            </div>
+          </div>
+        );
+      };
+
+      let tableContent = (
+        <List
+          itemData={this.state.filteredResults}
+          height={600}
+          itemCount={this.state.filteredResults ? this.state.filteredResults.length : 0}
+          itemSize={28}
+        >
+          {Row}
+        </List>
       );
+
+      content = tableContent;
+
+      // content = (
+      //   <table className="table table-centered apicallTable">
+      //     <thead>
+      //       <tr>
+      //         <th>Timestamp</th>
+      //         <th>Method</th>
+      //         <th>Arguments</th>
+      //       </tr>
+      //     </thead>
+      //     <tbody>{tableContent}</tbody>
+      //   </table>
+      // );
     }
 
     return (
@@ -119,12 +173,22 @@ class AnalysisApicall extends Component {
 
         <div className="card tilebox-one">
           <div className="card-body">
-            <OptionPicker
-              defaultSelection={url_pid}
-              data={this.state.processList}
-              onChange={this.pidChanged}
-              className="mb-1"
-            />
+            <div className="row mb-2">
+              <div className="col-9">
+                <OptionPicker
+                  defaultSelection={url_pid}
+                  data={this.state.processList}
+                  onChange={(pid) => this.pidChanged(parseInt(pid))}
+                />
+              </div>
+              <input
+                type="text"
+                className="form-control col-3"
+                placeholder="Search API calls..."
+                value={this.state.filter}
+                onChange={this.filterChanged}
+              />
+            </div>
             {content}
           </div>
         </div>
diff --git a/drakcore/drakcore/frontend/src/AnalysisMain.js b/drakcore/drakcore/frontend/src/AnalysisMain.js
index 361d6dc5c..2d2c33d09 100644
--- a/drakcore/drakcore/frontend/src/AnalysisMain.js
+++ b/drakcore/drakcore/frontend/src/AnalysisMain.js
@@ -163,7 +163,7 @@ class AnalysisMain extends Component {
     try {
       const res_graph = await api.getGraph(this.analysisID);
       if (res_graph.data) {
-        this.setState({ graphState: "loaded", graph: res_graph.data });
+        //this.setState({ graphState: "loaded", graph: res_graph.data });
       } else {
         this.setState({ graphState: "missing" });
       }
diff --git a/drakcore/drakcore/frontend/src/index.js b/drakcore/drakcore/frontend/src/index.js
index c24e9d807..a85313303 100644
--- a/drakcore/drakcore/frontend/src/index.js
+++ b/drakcore/drakcore/frontend/src/index.js
@@ -2,4 +2,5 @@ import React from "react";
 import ReactDOM from "react-dom";
 import App from "./App";
 
+
 ReactDOM.render(<App />, document.getElementById("root"));
diff --git a/drakrun/drakrun/main.py b/drakrun/drakrun/main.py
index 102779222..e5f348ea1 100644
--- a/drakrun/drakrun/main.py
+++ b/drakrun/drakrun/main.py
@@ -11,6 +11,7 @@
 import zipfile
 import json
 import re
+import functools
 from typing import Optional, List
 from stat import S_ISREG, ST_CTIME, ST_MODE, ST_SIZE
 
@@ -78,6 +79,47 @@ def start_dnsmasq(vm_id: int, dns_server: str) -> Optional[subprocess.Popen]:
     ])
 
 
+def local_logs(method):
+    class LocalBuffer(logging.Handler):
+        FIELDS = (
+            "levelname",
+            "message",
+            "created",
+        )
+
+        def __init__(self):
+            super().__init__()
+            self.buffer = []
+
+        def emit(self, record):
+            entry = {k: v for (k, v) in record.__dict__.items() if k in self.FIELDS}
+            self.buffer.append(entry)
+
+    @functools.wraps(method)
+    def wrapper(self: Karton, *args, **kwargs):
+        handler = LocalBuffer()
+        try:
+            # Register new log handler
+            self.log.addHandler(handler)
+            method(self, *args, **kwargs)
+        except Exception:
+            self.log.exception("Analysis failed")
+        finally:
+            # Unregister local handler
+            self.log.removeHandler(handler)
+            try:
+                res = LocalResource("analysis_log.json",
+                                    json.dumps(handler.buffer),
+                                    bucket="drakrun")
+                task_uid = self.current_task.payload.get('override_uid') or self.current_task.uid
+                res._uid = f"{task_uid}/{res.name}"
+                res.upload(self.minio)
+            except Exception:
+                self.log.exception("Failed to upload analysis logs")
+
+    return wrapper
+
+
 class DrakrunKarton(Karton):
     identity = "karton.drakrun-prod"
     filters = [
@@ -269,6 +311,7 @@ def get_profile_list() -> List[str]:
 
         return out
 
+    @local_logs
     def process(self):
         sample = self.current_task.get_resource("sample")
         self.log.info("hostname: {}".format(socket.gethostname()))