resurfaceio-logger 1.9.1 → 2.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f745e986a52a415313ca922f66c119d3de17234d1c9c7c654e0dd104be45237f
4
- data.tar.gz: 8a48a6124d974ef7082566aef5c4190d5de17d6b58f1e8b950efa61620f96646
3
+ metadata.gz: 96d9dcd75c1689a944d72d80ed12692efbdc75b8c10b391bece364c26090efc1
4
+ data.tar.gz: 6851901ff3dba16d290c479cd8262f6ff3789997089bb213ead746ccb74ebb93
5
5
  SHA512:
6
- metadata.gz: 35375e533763567753fcb9947a144b81d0908c2b812a96dc3f1c17c11cbdb3ec6f97e3ace13896f6fe5eec71f22d80efea4ed1bbc913e93aeb9907ab9d7e7435
7
- data.tar.gz: 75b8e7e419e755621b78b3f602a40deadfef1a78ee57e86caf4a5528c6dfa7655901991f1fc4f019b310f763fb3881ed6bd6358b4ff54a18df5c268d0e98bccf
6
+ metadata.gz: bf5eb2c3775d016416f6e55a35a1422f4ae33d89887ffe7bf69ac280e4f569d98eed2aacb4715efcf20af4e07527726f262ad268131605174d038231a152ce3c
7
+ data.tar.gz: 715e09307d21578168425a94ef905a00bcf5497153e3902d2a3a5b0f8e058caa8d14a158ea571b0fbc81c04dd1a6c2416a4342c892a941eaf0c26fbcdb0972a1
@@ -1,5 +1,5 @@
1
1
  # coding: utf-8
2
- # © 2016-2019 Resurface Labs Inc.
2
+ # © 2016-2020 Resurface Labs Inc.
3
3
 
4
4
  require 'resurfaceio/base_logger'
5
5
  require 'resurfaceio/http_logger'
@@ -1,9 +1,10 @@
1
1
  # coding: utf-8
2
- # © 2016-2019 Resurface Labs Inc.
2
+ # © 2016-2020 Resurface Labs Inc.
3
3
 
4
4
  require 'uri'
5
5
  require 'net/http'
6
6
  require 'net/https'
7
+ require 'socket'
7
8
  require 'zlib'
8
9
  require 'resurfaceio/usage_loggers'
9
10
 
@@ -11,6 +12,7 @@ class BaseLogger
11
12
 
12
13
  def initialize(agent, options = {})
13
14
  @agent = agent
15
+ @host = BaseLogger.host_lookup
14
16
  @skip_compression = false
15
17
  @skip_submission = false
16
18
  @version = BaseLogger.version_lookup
@@ -49,7 +51,12 @@ class BaseLogger
49
51
  end
50
52
  end
51
53
 
54
+ # finalize internal properties
52
55
  @enableable = !@queue.nil? || !@url.nil?
56
+ @submit_failures = 0
57
+ @submit_failures_lock = Mutex.new
58
+ @submit_successes = 0
59
+ @submit_successes_lock = Mutex.new
53
60
  end
54
61
 
55
62
  def agent
@@ -74,6 +81,14 @@ class BaseLogger
74
81
  @enabled && UsageLoggers.enabled?
75
82
  end
76
83
 
84
+ def host
85
+ @host
86
+ end
87
+
88
+ def queue
89
+ @queue
90
+ end
91
+
77
92
  def skip_compression?
78
93
  @skip_compression
79
94
  end
@@ -90,33 +105,46 @@ class BaseLogger
90
105
  @skip_submission = value
91
106
  end
92
107
 
93
- def submit(json)
94
- if json.nil? || @skip_submission || !enabled?
95
- true
108
+ def submit(msg)
109
+ if msg.nil? || @skip_submission || !enabled?
110
+ # do nothing
96
111
  elsif @queue
97
- @queue << json
98
- true
112
+ @queue << msg
113
+ @submit_successes_lock.synchronize { @submit_successes += 1 }
99
114
  else
100
115
  begin
101
116
  @url_parsed ||= URI.parse(@url)
102
117
  @url_connection ||= Net::HTTP.new(@url_parsed.host, @url_parsed.port)
103
118
  @url_connection.use_ssl = @url.include?('https')
