logster 2.1.2 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
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