logster 2.1.1 → 2.1.2
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/.gitignore +19 -19
- data/.rubocop.yml +1 -1
- data/.travis.yml +16 -16
- data/CHANGELOG.md +172 -169
- data/Gemfile +4 -4
- data/Guardfile +8 -8
- data/LICENSE.txt +22 -22
- data/README.md +99 -99
- data/Rakefile +21 -21
- data/assets/fonts/FontAwesome.otf +0 -0
- data/assets/fonts/fontawesome-webfont.eot +0 -0
- data/assets/fonts/fontawesome-webfont.svg +639 -639
- data/assets/fonts/fontawesome-webfont.ttf +0 -0
- data/assets/fonts/fontawesome-webfont.woff +0 -0
- data/assets/fonts/fontawesome-webfont.woff2 +0 -0
- data/assets/images/Icon-144_rounded.png +0 -0
- data/assets/images/Icon-144_square.png +0 -0
- data/assets/images/icon_144x144.png +0 -0
- data/assets/images/icon_64x64.png +0 -0
- data/assets/javascript/client-app.js +106 -100
- data/assets/stylesheets/client-app.css +1 -1
- data/build_client_app.sh +0 -0
- data/client-app/.editorconfig +20 -20
- data/client-app/.ember-cli +9 -9
- data/client-app/.eslintignore +19 -19
- data/client-app/.eslintrc.js +46 -46
- data/client-app/.gitignore +23 -23
- data/client-app/.travis.yml +27 -27
- data/client-app/.watchmanconfig +3 -3
- data/client-app/README.md +57 -57
- data/client-app/app/app.js +0 -0
- data/client-app/app/components/actions-menu.js +43 -37
- data/client-app/app/components/env-tab.js +80 -44
- data/client-app/app/components/message-info.js +0 -0
- data/client-app/app/components/message-row.js +0 -0
- data/client-app/app/components/panel-resizer.js +0 -0
- data/client-app/app/components/tab-contents.js +27 -27
- data/client-app/app/components/tabbed-section.js +0 -0
- data/client-app/app/components/time-formatter.js +0 -0
- data/client-app/app/components/update-time.js +0 -0
- data/client-app/app/controllers/index.js +0 -0
- data/client-app/app/controllers/show.js +0 -0
- data/client-app/app/index.html +29 -29
- data/client-app/app/initializers/app-init.js +67 -72
- data/client-app/app/lib/preload.js +20 -14
- data/client-app/app/lib/utilities.js +149 -140
- data/client-app/app/models/message-collection.js +0 -0
- data/client-app/app/models/message.js +100 -100
- data/client-app/app/resolver.js +0 -0
- data/client-app/app/router.js +0 -0
- data/client-app/app/routes/index.js +0 -0
- data/client-app/app/routes/show.js +0 -0
- data/client-app/app/styles/app.css +527 -521
- data/client-app/app/templates/application.hbs +2 -2
- data/client-app/app/templates/components/actions-menu.hbs +12 -12
- data/client-app/app/templates/components/env-tab.hbs +10 -10
- data/client-app/app/templates/components/message-info.hbs +41 -41
- data/client-app/app/templates/components/message-row.hbs +15 -15
- data/client-app/app/templates/components/panel-resizer.hbs +3 -3
- data/client-app/app/templates/components/tabbed-section.hbs +10 -10
- data/client-app/app/templates/components/time-formatter.hbs +1 -1
- data/client-app/app/templates/index.hbs +58 -58
- data/client-app/app/templates/show.hbs +7 -7
- data/client-app/config/environment.js +51 -51
- data/client-app/config/optional-features.json +3 -3
- data/client-app/config/targets.js +18 -18
- data/client-app/ember-cli-build.js +29 -29
- data/client-app/package-lock.json +11365 -11365
- data/client-app/package.json +56 -56
- data/client-app/testem.js +25 -25
- data/client-app/tests/index.html +34 -34
- data/client-app/tests/integration/components/env-tab-test.js +123 -73
- data/client-app/tests/integration/components/message-info-test.js +111 -26
- data/client-app/tests/test-helper.js +8 -8
- data/client-app/tests/unit/controllers/index-test.js +12 -12
- data/client-app/tests/unit/controllers/show-test.js +12 -12
- data/client-app/tests/unit/initializers/app-init-test.js +31 -31
- data/client-app/tests/unit/routes/index-test.js +11 -11
- data/client-app/tests/unit/routes/show-test.js +11 -11
- data/lib/examples/sidekiq_logster_reporter.rb +21 -21
- data/lib/logster.rb +54 -54
- data/lib/logster/base_store.rb +141 -141
- data/lib/logster/configuration.rb +26 -25
- data/lib/logster/defer_logger.rb +14 -14
- data/lib/logster/ignore_pattern.rb +65 -65
- data/lib/logster/logger.rb +113 -113
- data/lib/logster/message.rb +212 -212
- data/lib/logster/middleware/debug_exceptions.rb +26 -26
- data/lib/logster/middleware/reporter.rb +55 -55
- data/lib/logster/middleware/viewer.rb +222 -221
- data/lib/logster/rails/railtie.rb +63 -63
- data/lib/logster/redis_store.rb +566 -566
- data/lib/logster/scheduler.rb +54 -54
- data/lib/logster/version.rb +3 -3
- data/lib/logster/web.rb +14 -14
- data/logster.gemspec +35 -35
- data/test/examples/test_sidekiq_reporter_example.rb +46 -46
- data/test/fake_data/Gemfile +4 -4
- data/test/fake_data/generate.rb +10 -10
- data/test/logster/middleware/test_reporter.rb +19 -19
- data/test/logster/middleware/test_viewer.rb +96 -96
- data/test/logster/test_base_store.rb +147 -147
- data/test/logster/test_defer_logger.rb +34 -34
- data/test/logster/test_ignore_pattern.rb +41 -41
- data/test/logster/test_logger.rb +86 -86
- data/test/logster/test_message.rb +119 -119
- data/test/logster/test_redis_rate_limiter.rb +230 -230
- data/test/logster/test_redis_store.rb +720 -720
- data/test/test_helper.rb +38 -38
- data/vendor/assets/javascripts/logster.js.erb +39 -39
- metadata +1 -10
- data/client-app/app/components/tab-link.js +0 -5
- data/client-app/tests/integration/components/actions-menu-test.js +0 -26
- data/client-app/tests/integration/components/message-row-test.js +0 -26
- data/client-app/tests/integration/components/panel-resizer-test.js +0 -26
- data/client-app/tests/integration/components/tab-contents-test.js +0 -26
- data/client-app/tests/integration/components/tab-link-test.js +0 -26
- data/client-app/tests/integration/components/tabbed-section-test.js +0 -26
- data/client-app/tests/integration/components/time-formatter-test.js +0 -26
- data/client-app/tests/integration/components/update-time-test.js +0 -26
|
@@ -1,25 +1,26 @@
|
|
|
1
|
-
module Logster
|
|
2
|
-
class Configuration
|
|
3
|
-
attr_accessor :current_context, :allow_grouping, :environments,
|
|
4
|
-
:application_version, :web_title
|
|
5
|
-
|
|
6
|
-
attr_writer :subdirectory
|
|
7
|
-
|
|
8
|
-
def initialize
|
|
9
|
-
# lambda |env,block|
|
|
10
|
-
@current_context = lambda { |_, &block| block.call }
|
|
11
|
-
@environments = [:development, :production]
|
|
12
|
-
@subdirectory = nil
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
end
|
|
1
|
+
module Logster
|
|
2
|
+
class Configuration
|
|
3
|
+
attr_accessor :current_context, :allow_grouping, :environments,
|
|
4
|
+
:application_version, :web_title, :env_expandable_keys
|
|
5
|
+
|
|
6
|
+
attr_writer :subdirectory
|
|
7
|
+
|
|
8
|
+
def initialize
|
|
9
|
+
# lambda |env,block|
|
|
10
|
+
@current_context = lambda { |_, &block| block.call }
|
|
11
|
+
@environments = [:development, :production]
|
|
12
|
+
@subdirectory = nil
|
|
13
|
+
@env_expandable_keys = []
|
|
14
|
+
|
|
15
|
+
@allow_grouping = false
|
|
16
|
+
|
|
17
|
+
if defined?(::Rails) && defined?(::Rails.env) && ::Rails.env.production?
|
|
18
|
+
@allow_grouping = true
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def subdirectory
|
|
23
|
+
@subdirectory || '/logs'
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
data/lib/logster/defer_logger.rb
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
require 'logster/scheduler'
|
|
2
|
-
|
|
3
|
-
module Logster
|
|
4
|
-
class DeferLogger < ::Logster::Logger
|
|
5
|
-
private
|
|
6
|
-
|
|
7
|
-
def report_to_store(severity, progname, message, opts = {})
|
|
8
|
-
opts[:backtrace] ||= caller
|
|
9
|
-
Logster::Scheduler.schedule do
|
|
10
|
-
super(severity, progname, message, opts)
|
|
11
|
-
end
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
end
|
|
1
|
+
require 'logster/scheduler'
|
|
2
|
+
|
|
3
|
+
module Logster
|
|
4
|
+
class DeferLogger < ::Logster::Logger
|
|
5
|
+
private
|
|
6
|
+
|
|
7
|
+
def report_to_store(severity, progname, message, opts = {})
|
|
8
|
+
opts[:backtrace] ||= caller
|
|
9
|
+
Logster::Scheduler.schedule do
|
|
10
|
+
super(severity, progname, message, opts)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -1,65 +1,65 @@
|
|
|
1
|
-
module Logster
|
|
2
|
-
class IgnorePattern
|
|
3
|
-
|
|
4
|
-
def initialize(message_pattern = nil, env_patterns = nil)
|
|
5
|
-
@msg_match = message_pattern
|
|
6
|
-
@env_match = env_patterns
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
def self.from_message_and_request_uri(msg, request)
|
|
10
|
-
IgnorePattern.new(msg, REQUEST_URI: request)
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def matches?(message)
|
|
14
|
-
if @msg_match
|
|
15
|
-
return false unless compare(message.message, @msg_match)
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
if @env_match
|
|
19
|
-
return false unless compare(message.env, @env_match)
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
true
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def to_s
|
|
26
|
-
"<#Logster::IgnorePattern, msg_match: #{@msg_match.inspect}, env_match: #{@env_match.inspect}>"
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
private
|
|
30
|
-
|
|
31
|
-
def compare(message, pattern)
|
|
32
|
-
return false unless message && pattern
|
|
33
|
-
|
|
34
|
-
case pattern
|
|
35
|
-
when Regexp
|
|
36
|
-
message.to_s =~ pattern
|
|
37
|
-
when String
|
|
38
|
-
message.to_s =~ Regexp.new(pattern, Regexp::IGNORECASE)
|
|
39
|
-
when Hash
|
|
40
|
-
if Hash === message
|
|
41
|
-
compare_hash(message, pattern)
|
|
42
|
-
else
|
|
43
|
-
false
|
|
44
|
-
end
|
|
45
|
-
else
|
|
46
|
-
false
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def compare_hash(message_hash, pattern_hash)
|
|
51
|
-
return false unless message_hash
|
|
52
|
-
pattern_hash.each do |key, value|
|
|
53
|
-
return false unless compare(get_indifferent(message_hash, key), value)
|
|
54
|
-
end
|
|
55
|
-
true
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
def get_indifferent(hash, key)
|
|
59
|
-
return hash[key] if hash[key]
|
|
60
|
-
return hash[key.to_s] if hash[key.to_s]
|
|
61
|
-
# no key.to_sym please, memory leak in Ruby < 2.2
|
|
62
|
-
nil
|
|
63
|
-
end
|
|
64
|
-
end
|
|
65
|
-
end
|
|
1
|
+
module Logster
|
|
2
|
+
class IgnorePattern
|
|
3
|
+
|
|
4
|
+
def initialize(message_pattern = nil, env_patterns = nil)
|
|
5
|
+
@msg_match = message_pattern
|
|
6
|
+
@env_match = env_patterns
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def self.from_message_and_request_uri(msg, request)
|
|
10
|
+
IgnorePattern.new(msg, REQUEST_URI: request)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def matches?(message)
|
|
14
|
+
if @msg_match
|
|
15
|
+
return false unless compare(message.message, @msg_match)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
if @env_match
|
|
19
|
+
return false unless compare(message.env, @env_match)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
true
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def to_s
|
|
26
|
+
"<#Logster::IgnorePattern, msg_match: #{@msg_match.inspect}, env_match: #{@env_match.inspect}>"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def compare(message, pattern)
|
|
32
|
+
return false unless message && pattern
|
|
33
|
+
|
|
34
|
+
case pattern
|
|
35
|
+
when Regexp
|
|
36
|
+
message.to_s =~ pattern
|
|
37
|
+
when String
|
|
38
|
+
message.to_s =~ Regexp.new(pattern, Regexp::IGNORECASE)
|
|
39
|
+
when Hash
|
|
40
|
+
if Hash === message
|
|
41
|
+
compare_hash(message, pattern)
|
|
42
|
+
else
|
|
43
|
+
false
|
|
44
|
+
end
|
|
45
|
+
else
|
|
46
|
+
false
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def compare_hash(message_hash, pattern_hash)
|
|
51
|
+
return false unless message_hash
|
|
52
|
+
pattern_hash.each do |key, value|
|
|
53
|
+
return false unless compare(get_indifferent(message_hash, key), value)
|
|
54
|
+
end
|
|
55
|
+
true
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def get_indifferent(hash, key)
|
|
59
|
+
return hash[key] if hash[key]
|
|
60
|
+
return hash[key.to_s] if hash[key.to_s]
|
|
61
|
+
# no key.to_sym please, memory leak in Ruby < 2.2
|
|
62
|
+
nil
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
data/lib/logster/logger.rb
CHANGED
|
@@ -1,113 +1,113 @@
|
|
|
1
|
-
require 'logger'
|
|
2
|
-
|
|
3
|
-
module Logster
|
|
4
|
-
class Logger < ::Logger
|
|
5
|
-
LOGSTER_ENV = "logster_env".freeze
|
|
6
|
-
|
|
7
|
-
attr_accessor :store, :skip_store
|
|
8
|
-
attr_reader :chained
|
|
9
|
-
|
|
10
|
-
def initialize(store)
|
|
11
|
-
super(nil)
|
|
12
|
-
@store = store
|
|
13
|
-
@override_levels = nil
|
|
14
|
-
@chained = []
|
|
15
|
-
@skip_store = false
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def override_level=(val)
|
|
19
|
-
tid = Thread.current.object_id
|
|
20
|
-
|
|
21
|
-
ol = @override_levels
|
|
22
|
-
if val.nil? && ol && ol.key?(tid)
|
|
23
|
-
ol.delete(tid)
|
|
24
|
-
@override_levels = nil if ol.length == 0
|
|
25
|
-
elsif val
|
|
26
|
-
(@override_levels ||= {})[tid] = val
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def chain(logger)
|
|
31
|
-
@chained << logger
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def add_to_chained(logger, severity, message, progname, opts = nil, &block)
|
|
35
|
-
if logger.respond_to? :skip_store
|
|
36
|
-
old = logger.skip_store
|
|
37
|
-
logger.skip_store = @skip_store
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
if logger.is_a?(self.class)
|
|
41
|
-
logger.add(severity, message, progname, opts, &block)
|
|
42
|
-
else
|
|
43
|
-
logger.add(severity, message, progname, &block)
|
|
44
|
-
end
|
|
45
|
-
ensure
|
|
46
|
-
if logger.respond_to? :skip_store
|
|
47
|
-
logger.skip_store = old
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
def add(*args, &block)
|
|
52
|
-
add_with_opts(*args, &block)
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
def level
|
|
56
|
-
ol = @override_levels
|
|
57
|
-
(ol && ol[Thread.current.object_id]) || @level
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
def add_with_opts(severity, message, progname = progname(), opts = nil, &block)
|
|
61
|
-
if severity < level
|
|
62
|
-
return true
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
# it is not fun losing messages cause encoding is bad
|
|
66
|
-
# protect all messages by scrubbing if needed
|
|
67
|
-
if message && !message.valid_encoding?
|
|
68
|
-
message = message.scrub
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
if @chained
|
|
72
|
-
i = 0
|
|
73
|
-
# micro optimise for logging
|
|
74
|
-
while i < @chained.length
|
|
75
|
-
# TODO double yielding blocks
|
|
76
|
-
begin
|
|
77
|
-
add_to_chained(@chained[i], severity, message, progname, opts, &block)
|
|
78
|
-
rescue => e
|
|
79
|
-
# don't blow up if STDERR is somehow closed
|
|
80
|
-
STDERR.puts "Failed to report message to chained logger #{e}" rescue nil
|
|
81
|
-
end
|
|
82
|
-
i += 1
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
progname ||= @progname
|
|
87
|
-
if message.nil?
|
|
88
|
-
if block_given?
|
|
89
|
-
message = yield
|
|
90
|
-
else
|
|
91
|
-
message = progname
|
|
92
|
-
progname = @progname
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
return if @skip_store
|
|
97
|
-
|
|
98
|
-
opts ||= {}
|
|
99
|
-
opts[:env] ||= Thread.current[LOGSTER_ENV]
|
|
100
|
-
|
|
101
|
-
report_to_store(severity, progname, message, opts)
|
|
102
|
-
rescue => e
|
|
103
|
-
# don't blow up if STDERR is somehow closed
|
|
104
|
-
STDERR.puts "Failed to report error: #{e} #{severity} #{message} #{progname}" rescue nil
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
private
|
|
108
|
-
|
|
109
|
-
def report_to_store(severity, progname, message, opts = {})
|
|
110
|
-
@store.report(severity, progname, message, opts)
|
|
111
|
-
end
|
|
112
|
-
end
|
|
113
|
-
end
|
|
1
|
+
require 'logger'
|
|
2
|
+
|
|
3
|
+
module Logster
|
|
4
|
+
class Logger < ::Logger
|
|
5
|
+
LOGSTER_ENV = "logster_env".freeze
|
|
6
|
+
|
|
7
|
+
attr_accessor :store, :skip_store
|
|
8
|
+
attr_reader :chained
|
|
9
|
+
|
|
10
|
+
def initialize(store)
|
|
11
|
+
super(nil)
|
|
12
|
+
@store = store
|
|
13
|
+
@override_levels = nil
|
|
14
|
+
@chained = []
|
|
15
|
+
@skip_store = false
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def override_level=(val)
|
|
19
|
+
tid = Thread.current.object_id
|
|
20
|
+
|
|
21
|
+
ol = @override_levels
|
|
22
|
+
if val.nil? && ol && ol.key?(tid)
|
|
23
|
+
ol.delete(tid)
|
|
24
|
+
@override_levels = nil if ol.length == 0
|
|
25
|
+
elsif val
|
|
26
|
+
(@override_levels ||= {})[tid] = val
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def chain(logger)
|
|
31
|
+
@chained << logger
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def add_to_chained(logger, severity, message, progname, opts = nil, &block)
|
|
35
|
+
if logger.respond_to? :skip_store
|
|
36
|
+
old = logger.skip_store
|
|
37
|
+
logger.skip_store = @skip_store
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
if logger.is_a?(self.class)
|
|
41
|
+
logger.add(severity, message, progname, opts, &block)
|
|
42
|
+
else
|
|
43
|
+
logger.add(severity, message, progname, &block)
|
|
44
|
+
end
|
|
45
|
+
ensure
|
|
46
|
+
if logger.respond_to? :skip_store
|
|
47
|
+
logger.skip_store = old
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def add(*args, &block)
|
|
52
|
+
add_with_opts(*args, &block)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def level
|
|
56
|
+
ol = @override_levels
|
|
57
|
+
(ol && ol[Thread.current.object_id]) || @level
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def add_with_opts(severity, message, progname = progname(), opts = nil, &block)
|
|
61
|
+
if severity < level
|
|
62
|
+
return true
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# it is not fun losing messages cause encoding is bad
|
|
66
|
+
# protect all messages by scrubbing if needed
|
|
67
|
+
if message && !message.valid_encoding?
|
|
68
|
+
message = message.scrub
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
if @chained
|
|
72
|
+
i = 0
|
|
73
|
+
# micro optimise for logging
|
|
74
|
+
while i < @chained.length
|
|
75
|
+
# TODO double yielding blocks
|
|
76
|
+
begin
|
|
77
|
+
add_to_chained(@chained[i], severity, message, progname, opts, &block)
|
|
78
|
+
rescue => e
|
|
79
|
+
# don't blow up if STDERR is somehow closed
|
|
80
|
+
STDERR.puts "Failed to report message to chained logger #{e}" rescue nil
|
|
81
|
+
end
|
|
82
|
+
i += 1
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
progname ||= @progname
|
|
87
|
+
if message.nil?
|
|
88
|
+
if block_given?
|
|
89
|
+
message = yield
|
|
90
|
+
else
|
|
91
|
+
message = progname
|
|
92
|
+
progname = @progname
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
return if @skip_store
|
|
97
|
+
|
|
98
|
+
opts ||= {}
|
|
99
|
+
opts[:env] ||= Thread.current[LOGSTER_ENV]
|
|
100
|
+
|
|
101
|
+
report_to_store(severity, progname, message, opts)
|
|
102
|
+
rescue => e
|
|
103
|
+
# don't blow up if STDERR is somehow closed
|
|
104
|
+
STDERR.puts "Failed to report error: #{e} #{severity} #{message} #{progname}" rescue nil
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
private
|
|
108
|
+
|
|
109
|
+
def report_to_store(severity, progname, message, opts = {})
|
|
110
|
+
@store.report(severity, progname, message, opts)
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
data/lib/logster/message.rb
CHANGED
|
@@ -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
|