104
119
  request = Net::HTTP::Post.new(@url_parsed.path)
120
+ request.add_field('Content-Type', 'application/json; charset=UTF-8')
105
121
  if @skip_compression
106
- request.body = json
122
+ request.body = msg
107
123
  else
108
124
  request.add_field('Content-Encoding', 'deflated')
109
- request.body = Zlib::Deflate.deflate(json)
125
+ request.body = Zlib::Deflate.deflate(msg)
110
126
  end
111
127
  response = @url_connection.request(request)
112
- response.code.to_i == 204
128
+ if response.code.to_i == 204
129
+ @submit_successes_lock.synchronize { @submit_successes += 1 }
130
+ else
131
+ @submit_failures_lock.synchronize { @submit_failures += 1 }
132
+ end
113
133
  rescue SocketError
134
+ @submit_failures_lock.synchronize { @submit_failures += 1 }
114
135
  @url_connection = nil
115
- false
116
136
  end
117
137
  end
118
138
  end
119
139
 
140
+ def submit_failures
141
+ @submit_failures
142
+ end
143
+
144
+ def submit_successes
145
+ @submit_successes
146
+ end
147
+
120
148
  def url
121
149
  @url
122
150
  end
@@ -125,6 +153,16 @@ class BaseLogger
125
153
  @version
126
154
  end
127
155
 
156
+ def self.host_lookup
157
+ dyno = ENV['DYNO']
158
+ return dyno unless dyno.nil?
159
+ begin
160
+ Socket.gethostname
161
+ rescue
162
+ 'unknown'
163
+ end
164
+ end
165
+
128
166
  def self.version_lookup
129
167
  Gem.loaded_specs['resurfaceio-logger'].version.to_s
130
168
  end
@@ -1,5 +1,5 @@
1
1
  # coding: utf-8
2
- # © 2016-2019 Resurface Labs Inc.
2
+ # © 2016-2020 Resurface Labs Inc.
3
3
 
4
4
  require 'json'
5
5
  require 'resurfaceio/base_logger'
@@ -11,53 +11,20 @@ class HttpLogger < BaseLogger
11
11
 
12
12
  AGENT = 'http_logger.rb'.freeze
13
13
 
14
- @@default_rules = HttpRules.strict_rules
15
-
16
- def self.default_rules
17
- @@default_rules
18
- end
19
-
20
- def self.default_rules=(val)
21
- @@default_rules = val.gsub(/^\s*include default\s*$/, '')
22
- end
23
-
24
- def self.string_content_type?(s)
25
- !s.nil? && !(s =~ /^(text\/(html|plain|xml))|(application\/(json|soap|xml|x-www-form-urlencoded))/i).nil?
26
- end
27
-
28
14
  def initialize(options = {})
29
15
  super(AGENT, options)
30
16
 
31
- # read rules from param or defaults
17
+ # parse specified rules
32
18
  if options.respond_to?(:has_key?) && options.has_key?(:rules)
33
- @rules = options[:rules].gsub(/^\s*include default\s*$/, @@default_rules)
34
- @rules = @@default_rules unless @rules.strip.length > 0
19
+ @rules = HttpRules.new(options[:rules])
35
20
  else
36
- @rules = @@default_rules
21
+ @rules = HttpRules.new(nil)
37
22
  end
38
23
 
