resurfaceio-logger 1.8.4 → 1.9.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.
- checksums.yaml +4 -4
- data/lib/resurfaceio/all.rb +10 -2
- data/lib/resurfaceio/base_logger.rb +1 -1
- data/lib/resurfaceio/http_logger.rb +98 -7
- data/lib/resurfaceio/http_logger_for_rack.rb +1 -2
- data/lib/resurfaceio/http_logger_for_rails.rb +1 -2
- data/lib/resurfaceio/{http_message_impl.rb → http_message.rb} +1 -1
- data/lib/resurfaceio/http_request_impl.rb +5 -0
- data/lib/resurfaceio/http_rule.rb +15 -0
- data/lib/resurfaceio/http_rules.rb +130 -0
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 95113134bbf15fba0ef898a514d24a24dd98da6e
|
4
|
+
data.tar.gz: 998377326921781e89c99e9905ebef0c6f932fc8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d64d21c53d46de1336ac33fc81f71e3ed1354709c8ec91fc239ed964c1e7d73fa7fc484b42223077836b6a2be5bdf9ad851d406c2c1cf72b82d8b2b00c544569
|
7
|
+
data.tar.gz: d7bac6e321c7d292e2190f15500e4df3e6d18dd0458bb221c565573fea7a19073501261d093c1dbef45f684bc2882d74720b981b84b68f6817e6653a0beb1c62
|
data/lib/resurfaceio/all.rb
CHANGED
@@ -5,9 +5,11 @@ require 'resurfaceio/base_logger'
|
|
5
5
|
require 'resurfaceio/http_logger'
|
6
6
|
require 'resurfaceio/http_logger_for_rack'
|
7
7
|
require 'resurfaceio/http_logger_for_rails'
|
8
|
-
require 'resurfaceio/
|
8
|
+
require 'resurfaceio/http_message'
|
9
9
|
require 'resurfaceio/http_request_impl'
|
10
10
|
require 'resurfaceio/http_response_impl'
|
11
|
+
require 'resurfaceio/http_rule'
|
12
|
+
require 'resurfaceio/http_rules'
|
11
13
|
require 'resurfaceio/usage_loggers'
|
12
14
|
|
13
15
|
module Resurfaceio
|
@@ -24,7 +26,7 @@ module Resurfaceio
|
|
24
26
|
class HttpLoggerForRails < HttpLoggerForRails
|
25
27
|
end
|
26
28
|
|
27
|
-
class
|
29
|
+
class HttpMessage < HttpMessage
|
28
30
|
end
|
29
31
|
|
30
32
|
class HttpRequestImpl < HttpRequestImpl
|
@@ -33,6 +35,12 @@ module Resurfaceio
|
|
33
35
|
class HttpResponseImpl < HttpResponseImpl
|
34
36
|
end
|
35
37
|
|
38
|
+
class HttpRule < HttpRule
|
39
|
+
end
|
40
|
+
|
41
|
+
class HttpRules < HttpRules
|
42
|
+
end
|
43
|
+
|
36
44
|
class UsageLoggers < UsageLoggers
|
37
45
|
end
|
38
46
|
|
@@ -3,30 +3,121 @@
|
|
3
3
|
|
4
4
|
require 'json'
|
5
5
|
require 'resurfaceio/base_logger'
|
6
|
-
require 'resurfaceio/
|
6
|
+
require 'resurfaceio/http_message'
|
7
|
+
require 'resurfaceio/http_rule'
|
8
|
+
require 'resurfaceio/http_rules'
|
7
9
|
|
8
10
|
class HttpLogger < BaseLogger
|
9
11
|
|
10
12
|
AGENT = 'http_logger.rb'.freeze
|
11
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
|
+
|
12
24
|
def self.string_content_type?(s)
|
13
25
|
!s.nil? && !(s =~ /^(text\/(html|plain|xml))|(application\/(json|soap|xml|x-www-form-urlencoded))/i).nil?
|
14
26
|
end
|
15
27
|
|
16
28
|
def initialize(options = {})
|
17
29
|
super(AGENT, options)
|
30
|
+
|
31
|
+
# read rules from param or defaults
|
32
|
+
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
|
35
|
+
else
|
36
|
+
@rules = @@default_rules
|
37
|
+
end
|
38
|
+
|
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
|
61
|
+
@enableable = false
|
62
|
+
@enabled = false
|
63
|
+
end
|
18
64
|
end
|
19
65
|
|
20
|
-
def
|
21
|
-
|
22
|
-
message << ['agent', @agent]
|
23
|
-
message << ['version', @version]
|
24
|
-
message << ['now', now.nil? ? (Time.now.to_f * 1000).floor.to_s : now]
|
25
|
-
JSON.generate message
|
66
|
+
def rules
|
67
|
+
@rules
|
26
68
|
end
|
27
69
|
|
28
70
|
def log(request, response, response_body = nil, request_body = nil)
|
29
71
|
!enabled? || submit(format(request, response, response_body, request_body))
|
30
72
|
end
|
31
73
|
|
74
|
+
def format(request, response, response_body = nil, request_body = nil, now = nil)
|
75
|
+
details = HttpMessage.build(request, response, response_body, request_body)
|
76
|
+
|
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]
|
118
|
+
details << ['agent', @agent]
|
119
|
+
details << ['version', @version]
|
120
|
+
JSON.generate details
|
121
|
+
end
|
122
|
+
|
32
123
|
end
|
@@ -21,8 +21,7 @@ class HttpLoggerForRack # http://rack.rubyforge.org/doc/SPEC.html
|
|
21
21
|
response = Rack::Response.new(body, status, headers)
|
22
22
|
if HttpLogger::string_content_type?(response.content_type)
|
23
23
|
request = Rack::Request.new(env)
|
24
|
-
|
25
|
-
@logger.submit(message)
|
24
|
+
@logger.submit(@logger.format(request, response))
|
26
25
|
end
|
27
26
|
end
|
28
27
|
[status, headers, body]
|
@@ -20,8 +20,7 @@ class HttpLoggerForRails
|
|
20
20
|
response = controller.response
|
21
21
|
status = response.status
|
22
22
|
if (status < 300 || status == 302) && HttpLogger::string_content_type?(response.content_type)
|
23
|
-
|
24
|
-
@logger.submit(message)
|
23
|
+
@logger.submit(@logger.format(request, response))
|
25
24
|
end
|
26
25
|
end
|
27
26
|
end
|
@@ -7,6 +7,7 @@ class HttpRequestImpl
|
|
7
7
|
@form_hash = Hash.new
|
8
8
|
@headers = Hash.new
|
9
9
|
@query_hash = Hash.new
|
10
|
+
@session = Hash.new
|
10
11
|
end
|
11
12
|
|
12
13
|
def add_header(key, value)
|
@@ -40,6 +41,10 @@ class HttpRequestImpl
|
|
40
41
|
@query_hash
|
41
42
|
end
|
42
43
|
|
44
|
+
def session
|
45
|
+
@session
|
46
|
+
end
|
47
|
+
|
43
48
|
attr_accessor :request_method
|
44
49
|
attr_accessor :url
|
45
50
|
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# © 2016-2018 Resurface Labs LLC
|
3
|
+
|
4
|
+
class HttpRule
|
5
|
+
|
6
|
+
def initialize(verb, scope = nil, param1 = nil, param2 = nil)
|
7
|
+
@verb = verb
|
8
|
+
@scope = scope
|
9
|
+
@param1 = param1
|
10
|
+
@param2 = param2
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_reader :verb, :scope, :param1, :param2
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# © 2016-2018 Resurface Labs LLC
|
3
|
+
|
4
|
+
class HttpRules
|
5
|
+
|
6
|
+
def self.debug_rules
|
7
|
+
"allow_http_url\ncopy_session_field /.*/\n"
|
8
|
+
end
|
9
|
+
|
10
|
+
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
|
+
)
|
15
|
+
end
|
16
|
+
|
17
|
+
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
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.parse_rule(r)
|
38
|
+
if r.nil? || r.match(REGEX_BLANK_OR_COMMENT)
|
39
|
+
nil
|
40
|
+
elsif r.match(REGEX_ALLOW_HTTP_URL)
|
41
|
+
HttpRule.new('allow_http_url')
|
42
|
+
elsif (m = r.match(REGEX_COPY_SESSION_FIELD))
|
43
|
+
HttpRule.new('copy_session_field', nil, parse_regex(r, m[1]))
|
44
|
+
elsif (m = r.match(REGEX_REMOVE))
|
45
|
+
HttpRule.new('remove', parse_regex(r, m[1]))
|
46
|
+
elsif (m = r.match(REGEX_REMOVE_IF))
|
47
|
+
HttpRule.new('remove_if', parse_regex(r, m[1]), parse_regex(r, m[2]))
|
48
|
+
elsif (m = r.match(REGEX_REMOVE_IF_FOUND))
|
49
|
+
HttpRule.new('remove_if_found', parse_regex(r, m[1]), parse_regex_find(r, m[2]))
|
50
|
+
elsif (m = r.match(REGEX_REMOVE_UNLESS))
|
51
|
+
HttpRule.new('remove_unless', parse_regex(r, m[1]), parse_regex(r, m[2]))
|
52
|
+
elsif (m = r.match(REGEX_REMOVE_UNLESS_FOUND))
|
53
|
+
HttpRule.new('remove_unless_found', parse_regex(r, m[1]), parse_regex_find(r, m[2]))
|
54
|
+
elsif (m = r.match(REGEX_REPLACE))
|
55
|
+
HttpRule.new('replace', parse_regex(r, m[1]), parse_regex_find(r, m[2]), parse_string(r, m[3]))
|
56
|
+
elsif (m = r.match(REGEX_SAMPLE))
|
57
|
+
m1 = m[1].to_i
|
58
|
+
raise RuntimeError.new("Invalid sample percent: #{m1}") if m1 < 1 || m1 > 99
|
59
|
+
HttpRule.new('sample', nil, m1)
|
60
|
+
elsif r.match(REGEX_SKIP_COMPRESSION)
|
61
|
+
HttpRule.new('skip_compression')
|
62
|
+
elsif r.match(REGEX_SKIP_SUBMISSION)
|
63
|
+
HttpRule.new('skip_submission')
|
64
|
+
elsif (m = r.match(REGEX_STOP))
|
65
|
+
HttpRule.new('stop', parse_regex(r, m[1]))
|
66
|
+
elsif (m = r.match(REGEX_STOP_IF))
|
67
|
+
HttpRule.new('stop_if', parse_regex(r, m[1]), parse_regex(r, m[2]))
|
68
|
+
elsif (m = r.match(REGEX_STOP_IF_FOUND))
|
69
|
+
HttpRule.new('stop_if_found', parse_regex(r, m[1]), parse_regex_find(r, m[2]))
|
70
|
+
elsif (m = r.match(REGEX_STOP_UNLESS))
|
71
|
+
HttpRule.new('stop_unless', parse_regex(r, m[1]), parse_regex(r, m[2]))
|
72
|
+
elsif (m = r.match(REGEX_STOP_UNLESS_FOUND))
|
73
|
+
HttpRule.new('stop_unless_found', parse_regex(r, m[1]), parse_regex_find(r, m[2]))
|
74
|
+
else
|
75
|
+
raise RuntimeError.new("Invalid rule: #{r}")
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
protected
|
80
|
+
|
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?('$')
|
86
|
+
begin
|
87
|
+
return Regexp.compile(str)
|
88
|
+
rescue RegexpError
|
89
|
+
raise RuntimeError.new("Invalid regex (#{regex}) in rule: #{r}")
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.parse_regex_find(r, regex)
|
94
|
+
begin
|
95
|
+
return Regexp.compile(parse_string(r, regex))
|
96
|
+
rescue RegexpError
|
97
|
+
raise RuntimeError.new("Invalid regex (#{regex}) in rule: #{r}")
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.parse_string(r, str)
|
102
|
+
%w(~ ! % | /).each do |sep|
|
103
|
+
if (m = str.match(/^[#{sep}](.*)[#{sep}]$/))
|
104
|
+
m1 = m[1]
|
105
|
+
raise RuntimeError.new("Unescaped separator (#{sep}) in rule: #{r}") if m1.match(/^[#{sep}].*|.*[^\\][#{sep}].*/)
|
106
|
+
return m1.gsub("\\#{sep}", sep)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
raise RuntimeError.new("Invalid expression (#{str}) in rule: #{r}")
|
110
|
+
end
|
111
|
+
|
112
|
+
REGEX_ALLOW_HTTP_URL = /^\s*allow_http_url\s*(#.*)?$/.freeze
|
113
|
+
REGEX_BLANK_OR_COMMENT = /^\s*([#].*)*$/.freeze
|
114
|
+
REGEX_COPY_SESSION_FIELD = /^\s*copy_session_field\s+([~!%|\/].+[~!%|\/])\s*(#.*)?$/.freeze
|
115
|
+
REGEX_REMOVE = /^\s*([~!%|\/].+[~!%|\/])\s*remove\s*(#.*)?$/.freeze
|
116
|
+
REGEX_REMOVE_IF = /^\s*([~!%|\/].+[~!%|\/])\s*remove_if\s+([~!%|\/].+[~!%|\/])\s*(#.*)?$/.freeze
|
117
|
+
REGEX_REMOVE_IF_FOUND = /^\s*([~!%|\/].+[~!%|\/])\s*remove_if_found\s+([~!%|\/].+[~!%|\/])\s*(#.*)?$/.freeze
|
118
|
+
REGEX_REMOVE_UNLESS = /^\s*([~!%|\/].+[~!%|\/])\s*remove_unless\s+([~!%|\/].+[~!%|\/])\s*(#.*)?$/.freeze
|
119
|
+
REGEX_REMOVE_UNLESS_FOUND = /^\s*([~!%|\/].+[~!%|\/])\s*remove_unless_found\s+([~!%|\/].+[~!%|\/])\s*(#.*)?$/.freeze
|
120
|
+
REGEX_REPLACE = /^\s*([~!%|\/].+[~!%|\/])\s*replace[\s]+([~!%|\/].+[~!%|\/]),[\s]+([~!%|\/].*[~!%|\/])\s*(#.*)?$/.freeze
|
121
|
+
REGEX_SAMPLE = /^\s*sample\s+(\d+)\s*(#.*)?$/.freeze
|
122
|
+
REGEX_SKIP_COMPRESSION = /^\s*skip_compression\s*(#.*)?$/.freeze
|
123
|
+
REGEX_SKIP_SUBMISSION = /^\s*skip_submission\s*(#.*)?$/.freeze
|
124
|
+
REGEX_STOP = /^\s*([~!%|\/].+[~!%|\/])\s*stop\s*(#.*)?$/.freeze
|
125
|
+
REGEX_STOP_IF = /^\s*([~!%|\/].+[~!%|\/])\s*stop_if\s+([~!%|\/].+[~!%|\/])\s*(#.*)?$/.freeze
|
126
|
+
REGEX_STOP_IF_FOUND = /^\s*([~!%|\/].+[~!%|\/])\s*stop_if_found\s+([~!%|\/].+[~!%|\/])\s*(#.*)?$/.freeze
|
127
|
+
REGEX_STOP_UNLESS = /^\s*([~!%|\/].+[~!%|\/])\s*stop_unless\s+([~!%|\/].+[~!%|\/])\s*(#.*)?$/.freeze
|
128
|
+
REGEX_STOP_UNLESS_FOUND = /^\s*([~!%|\/].+[~!%|\/])\s*stop_unless_found\s+([~!%|\/].+[~!%|\/])\s*(#.*)?$/.freeze
|
129
|
+
|
130
|
+
end
|
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.
|
4
|
+
version: 1.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- RobDickinson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-06-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -77,9 +77,11 @@ files:
|
|
77
77
|
- lib/resurfaceio/http_logger.rb
|
78
78
|
- lib/resurfaceio/http_logger_for_rack.rb
|
79
79
|
- lib/resurfaceio/http_logger_for_rails.rb
|
80
|
-
- lib/resurfaceio/
|
80
|
+
- lib/resurfaceio/http_message.rb
|
81
81
|
- lib/resurfaceio/http_request_impl.rb
|
82
82
|
- lib/resurfaceio/http_response_impl.rb
|
83
|
+
- lib/resurfaceio/http_rule.rb
|
84
|
+
- lib/resurfaceio/http_rules.rb
|
83
85
|
- lib/resurfaceio/usage_loggers.rb
|
84
86
|
homepage: https://github.com/resurfaceio/logger-ruby
|
85
87
|
licenses:
|