logster 2.5.1 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +2 -0
- data/CHANGELOG.md +9 -0
- data/README.md +15 -1
- data/Rakefile +1 -0
- data/assets/javascript/client-app.js +204 -168
- data/assets/javascript/vendor.js +5132 -5833
- data/assets/stylesheets/client-app.css +1 -1
- data/client-app/.eslintrc.js +17 -5
- data/client-app/.travis.yml +4 -3
- data/client-app/app/app.js +5 -7
- data/client-app/app/components/actions-menu.js +24 -17
- data/client-app/app/components/back-trace.js +148 -0
- data/client-app/app/components/env-tab.js +16 -12
- data/client-app/app/components/message-info.js +84 -7
- data/client-app/app/components/message-row.js +13 -15
- data/client-app/app/components/panel-resizer.js +63 -45
- data/client-app/app/components/patterns-list.js +6 -6
- data/client-app/app/components/update-time.js +13 -13
- data/client-app/app/controllers/index.js +4 -2
- data/client-app/app/index.html +1 -1
- data/client-app/app/initializers/app-init.js +1 -1
- data/client-app/app/lib/decorators.js +11 -0
- data/client-app/app/lib/preload.js +14 -3
- data/client-app/app/lib/utilities.js +63 -36
- data/client-app/app/models/group.js +6 -1
- data/client-app/app/models/message-collection.js +9 -7
- data/client-app/app/models/message.js +25 -20
- data/client-app/app/router.js +4 -6
- data/client-app/app/styles/app.css +18 -4
- data/client-app/app/templates/components/actions-menu.hbs +6 -2
- data/client-app/app/templates/components/back-trace.hbs +8 -0
- data/client-app/app/templates/components/message-info.hbs +7 -2
- data/client-app/app/templates/index.hbs +4 -1
- data/client-app/config/environment.js +1 -1
- data/client-app/config/optional-features.json +4 -1
- data/client-app/ember-cli-build.js +2 -3
- data/client-app/package-lock.json +9712 -2884
- data/client-app/package.json +25 -22
- data/client-app/preload-json-manager.rb +62 -0
- data/client-app/testem.js +0 -1
- data/client-app/tests/index.html +1 -1
- data/client-app/tests/integration/components/back-trace-test.js +109 -0
- data/client-app/tests/integration/components/message-info-test.js +4 -3
- data/client-app/tests/integration/components/patterns-list-test.js +7 -2
- data/lib/logster.rb +1 -0
- data/lib/logster/base_store.rb +16 -9
- data/lib/logster/configuration.rb +12 -2
- data/lib/logster/defer_logger.rb +1 -1
- data/lib/logster/logger.rb +12 -0
- data/lib/logster/message.rb +89 -30
- data/lib/logster/middleware/viewer.rb +44 -8
- data/lib/logster/redis_store.rb +69 -51
- data/lib/logster/suppression_pattern.rb +1 -1
- data/lib/logster/version.rb +1 -1
- data/logster.gemspec +1 -1
- data/test/logster/middleware/test_viewer.rb +100 -0
- data/test/logster/test_base_store.rb +16 -0
- data/test/logster/test_defer_logger.rb +1 -1
- data/test/logster/test_message.rb +142 -54
- data/test/logster/test_redis_store.rb +99 -39
- metadata +11 -6
@@ -21,25 +21,23 @@ export default Component.extend({
|
|
21
21
|
return;
|
22
22
|
}
|
23
23
|
|
24
|
-
const
|
24
|
+
const topPanel = document.getElementById("top-panel");
|
25
|
+
if (!topPanel) return;
|
25
26
|
|
26
|
-
const
|
27
|
-
|
28
|
-
const scrollHeight = $topPanel[0].scrollHeight;
|
29
|
-
|
30
|
-
STICK_TO_BOTTOM = scrollHeight - 20 < height + scrollTop;
|
27
|
+
const height = parseFloat(getComputedStyle(topPanel).height);
|
28
|
+
STICK_TO_BOTTOM = topPanel.scrollHeight - 20 < height + topPanel.scrollTop;
|
31
29
|
CHECKED_BOTTOM = true;
|
32
30
|
},
|
33
31
|
|
34
32
|
didInsertElement() {
|
35
|
-
const
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
}
|
33
|
+
const topPanel = document.getElementById("top-panel");
|
34
|
+
if (!topPanel) return;
|
35
|
+
|
36
|
+
CHECKED_BOTTOM = false;
|
37
|
+
if (STICK_TO_BOTTOM) {
|
38
|
+
STICK_TO_BOTTOM = false;
|
39
|
+
topPanel.scrollTop =
|
40
|
+
topPanel.scrollHeight - parseFloat(getComputedStyle(topPanel).height);
|
41
|
+
}
|
44
42
|
}
|
45
43
|
});
|
@@ -1,74 +1,92 @@
|
|
1
1
|
import Component from "@ember/component";
|
2
|
+
import { scheduleOnce, throttle } from "@ember/runloop";
|
3
|
+
import { bound } from "client-app/lib/decorators";
|
4
|
+
|
2
5
|
const MOVE_EVENTS = ["touchmove", "mousemove"];
|
3
6
|
const UP_EVENTS = ["touchend", "mouseup"];
|
4
7
|
const DOWN_EVENTS = ["touchstart", "mousedown"];
|
5
8
|
|
6
9
|
export default Component.extend({
|
10
|
+
resizing: false,
|
7
11
|
classNames: ["divider"],
|
8
12
|
|
9
|
-
divideView(fromTop
|
10
|
-
const
|
11
|
-
const
|
12
|
-
const fromBottom = $win.height() - fromTop;
|
13
|
+
divideView(fromTop) {
|
14
|
+
const height = window.innerHeight;
|
15
|
+
const fromBottom = height - fromTop;
|
13
16
|
|
14
17
|
if (fromTop < 100 || fromTop + 170 > height) {
|
15
18
|
return;
|
16
19
|
}
|
17
20
|
|
18
|
-
this.divider.
|
21
|
+
this.divider.style.bottom = `${fromBottom - 5}px`;
|
19
22
|
this.events.trigger("panelResized", fromBottom);
|
20
23
|
},
|
21
24
|
|
22
|
-
|
23
|
-
|
24
|
-
this.
|
25
|
+
@bound
|
26
|
+
performDrag(e) {
|
27
|
+
throttle(this, this.throttledPerformDrag, e, 25);
|
28
|
+
},
|
25
29
|
|
26
|
-
|
27
|
-
|
30
|
+
throttledPerformDrag(e) {
|
31
|
+
if (this.resizing) {
|
32
|
+
this.divideView(
|
33
|
+
e.clientY || (e.touches && e.touches[0] && e.touches[0].clientY)
|
34
|
+
);
|
35
|
+
}
|
36
|
+
},
|
28
37
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
};
|
38
|
+
@bound
|
39
|
+
endDrag(/* e */) {
|
40
|
+
const overlay = document.getElementById("overlay");
|
41
|
+
if (overlay) {
|
42
|
+
overlay.parentElement.removeChild(overlay);
|
43
|
+
}
|
44
|
+
this.set("resizing", false);
|
37
45
|
|
38
|
-
|
39
|
-
|
40
|
-
|
46
|
+
if (localStorage) {
|
47
|
+
localStorage.logster_divider_bottom = parseInt(
|
48
|
+
this.divider.style.bottom,
|
49
|
+
10
|
50
|
+
);
|
51
|
+
}
|
41
52
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
}
|
53
|
+
MOVE_EVENTS.forEach(name =>
|
54
|
+
document.removeEventListener(name, this.performDrag)
|
55
|
+
);
|
56
|
+
UP_EVENTS.forEach(name => document.removeEventListener(name, this.endDrag));
|
57
|
+
},
|
48
58
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
59
|
+
@bound
|
60
|
+
dividerClickHandler(e) {
|
61
|
+
e.preventDefault(); // for disabling pull-down-to-refresh on mobile
|
62
|
+
const overlay = document.createElement("DIV");
|
63
|
+
overlay.id = "overlay";
|
64
|
+
document.body.appendChild(overlay);
|
65
|
+
this.set("resizing", true);
|
66
|
+
MOVE_EVENTS.forEach(name =>
|
67
|
+
document.addEventListener(name, this.performDrag)
|
68
|
+
);
|
69
|
+
UP_EVENTS.forEach(name => document.addEventListener(name, this.endDrag));
|
70
|
+
},
|
53
71
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
.on(MOVE_EVENTS.join(" "), _.throttle(performDrag, 25))
|
60
|
-
.on(UP_EVENTS.join(" "), endDrag);
|
72
|
+
didInsertElement() {
|
73
|
+
// inspired by http://plugins.jquery.com/misc/textarea.js
|
74
|
+
this.set("divider", document.querySelector(".divider"));
|
75
|
+
DOWN_EVENTS.forEach(name => {
|
76
|
+
this.divider.addEventListener(name, this.dividerClickHandler);
|
61
77
|
});
|
78
|
+
scheduleOnce("afterRender", this, "initialDivideView");
|
79
|
+
},
|
62
80
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
this.divideView(fromTop, $win);
|
68
|
-
});
|
81
|
+
initialDivideView() {
|
82
|
+
const amount = (localStorage && localStorage.logster_divider_bottom) || 300;
|
83
|
+
const fromTop = window.innerHeight - parseInt(amount, 10);
|
84
|
+
this.divideView(fromTop);
|
69
85
|
},
|
70
86
|
|
71
87
|
willDestroyElement() {
|
72
|
-
|
88
|
+
DOWN_EVENTS.forEach(name =>
|
89
|
+
this.divider.removeEventListener(name, this.dividerClickHandler)
|
90
|
+
);
|
73
91
|
}
|
74
92
|
});
|
@@ -28,7 +28,7 @@ export default Component.extend({
|
|
28
28
|
return ajax(`/patterns/${this.get("key")}.json`, { method, data });
|
29
29
|
},
|
30
30
|
|
31
|
-
|
31
|
+
finallyBlock(pattern) {
|
32
32
|
pattern.set("saving", false);
|
33
33
|
},
|
34
34
|
|
@@ -70,7 +70,7 @@ export default Component.extend({
|
|
70
70
|
pattern.destroy();
|
71
71
|
})
|
72
72
|
.catch(response => this.catchBlock(pattern, response))
|
73
|
-
.
|
73
|
+
.finally(() => this.finallyBlock(pattern));
|
74
74
|
}
|
75
75
|
},
|
76
76
|
|
@@ -80,8 +80,8 @@ export default Component.extend({
|
|
80
80
|
if (pattern.get("isNew")) {
|
81
81
|
promise = this.makeAPICall({
|
82
82
|
method: "POST",
|
83
|
-
pattern: pattern.
|
84
|
-
retroactive: pattern.retroactive
|
83
|
+
pattern: pattern.valueBuffer,
|
84
|
+
retroactive: !!pattern.retroactive
|
85
85
|
}).then(response => {
|
86
86
|
pattern.updateValue(response.pattern);
|
87
87
|
pattern.set("isNew", false);
|
@@ -102,7 +102,7 @@ export default Component.extend({
|
|
102
102
|
.catch(response => {
|
103
103
|
this.catchBlock(pattern, response);
|
104
104
|
})
|
105
|
-
.
|
105
|
+
.finally(() => this.finallyBlock(pattern));
|
106
106
|
},
|
107
107
|
|
108
108
|
resetCount(pattern) {
|
@@ -115,7 +115,7 @@ export default Component.extend({
|
|
115
115
|
pattern.set("count", 0);
|
116
116
|
})
|
117
117
|
.catch(response => this.catchBlock(pattern, response))
|
118
|
-
.
|
118
|
+
.finally(() => this.finallyBlock(pattern));
|
119
119
|
},
|
120
120
|
|
121
121
|
checkboxChanged(pattern) {
|
@@ -1,21 +1,21 @@
|
|
1
1
|
import Component from "@ember/component";
|
2
2
|
import { formatTime } from "client-app/lib/utilities";
|
3
|
+
import { later } from "@ember/runloop";
|
3
4
|
|
4
5
|
export default Component.extend({
|
5
6
|
didInsertElement() {
|
6
|
-
|
7
|
-
|
8
|
-
const timestamp = parseInt(this.getAttribute("data-timestamp"), 10);
|
9
|
-
const elem = this;
|
10
|
-
const text = formatTime(timestamp);
|
7
|
+
later(this, this.updateTimes, 60000);
|
8
|
+
},
|
11
9
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
10
|
+
updateTimes() {
|
11
|
+
Array.from(document.querySelectorAll(".auto-update-time")).forEach(node => {
|
12
|
+
const timestamp = parseInt(node.dataset.timestamp);
|
13
|
+
if (!timestamp) return;
|
14
|
+
const formatted = formatTime(timestamp);
|
15
|
+
if (formatted !== node.innerText) {
|
16
|
+
node.innerText = formatted;
|
17
|
+
}
|
18
|
+
});
|
19
|
+
later(this, this.updateTimes, 60000);
|
20
20
|
}
|
21
21
|
});
|
@@ -18,8 +18,10 @@ export default Controller.extend({
|
|
18
18
|
}),
|
19
19
|
|
20
20
|
resizePanels(amount) {
|
21
|
-
|
22
|
-
|
21
|
+
const bottomPanel = document.getElementById("bottom-panel");
|
22
|
+
const topPanel = document.getElementById("top-panel");
|
23
|
+
bottomPanel.style.height = `${amount - 13}px`;
|
24
|
+
topPanel.style.bottom = `${amount + 12}px`;
|
23
25
|
},
|
24
26
|
|
25
27
|
actionsInMenu: computed(function() {
|
data/client-app/app/index.html
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
<title>ClientApp</title>
|
7
7
|
<meta name="description" content="">
|
8
8
|
<meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0, user-scalable=yes">
|
9
|
-
<meta id="preloaded-data" data-root-path="/logs" data-preloaded="{"env_expandable_keys":[],"patterns_enabled":true}">
|
9
|
+
<meta id="preloaded-data" data-root-path="/logs" data-preloaded="{"env_expandable_keys":[],"patterns_enabled":true,"gems_dir":"/home/sam/.rbenv/versions/2.1.2.discourse/lib/ruby/gems/2.1.0/gems/","backtrace_links_enabled":true,"gems_data":[],"directories":[{"path":"/home/sam/Source/discourse","url":"https://github.com/discourse/discourse","main_app":true}],"application_version":"b329e23f8511b7248c0e4aee370a9f8a249e1b84"}">
|
10
10
|
|
11
11
|
{{content-for "head"}}
|
12
12
|
|
@@ -56,7 +56,7 @@ export function initialize(app) {
|
|
56
56
|
const isMobile =
|
57
57
|
/mobile/i.test(navigator.userAgent) && !/iPad/.test(navigator.userAgent);
|
58
58
|
if (isMobile) {
|
59
|
-
|
59
|
+
document.body.classList.add("mobile");
|
60
60
|
}
|
61
61
|
app.register("site:main", { isMobile }, { instantiate: false });
|
62
62
|
app.inject("controller", "site", "site:main");
|
@@ -4,9 +4,8 @@ let isInitialized = false;
|
|
4
4
|
// exported so that it can be used in tests
|
5
5
|
export function init() {
|
6
6
|
const dataset = document.getElementById("preloaded-data").dataset;
|
7
|
-
CONTAINER =
|
8
|
-
|
9
|
-
});
|
7
|
+
CONTAINER = JSON.parse(dataset.preloaded);
|
8
|
+
CONTAINER.rootPath = dataset.rootPath;
|
10
9
|
isInitialized = true;
|
11
10
|
}
|
12
11
|
|
@@ -18,3 +17,15 @@ export default {
|
|
18
17
|
return Em.get(CONTAINER, key);
|
19
18
|
}
|
20
19
|
};
|
20
|
+
|
21
|
+
// used in tests
|
22
|
+
export function mutatePreload(key, value) {
|
23
|
+
if (!isInitialized) {
|
24
|
+
init();
|
25
|
+
}
|
26
|
+
Em.set(CONTAINER, key, value);
|
27
|
+
}
|
28
|
+
|
29
|
+
export function uninitialize() {
|
30
|
+
isInitialized = false;
|
31
|
+
}
|
@@ -1,4 +1,5 @@
|
|
1
1
|
import Preload from "client-app/lib/preload";
|
2
|
+
import { Promise, resolve } from "rsvp";
|
2
3
|
|
3
4
|
const entityMap = {
|
4
5
|
"&": "&",
|
@@ -14,16 +15,47 @@ export function escapeHtml(string) {
|
|
14
15
|
}
|
15
16
|
|
16
17
|
export function ajax(url, settings) {
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
18
|
+
return new Promise((resolve, reject) => {
|
19
|
+
settings = settings || {};
|
20
|
+
const xhr = new XMLHttpRequest();
|
21
|
+
url = Preload.get("rootPath") + url;
|
22
|
+
if (settings.data) {
|
23
|
+
for (let param in settings.data) {
|
24
|
+
const prefix = url.indexOf("?") === -1 ? "?" : "&";
|
25
|
+
url += prefix;
|
26
|
+
url += `${param}=${encodeURIComponent(settings.data[param])}`;
|
27
|
+
}
|
28
|
+
}
|
29
|
+
xhr.open(settings.method || settings.type || "GET", url);
|
30
|
+
xhr.setRequestHeader("X-SILENCE-LOGGER", true);
|
31
|
+
if (settings.headers) {
|
32
|
+
for (let header in settings.headers) {
|
33
|
+
xhr.setRequestHeader(header, settings.headers[header]);
|
34
|
+
}
|
35
|
+
}
|
36
|
+
xhr.onreadystatechange = () => {
|
37
|
+
if (xhr.readyState === 4) {
|
38
|
+
let status = xhr.status;
|
39
|
+
if ((status >= 200 && status < 300) || status === 304) {
|
40
|
+
const type = xhr.getResponseHeader("Content-Type");
|
41
|
+
let data = xhr.responseText;
|
42
|
+
if (/\bjson\b/.test(type)) {
|
43
|
+
data = JSON.parse(data);
|
44
|
+
}
|
45
|
+
resolve(data);
|
46
|
+
} else {
|
47
|
+
reject(xhr);
|
48
|
+
}
|
49
|
+
}
|
50
|
+
};
|
51
|
+
xhr.send();
|
52
|
+
});
|
21
53
|
}
|
22
54
|
|
23
55
|
export function preloadOrAjax(url, settings) {
|
24
56
|
const preloaded = Preload.get(url.replace(".json", ""));
|
25
57
|
if (preloaded) {
|
26
|
-
return
|
58
|
+
return resolve(preloaded);
|
27
59
|
} else {
|
28
60
|
return ajax(url, settings);
|
29
61
|
}
|
@@ -96,32 +128,33 @@ export function buildArrayString(array) {
|
|
96
128
|
return "[" + buffer.join(", ") + "]";
|
97
129
|
}
|
98
130
|
|
99
|
-
export function buildHashString(hash, recurse, expanded = []) {
|
131
|
+
export function buildHashString(hash, recurse, expanded = [], lists = {}) {
|
100
132
|
if (!hash) return "";
|
101
133
|
|
102
134
|
const buffer = [];
|
103
135
|
const hashes = [];
|
104
136
|
const expandableKeys = Preload.get("env_expandable_keys") || [];
|
105
|
-
|
137
|
+
Object.keys(hash).forEach(k => {
|
138
|
+
const v = hash[k];
|
106
139
|
if (v === null) {
|
107
140
|
buffer.push("null");
|
108
|
-
} else if (
|
141
|
+
} else if (expandableKeys.indexOf(k) !== -1 && !recurse) {
|
109
142
|
let valueHtml = "";
|
110
|
-
if (
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
) {
|
116
|
-
valueHtml = `${escapeHtml(
|
117
|
-
v[0]
|
118
|
-
)}, <a class="expand-list" data-key=${k}>${v.length - 1} more</a>`;
|
143
|
+
if (expanded.indexOf(k) !== -1 || (lists[k] && lists[k].length < 3)) {
|
144
|
+
valueHtml =
|
145
|
+
lists[k] && lists[k].length === 1
|
146
|
+
? escapeHtml(lists[k][0])
|
147
|
+
: buildArrayString(lists[k]);
|
119
148
|
} else {
|
120
|
-
valueHtml =
|
149
|
+
valueHtml = `${escapeHtml(
|
150
|
+
lists[k][0]
|
151
|
+
)}, <a class="expand-list" data-key=${k}>${lists[k].length -
|
152
|
+
1} more</a>`;
|
121
153
|
}
|
122
|
-
buffer.push(
|
123
|
-
|
124
|
-
);
|
154
|
+
buffer.push(`<tr><td>${escapeHtml(k)}</td><td>${valueHtml}</td></tr>`);
|
155
|
+
} else if (Object.prototype.toString.call(v) === "[object Array]") {
|
156
|
+
const valueHtml = buildArrayString(v);
|
157
|
+
buffer.push(`<tr><td>${escapeHtml(k)}</td><td>${valueHtml}</td></tr>`);
|
125
158
|
} else if (typeof v === "object") {
|
126
159
|
hashes.push(k);
|
127
160
|
} else {
|
@@ -137,20 +170,14 @@ export function buildHashString(hash, recurse, expanded = []) {
|
|
137
170
|
}
|
138
171
|
});
|
139
172
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
buildHashString(v, true) +
|
149
|
-
"</td>"
|
150
|
-
);
|
151
|
-
buffer.push("</table></td></tr>");
|
152
|
-
});
|
153
|
-
}
|
173
|
+
hashes.forEach(k1 => {
|
174
|
+
const v = hash[k1];
|
175
|
+
buffer.push("<tr><td></td><td><table>");
|
176
|
+
buffer.push(
|
177
|
+
`<td>${escapeHtml(k1)}</td><td>${buildHashString(v, true)}</td>`
|
178
|
+
);
|
179
|
+
buffer.push("</table></td></tr>");
|
180
|
+
});
|
154
181
|
const className = recurse ? "" : "env-table";
|
155
|
-
return
|
182
|
+
return `<table class='${className}'>${buffer.join("\n")}</table>`;
|
156
183
|
}
|