39
- # parse and break rules out by verb
40
- prs = HttpRules.parse(@rules)
41
- @rules_allow_http_url = prs.select {|r| 'allow_http_url' == r.verb}.length > 0
42
- @rules_copy_session_field = prs.select {|r| 'copy_session_field' == r.verb}
43
- @rules_remove = prs.select {|r| 'remove' == r.verb}
44
- @rules_remove_if = prs.select {|r| 'remove_if' == r.verb}
45
- @rules_remove_if_found = prs.select {|r| 'remove_if_found' == r.verb}
46
- @rules_remove_unless = prs.select {|r| 'remove_unless' == r.verb}
47
- @rules_remove_unless_found = prs.select {|r| 'remove_unless_found' == r.verb}
48
- @rules_replace = prs.select {|r| 'replace' == r.verb}
49
- @rules_sample = prs.select {|r| 'sample' == r.verb}
50
- @rules_stop = prs.select {|r| 'stop' == r.verb}
51
- @rules_stop_if = prs.select {|r| 'stop_if' == r.verb}
52
- @rules_stop_if_found = prs.select {|r| 'stop_if_found' == r.verb}
53
- @rules_stop_unless = prs.select {|r| 'stop_unless' == r.verb}
54
- @rules_stop_unless_found = prs.select {|r| 'stop_unless_found' == r.verb}
55
- @skip_compression = prs.select {|r| 'skip_compression' == r.verb}.length > 0
56
- @skip_submission = prs.select {|r| 'skip_submission' == r.verb}.length > 0
57
-
58
- # finish validating rules
59
- raise RuntimeError.new('Multiple sample rules') if @rules_sample.length > 1
60
- unless @url.nil? || @url.start_with?('https') || @rules_allow_http_url
24
+ # apply configuration rules
25
+ @skip_compression = @rules.skip_compression
26
+ @skip_submission = @rules.skip_submission
27
+ unless @url.nil? || @url.start_with?('https') || @rules.allow_http_url
61
28
  @enableable = false
62
29
  @enabled = false
63
30
  end
@@ -67,57 +34,21 @@ class HttpLogger < BaseLogger
67
34
  @rules
68
35
  end
69
36
 
70
- def log(request, response, response_body = nil, request_body = nil)
71
- !enabled? || submit(format(request, response, response_body, request_body))
72
- end
73
-
74
- def format(request, response, response_body = nil, request_body = nil, now = nil)
75
- details = HttpMessage.build(request, response, response_body, request_body)
37
+ def submit_if_passing(details)
38
+ # apply active rules
39
+ details = @rules.apply(details)
40
+ return nil if details.nil?
76
41
 
77
- # copy data from session if configured
78
- unless @rules_copy_session_field.empty?
79
- ssn = request.session
80
- if !ssn.nil? && ssn.respond_to?(:keys)
81
- @rules_copy_session_field.each do |r|
82
- ssn.keys.each {|d| (details << ["session_field:#{d}", ssn[d].to_s]) if r.param1.match(d)}
83
- end
84
- end
85
- end
86
-
87
- # quit early based on stop rules if configured
88
- @rules_stop.each {|r| details.each {|d| return nil if r.scope.match(d[0])}}
89
- @rules_stop_if_found.each {|r| details.each {|d| return nil if r.scope.match(d[0]) && r.param1.match(d[1])}}
90
- @rules_stop_if.each {|r| details.each {|d| return nil if r.scope.match(d[0]) && r.param1.match(d[1])}}
91
- passed = 0
92
- @rules_stop_unless_found.each {|r| details.each {|d| passed += 1 if r.scope.match(d[0]) && r.param1.match(d[1])}}
93
- return nil if passed != @rules_stop_unless_found.length
94
- passed = 0
95
- @rules_stop_unless.each {|r| details.each {|d| passed += 1 if r.scope.match(d[0]) && r.param1.match(d[1])}}
96
- return nil if passed != @rules_stop_unless.length
97
-
98
- # do sampling if configured
99
- return nil if !@rules_sample[0].nil? && (rand * 100 >= @rules_sample[0].param1)
100
-
101
- # winnow sensitive details based on remove rules if configured
102
- @rules_remove.each {|r| details.delete_if {|d| r.scope.match(d[0])}}
103
- @rules_remove_unless_found.each {|r| details.delete_if {|d| r.scope.match(d[0]) && !r.param1.match(d[1])}}
104
- @rules_remove_if_found.each {|r| details.delete_if {|d| r.scope.match(d[0]) && r.param1.match(d[1])}}
105
- @rules_remove_unless.each {|r| details.delete_if {|d| r.scope.match(d[0]) && !r.param1.match(d[1])}}
106
- @rules_remove_if.each {|r| details.delete_if {|d| r.scope.match(d[0]) && r.param1.match(d[1])}}
107
- return nil if details.empty?
108
-
109
- # mask sensitive details based on replace rules if configured
110
- @rules_replace.each {|r| details.each {|d| d[1] = d[1].gsub(r.param1, r.param2) if r.scope.match(d[0])}}
111
-
112
- # remove any details with empty values
113
- details.delete_if {|d| '' == d[1]}
114
- return nil if details.empty?
115
-
116
- # finish message
117
- details << ['now', now.nil? ? (Time.now.to_f * 1000).floor.to_s : now]
42
+ # finalize message
118
43
  details << ['agent', @agent]
