logster 2.1.2 → 2.2.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.
Files changed (127) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +21 -19
  3. data/.rubocop.yml +1 -1
  4. data/.travis.yml +16 -16
  5. data/CHANGELOG.md +224 -172
  6. data/Gemfile +4 -4
  7. data/Guardfile +8 -8
  8. data/LICENSE.txt +22 -22
  9. data/README.md +99 -99
  10. data/Rakefile +21 -21
  11. data/assets/fonts/FontAwesome.otf +0 -0
  12. data/assets/fonts/fontawesome-webfont.eot +0 -0
  13. data/assets/fonts/fontawesome-webfont.svg +639 -639
  14. data/assets/fonts/fontawesome-webfont.ttf +0 -0
  15. data/assets/fonts/fontawesome-webfont.woff +0 -0
  16. data/assets/fonts/fontawesome-webfont.woff2 +0 -0
  17. data/assets/images/Icon-144_rounded.png +0 -0
  18. data/assets/images/Icon-144_square.png +0 -0
  19. data/assets/images/icon_144x144.png +0 -0
  20. data/assets/images/icon_64x64.png +0 -0
  21. data/assets/javascript/client-app.js +115 -106
  22. data/assets/stylesheets/client-app.css +1 -1
  23. data/build_client_app.sh +0 -0
  24. data/client-app/.editorconfig +20 -20
  25. data/client-app/.ember-cli +9 -9
  26. data/client-app/.eslintignore +19 -19
  27. data/client-app/.eslintrc.js +46 -46
  28. data/client-app/.gitignore +23 -23
  29. data/client-app/.travis.yml +27 -27
  30. data/client-app/.watchmanconfig +3 -3
  31. data/client-app/README.md +57 -57
  32. data/client-app/app/app.js +0 -0
  33. data/client-app/app/components/actions-menu.js +43 -43
  34. data/client-app/app/components/env-tab.js +80 -80
  35. data/client-app/app/components/message-info.js +0 -0
  36. data/client-app/app/components/message-row.js +0 -0
  37. data/client-app/app/components/panel-resizer.js +0 -0
  38. data/client-app/app/components/patterns-list.js +109 -0
  39. data/client-app/app/components/tab-contents.js +27 -27
  40. data/client-app/app/components/tabbed-section.js +0 -0
  41. data/client-app/app/components/time-formatter.js +0 -0
  42. data/client-app/app/components/update-time.js +0 -0
  43. data/client-app/app/controllers/index.js +22 -6
  44. data/client-app/app/controllers/show.js +0 -0
  45. data/client-app/app/helpers/logster-url.js +12 -0
  46. data/client-app/app/helpers/or.js +7 -0
  47. data/client-app/app/index.html +30 -29
  48. data/client-app/app/initializers/app-init.js +67 -67
  49. data/client-app/app/lib/preload.js +20 -20
  50. data/client-app/app/lib/utilities.js +150 -149
  51. data/client-app/app/models/message-collection.js +0 -0
  52. data/client-app/app/models/message.js +100 -100
  53. data/client-app/app/models/pattern-item.js +25 -0
  54. data/client-app/app/resolver.js +0 -0
  55. data/client-app/app/router.js +1 -0
  56. data/client-app/app/routes/index.js +2 -9
  57. data/client-app/app/routes/settings.js +15 -0
  58. data/client-app/app/routes/show.js +0 -0
  59. data/client-app/app/styles/app.css +633 -527
  60. data/client-app/app/templates/application.hbs +2 -2
  61. data/client-app/app/templates/components/actions-menu.hbs +12 -12
  62. data/client-app/app/templates/components/env-tab.hbs +10 -10
  63. data/client-app/app/templates/components/message-info.hbs +41 -41
  64. data/client-app/app/templates/components/message-row.hbs +15 -15
  65. data/client-app/app/templates/components/panel-resizer.hbs +3 -3
  66. data/client-app/app/templates/components/patterns-list.hbs +25 -0
  67. data/client-app/app/templates/components/tabbed-section.hbs +10 -10
  68. data/client-app/app/templates/components/time-formatter.hbs +1 -1
  69. data/client-app/app/templates/index.hbs +65 -58
  70. data/client-app/app/templates/settings.hbs +26 -0
  71. data/client-app/app/templates/show.hbs +7 -7
  72. data/client-app/config/environment.js +51 -51
  73. data/client-app/config/optional-features.json +3 -3
  74. data/client-app/config/targets.js +18 -18
  75. data/client-app/ember-cli-build.js +29 -29
  76. data/client-app/package-lock.json +11357 -11365
  77. data/client-app/package.json +57 -56
  78. data/client-app/public/assets/images/icon_144x144.png +0 -0
  79. data/client-app/public/assets/images/icon_64x64.png +0 -0
  80. data/client-app/testem.js +25 -25
  81. data/client-app/tests/index.html +34 -34
  82. data/client-app/tests/integration/components/env-tab-test.js +134 -123
  83. data/client-app/tests/integration/components/message-info-test.js +111 -111
  84. data/client-app/tests/integration/components/patterns-list-test.js +56 -0
  85. data/client-app/tests/test-helper.js +8 -8
  86. data/client-app/tests/unit/controllers/index-test.js +12 -12
  87. data/client-app/tests/unit/controllers/show-test.js +12 -12
  88. data/client-app/tests/unit/initializers/app-init-test.js +31 -31
  89. data/client-app/tests/unit/routes/index-test.js +11 -11
  90. data/client-app/tests/unit/routes/show-test.js +11 -11
  91. data/lib/examples/sidekiq_logster_reporter.rb +21 -21
  92. data/lib/logster.rb +59 -54
  93. data/lib/logster/base_store.rb +169 -141
  94. data/lib/logster/cache.rb +20 -0
  95. data/lib/logster/configuration.rb +27 -26
  96. data/lib/logster/defer_logger.rb +14 -14
  97. data/lib/logster/ignore_pattern.rb +65 -65
  98. data/lib/logster/logger.rb +113 -113
  99. data/lib/logster/message.rb +212 -212
  100. data/lib/logster/middleware/debug_exceptions.rb +26 -26
  101. data/lib/logster/middleware/reporter.rb +55 -55
  102. data/lib/logster/middleware/viewer.rb +297 -222
  103. data/lib/logster/pattern.rb +95 -0
  104. data/lib/logster/rails/railtie.rb +63 -63
  105. data/lib/logster/redis_store.rb +584 -566
  106. data/lib/logster/scheduler.rb +54 -54
  107. data/lib/logster/suppression_pattern.rb +17 -0
  108. data/lib/logster/version.rb +3 -3
  109. data/lib/logster/web.rb +14 -14
  110. data/logster.gemspec +35 -35
  111. data/test/examples/test_sidekiq_reporter_example.rb +46 -46
  112. data/test/fake_data/Gemfile +4 -4
  113. data/test/fake_data/generate.rb +10 -10
  114. data/test/logster/middleware/test_reporter.rb +19 -19
  115. data/test/logster/middleware/test_viewer.rb +267 -96
  116. data/test/logster/test_base_store.rb +147 -147
  117. data/test/logster/test_cache.rb +38 -0
  118. data/test/logster/test_defer_logger.rb +34 -34
  119. data/test/logster/test_ignore_pattern.rb +41 -41
  120. data/test/logster/test_logger.rb +100 -86
  121. data/test/logster/test_message.rb +119 -119
  122. data/test/logster/test_pattern.rb +152 -0
  123. data/test/logster/test_redis_rate_limiter.rb +230 -230
  124. data/test/logster/test_redis_store.rb +689 -720
  125. data/test/test_helper.rb +38 -38
  126. data/vendor/assets/javascripts/logster.js.erb +39 -39
  127. metadata +24 -7
