logster 2.5.1 → 2.6.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
data/client-app/package.json
CHANGED
@@ -18,40 +18,43 @@
|
|
18
18
|
"test": "ember test"
|
19
19
|
},
|
20
20
|
"devDependencies": {
|
21
|
-
"@ember/optional-features": "^
|
22
|
-
"
|
23
|
-
"
|
24
|
-
"
|
21
|
+
"@ember/optional-features": "^1.1.0",
|
22
|
+
"@glimmer/component": "^1.0.0",
|
23
|
+
"babel-eslint": "^10.0.3",
|
24
|
+
"broccoli-asset-rev": "^3.0.0",
|
25
|
+
"ember-auto-import": "^1.5.3",
|
26
|
+
"ember-cli": "^3.15.0",
|
25
27
|
"ember-cli-app-version": "^3.2.0",
|
26
|
-
"ember-cli-babel": "^7.
|
28
|
+
"ember-cli-babel": "^7.13.0",
|
27
29
|
"ember-cli-dependency-checker": "^3.2.0",
|
28
|
-
"ember-cli-eslint": "^
|
29
|
-
"ember-cli-htmlbars": "^
|
30
|
-
"ember-cli-
|
31
|
-
"ember-cli-inject-live-reload": "^1.8.2",
|
30
|
+
"ember-cli-eslint": "^5.1.0",
|
31
|
+
"ember-cli-htmlbars": "^4.2.0",
|
32
|
+
"ember-cli-inject-live-reload": "^2.0.1",
|
32
33
|
"ember-cli-sri": "^2.1.1",
|
33
34
|
"ember-cli-template-lint": "^1.0.0-beta.3",
|
34
|
-
"ember-cli-uglify": "^
|
35
|
-
"ember-data": "~3.
|
36
|
-
"ember-export-application-global": "^2.0.
|
35
|
+
"ember-cli-uglify": "^3.0.0",
|
36
|
+
"ember-data": "~3.15.0",
|
37
|
+
"ember-export-application-global": "^2.0.1",
|
38
|
+
"ember-fetch": "^7.0.0",
|
37
39
|
"ember-font-awesome": "^4.0.0-rc.4",
|
38
|
-
"ember-load-initializers": "^1.1
|
40
|
+
"ember-load-initializers": "^2.1.1",
|
39
41
|
"ember-maybe-import-regenerator": "^0.1.6",
|
40
|
-
"ember-qunit": "^
|
41
|
-
"ember-resolver": "^
|
42
|
-
"ember-source": "^3.
|
43
|
-
"
|
44
|
-
"eslint-plugin-
|
45
|
-
"jquery": "3.4.1",
|
42
|
+
"ember-qunit": "^4.6.0",
|
43
|
+
"ember-resolver": "^7.0.0",
|
44
|
+
"ember-source": "^3.15.0",
|
45
|
+
"eslint-plugin-ember": "^7.7.1",
|
46
|
+
"eslint-plugin-node": "^10.0.0",
|
46
47
|
"loader.js": "^4.7.0",
|
47
48
|
"prettier": "^1.19.1",
|
48
|
-
"qunit-dom": "^0.
|
49
|
+
"qunit-dom": "^0.9.2"
|
49
50
|
},
|
50
51
|
"engines": {
|
51
|
-
"node": "
|
52
|
+
"node": "8.* || >= 10.*"
|
53
|
+
},
|
54
|
+
"ember": {
|
55
|
+
"edition": "octane"
|
52
56
|
},
|
53
57
|
"dependencies": {
|
54
|
-
"lodash": "^4.17.15",
|
55
58
|
"moment": "~2.22.2"
|
56
59
|
}
|
57
60
|
}
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This script takes care of updating the content of the preloaded json in app/index.html and tests/index.html
|
4
|
+
# All you need to do is update the ruby hash of the corresponding file you want to update and run the script
|
5
|
+
|
6
|
+
require 'bundler/inline'
|
7
|
+
require 'json'
|
8
|
+
require 'cgi'
|
9
|
+
|
10
|
+
gemfile do
|
11
|
+
source 'https://rubygems.org'
|
12
|
+
gem 'nokogiri'
|
13
|
+
end
|
14
|
+
|
15
|
+
tests_index_html = {
|
16
|
+
env_expandable_keys: [],
|
17
|
+
gems_dir: "/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/",
|
18
|
+
backtrace_links_enabled: true,
|
19
|
+
gems_data: [
|
20
|
+
{
|
21
|
+
name: "activerecord",
|
22
|
+
url: "https://github.com/rails/rails/tree/v6.0.1/activerecord"
|
23
|
+
}
|
24
|
+
],
|
25
|
+
directories: [
|
26
|
+
{
|
27
|
+
path: "/var/www/discourse",
|
28
|
+
url: "https://github.com/discourse/discourse",
|
29
|
+
main_app: true
|
30
|
+
},
|
31
|
+
{
|
32
|
+
path: "/var/www/discourse/plugins/discourse-prometheus",
|
33
|
+
url: "https://github.com/discourse/discourse-prometheus"
|
34
|
+
}
|
35
|
+
],
|
36
|
+
application_version: "ce512452b512b909c38e9c63f2a0e1f8c17a2399"
|
37
|
+
}
|
38
|
+
|
39
|
+
app_index_html = {
|
40
|
+
env_expandable_keys: [],
|
41
|
+
patterns_enabled: true,
|
42
|
+
gems_dir: "/home/sam/.rbenv/versions/2.1.2.discourse/lib/ruby/gems/2.1.0/gems/",
|
43
|
+
backtrace_links_enabled: true,
|
44
|
+
gems_data: [],
|
45
|
+
directories: [
|
46
|
+
{
|
47
|
+
path: "/home/sam/Source/discourse",
|
48
|
+
url: "https://github.com/discourse/discourse",
|
49
|
+
main_app: true
|
50
|
+
}
|
51
|
+
],
|
52
|
+
application_version: "b329e23f8511b7248c0e4aee370a9f8a249e1b84"
|
53
|
+
}
|
54
|
+
|
55
|
+
types = { app: app_index_html, tests: tests_index_html }
|
56
|
+
|
57
|
+
%i{app tests}.each do |type|
|
58
|
+
content = File.read("#{type}/index.html")
|
59
|
+
json = CGI.escapeHTML(JSON.generate(types[type]))
|
60
|
+
content.sub!(/data-preloaded=".*">$/, "data-preloaded=\"#{json}\">")
|
61
|
+
File.write("#{type}/index.html", content)
|
62
|
+
end
|
data/client-app/testem.js
CHANGED
data/client-app/tests/index.html
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
<title>ClientApp Tests</title>
|
7
7
|
<meta name="description" content="">
|
8
8
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
9
|
-
<meta id="preloaded-data" data-root-path="/logs" data-preloaded="{"env_expandable_keys":[]}">
|
9
|
+
<meta id="preloaded-data" data-root-path="/logs" data-preloaded="{"env_expandable_keys":[],"gems_dir":"/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/","backtrace_links_enabled":true,"gems_data":[{"name":"activerecord","url":"https://github.com/rails/rails/tree/v6.0.1/activerecord"}],"directories":[{"path":"/var/www/discourse","url":"https://github.com/discourse/discourse","main_app":true},{"path":"/var/www/discourse/plugins/discourse-prometheus","url":"https://github.com/discourse/discourse-prometheus"}],"application_version":"ce512452b512b909c38e9c63f2a0e1f8c17a2399"}">
|
10
10
|
|
11
11
|
{{content-for "head"}}
|
12
12
|
{{content-for "test-head"}}
|
@@ -0,0 +1,109 @@
|
|
1
|
+
import { module, test } from "qunit";
|
2
|
+
import { setupRenderingTest } from "ember-qunit";
|
3
|
+
import { render, find, findAll } from "@ember/test-helpers";
|
4
|
+
import hbs from "htmlbars-inline-precompile";
|
5
|
+
import { uninitialize, mutatePreload } from "client-app/lib/preload";
|
6
|
+
|
7
|
+
module("Integration | Component | back-trace", function(hooks) {
|
8
|
+
setupRenderingTest(hooks);
|
9
|
+
|
10
|
+
hooks.beforeEach(function() {
|
11
|
+
uninitialize();
|
12
|
+
});
|
13
|
+
|
14
|
+
hooks.afterEach(function() {
|
15
|
+
uninitialize();
|
16
|
+
});
|
17
|
+
|
18
|
+
test("backtrace lines display and work correctly", async function(assert) {
|
19
|
+
const backtrace = `/var/www/discourse/vendor/bundle/ruby/2.6.0/gems/activerecord-6.0.1/lib/active_record/relation/finder_methods.rb:317:in \`exists?'
|
20
|
+
/var/www/discourse/lib/permalink_constraint.rb:6:in \`matches?'
|
21
|
+
/var/www/discourse/plugins/discourse-prometheus/lib/middleware/metrics.rb:17:in \`call'`;
|
22
|
+
this.set("backtrace", backtrace);
|
23
|
+
await render(hbs`{{back-trace backtrace=backtrace}}`);
|
24
|
+
|
25
|
+
const [gem, app, plugin] = findAll("a");
|
26
|
+
assert.equal(
|
27
|
+
gem.href,
|
28
|
+
"https://github.com/rails/rails/tree/v6.0.1/activerecord/lib/active_record/relation/finder_methods.rb#L317"
|
29
|
+
);
|
30
|
+
|
31
|
+
assert.equal(
|
32
|
+
app.href,
|
33
|
+
"https://github.com/discourse/discourse/blob/ce512452b512b909c38e9c63f2a0e1f8c17a2399/lib/permalink_constraint.rb#L6"
|
34
|
+
);
|
35
|
+
|
36
|
+
assert.equal(
|
37
|
+
plugin.href,
|
38
|
+
"https://github.com/discourse/discourse-prometheus/blob/master/lib/middleware/metrics.rb#L17"
|
39
|
+
);
|
40
|
+
|
41
|
+
let gemLine = find("div.backtrace-line");
|
42
|
+
assert.equal(
|
43
|
+
gemLine.textContent,
|
44
|
+
"activerecord-6.0.1/lib/active_record/relation/finder_methods.rb:317:in `exists?'",
|
45
|
+
"gem lines are truncated"
|
46
|
+
);
|
47
|
+
});
|
48
|
+
|
49
|
+
test("non-ruby backtraces don't break things", async function(assert) {
|
50
|
+
this.set(
|
51
|
+
"backtrace",
|
52
|
+
`m/<@https://discourse-cdn.com/assets/application-f59d2.br.js:1:27448
|
53
|
+
m@https://discourse-cdn.com/assets/application-f59d2.br.js:1:27560
|
54
|
+
string@https://discourse-cdn.com/assets/application-f59d2.br.js:1:27869`
|
55
|
+
);
|
56
|
+
await render(hbs`{{back-trace backtrace=backtrace}}`);
|
57
|
+
const lines = this.backtrace.split("\n");
|
58
|
+
findAll("div.backtrace-line").forEach((node, index) => {
|
59
|
+
assert.equal(node.textContent.trim(), lines[index]);
|
60
|
+
});
|
61
|
+
});
|
62
|
+
|
63
|
+
test("Github links use commit sha", async function(assert) {
|
64
|
+
const backtrace = `/var/www/discourse/lib/permalink_constraint.rb:6:in \`matches?'`;
|
65
|
+
let env = [
|
66
|
+
{ application_version: "123abc" },
|
67
|
+
{ application_version: "abc123" }
|
68
|
+
];
|
69
|
+
this.setProperties({
|
70
|
+
backtrace,
|
71
|
+
env
|
72
|
+
});
|
73
|
+
await render(hbs`{{back-trace backtrace=backtrace env=env}}`);
|
74
|
+
let href = find("a").href;
|
75
|
+
assert.equal(
|
76
|
+
href,
|
77
|
+
"https://github.com/discourse/discourse/blob/123abc/lib/permalink_constraint.rb#L6",
|
78
|
+
"uses the first application_version if there are multiple versions"
|
79
|
+
);
|
80
|
+
|
81
|
+
env = { application_version: "567def" };
|
82
|
+
this.set("env", env);
|
83
|
+
await render(hbs`{{back-trace backtrace=backtrace env=env}}`);
|
84
|
+
href = find("a").href;
|
85
|
+
assert.equal(
|
86
|
+
href,
|
87
|
+
"https://github.com/discourse/discourse/blob/567def/lib/permalink_constraint.rb#L6",
|
88
|
+
"uses application_version when env is only a hash"
|
89
|
+
);
|
90
|
+
|
91
|
+
this.set("env", null);
|
92
|
+
await render(hbs`{{back-trace backtrace=backtrace env=env}}`);
|
93
|
+
href = find("a").href;
|
94
|
+
assert.equal(
|
95
|
+
href,
|
96
|
+
"https://github.com/discourse/discourse/blob/ce512452b512b909c38e9c63f2a0e1f8c17a2399/lib/permalink_constraint.rb#L6",
|
97
|
+
"falls back to preload if env doesn't contain application_version"
|
98
|
+
);
|
99
|
+
|
100
|
+
mutatePreload("application_version", null);
|
101
|
+
await render(hbs`{{back-trace backtrace=backtrace}}`);
|
102
|
+
href = find("a").href;
|
103
|
+
assert.equal(
|
104
|
+
href,
|
105
|
+
"https://github.com/discourse/discourse/blob/master/lib/permalink_constraint.rb#L6",
|
106
|
+
"falls back to master branch when neither preload nor application_version in env are available"
|
107
|
+
);
|
108
|
+
});
|
109
|
+
});
|
@@ -32,11 +32,12 @@ module("Integration | Component | message-info", function(hooks) {
|
|
32
32
|
showTitle=showTitle
|
33
33
|
currentEnvPosition=envPosition
|
34
34
|
envChangedAction=callback
|
35
|
+
showShare=true
|
35
36
|
actionsInMenu=actionsInMenu}}`
|
36
37
|
);
|
37
38
|
let activeTab = find(".message-info .content.active pre");
|
38
39
|
assert.equal(
|
39
|
-
activeTab.textContent,
|
40
|
+
activeTab.textContent.trim(),
|
40
41
|
backtrace,
|
41
42
|
"default active tab is backtrace"
|
42
43
|
);
|
@@ -60,7 +61,7 @@ module("Integration | Component | message-info", function(hooks) {
|
|
60
61
|
await click(find(".message-actions button.expand.no-text"));
|
61
62
|
assert.equal(
|
62
63
|
findAll(".actions-menu button").length,
|
63
|
-
|
64
|
+
3,
|
64
65
|
"extra buttons shown inside a menu"
|
65
66
|
);
|
66
67
|
assert
|
@@ -85,7 +86,7 @@ module("Integration | Component | message-info", function(hooks) {
|
|
85
86
|
.doesNotExist("menu expand button is not shown");
|
86
87
|
assert.equal(
|
87
88
|
findAll(".message-actions button").length,
|
88
|
-
|
89
|
+
4,
|
89
90
|
"all actions buttons are shown inline when `actionsInMenu` is false"
|
90
91
|
);
|
91
92
|
|
@@ -12,7 +12,9 @@ module("Integration | Component | patterns-list", function(hooks) {
|
|
12
12
|
mutable: true,
|
13
13
|
patterns: []
|
14
14
|
});
|
15
|
-
await render(
|
15
|
+
await render(
|
16
|
+
hbs`{{patterns-list patterns=patterns mutable=mutable showCounter=true}}`
|
17
|
+
);
|
16
18
|
assert
|
17
19
|
.dom(".pattern-input")
|
18
20
|
.exists("It shows an input when patterns are emtpy");
|
@@ -33,7 +35,10 @@ module("Integration | Component | patterns-list", function(hooks) {
|
|
33
35
|
.doesNotExist("No save buttons are shown when there is 0 buffer");
|
34
36
|
const counters = findAll("input.count");
|
35
37
|
assert.equal(counters.length, 3, "counters shown for all patterns");
|
36
|
-
assert.ok(
|
38
|
+
assert.ok(
|
39
|
+
counters.every(c => c.disabled),
|
40
|
+
"counters are disabled"
|
41
|
+
);
|
37
42
|
|
38
43
|
pattern1.set("count", 6);
|
39
44
|
this.set("patterns", [pattern1, pattern2]);
|
data/lib/logster.rb
CHANGED
data/lib/logster/base_store.rb
CHANGED
@@ -18,7 +18,7 @@ module Logster
|
|
18
18
|
end
|
19
19
|
|
20
20
|
# Modify the saved message to the given one (identified by message.key) and bump it to the top of the latest list
|
21
|
-
def replace_and_bump(message
|
21
|
+
def replace_and_bump(message)
|
22
22
|
not_implemented
|
23
23
|
end
|
24
24
|
|
@@ -195,26 +195,33 @@ module Logster
|
|
195
195
|
similar = nil
|
196
196
|
|
197
197
|
if Logster.config.allow_grouping
|
198
|
+
message.apply_message_size_limit(
|
199
|
+
Logster.config.maximum_message_size_bytes,
|
200
|
+
gems_dir: Logster.config.gems_dir
|
201
|
+
)
|
198
202
|
key = self.similar_key(message)
|
199
203
|
similar = get(key, load_env: false) if key
|
200
204
|
end
|
201
205
|
|
206
|
+
message.drop_redundant_envs(Logster.config.max_env_count_per_message)
|
207
|
+
message.apply_env_size_limit(Logster.config.max_env_bytes)
|
208
|
+
saved = true
|
202
209
|
if similar
|
203
|
-
|
204
|
-
|
205
|
-
end
|
206
|
-
save_env = similar.merge_similar_message(message)
|
207
|
-
|
208
|
-
replace_and_bump(similar, save_env: save_env)
|
210
|
+
similar.merge_similar_message(message)
|
211
|
+
replace_and_bump(similar)
|
209
212
|
similar
|
210
213
|
else
|
211
|
-
|
214
|
+
message.apply_message_size_limit(
|
215
|
+
Logster.config.maximum_message_size_bytes,
|
216
|
+
gems_dir: Logster.config.gems_dir
|
217
|
+
)
|
218
|
+
saved = save(message)
|
212
219
|
message
|
213
220
|
end
|
214
221
|
|
215
222
|
message = similar || message
|
216
223
|
|
217
|
-
if Logster.config.enable_custom_patterns_via_ui || allow_custom_patterns
|
224
|
+
if (Logster.config.enable_custom_patterns_via_ui || allow_custom_patterns) && saved
|
218
225
|
grouping_patterns = @patterns_cache.fetch(Logster::GroupingPattern::CACHE_KEY) do
|
219
226
|
Logster::GroupingPattern.find_all(store: self)
|
220
227
|
end
|
@@ -12,7 +12,12 @@ module Logster
|
|
12
12
|
:environments,
|
13
13
|
:rate_limit_error_reporting,
|
14
14
|
:web_title,
|
15
|
-
:maximum_message_size_bytes
|
15
|
+
:maximum_message_size_bytes,
|
16
|
+
:project_directories,
|
17
|
+
:enable_backtrace_links,
|
18
|
+
:gems_dir,
|
19
|
+
:max_env_bytes,
|
20
|
+
:max_env_count_per_message
|
16
21
|
)
|
17
22
|
|
18
23
|
attr_writer :subdirectory
|
@@ -26,7 +31,12 @@ module Logster
|
|
26
31
|
@enable_custom_patterns_via_ui = false
|
27
32
|
@rate_limit_error_reporting = true
|
28
33
|
@enable_js_error_reporting = true
|
29
|
-
@maximum_message_size_bytes =
|
34
|
+
@maximum_message_size_bytes = 10_000
|
35
|
+
@max_env_bytes = 1000
|
36
|
+
@max_env_count_per_message = 50
|
37
|
+
@project_directories = []
|
38
|
+
@enable_backtrace_links = true
|
39
|
+
@gems_dir = Gem.dir + "/gems/"
|
30
40
|
|
31
41
|
@allow_grouping = false
|
32
42
|
|
data/lib/logster/defer_logger.rb
CHANGED
data/lib/logster/logger.rb
CHANGED
@@ -70,6 +70,18 @@ module Logster
|
|
70
70
|
message = message.scrub
|
71
71
|
end
|
72
72
|
|
73
|
+
# we want to get the backtrace as early as possible so that logster's
|
74
|
+
# own methods don't show up as the first few frames in the backtrace
|
75
|
+
if !opts || !opts.key?(:backtrace)
|
76
|
+
opts ||= {}
|
77
|
+
backtrace = caller_locations
|
78
|
+
while backtrace.first.path.end_with?("/logger.rb")
|
79
|
+
backtrace.shift
|
80
|
+
end
|
81
|
+
backtrace = backtrace.join("\n")
|
82
|
+
opts[:backtrace] = backtrace
|
83
|
+
end
|
84
|
+
|
73
85
|
if @chained
|
74
86
|
i = 0
|
75
87
|
# micro optimise for logging
|
data/lib/logster/message.rb
CHANGED
@@ -4,8 +4,6 @@ require 'digest/sha1'
|
|
4
4
|
require 'securerandom'
|
5
5
|
|
6
6
|
module Logster
|
7
|
-
|
8
|
-
MAX_GROUPING_LENGTH = 50
|
9
7
|
MAX_MESSAGE_LENGTH = 600
|
10
8
|
|
11
9
|
class Message
|
@@ -22,9 +20,10 @@ module Logster
|
|
22
20
|
hostname
|
23
21
|
process_id
|
24
22
|
application_version
|
23
|
+
time
|
25
24
|
}
|
26
25
|
|
27
|
-
attr_accessor :timestamp, :severity, :progname, :key, :backtrace, :count, :protected, :first_timestamp
|
26
|
+
attr_accessor :timestamp, :severity, :progname, :key, :backtrace, :count, :protected, :first_timestamp, :env_buffer
|
28
27
|
attr_reader :message, :env
|
29
28
|
|
30
29
|
def initialize(severity, progname, message, timestamp = nil, key = nil, count: 1)
|
@@ -37,6 +36,7 @@ module Logster
|
|
37
36
|
@count = count || 1
|
38
37
|
@protected = false
|
39
38
|
@first_timestamp = nil
|
39
|
+
@env_buffer = []
|
40
40
|
end
|
41
41
|
|
42
42
|
def to_h(exclude_env: false)
|
@@ -82,7 +82,6 @@ module Logster
|
|
82
82
|
end
|
83
83
|
|
84
84
|
def env=(env)
|
85
|
-
@env_json = nil
|
86
85
|
@env = self.class.scrub_params(env)
|
87
86
|
end
|
88
87
|
|
@@ -94,12 +93,18 @@ module Logster
|
|
94
93
|
env ||= {}
|
95
94
|
if Array === env
|
96
95
|
env = env.map do |single_env|
|
97
|
-
self.class.default_env.merge(single_env)
|
96
|
+
single_env = self.class.default_env.merge(single_env)
|
97
|
+
if !single_env.key?("time") && !single_env.key?(:time)
|
98
|
+
single_env["time"] = @timestamp || get_timestamp
|
99
|
+
end
|
100
|
+
single_env
|
98
101
|
end
|
99
102
|
else
|
100
103
|
env = self.class.default_env.merge(env)
|
104
|
+
if !env.key?("time") && !env.key?(:time)
|
105
|
+
env["time"] = @timestamp || get_timestamp
|
106
|
+
end
|
101
107
|
end
|
102
|
-
@env_json = nil
|
103
108
|
@env = Message.populate_from_env(env)
|
104
109
|
end
|
105
110
|
|
@@ -127,12 +132,11 @@ module Logster
|
|
127
132
|
if Array === env
|
128
133
|
versions = env.map { |single_env| single_env["application_version"] }
|
129
134
|
else
|
130
|
-
versions = env["application_version"]
|
135
|
+
versions = [env["application_version"]]
|
131
136
|
end
|
137
|
+
versions.compact!
|
132
138
|
|
133
|
-
if
|
134
|
-
versions = [versions] if String === versions
|
135
|
-
|
139
|
+
if backtrace && backtrace.length > 0
|
136
140
|
versions.map do |version|
|
137
141
|
Digest::SHA1.hexdigest "#{version} #{backtrace}"
|
138
142
|
end
|
@@ -146,31 +150,24 @@ module Logster
|
|
146
150
|
def merge_similar_message(other)
|
147
151
|
self.first_timestamp ||= self.timestamp
|
148
152
|
self.timestamp = [self.timestamp, other.timestamp].max
|
149
|
-
|
150
153
|
self.count += other.count || 1
|
151
|
-
return false if self.count > Logster::MAX_GROUPING_LENGTH
|
152
154
|
|
153
|
-
|
154
|
-
|
155
|
-
return false if size + extra_env_size > Logster.config.maximum_message_size_bytes
|
156
|
-
|
157
|
-
other_env = JSON.load JSON.fast_generate other.env
|
158
|
-
if Hash === other_env && !other_env.key?("time")
|
159
|
-
other_env["time"] = other.timestamp
|
160
|
-
end
|
161
|
-
if Hash === self.env && !self.env.key?("time")
|
162
|
-
self.env["time"] = self.first_timestamp
|
155
|
+
if Hash === other.env && !other.env.key?("time") && !other.env.key?(:time)
|
156
|
+
other.env["time"] = other.timestamp
|
163
157
|
end
|
164
158
|
|
165
|
-
if Array ===
|
166
|
-
|
159
|
+
if Array === other.env
|
160
|
+
env_buffer.unshift(*other.env)
|
167
161
|
else
|
168
|
-
|
162
|
+
env_buffer.unshift(other.env)
|
169
163
|
end
|
170
|
-
@env_json = nil
|
171
164
|
true
|
172
165
|
end
|
173
166
|
|
167
|
+
def has_env_buffer?
|
168
|
+
env_buffer.size > 0
|
169
|
+
end
|
170
|
+
|
174
171
|
def self.populate_from_env(env)
|
175
172
|
if Array === env
|
176
173
|
env.map do |single_env|
|
@@ -229,10 +226,6 @@ module Logster
|
|
229
226
|
end
|
230
227
|
end
|
231
228
|
|
232
|
-
def env_json
|
233
|
-
@env_json ||= (self.env || {}).to_json
|
234
|
-
end
|
235
|
-
|
236
229
|
def self.scrub_params(params)
|
237
230
|
if Array === params
|
238
231
|
params.map! { |p| scrub_params(p) }
|
@@ -250,8 +243,74 @@ module Logster
|
|
250
243
|
end
|
251
244
|
end
|
252
245
|
|
246
|
+
def drop_redundant_envs(limit)
|
247
|
+
if Array === env
|
248
|
+
env.slice!(limit..-1)
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
def apply_env_size_limit(size_limit)
|
253
|
+
if Array === env
|
254
|
+
env.each { |e| truncate_env(e, size_limit) }
|
255
|
+
elsif Hash === env
|
256
|
+
truncate_env(env, size_limit)
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
def apply_message_size_limit(limit, gems_dir: nil)
|
261
|
+
size = self.to_json(exclude_env: true).bytesize
|
262
|
+
if size > limit && @backtrace
|
263
|
+
@backtrace.gsub!(gems_dir, "") if gems_dir
|
264
|
+
@backtrace.strip!
|
265
|
+
size = self.to_json(exclude_env: true).bytesize
|
266
|
+
backtrace_limit = limit - (size - @backtrace.bytesize)
|
267
|
+
return if backtrace_limit <= 0 || size <= limit
|
268
|
+
truncate_backtrace(backtrace_limit)
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
def truncate_backtrace(bytes_limit)
|
273
|
+
@backtrace = @backtrace.byteslice(0...bytes_limit)
|
274
|
+
while !@backtrace[-1].valid_encoding? && @backtrace.size > 1
|
275
|
+
@backtrace.slice!(-1)
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
253
279
|
protected
|
254
280
|
|
281
|
+
def truncate_env(env, limit)
|
282
|
+
if JSON.fast_generate(env).bytesize > limit
|
283
|
+
sizes = {}
|
284
|
+
braces = '{}'.bytesize
|
285
|
+
env.each do |k, v|
|
286
|
+
sizes[k] = JSON.fast_generate(k => v).bytesize - braces
|
287
|
+
end
|
288
|
+
sorted = env.keys.sort { |a, b| sizes[a] <=> sizes[b] }
|
289
|
+
|
290
|
+
kept_keys = []
|
291
|
+
if env.key?(:time)
|
292
|
+
kept_keys << :time
|
293
|
+
elsif env.key?("time")
|
294
|
+
kept_keys << "time"
|
295
|
+
end
|
296
|
+
|
297
|
+
sum = braces
|
298
|
+
if time_key = kept_keys.first
|
299
|
+
sum += sizes[time_key]
|
300
|
+
sorted.delete(time_key)
|
301
|
+
end
|
302
|
+
comma = ','.bytesize
|
303
|
+
|
304
|
+
sorted.each do |k|
|
305
|
+
extra = kept_keys.size == 0 ? 0 : comma
|
306
|
+
break if sum + sizes[k] + extra > limit
|
307
|
+
kept_keys << k
|
308
|
+
sum += sizes[k] + extra
|
309
|
+
end
|
310
|
+
env.select! { |k| kept_keys.include?(k) }
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
255
314
|
def truncate_message(msg)
|
256
315
|
return msg unless msg
|
257
316
|
msg = msg.inspect unless String === msg
|