44
+ details << ['host', @host]
119
45
  details << ['version', @version]
120
- JSON.generate details
46
+
47
+ # let's do this thing
48
+ submit(JSON.generate(details))
121
49
  end
122
50
 
51
+ def self.string_content_type?(s)
52
+ !s.nil? && !(s =~ /^(text\/(html|plain|xml))|(application\/(json|soap|xml|x-www-form-urlencoded))/i).nil?
53
+ end
123
54
  end
@@ -1,8 +1,10 @@
1
1
  # coding: utf-8
2
- # © 2016-2019 Resurface Labs Inc.
2
+ # © 2016-2020 Resurface Labs Inc.
3
3
 
4
4
  require 'rack'
5
5
  require 'resurfaceio/http_logger'
6
+ require 'resurfaceio/http_message'
7
+ require 'resurfaceio/timer'
6
8
 
7
9
  class HttpLoggerForRack # http://rack.rubyforge.org/doc/SPEC.html
8
10
 
@@ -16,12 +18,13 @@ class HttpLoggerForRack # http://rack.rubyforge.org/doc/SPEC.html
16
18
  end
17
19
 
18
20
  def call(env)
21
+ timer = Timer.new
19
22
  status, headers, body = @app.call(env)
20
23
  if @logger.enabled? && (status < 300 || status == 302)
21
24
  response = Rack::Response.new(body, status, headers)
22
25
  if HttpLogger::string_content_type?(response.content_type)
23
26
  request = Rack::Request.new(env)
24
- @logger.submit(@logger.format(request, response))
27
+ HttpMessage.send(logger, request, response, nil, nil, nil, timer.millis)
25
28
  end
26
29
  end
27
30
  [status, headers, body]
@@ -1,7 +1,9 @@
1
1
  # coding: utf-8
2
- # © 2016-2019 Resurface Labs Inc.
2
+ # © 2016-2020 Resurface Labs Inc.
3
3
 
4
4
  require 'resurfaceio/http_logger'
5
+ require 'resurfaceio/http_message'
6
+ require 'resurfaceio/timer'
5
7
 
6
8
  class HttpLoggerForRails
7
9
 
@@ -14,13 +16,14 @@ class HttpLoggerForRails
14
16
  end
15
17
 
16
18
  def around(controller)
19
+ timer = Timer.new
17
20
  yield
18
21
  if @logger.enabled?
19
22
  request = controller.request
20
23
  response = controller.response
21
24
  status = response.status
22
25
  if (status < 300 || status == 302) && HttpLogger::string_content_type?(response.content_type)
23
- @logger.submit(@logger.format(request, response))
26
+ HttpMessage.send(logger, request, response, nil, nil, nil, timer.millis)
24
27
  end
25
28
  end
26
29
  end
@@ -1,15 +1,38 @@
1
1
  # coding: utf-8
2
- # © 2016-2019 Resurface Labs Inc.
2
+ # © 2016-2020 Resurface Labs Inc.
3
3
 
4
4
  require 'json'
5
5
 
6
6
  class HttpMessage
7
7
 
8
+ def self.send(logger, request, response, response_body = nil, request_body = nil, now = nil, interval = nil)
9
+ return unless logger.enabled?
10
+
11
+ # copy details from request & response
12
+ message = build(request, response, response_body, request_body)
13
+
14
+ # copy details from active session
15
+ unless logger.rules.copy_session_field.empty?
16
+ ssn = request.session
17
+ if !ssn.nil? && ssn.respond_to?(:keys)
18
+ logger.rules.copy_session_field.each do |r|
19
+ ssn.keys.each {|d| (message << ["session_field:#{d}", ssn[d].to_s]) if r.param1.match(d)}
20
+ end
21
+ end
22
+ end
23
+
24
+ # add timing details
25
+ message << ['now', now.nil? ? (Time.now.to_f * 1000).floor.to_s : now]
26
+ message << ['interval', interval] unless interval.nil?
27
+
28
+ logger.submit_if_passing(message)
29
+ end
30
+
8
31
  def self.build(request, response, response_body = nil, request_body = nil)