@@ -1,212 +1,212 @@
1
- require 'digest/sha1'
2
- require 'securerandom'
3
-
4
- module Logster
5
-
6
- MAX_GROUPING_LENGTH = 50
7
-
8
- class Message
9
- LOGSTER_ENV = "_logster_env".freeze
10
- ALLOWED_ENV = %w{
11
- HTTP_HOST
12
- REQUEST_URI
13
- REQUEST_METHOD
14
- HTTP_USER_AGENT
15
- HTTP_ACCEPT
16
- HTTP_REFERER
17
- HTTP_X_FORWARDED_FOR
18
- HTTP_X_REAL_IP
19
- hostname
20
- process_id
21
- application_version
22
- }
23
-
24
- attr_accessor :timestamp, :severity, :progname, :message, :key, :backtrace, :count, :env, :protected, :first_timestamp
25
-
26
- def initialize(severity, progname, message, timestamp = nil, key = nil, count: 1)
27
- @timestamp = timestamp || get_timestamp
28
- @severity = severity
29
- @progname = progname
30
- @message = message
31
- @key = key || SecureRandom.hex
32
- @backtrace = nil
33
- @count = count || 1
34
- @protected = false
35
- @first_timestamp = nil
36
- end
37
-
38
- def to_h(exclude_env: false)
39
- h = {
40
- message: @message,
41
- progname: @progname,
42
- severity: @severity,
43
- timestamp: @timestamp,
44
- key: @key,
45
- backtrace: @backtrace,
46
- count: @count,
47
- protected: @protected
48
- }
49
-
50
- h[:first_timestamp] = @first_timestamp if @first_timestamp
51
- h[:env] = @env unless exclude_env
52
-
53
- h
54
- end
55
-
56
- def to_json(opts = nil)
57
- exclude_env = Hash === opts && opts.delete(:exclude_env)
58
- JSON.fast_generate(to_h(exclude_env: exclude_env), opts)
59
- end
60
-
61
- def self.from_json(json)
62
- parsed = ::JSON.parse(json)
63
- msg = new(parsed["severity"],
64
- parsed["progname"],
65
- parsed["message"],
66
- parsed["timestamp"],
67
- parsed["key"])
68
- msg.backtrace = parsed["backtrace"]
69
- msg.env = parsed["env"]
70
- msg.count = parsed["count"]
71
- msg.protected = parsed["protected"]
72
- msg.first_timestamp = parsed["first_timestamp"]
73
- msg
74
- end
75
-
76
- def self.hostname
77
- @hostname ||= `hostname`.strip! rescue "<unknown>"
78
- end
79
-
80
- def populate_from_env(env)
81
- env ||= {}
82
- if Array === env
83
- env = env.map do |single_env|
84
- self.class.default_env.merge(single_env)
85
- end
86
- else
87
- env = self.class.default_env.merge(env)
88
- end
89
- @env = Message.populate_from_env(env)
90
- end
91
-
92
- def self.default_env
93
- env = {
94
- "hostname" => hostname,
95
- "process_id" => Process.pid
96
- }
97
- env["application_version"] = Logster.config.application_version if Logster.config.application_version
98
- env
99
- end
100
-
101
- # in its own method so it can be overridden
102
- def grouping_hash
103
- return { message: self.message, severity: self.severity, backtrace: self.backtrace }
104
- end
105
-
106
- # todo - memoize?
107
- def grouping_key
108
- Digest::SHA1.hexdigest JSON.fast_generate grouping_hash
109
- end
110
-
111
- # todo - memoize?
112
- def solved_keys
113
- if Array === env
114
- versions = env.map { |single_env| single_env["application_version"] }
115
- else
116
- versions = env["application_version"]
117
- end
118
-
119
- if versions && backtrace && backtrace.length > 0
120
- versions = [versions] if String === versions
121
-
122
- versions.map do |version|
123
- Digest::SHA1.hexdigest "#{version} #{backtrace}"
124
- end
125
- end
126
- end
127
-
128
- def is_similar?(other)
129
- self.grouping_key == other.grouping_key
130
- end
131
-
132
- def merge_similar_message(other)
133
- self.first_timestamp ||= self.timestamp
134
- self.timestamp = [self.timestamp, other.timestamp].max
135
-
136
- self.count += other.count || 1
137
- return false if self.count > Logster::MAX_GROUPING_LENGTH
138
-
139
- other_env = JSON.load JSON.fast_generate other.env
140
- if Array === self.env
141
- Array === other_env ? self.env.concat(other_env) : self.env << other_env
142
- else
143
- Array === other_env ? self.env = [self.env, *other_env] : self.env = [self.env, other_env]
144
- end
145
- true
146
- end
147
-
148
- def self.populate_from_env(env)
149
- if Array === env
150
- env.map do |single_env|
151
- self.populate_env_helper(single_env)
152
- end
153
- else
154
- self.populate_env_helper(env)
155
- end
156
- end
157
-
158
- def self.populate_env_helper(env)
159
- env[LOGSTER_ENV] ||= begin
160
- unless env.include? "rack.input"
161
- # Not a web request
162
- return env
163
- end
164
- scrubbed = default_env
165
- request = Rack::Request.new(env)
166
- params = {}
167
- request.params.each do |k, v|
168
- if k.include? "password"
169
- params[k] = "[redacted]"
170
- elsif Array === v
171
- params[k] = v[0..20]
172
- else
173
- params[k] = v && v[0..100]
174
- end
175
- end
176
- scrubbed["params"] = params if params.length > 0
177
- ALLOWED_ENV.map { |k|
178
- scrubbed[k] = env[k] if env[k]
179
- }
180
- scrubbed
181
- end
182
- end
183
-
184
- def <=>(other)
185
- time = self.timestamp <=> other.timestamp
186
- return time if time && time != 0
187
-
188
- self.key <=> other.key
189
- end
190
-
191
- def =~(pattern)
192
- case pattern
193
- when Hash
194
- IgnorePattern.new(nil, pattern).matches? self
195
- when String
196
- IgnorePattern.new(pattern, nil).matches? self
197
- when Regexp
198
- IgnorePattern.new(pattern, nil).matches? self
199
- when IgnorePattern
200
- pattern.matches? self
201
- else
202
- nil
203
- end
204
- end
205
-
206
- protected
207
-
208
- def get_timestamp
209
- (Time.new.to_f * 1000).to_i
210
- end
211
- end
212
- end
1
+ require 'digest/sha1'
2
+ require 'securerandom'
3
+
4
+ module Logster
5
+
6
+ MAX_GROUPING_LENGTH = 50
7
+
8
+ class Message
9
+ LOGSTER_ENV = "_logster_env".freeze
10
+ ALLOWED_ENV = %w{
11
+ HTTP_HOST
12
+ REQUEST_URI
13
+ REQUEST_METHOD
14
+ HTTP_USER_AGENT
15
+ HTTP_ACCEPT
16
+ HTTP_REFERER
17
+ HTTP_X_FORWARDED_FOR
18
+ HTTP_X_REAL_IP
19
+ hostname
20
+ process_id
21
+ application_version
22
+ }
23
+
24
+ attr_accessor :timestamp, :severity, :progname, :message, :key, :backtrace, :count, :env, :protected, :first_timestamp
25
+
26
+ def initialize(severity, progname, message, timestamp = nil, key = nil, count: 1)
27
+ @timestamp = timestamp || get_timestamp
28
+ @severity = severity
29
+ @progname = progname
30
+ @message = message
31
+ @key = key || SecureRandom.hex
32
+ @backtrace = nil
33
+ @count = count || 1
34
+ @protected = false
35
+ @first_timestamp = nil
36
+ end
37
+
38
+ def to_h(exclude_env: false)
39
+ h = {
40
+ message: @message,
41
+ progname: @progname,
42
+ severity: @severity,
43
+ timestamp: @timestamp,
44
+ key: @key,
45
+ backtrace: @backtrace,
46
+ count: @count,
47
+ protected: @protected
48
+ }
49
+
50
+ h[:first_timestamp] = @first_timestamp if @first_timestamp
51
+ h[:env] = @env unless exclude_env
52
+
53
+ h
54
+ end
55
+
56
+ def to_json(opts = nil)
57
+ exclude_env = Hash === opts && opts.delete(:exclude_env)
58
+ JSON.fast_generate(to_h(exclude_env: exclude_env), opts)
59
+ end
60
+
61
+ def self.from_json(json)
62
+ parsed = ::JSON.parse(json)
63
+ msg = new(parsed["severity"],
64
+ parsed["progname"],
65
+ parsed["message"],
66
+ parsed["timestamp"],
67
+ parsed["key"])
68
+ msg.backtrace = parsed["backtrace"]
69
+ msg.env = parsed["env"]
70
+ msg.count = parsed["count"]
71
+ msg.protected = parsed["protected"]
72
+ msg.first_timestamp = parsed["first_timestamp"]
73
+ msg
74
+ end
75
+
76
+ def self.hostname
77
+ @hostname ||= `hostname`.strip! rescue "<unknown>"
78
+ end
79
+
80
+ def populate_from_env(env)
81
+ env ||= {}
82
+ if Array === env
83
+ env = env.map do |single_env|
84
+ self.class.default_env.merge(single_env)
85
+ end
86
+ else
87
+ env = self.class.default_env.merge(env)
88
+ end
89
+ @env = Message.populate_from_env(env)
90
+ end
91
+
92
+ def self.default_env
93
+ env = {
94
+ "hostname" => hostname,
95
+ "process_id" => Process.pid
96
+ }
97
+ env["application_version"] = Logster.config.application_version if Logster.config.application_version
98
+ env
99
+ end
100
+
101
+ # in its own method so it can be overridden
102
+ def grouping_hash
103
+ return { message: self.message, severity: self.severity, backtrace: self.backtrace }
104
+ end
105
+
106
+ # todo - memoize?
107
+ def grouping_key
108
+ Digest::SHA1.hexdigest JSON.fast_generate grouping_hash
109
+ end
110
+
111
+ # todo - memoize?
112
+ def solved_keys
113
+ if Array === env
114
+ versions = env.map { |single_env| single_env["application_version"] }
115
+ else
116
+ versions = env["application_version"]
117
+ end
118
+
119
+ if versions && backtrace && backtrace.length > 0
120
+ versions = [versions] if String === versions
121
+
122
+ versions.map do |version|
123
+ Digest::SHA1.hexdigest "#{version} #{backtrace}"
124
+ end
125
+ end
126
+ end
127
+
128
+ def is_similar?(other)
129
+ self.grouping_key == other.grouping_key
130
+ end
131
+
132
+ def merge_similar_message(other)
133
+ self.first_timestamp ||= self.timestamp
134
+ self.timestamp = [self.timestamp, other.timestamp].max
135
+
136
+ self.count += other.count || 1
137
+ return false if self.count > Logster::MAX_GROUPING_LENGTH
138
+
139
+ other_env = JSON.load JSON.fast_generate other.env
140
+ if Array === self.env
141
+ Array === other_env ? self.env.concat(other_env) : self.env << other_env
142
+ else
143
+ Array === other_env ? self.env = [self.env, *other_env] : self.env = [self.env, other_env]
144
+ end
145
+ true
146
+ end
147
+
148
+ def self.populate_from_env(env)
149
+ if Array === env
150
+ env.map do |single_env|
151
+ self.populate_env_helper(single_env)
152
+ end
153
+ else
154
+ self.populate_env_helper(env)
155
+ end
156
+ end
157
+
158
+ def self.populate_env_helper(env)
159
+ env[LOGSTER_ENV] ||= begin
160
+ unless env.include? "rack.input"
161
+ # Not a web request
162
+ return env
163
+ end
164
+ scrubbed = default_env
165
+ request = Rack::Request.new(env)
166
+ params = {}
167
+ request.params.each do |k, v|
168
+ if k.include? "password"
169
+ params[k] = "[redacted]"
170
+ elsif Array === v
171
+ params[k] = v[0..20]
172
+ else
173
+ params[k] = v && v[0..100]
174
+ end
175
+ end
176
+ scrubbed["params"] = params if params.length > 0
177
+ ALLOWED_ENV.map { |k|
178
+ scrubbed[k] = env[k] if env[k]
179
+ }
180
+ scrubbed
181
+ end
182
+ end
183
+
184
+ def <=>(other)
185
+ time = self.timestamp <=> other.timestamp
186
+ return time if time && time != 0
187
+
188
+ self.key <=> other.key
189
+ end
190
+
191
+ def =~(pattern)
192
+ case pattern
193
+ when Hash
194
+ IgnorePattern.new(nil, pattern).matches? self
195
+ when String
196
+ IgnorePattern.new(pattern, nil).matches? self
197
+ when Regexp
198
+ IgnorePattern.new(pattern, nil).matches? self
199
+ when IgnorePattern
200
+ pattern.matches? self
201
+ else
202
+ nil
203
+ end
204
+ end
205
+
206
+ protected
207
+
208
+ def get_timestamp
209
+ (Time.new.to_f * 1000).to_i
210
+ end
211
+ end
212
+ end
@@ -1,26 +1,26 @@
1
- class Logster::Middleware::DebugExceptions < ActionDispatch::DebugExceptions
2
- private
3
-
4
- def log_error(request_or_env, wrapper)
5
- env =
6
- if Rails::VERSION::MAJOR > 4
7
- request_or_env.env
8
- else
9
- request_or_env
10
- end
11
-
12
- exception = wrapper.exception
13
-
14
- Logster.config.current_context.call(env) do
15
- location = exception.backtrace[0]
16
- exception_string = exception.to_s
17
-
18
- Logster.logger.add_with_opts(::Logger::Severity::FATAL,
19
- exception.class.to_s << " (" << exception_string << ")\n#{location}",
20
- "web-exception",
21
- backtrace: exception.backtrace.join("\n"),
22
- env: env)
23
- end
24
-
25
- end
26
- end
1
+ class Logster::Middleware::DebugExceptions < ActionDispatch::DebugExceptions
2
+ private
3
+
4
+ def log_error(request_or_env, wrapper)
5
+ env =
6
+ if Rails::VERSION::MAJOR > 4
7
+ request_or_env.env
8
+ else
9
+ request_or_env
10
+ end
11
+
12
+ exception = wrapper.exception
13
+
14
+ Logster.config.current_context.call(env) do
15
+ location = exception.backtrace[0]
16
+ exception_string = exception.to_s
17
+
18
+ Logster.logger.add_with_opts(::Logger::Severity::FATAL,
19
+ exception.class.to_s << " (" << exception_string << ")\n#{location}",
20
+ "web-exception",
21
+ backtrace: exception.backtrace.join("\n"),
22
+ env: env)
23
+ end
24
+
25
+ end
26
+ end