diff --git a/.editorconfig b/.editorconfig
index 83ff3366d4e2cc77d1184c2837bcec54d68724dc..b2ab82eb6af18ff5e2265f28c409f85d281f565b 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -16,6 +16,7 @@ indent_style = tab
 
 [*.clj]
 indent_size = 2
+max_line_length = 120
 
 [*.css]
 indent_size = 2
diff --git a/frontend/src/metabase/admin/tasks/containers/Logs.jsx b/frontend/src/metabase/admin/tasks/containers/Logs.jsx
index 9277d99d3c44e7670ecc851f15ca3b0bb8656bd9..247779c9ad320b56272c52825dd0e55417c3b006 100644
--- a/frontend/src/metabase/admin/tasks/containers/Logs.jsx
+++ b/frontend/src/metabase/admin/tasks/containers/Logs.jsx
@@ -9,7 +9,10 @@ import reactAnsiStyle from "react-ansi-style";
 import "react-ansi-style/inject-css";
 
 import _ from "underscore";
+import moment from "moment";
+import { t } from "ttag";
 
+import Select, { Option } from "metabase/components/Select";
 import { addCSSRule } from "metabase/lib/dom";
 import colors from "metabase/lib/colors";
 
@@ -27,6 +30,22 @@ const ANSI_COLORS = {
 for (const [name, color] of Object.entries(ANSI_COLORS)) {
   addCSSRule(`.react-ansi-style-${name}`, `color: ${color} !important`);
 }
+const MAX_LOGS = 50000;
+
+function logEventKey(ev) {
+  return `${ev.timestamp}, ${ev.process_uuid}, ${ev.fqns}, ${ev.msg}`;
+}
+
+function mergeLogs(...logArrays) {
+  return _.chain(logArrays)
+    .flatten(true)
+    .sortBy(ev => ev.msg)
+    .sortBy(ev => ev.process_uuid)
+    .sortBy(ev => ev.timestamp)
+    .uniq(true, logEventKey)
+    .last(MAX_LOGS)
+    .value();
+}
 
 export default class Logs extends Component {
   constructor() {
@@ -34,6 +53,7 @@ export default class Logs extends Component {
     this.state = {
       logs: [],
       scrollToBottom: true,
+      selectedProcessUUID: "ALL",
     };
 
     this._onScroll = () => {
@@ -52,7 +72,7 @@ export default class Logs extends Component {
 
   async fetchLogs() {
     const logs = await UtilApi.logs();
-    this.setState({ logs: logs.reverse() });
+    this.setState({ logs: mergeLogs(this.state.logs, logs.reverse()) });
   }
 
   componentWillMount() {
@@ -80,23 +100,69 @@ export default class Logs extends Component {
   }
 
   render() {
-    const { logs } = this.state;
-    return (
-      <LoadingAndErrorWrapper loading={!logs || logs.length === 0}>
-        {() => (
-          <div
-            className="rounded bordered bg-light"
-            style={{
-              fontFamily: '"Lucida Console", Monaco, monospace',
-              fontSize: "14px",
-              whiteSpace: "pre-line",
-              padding: "1em",
-            }}
+    const { logs, selectedProcessUUID } = this.state;
+    const filteredLogs = logs.filter(
+      ev =>
+        !selectedProcessUUID ||
+        selectedProcessUUID === "ALL" ||
+        ev.process_uuid === selectedProcessUUID,
+    );
+    const processUUIDs = _.uniq(
+      logs.map(ev => ev.process_uuid).filter(Boolean),
+    ).sort();
+    const renderedLogs = filteredLogs.map(ev => {
+      const timestamp = moment(ev.timestamp).format();
+      const uuid = ev.process_uuid || "---";
+      return `[${uuid}] ${timestamp} ${ev.level} ${ev.fqns} ${ev.msg}`;
+    });
+
+    let processUUIDSelect = null;
+    if (processUUIDs.length > 1) {
+      processUUIDSelect = (
+        <div className="pb1">
+          <label>{t`Select Metabase process:`}</label>
+          <Select
+            defaultValue="ALL"
+            value={this.state.selectedProcessUUID}
+            onChange={e =>
+              this.setState({ selectedProcessUUID: e.target.value })
+            }
+            className="inline-block ml1"
+            width={400}
           >
-            {reactAnsiStyle(React, logs.join("\n"))}
-          </div>
-        )}
-      </LoadingAndErrorWrapper>
+            <Option value="ALL" key="ALL">{t`All Metabase processes`}</Option>
+            {processUUIDs.map(uuid => (
+              <Option key={uuid} value={uuid}>
+                <code>{uuid}</code>
+              </Option>
+            ))}
+          </Select>
+        </div>
+      );
+    }
+
+    return (
+      <div>
+        {processUUIDSelect}
+
+        <LoadingAndErrorWrapper
+          loading={!filteredLogs || filteredLogs.length === 0}
+        >
+          {() => (
+            <div
+              className="rounded bordered bg-light"
+              style={{
+                fontFamily: '"Lucida Console", Monaco, monospace',
+                fontSize: "14px",
+                whiteSpace: "pre-line",
+                padding: "1em",
+              }}
+            >
+              {reactAnsiStyle(React, renderedLogs.join("\n"))}
+            </div>
+          )}
+        </LoadingAndErrorWrapper>
+      </div>
     );
   }
 }
diff --git a/frontend/src/metabase/components/Select.jsx b/frontend/src/metabase/components/Select.jsx
index 8c25a4cea80ea23f0abcd6efc86287b2772c0ef0..6cd29e5a1df7d627b47673d9d1c3253fccf16330 100644
--- a/frontend/src/metabase/components/Select.jsx
+++ b/frontend/src/metabase/components/Select.jsx
@@ -97,7 +97,7 @@ class BrowserSelect extends Component {
       multiple,
     } = this.props;
 
-    let children = this.props.children;
+    let children = _.flatten(this.props.children);
 
     let selectedNames = children
       .filter(child => this.isSelected(child.props.value))
diff --git a/src/metabase/config.clj b/src/metabase/config.clj
index 79680006622fc364d49ad3901d882487f1ac3774..09f33eff7e9a8644504e933052dccc22ac54b912 100644
--- a/src/metabase/config.clj
+++ b/src/metabase/config.clj
@@ -5,7 +5,8 @@
             [clojure.string :as str]
             [environ.core :as environ]
             [metabase.plugins.classloader :as classloader])
-  (:import clojure.lang.Keyword))
+  (:import clojure.lang.Keyword
+           java.util.UUID))
 
 (def ^Boolean is-windows?
   "Are we running on a Windows machine?"
@@ -101,6 +102,12 @@
    Looks something like `Metabase v0.25.0.RC1`."
   (str "Metabase " (mb-version-info :tag)))
 
+(defonce ^{:doc "This UUID is randomly-generated upon launch and used to identify this specific Metabase instance during
+                this specifc run. Restarting the server will change this UUID, and each server in a horizontal cluster
+                will have its own ID, making this different from the `site-uuid` Setting."}
+  local-process-uuid
+  (str (UUID/randomUUID)))
+
 
 ;; This only affects dev:
 ;;
diff --git a/src/metabase/logger.clj b/src/metabase/logger.clj
index 70cb740d4f3dcf61fa112c84c5e35b1be4ea2aa7..8accfc1d0658bb957e10c5cfae893d803b677c2a 100644
--- a/src/metabase/logger.clj
+++ b/src/metabase/logger.clj
@@ -2,9 +2,8 @@
   (:require [amalloy.ring-buffer :refer [ring-buffer]]
             [clj-time
              [coerce :as coerce]
-             [core :as t]
              [format :as time]]
-            [clojure.string :as str])
+            [metabase.config :refer [local-process-uuid]])
   (:import [org.apache.log4j Appender AppenderSkeleton Logger]
            org.apache.log4j.spi.LoggingEvent))
 
@@ -17,26 +16,19 @@
   []
   (reverse (seq @messages*)))
 
-(defonce ^:private formatter (time/formatter "MMM dd HH:mm:ss" (t/default-time-zone)))
-
-(defn- event->log-string [^LoggingEvent event]
-  ;; for messages that include an Exception, include the string representation of it (i.e., its stacktrace)
-  ;; separated by newlines
-  (str/join
-   "\n"
-   (cons
-    (let [ts    (time/unparse formatter (coerce/from-long (.getTimeStamp event)))
-          level (.getLevel event)
-          fqns  (.getLoggerName event)
-          msg   (.getMessage event)]
-      (format "%s \033[1m%s %s\033[0m :: %s" ts level fqns msg))
-    (seq (.getThrowableStrRep event)))))
-
+(defn- event->log-data [^LoggingEvent event]
+  {:timestamp    (time/unparse (time/formatter :date-time)
+                               (coerce/from-long (.getTimeStamp event)))
+   :level        (.getLevel event)
+   :fqns         (.getLoggerName event)
+   :msg          (.getMessage event)
+   :exception    (.getThrowableStrRep event)
+   :process_uuid local-process-uuid})
 
 (defn- metabase-appender ^Appender []
   (proxy [AppenderSkeleton] []
     (append [event]
-      (swap! messages* conj (event->log-string event))
+      (swap! messages* conj (event->log-data event))
       nil)
     (close []
       nil)
diff --git a/src/metabase/metabot/instance.clj b/src/metabase/metabot/instance.clj
index 0b802a4e9de208f459a57b7ca017e307a5eaa486..57bf42fa8367b98237819a756f02d63f5c46ebd8 100644
--- a/src/metabase/metabot/instance.clj
+++ b/src/metabase/metabot/instance.clj
@@ -14,26 +14,22 @@
 
   How do we uniquiely identify each instance?
 
-  `local-process-uuid` is randomly-generated upon launch and used to identify this specific Metabase instance during
-  this specifc run. Restarting the server will change this UUID, and each server in a hortizontal cluster will have
-  its own ID, making this different from the `site-uuid` Setting. The local process UUID is used to differentiate
-  different horizontally clustered MB instances so we can determine which of them will handle MetaBot duties.
-
-  TODO - if we ever want to use this elsewhere, we need to move it to `metabase.config` or somewhere else central like
-  that."
+  `metabase.public-settings/local-process-uuid` is randomly-generated upon launch and used to identify this specific
+  Metabase instance during this specifc run. Restarting the server will change this UUID, and each server in a
+  hortizontal cluster will have its own ID, making this different from the `site-uuid` Setting. The local process UUID
+  is used to differentiate different horizontally clustered MB instances so we can determine which of them will handle
+  MetaBot duties."
   (:require [clojure.tools.logging :as log]
             [honeysql.core :as hsql]
+            [metabase
+             [config :refer [local-process-uuid]]
+             [util :as u]]
             [metabase.models.setting :as setting :refer [defsetting]]
-            [metabase.util :as u]
             [metabase.util
              [date :as du]
              [i18n :refer [trs]]]
             [toucan.db :as db])
-  (:import java.sql.Timestamp
-           java.util.UUID))
-
-(defonce ^:private local-process-uuid
-  (str (UUID/randomUUID)))
+  (:import java.sql.Timestamp))
 
 (defsetting ^:private metabot-instance-uuid
   "UUID of the active MetaBot instance (the Metabase process currently handling MetaBot duties.)"
diff --git a/src/metabase/pulse/render/table.clj b/src/metabase/pulse/render/table.clj
index 99ab03aa10974c7c8023d01f80bfba03a85ca511..aa33bf6c036262a27465b4ef86488957f3849396 100644
--- a/src/metabase/pulse/render/table.clj
+++ b/src/metabase/pulse/render/table.clj
@@ -4,8 +4,12 @@
             [metabase.pulse.render
              [color :as color]
              [style :as style]])
-  (:import jdk.nashorn.api.scripting.JSObject
-           metabase.pulse.render.common.NumericWrapper))
+  (:import jdk.nashorn.api.scripting.JSObject))
+
+;; Our 'helpful' NS declaration linter will complain that common is unused. But we need to require it so
+;; NumericWrapper exists in the first place.
+(require 'metabase.pulse.render.common)
+(import 'metabase.pulse.render.common.NumericWrapper)
 
 (defn- bar-th-style []
   (merge (style/font-style) {:font-size :14.22px