9
32
  message = []
10
- append_value message, 'request_method', request.request_method
11
- append_value message, 'request_url', request.url
12
- append_value message, 'response_code', response.status
33
+ append_value message, 'request_method', request.request_method unless request.request_method.nil?
34
+ append_value message, 'request_url', request.url unless request.url.nil?
35
+ append_value message, 'response_code', response.status unless response.status.nil?
13
36
  append_request_headers message, request
14
37
  append_request_params message, request
15
38
  append_response_headers message, response
@@ -19,8 +42,6 @@ class HttpMessage
19
42
  return message
20
43
  end
21
44
 
22
- protected
23
-
24
45
  def self.append_request_headers(message, request)
25
46
  respond_to_env = request.respond_to?(:env)
26
47
  if respond_to_env || request.respond_to?(:headers)
@@ -74,12 +95,12 @@ class HttpMessage
74
95
  unless key.nil?
75
96
  unless value.nil?
76
97
  case value
77
- when Array
78
- message << [key, value.join]
79
- when String
80
- message << [key, value]
81
- else
82
- message << [key, value.to_s]
98
+ when Array
99
+ message << [key, value.join]
100
+ when String
101
+ message << [key, value]
102
+ else
103
+ message << [key, value.to_s]
83
104
  end
84
105
  end
85
106
  end
@@ -1,5 +1,5 @@
1
1
  # coding: utf-8
2
- # © 2016-2019 Resurface Labs Inc.
2
+ # © 2016-2020 Resurface Labs Inc.
3
3
 
4
4
  class HttpRequestImpl
5
5
 
@@ -1,5 +1,5 @@
1
1
  # coding: utf-8
2
- # © 2016-2019 Resurface Labs Inc.
2
+ # © 2016-2020 Resurface Labs Inc.
3
3
 
4
4
  class HttpResponseImpl
5
5
 
@@ -1,5 +1,5 @@
1
1
  # coding: utf-8
2
- # © 2016-2019 Resurface Labs Inc.
2
+ # © 2016-2020 Resurface Labs Inc.
3
3
 
4
4
  class HttpRule
5
5
 
@@ -1,37 +1,39 @@
1
1
  # coding: utf-8
2
- # © 2016-2019 Resurface Labs Inc.
2
+ # © 2016-2020 Resurface Labs Inc.
3
3
 
4
4
  class HttpRules
5
5
 
6
+ DEBUG_RULES = "allow_http_url\ncopy_session_field /.*/\n".freeze
7
+
8
+ STANDARD_RULES = %q(/request_header:cookie|response_header:set-cookie/ remove
9
+ /(request|response)_body|request_param/ replace /[a-zA-Z0-9.!#$%&’*+\/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)/, /x@y.com/
10
+ /request_body|request_param|response_body/ replace /[0-9\.\-\/]{9,}/, /xyxy/
11
+ ).freeze
12
+
13
+ STRICT_RULES = %q(/request_url/ replace /([^\?;]+).*/, !\\\\1!
14
+ /request_body|response_body|request_param:.*|request_header:(?!user-agent).*|response_header:(?!(content-length)|(content-type)).*/ remove
15
+ ).freeze
16
+
17
+ @@default_rules = STRICT_RULES
18
+
19
+ def self.default_rules
20
+ @@default_rules
21
+ end
22
+
23
+ def self.default_rules=(val)
24
+ @@default_rules = val.gsub(/^\s*include default\s*$/, '')
25
+ end
26
+
6
27
  def self.debug_rules
7
- "allow_http_url\ncopy_session_field /.*/\n"
28
+ DEBUG_RULES
8
29
  end
9
30
 
10
31
  def self.standard_rules
11
- %q(/request_header:cookie|response_header:set-cookie/ remove
12
- /(request|response)_body|request_param/ replace /[a-zA-Z0-9.!#$%&’*+\/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)/, /x@y.com/
13
- /request_body|request_param|response_body/ replace /[0-9\.\-\/]{9,}/, /xyxy/
14
- )
32
+ STANDARD_RULES
15
33
  end
16
34
 
17
35
  def self.strict_rules
18
- %q(/request_url/ replace /([^\?;]+).*/, !\\\\1!
19
- /request_body|response_body|request_param:.*|request_header:(?!user-agent).*|response_header:(?!(content-length)|(content-type)).*/ remove
20
- )
21
- end
22
-
23
- def self.parse(rules)
24
- result = []
25
- unless rules.nil?
26
- rules = rules.gsub(/^\s*include debug\s*$/, debug_rules)
27
- rules = rules.gsub(/^\s*include standard\s*$/, standard_rules)
28
- rules = rules.gsub(/^\s*include strict\s*$/, strict_rules)
29
- rules.each_line do |rule|
30
- parsed = parse_rule(rule)
31
- result << parsed unless parsed.nil?
32
- end
33
- end
34
- result
36
+ STRICT_RULES
35
37
  end
36
38
 
37
39
  def self.parse_rule(r)
@@ -76,15 +78,13 @@ class HttpRules
76
78
  end
77
79
  end
78
80
 
79
- protected
80
-
81
81
  def self.parse_regex(r, regex)
82
- str = parse_string(r, regex)
83
- raise RuntimeError.new("Invalid regex (#{regex}) in rule: #{r}") if '*' == str || '+' == str || '?' == str
84
- str = "^#{str}" unless str.start_with?('^')
85
- str = "#{str}$" unless str.end_with?('$')
82
+ s = parse_string(r, regex)
83
+ raise RuntimeError.new("Invalid regex (#{regex}) in rule: #{r}") if '*' == s || '+' == s || '?' == s
84
+ s = "^#{s}" unless s.start_with?('^')
85
+ s = "#{s}$" unless s.end_with?('$')
86
86
  begin
87
- return Regexp.compile(str)
87
+ return Regexp.compile(s)
88
88
  rescue RegexpError
89
89
  raise RuntimeError.new("Invalid regex (#{regex}) in rule: #{r}")
90
90
  end
@@ -98,15 +98,171 @@ class HttpRules
98
98
  end
99
99
  end
100
100
 
101
- def self.parse_string(r, str)
101
+ def self.parse_string(r, expr)
102
102
  %w(~ ! % | /).each do |sep|
103
- if (m = str.match(/^[#{sep}](.*)[#{sep}]$/))
103
+ if (m = expr.match(/^[#{sep}](.*)[#{sep}]$/))
104
104
  m1 = m[1]
105
105
  raise RuntimeError.new("Unescaped separator (#{sep}) in rule: #{r}") if m1.match(/^[#{sep}].*|.*[^\\][#{sep}].*/)
106
106
  return m1.gsub("\\#{sep}", sep)
107
107
  end
108
108
  end
109
- raise RuntimeError.new("Invalid expression (#{str}) in rule: #{r}")
109
+ raise RuntimeError.new("Invalid expression (#{expr}) in rule: #{r}")
110
+ end
111
+
112
+ def initialize(rules)
113
+ rules = HttpRules.default_rules if rules.nil?
114
+
115
+ # load rules from external files
116
+ if rules.start_with?('file://')
117
+ rfile = rules[7..]
118
+ begin
119
+ rules = File.read(rfile)
120
+ rescue
121
+ raise RuntimeError.new("Failed to load rules: #{rfile}")
122
+ end
123
+ end
124
+
125
+ # force default rules if necessary
126
+ rules = rules.gsub(/^\s*include default\s*$/, HttpRules.default_rules)
127
+ rules = HttpRules.default_rules unless rules.strip.length > 0
128
+
129
+ # expand rule inclues
130
+ rules = rules.gsub(/^\s*include debug\s*$/, DEBUG_RULES)
131
+ rules = rules.gsub(/^\s*include standard\s*$/, STANDARD_RULES)
132
+ rules = rules.gsub(/^\s*include strict\s*$/, STRICT_RULES)
133
+ @text = rules
134
+
135
+ # parse all rules
136
+ prs = []
137
+ rules.each_line do |rule|
138
+ parsed = HttpRules.parse_rule(rule)
139
+ prs << parsed unless parsed.nil?
140
+ end
141
+ @length = prs.length
142
+
143
+ # break out rules by verb
144
+ @allow_http_url = prs.select {|r| 'allow_http_url' == r.verb}.length > 0
145
+ @copy_session_field = prs.select {|r| 'copy_session_field' == r.verb}
146
+ @remove = prs.select {|r| 'remove' == r.verb}
147
+ @remove_if = prs.select {|r| 'remove_if' == r.verb}
148
+ @remove_if_found = prs.select {|r| 'remove_if_found' == r.verb}
149
+ @remove_unless = prs.select {|r| 'remove_unless' == r.verb}
150
+ @remove_unless_found = prs.select {|r| 'remove_unless_found' == r.verb}
151
+ @replace = prs.select {|r| 'replace' == r.verb}
152
+ @sample = prs.select {|r| 'sample' == r.verb}
153
+ @skip_compression = prs.select {|r| 'skip_compression' == r.verb}.length > 0
154
+ @skip_submission = prs.select {|r| 'skip_submission' == r.verb}.length > 0
155
+ @stop = prs.select {|r| 'stop' == r.verb}
156
+ @stop_if = prs.select {|r| 'stop_if' == r.verb}
157
+ @stop_if_found = prs.select {|r| 'stop_if_found' == r.verb}
158
+ @stop_unless = prs.select {|r| 'stop_unless' == r.verb}
159
+ @stop_unless_found = prs.select {|r| 'stop_unless_found' == r.verb}
160
+
161
+ # validate rules
162
+ raise RuntimeError.new('Multiple sample rules') if @sample.length > 1
163
+ end
164
+
165
+ def allow_http_url
166
+ @allow_http_url
167
+ end
168
+
169
+ def copy_session_field
170
+ @copy_session_field
171
+ end
172
+
173
+ def length
174
+ @length
175
+ end
176
+
177
+ def remove
178
+ @remove
179
+ end
180
+
181
+ def remove_if
182
+ @remove_if
183
+ end
184
+
185
+ def remove_if_found
186
+ @remove_if_found
187
+ end
188
+
189
+ def remove_unless
190
+ @remove_unless
191
+ end
192
+
193
+ def remove_unless_found
194
+ @remove_unless_found
195
+ end
196
+
197
+ def replace
198
+ @replace
199
+ end
200
+
201
+ def sample
202
+ @sample
203
+ end
204
+
205
+ def skip_compression
206
+ @skip_compression
207
+ end
208
+
209
+ def skip_submission
210
+ @skip_submission
211
+ end
212
+
213
+ def stop
214
+ @stop
215
+ end
216
+
217
+ def stop_if
218
+ @stop_if
219
+ end
220
+
221
+ def stop_if_found
222
+ @stop_if_found
223
+ end
224
+
225
+ def stop_unless
226
+ @stop_unless
227
+ end
228
+
229
+ def stop_unless_found
230
+ @stop_unless_found
231
+ end
232
+
233
+ def text
234
+ @text
235
+ end
236
+
237
+ def apply(details)
238
+ # stop rules come first
239
+ @stop.each {|r| details.each {|d| return nil if r.scope.match(d[0])}}
240
+ @stop_if_found.each {|r| details.each {|d| return nil if r.scope.match(d[0]) && r.param1.match(d[1])}}
241
+ @stop_if.each {|r| details.each {|d| return nil if r.scope.match(d[0]) && r.param1.match(d[1])}}
242
+ passed = 0
243
+ @stop_unless_found.each {|r| details.each {|d| passed += 1 if r.scope.match(d[0]) && r.param1.match(d[1])}}
244
+ return nil if passed != @stop_unless_found.length
245
+ passed = 0
246
+ @stop_unless.each {|r| details.each {|d| passed += 1 if r.scope.match(d[0]) && r.param1.match(d[1])}}
247
+ return nil if passed != @stop_unless.length
248
+
249
+ # do sampling if configured
250
+ return nil if !@sample[0].nil? && (rand * 100 >= @sample[0].param1)
251
+
252
+ # winnow sensitive details based on remove rules if configured
253
+ @remove.each {|r| details.delete_if {|d| r.scope.match(d[0])}}
254
+ @remove_unless_found.each {|r| details.delete_if {|d| r.scope.match(d[0]) && !r.param1.match(d[1])}}
255
+ @remove_if_found.each {|r| details.delete_if {|d| r.scope.match(d[0]) && r.param1.match(d[1])}}
256
+ @remove_unless.each {|r| details.delete_if {|d| r.scope.match(d[0]) && !r.param1.match(d[1])}}
257
+ @remove_if.each {|r| details.delete_if {|d| r.scope.match(d[0]) && r.param1.match(d[1])}}
258
+ return nil if details.empty?
259
+
260
+ # mask sensitive details based on replace rules if configured
261
+ @replace.each {|r| details.each {|d| d[1] = d[1].gsub(r.param1, r.param2) if r.scope.match(d[0])}}
262
+
263
+ # remove any details with empty values
264
+ details.delete_if {|d| '' == d[1]}
265
+ details.empty? ? nil : details
110
266
  end
111
267
 
112
268
  REGEX_ALLOW_HTTP_URL = /^\s*allow_http_url\s*(#.*)?$/.freeze
@@ -0,0 +1,14 @@
1
+ # coding: utf-8
2
+ # © 2016-2020 Resurface Labs Inc.
3
+
4
+ class Timer
5
+
6
+ def initialize
7
+ @started = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond)
8
+ end
9
+
10
+ def millis
11
+ (Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond) - @started).to_s
12
+ end
13
+
14
+ end
@@ -1,18 +1,18 @@
1
1
  # coding: utf-8
2
- # © 2016-2019 Resurface Labs Inc.
2
+ # © 2016-2020 Resurface Labs Inc.
3
3
 
4
4
  class UsageLoggers
5
5
 
6
- @@DISABLED = 'true'.eql?(ENV['USAGE_LOGGERS_DISABLE'])
6
+ @@BRICKED = 'true'.eql?(ENV['USAGE_LOGGERS_DISABLE'])
7
7
 
8
- @@disabled = @@DISABLED
8
+ @@disabled = @@BRICKED
9
9
 
10
10
  def self.disable
11
11
  @@disabled = true
12
12
  end
13
13
 
14
14
  def self.enable
15
- @@disabled = false unless @@DISABLED
15
+ @@disabled = false unless @@BRICKED
16
16
  end
17
17
 
18
18
  def self.enabled?
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: resurfaceio-logger
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.9.1
4
+ version: 2.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - RobDickinson
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-05-25 00:00:00.000000000 Z
11
+ date: 2020-07-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -16,58 +16,58 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.11'
19
+ version: '1.17'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.11'
26
+ version: '1.17'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rack
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '1.6'
33
+ version: '2.2'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '1.6'
40
+ version: '2.2'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '10.0'
47
+ version: '13.0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '10.0'
54
+ version: '13.0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rspec
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '3.0'
61
+ version: '3.9'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '3.0'
68
+ version: '3.9'
69
69
  description: Library for usage logging
70
- email:
70
+ email:
71
71
  executables: []
72
72
  extensions: []
73
73
  extra_rdoc_files: []
@@ -82,12 +82,13 @@ files:
82
82
  - lib/resurfaceio/http_response_impl.rb
83
83
  - lib/resurfaceio/http_rule.rb
84
84
  - lib/resurfaceio/http_rules.rb
85
+ - lib/resurfaceio/timer.rb
85
86
  - lib/resurfaceio/usage_loggers.rb
86
87
  homepage: https://github.com/resurfaceio/logger-ruby
87
88
  licenses:
88
89
  - Apache-2.0
89
90
  metadata: {}
90
- post_install_message:
91
+ post_install_message:
91
92
  rdoc_options: []
92
93
  require_paths:
93
94
  - lib
@@ -95,16 +96,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
95
96
  requirements:
96
97
  - - "~>"
97
98
  - !ruby/object:Gem::Version
98
- version: '2.2'
99
+ version: '2.6'
99
100
  required_rubygems_version: !ruby/object:Gem::Requirement
100
101
  requirements:
101
102
  - - ">="
102
103
  - !ruby/object:Gem::Version
103
104
  version: '0'
104
105
  requirements: []
105
- rubyforge_project:
106
- rubygems_version: 2.7.7
107
- signing_key:
106
+ rubygems_version: 3.0.3
107
+ signing_key:
108
108
  specification_version: 4
109
109
  summary: Library for usage logging
110
110
  test_files: []