logasm 0.2.6 → 0.3.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/README.md +71 -0
- data/lib/logasm/preprocessors/blacklist.rb +67 -0
- data/lib/logasm/preprocessors/whitelist.rb +88 -0
- data/lib/logasm/preprocessors.rb +9 -0
- data/lib/logasm.rb +16 -4
- data/logasm.gemspec +1 -1
- data/spec/logasm_spec.rb +32 -1
- data/spec/preprocessors/blacklist_spec.rb +131 -0
- data/spec/preprocessors/whitelist_spec.rb +152 -0
- data/spec/support/implement_interface.rb +11 -0
- metadata +12 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 440665fe491c115f43c411822de433ead259e193
|
4
|
+
data.tar.gz: 06568509dacd084a9607ac545b3cfd03447947db
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 380b1118c988bbf141b9c03a63ca3c68d33f1f8873709bd92d5e52188dfb3adf980edf7d040eca62c2bdbc66fb4a142e0906e2272aa278cceb4109d6c88e52a2
|
7
|
+
data.tar.gz: b93a280171c1123d5559ac630a5db52710cd429a7f5bbaddbb73885f7aa75d287d21b117ca3a8fe75b6a2399667c05bcb1631ce889795f2b493036f664742992
|
data/README.md
CHANGED
@@ -61,3 +61,74 @@ logasm = Logasm.build('myApp', { stdout: nil, logstash: { host: "localhost", por
|
|
61
61
|
```
|
62
62
|
|
63
63
|
When no loggers are specified, it creates a stdout logger by default.
|
64
|
+
|
65
|
+
## Preprocessors
|
66
|
+
|
67
|
+
Preprocessors allow modification of log messages, prior to sending of the message to the configured logger(s).
|
68
|
+
|
69
|
+
### Blacklist
|
70
|
+
|
71
|
+
Excludes or masks defined fields of the passed hash object.
|
72
|
+
You can specify the name of the field and which action to take on it.
|
73
|
+
Nested hashes of any level are preprocessed as well.
|
74
|
+
|
75
|
+
Available actions:
|
76
|
+
|
77
|
+
* exclude(`default`) - fully excludes the field and its value from the hash.
|
78
|
+
* mask - replaces every character from the original value with `*`. In case of `array`, `hash` or `boolean` value is replaced with one `*`.
|
79
|
+
|
80
|
+
#### Configuration
|
81
|
+
|
82
|
+
```yaml
|
83
|
+
preprocessors:
|
84
|
+
blacklist:
|
85
|
+
fields:
|
86
|
+
- key: password
|
87
|
+
- key: phone
|
88
|
+
action: mask
|
89
|
+
```
|
90
|
+
|
91
|
+
#### Usage
|
92
|
+
|
93
|
+
```ruby
|
94
|
+
logger = Logasm.build(application_name, logger_config, preprocessors)
|
95
|
+
|
96
|
+
input = {password: 'password', info: {phone: '+12055555555'}}
|
97
|
+
|
98
|
+
logger.debug("Received request", input)
|
99
|
+
```
|
100
|
+
|
101
|
+
Logger output:
|
102
|
+
|
103
|
+
```
|
104
|
+
Received request {"info":{"phone":"************"}}
|
105
|
+
```
|
106
|
+
|
107
|
+
### Whitelist
|
108
|
+
|
109
|
+
Masks all the fields except those whitelisted in the configuration using [JSON Pointer](https://tools.ietf.org/html/rfc6901).
|
110
|
+
Only simple values(`string`, `number`, `boolean`) can be whitelisted.
|
111
|
+
|
112
|
+
#### Configuration
|
113
|
+
|
114
|
+
```yaml
|
115
|
+
preprocessors:
|
116
|
+
whitelist:
|
117
|
+
pointers: ['/info/phone']
|
118
|
+
```
|
119
|
+
|
120
|
+
#### Usage
|
121
|
+
|
122
|
+
```ruby
|
123
|
+
logger = Logasm.build(application_name, logger_config, preprocessors)
|
124
|
+
|
125
|
+
input = {password: 'password', info: {phone: '+12055555555'}}
|
126
|
+
|
127
|
+
logger.debug("Received request", input)
|
128
|
+
```
|
129
|
+
|
130
|
+
Logger output:
|
131
|
+
|
132
|
+
```
|
133
|
+
Received request {password: "********", "info":{"phone":"+12055555555"}}
|
134
|
+
```
|
@@ -0,0 +1,67 @@
|
|
1
|
+
class Logasm
|
2
|
+
module Preprocessors
|
3
|
+
class Blacklist
|
4
|
+
|
5
|
+
DEFAULT_ACTION = 'exclude'
|
6
|
+
MASK_SYMBOL = '*'
|
7
|
+
|
8
|
+
class UnsupportedActionException < Exception
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(config = {})
|
12
|
+
@fields_to_process = config[:fields].inject({}) do |mem, field|
|
13
|
+
key = field.delete(:key)
|
14
|
+
options = {action: DEFAULT_ACTION}.merge(field)
|
15
|
+
validate_action_supported(options[:action])
|
16
|
+
mem.merge(key => options)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def process(data)
|
21
|
+
if data.is_a? Hash
|
22
|
+
data.inject({}) do |mem, (key, val)|
|
23
|
+
if (field = @fields_to_process[key.to_s])
|
24
|
+
self.send(action_method(field[:action]), mem, key, val)
|
25
|
+
else
|
26
|
+
mem.merge(key => process(val))
|
27
|
+
end
|
28
|
+
end
|
29
|
+
elsif data.is_a? Array
|
30
|
+
data.inject([]) do |mem, val|
|
31
|
+
mem + [process(val)]
|
32
|
+
end
|
33
|
+
else
|
34
|
+
data
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def action_method(action)
|
41
|
+
"#{action}_field"
|
42
|
+
end
|
43
|
+
|
44
|
+
def validate_action_supported(action)
|
45
|
+
unless self.respond_to?(action_method(action).to_sym, true)
|
46
|
+
raise UnsupportedActionException.new("Action: #{action} is not supported")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def mask_field(data, key, val)
|
51
|
+
if val.is_a?(Hash) || val.is_a?(Array) || is_boolean?(val)
|
52
|
+
data.merge(key => MASK_SYMBOL)
|
53
|
+
else
|
54
|
+
data.merge(key => MASK_SYMBOL * val.to_s.length)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def exclude_field(data, *)
|
59
|
+
data
|
60
|
+
end
|
61
|
+
|
62
|
+
def is_boolean?(val)
|
63
|
+
val.is_a?(TrueClass) || val.is_a?(FalseClass)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
class Logasm
|
2
|
+
module Preprocessors
|
3
|
+
class Whitelist
|
4
|
+
|
5
|
+
DEFAULT_WHITELIST = %w(/id /message /correlation_id /queue)
|
6
|
+
MASK_SYMBOL = '*'
|
7
|
+
|
8
|
+
class InvalidPointerFormatException < Exception
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(config = {})
|
12
|
+
pointers = (config[:pointers] || []) + DEFAULT_WHITELIST
|
13
|
+
@fields_to_include = pointers.inject({}) do |mem, pointer|
|
14
|
+
validate_pointer(pointer)
|
15
|
+
mem.merge(decode(pointer) => true)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def process(data)
|
20
|
+
process_data('', data)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def validate_pointer(pointer)
|
26
|
+
if pointer.slice(-1) == '/'
|
27
|
+
raise InvalidPointerFormatException.new('Pointer should not contain trailing slash')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def decode(pointer)
|
32
|
+
pointer
|
33
|
+
.gsub('~0', '~')
|
34
|
+
.gsub('~1', '/')
|
35
|
+
end
|
36
|
+
|
37
|
+
def process_data(parent_pointer, data)
|
38
|
+
self.send("process_#{get_type(data)}", parent_pointer, data)
|
39
|
+
end
|
40
|
+
|
41
|
+
def get_type(data)
|
42
|
+
if data.is_a? Hash
|
43
|
+
'hash'
|
44
|
+
elsif data.is_a? Array
|
45
|
+
'array'
|
46
|
+
else
|
47
|
+
'value'
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def process_hash(parent_pointer, hash)
|
52
|
+
hash.inject({}) do |mem, (key, value)|
|
53
|
+
pointer = "#{parent_pointer}/#{key}"
|
54
|
+
processed_value = process_data(pointer, value)
|
55
|
+
mem.merge(key => processed_value)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def process_array(parent_pointer, array)
|
60
|
+
array.each_with_index.inject([]) do |mem, (value, index)|
|
61
|
+
pointer = "#{parent_pointer}/#{index}"
|
62
|
+
processed_value = process_data(pointer, value)
|
63
|
+
mem + [processed_value]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def process_value(parent_pointer, value)
|
68
|
+
if @fields_to_include[parent_pointer]
|
69
|
+
value
|
70
|
+
else
|
71
|
+
mask value
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def mask(value)
|
76
|
+
if value && value.respond_to?(:to_s) && !is_boolean?(value)
|
77
|
+
MASK_SYMBOL * value.to_s.length
|
78
|
+
else
|
79
|
+
MASK_SYMBOL
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def is_boolean?(value)
|
84
|
+
value.is_a?(TrueClass) || value.is_a?(FalseClass)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
data/lib/logasm.rb
CHANGED
@@ -4,20 +4,25 @@ require 'json'
|
|
4
4
|
require_relative 'logasm/adapters'
|
5
5
|
require_relative 'logasm/utils'
|
6
6
|
require_relative 'logasm/null_logger'
|
7
|
+
require_relative 'logasm/preprocessors'
|
7
8
|
|
8
9
|
LOG_LEVEL_QUERY_METHODS = [:debug?, :info?, :warn?, :error?, :fatal?]
|
9
10
|
|
10
11
|
class Logasm
|
11
|
-
def self.build(service_name, loggers_config)
|
12
|
+
def self.build(service_name, loggers_config, preprocessors_config = {})
|
12
13
|
loggers_config ||= {stdout: nil}
|
14
|
+
preprocessors = preprocessors_config.map do |type, arguments|
|
15
|
+
Preprocessors.get(type.to_s, arguments || {})
|
16
|
+
end
|
13
17
|
adapters = loggers_config.map do |type, arguments|
|
14
18
|
Adapters.get(type.to_s, service_name, arguments || {})
|
15
19
|
end
|
16
|
-
new(adapters)
|
20
|
+
new(adapters, preprocessors)
|
17
21
|
end
|
18
22
|
|
19
|
-
def initialize(adapters)
|
23
|
+
def initialize(adapters, preprocessors)
|
20
24
|
@adapters = adapters
|
25
|
+
@preprocessors = preprocessors
|
21
26
|
end
|
22
27
|
|
23
28
|
def debug(*args)
|
@@ -50,9 +55,16 @@ class Logasm
|
|
50
55
|
|
51
56
|
def log(level, *args)
|
52
57
|
data = parse_log_data(*args)
|
58
|
+
processed_data = preprocess(data)
|
53
59
|
|
54
60
|
@adapters.each do |adapter|
|
55
|
-
adapter.log(level,
|
61
|
+
adapter.log(level, processed_data)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def preprocess(data)
|
66
|
+
@preprocessors.inject(data) do |data_to_process, preprocessor|
|
67
|
+
preprocessor.process(data_to_process)
|
56
68
|
end
|
57
69
|
end
|
58
70
|
|
data/logasm.gemspec
CHANGED
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
|
5
5
|
Gem::Specification.new do |gem|
|
6
6
|
gem.name = "logasm"
|
7
|
-
gem.version = '0.
|
7
|
+
gem.version = '0.3.0'
|
8
8
|
gem.authors = ["Salemove"]
|
9
9
|
gem.email = ["support@salemove.com"]
|
10
10
|
gem.description = %q{It's logasmic}
|
data/spec/logasm_spec.rb
CHANGED
@@ -38,11 +38,36 @@ describe Logasm do
|
|
38
38
|
|
39
39
|
described_class.build('test_service', nil)
|
40
40
|
end
|
41
|
+
|
42
|
+
it 'creates preprocessor when preprocessor defined' do
|
43
|
+
expect(described_class).to receive(:new) do |adapters, preprocessors|
|
44
|
+
expect(preprocessors.count).to be(1)
|
45
|
+
expect(preprocessors.first).to be_a(described_class::Preprocessors::Blacklist)
|
46
|
+
end
|
47
|
+
|
48
|
+
preprocessors = {blacklist: {fields: []}}
|
49
|
+
described_class.build('test_service', nil, preprocessors)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'when preprocessor defined' do
|
54
|
+
let(:logasm) { described_class.new([adapter], [preprocessor]) }
|
55
|
+
let(:adapter) { double }
|
56
|
+
let(:preprocessor) { double }
|
57
|
+
let(:data) { {data: 'data'} }
|
58
|
+
|
59
|
+
it 'preprocesses data before logging' do
|
60
|
+
expect(preprocessor).to receive(:process).with(data).and_return(data.merge(processed: true)).ordered
|
61
|
+
expect(adapter).to receive(:log).with(:info, data.merge(processed: true)).ordered
|
62
|
+
|
63
|
+
logasm.info(data)
|
64
|
+
end
|
41
65
|
end
|
42
66
|
|
43
67
|
context 'when parsing log data' do
|
44
|
-
let(:logasm) { described_class.new([adapter]) }
|
68
|
+
let(:logasm) { described_class.new([adapter], preprocessors) }
|
45
69
|
let(:adapter) { double }
|
70
|
+
let(:preprocessors) { [] }
|
46
71
|
|
47
72
|
it 'parses empty string with nil metadata' do
|
48
73
|
expect(adapter).to receive(:log).with(:info, message: '')
|
@@ -114,4 +139,10 @@ describe Logasm do
|
|
114
139
|
end
|
115
140
|
end
|
116
141
|
end
|
142
|
+
|
143
|
+
it 'has the same interface as Ruby logger' do
|
144
|
+
skip "https://salemove.atlassian.net/browse/INF-464"
|
145
|
+
logger = described_class.build('test_service', stdout: {level: 'debug'})
|
146
|
+
expect(logger).to implement_interface(Logger)
|
147
|
+
end
|
117
148
|
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require_relative '../../lib/logasm/preprocessors/blacklist'
|
3
|
+
|
4
|
+
describe Logasm::Preprocessors::Blacklist do
|
5
|
+
subject(:processed_data) { described_class.new(config).process(data) }
|
6
|
+
|
7
|
+
let(:config) {{
|
8
|
+
fields: [{ key: 'field', action: action }]
|
9
|
+
}}
|
10
|
+
|
11
|
+
let(:action) {}
|
12
|
+
let(:data) {{
|
13
|
+
field: value,
|
14
|
+
data: {
|
15
|
+
field: 'secret'
|
16
|
+
},
|
17
|
+
array: [{field: 'secret'}]
|
18
|
+
}}
|
19
|
+
|
20
|
+
let(:value) { 'secret' }
|
21
|
+
|
22
|
+
context 'when action is unsupported' do
|
23
|
+
let(:action) { 'reverse' }
|
24
|
+
|
25
|
+
it 'throws exception' do
|
26
|
+
expect { processed_data }.to raise_exception(Logasm::Preprocessors::Blacklist::UnsupportedActionException)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'when action is "exclude"' do
|
31
|
+
let(:action) { 'exclude' }
|
32
|
+
|
33
|
+
it 'removes the field' do
|
34
|
+
expect(processed_data).not_to include(:field)
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'removes nested field' do
|
38
|
+
expect(processed_data).not_to include_at_depth({field: 'secret'}, 1)
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'removes nested in array field' do
|
42
|
+
expect(processed_data[:array]).not_to include({field: 'secret'})
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'when field is deeply nested' do
|
46
|
+
let(:depth) { 10 }
|
47
|
+
let(:data) { data_with_nested_field({field: 'secret'}, depth) }
|
48
|
+
|
49
|
+
it 'removes deeply nested field' do
|
50
|
+
expect(processed_data).not_to include_at_depth({field: 'secret'}, depth)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'when action is "mask"' do
|
56
|
+
let(:action) { 'mask' }
|
57
|
+
|
58
|
+
it 'masks nested field' do
|
59
|
+
expect(processed_data).to include_at_depth({field: '******'}, 1)
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'masks nested in array field' do
|
63
|
+
expect(processed_data[:array]).to include({field: '******'})
|
64
|
+
end
|
65
|
+
|
66
|
+
context 'when field is string' do
|
67
|
+
let(:value) { 'secret' }
|
68
|
+
|
69
|
+
it 'masks value with asterisks' do
|
70
|
+
expect(processed_data).to include(field: '******')
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
context 'when field is number' do
|
75
|
+
let(:value) { 42 }
|
76
|
+
|
77
|
+
it 'masks number value' do
|
78
|
+
expect(processed_data).to include(field: '**')
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
context 'when field is boolean' do
|
83
|
+
let(:value) { true }
|
84
|
+
|
85
|
+
it 'masks value with one asterisk' do
|
86
|
+
expect(processed_data).to include(field: '*')
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context 'when field is array' do
|
91
|
+
let(:value) { [1,2,3,4] }
|
92
|
+
|
93
|
+
it 'masks value with one asterisk' do
|
94
|
+
expect(processed_data).to include(field: '*')
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
context 'when field is hash' do
|
99
|
+
let(:value) { {data: {}} }
|
100
|
+
|
101
|
+
it 'masks value with one asterisk' do
|
102
|
+
expect(processed_data).to include(field: '*')
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
context 'when field is deeply nested' do
|
107
|
+
let(:depth) { 10 }
|
108
|
+
let(:data) { data_with_nested_field({field: 'secret'}, depth) }
|
109
|
+
|
110
|
+
it 'masks deeply nested field' do
|
111
|
+
expect(processed_data).to include_at_depth({field: '******'}, depth)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def data_with_nested_field(field, depth)
|
117
|
+
depth.times.inject(field) do |mem|
|
118
|
+
{}.merge(data: mem)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
RSpec::Matchers.define :include_at_depth do |expected_hash, depth|
|
123
|
+
match do |actual|
|
124
|
+
nested_data = depth.times.inject(actual) do |mem|
|
125
|
+
mem[:data]
|
126
|
+
end
|
127
|
+
|
128
|
+
expect(nested_data).to include(expected_hash)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require_relative '../../lib/logasm/preprocessors/whitelist'
|
3
|
+
|
4
|
+
describe Logasm::Preprocessors::Whitelist do
|
5
|
+
subject(:processed_data) { described_class.new(config).process(data) }
|
6
|
+
|
7
|
+
let(:config) { {pointers: pointers} }
|
8
|
+
let(:pointers) { [] }
|
9
|
+
let(:data) {{
|
10
|
+
field: 'secret',
|
11
|
+
data: {
|
12
|
+
field: 'secret'
|
13
|
+
},
|
14
|
+
array: [{field: 'secret'}]
|
15
|
+
}}
|
16
|
+
|
17
|
+
it 'masks all non-whitelisted fields' do
|
18
|
+
expect(processed_data).to eq({
|
19
|
+
field: '******',
|
20
|
+
data: {
|
21
|
+
field: '******'
|
22
|
+
},
|
23
|
+
array: [{field: '******'}]
|
24
|
+
})
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'when pointer has trailing slash' do
|
28
|
+
let(:pointers) { ['/field/'] }
|
29
|
+
|
30
|
+
it 'throws exception' do
|
31
|
+
expect { processed_data }.to raise_exception(Logasm::Preprocessors::Whitelist::InvalidPointerFormatException)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'with whitelisted field' do
|
36
|
+
let(:pointers) { ['/field'] }
|
37
|
+
|
38
|
+
it 'includes the field' do
|
39
|
+
expect(processed_data).to eq({
|
40
|
+
field: 'secret',
|
41
|
+
data: {
|
42
|
+
field: '******'
|
43
|
+
},
|
44
|
+
array: [{field: '******'}]
|
45
|
+
})
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context 'with whitelisted nested field' do
|
50
|
+
let(:pointers) { ['/data/field'] }
|
51
|
+
|
52
|
+
it 'includes nested field' do
|
53
|
+
expect(processed_data).to eq({
|
54
|
+
field: '******',
|
55
|
+
data: {
|
56
|
+
field: 'secret'
|
57
|
+
},
|
58
|
+
array: [{field: '******'}]
|
59
|
+
})
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context 'with whitelisted array element field' do
|
64
|
+
let(:pointers) { ['/array/0/field'] }
|
65
|
+
|
66
|
+
it 'includes array element' do
|
67
|
+
expect(processed_data).to eq({
|
68
|
+
field: '******',
|
69
|
+
data: {
|
70
|
+
field: '******'
|
71
|
+
},
|
72
|
+
array: [{field: 'secret'}]
|
73
|
+
})
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context 'with whitelisted array element' do
|
78
|
+
let(:pointers) { ['/array/0'] }
|
79
|
+
|
80
|
+
it 'masks array element' do
|
81
|
+
expect(processed_data).to include(array: [{field: '******'}])
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
context 'with whitelisted array' do
|
86
|
+
let(:pointers) { ['/array'] }
|
87
|
+
|
88
|
+
it 'masks array' do
|
89
|
+
expect(processed_data).to include(array: [{field: '******'}])
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
context 'with whitelisted hash' do
|
94
|
+
let(:pointers) { ['/data'] }
|
95
|
+
|
96
|
+
it 'masks hash' do
|
97
|
+
expect(processed_data).to include(data: {field: '******'})
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
context 'when boolean present' do
|
102
|
+
let(:data) { {bool: true} }
|
103
|
+
|
104
|
+
it 'masks it with single asteriks' do
|
105
|
+
expect(processed_data).to eq(bool: '*')
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
context 'when field has slash in the name' do
|
110
|
+
let(:data) {{
|
111
|
+
'field_with_/' => 'secret'
|
112
|
+
}}
|
113
|
+
let(:pointers) { ['/field_with_~1'] }
|
114
|
+
|
115
|
+
it 'includes field' do
|
116
|
+
expect(processed_data).to include('field_with_/'=> 'secret')
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
context 'when field has tilde in the name' do
|
121
|
+
let(:data) {{
|
122
|
+
'field_with_~' => 'secret'
|
123
|
+
}}
|
124
|
+
let(:pointers) { ['/field_with_~0'] }
|
125
|
+
|
126
|
+
it 'includes field' do
|
127
|
+
expect(processed_data).to include('field_with_~'=> 'secret')
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
context 'when field has slash in the name' do
|
132
|
+
let(:data) {{
|
133
|
+
'field_with_/' => 'secret'
|
134
|
+
}}
|
135
|
+
let(:pointers) { ['/field_with_~1'] }
|
136
|
+
|
137
|
+
it 'includes field' do
|
138
|
+
expect(processed_data).to include('field_with_/'=> 'secret')
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
context 'when field has tilde in the name' do
|
143
|
+
let(:data) {{
|
144
|
+
'field_with_~' => 'secret'
|
145
|
+
}}
|
146
|
+
let(:pointers) { ['/field_with_~0'] }
|
147
|
+
|
148
|
+
it 'includes field' do
|
149
|
+
expect(processed_data).to include('field_with_~'=> 'secret')
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
RSpec::Matchers.define :implement_interface do |expected|
|
2
|
+
required_methods = expected.instance_methods(false)
|
3
|
+
match do |actual|
|
4
|
+
required_methods - actual.methods == []
|
5
|
+
end
|
6
|
+
|
7
|
+
failure_message do |actual|
|
8
|
+
missing_methods = required_methods - actual.methods
|
9
|
+
"Expected instance methods #{missing_methods.inspect} to be implemented"
|
10
|
+
end
|
11
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: logasm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Salemove
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-07-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: inflecto
|
@@ -88,6 +88,9 @@ files:
|
|
88
88
|
- lib/logasm/adapters/rabbitmq_adapter.rb
|
89
89
|
- lib/logasm/adapters/stdout_adapter.rb
|
90
90
|
- lib/logasm/null_logger.rb
|
91
|
+
- lib/logasm/preprocessors.rb
|
92
|
+
- lib/logasm/preprocessors/blacklist.rb
|
93
|
+
- lib/logasm/preprocessors/whitelist.rb
|
91
94
|
- lib/logasm/utils.rb
|
92
95
|
- logasm.gemspec
|
93
96
|
- spec/adapters/logstash_adapter/formatter_spec.rb
|
@@ -95,8 +98,11 @@ files:
|
|
95
98
|
- spec/adapters/rabbitmq_adapter_spec.rb
|
96
99
|
- spec/adapters/stdout_adapter_spec.rb
|
97
100
|
- spec/logasm_spec.rb
|
101
|
+
- spec/preprocessors/blacklist_spec.rb
|
102
|
+
- spec/preprocessors/whitelist_spec.rb
|
98
103
|
- spec/spec_helper.rb
|
99
104
|
- spec/support/freddy_mock.rb
|
105
|
+
- spec/support/implement_interface.rb
|
100
106
|
- spec/utils_spec.rb
|
101
107
|
homepage:
|
102
108
|
licenses:
|
@@ -118,7 +124,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
118
124
|
version: '0'
|
119
125
|
requirements: []
|
120
126
|
rubyforge_project:
|
121
|
-
rubygems_version: 2.4.
|
127
|
+
rubygems_version: 2.4.8
|
122
128
|
signing_key:
|
123
129
|
specification_version: 4
|
124
130
|
summary: What description said
|
@@ -128,6 +134,9 @@ test_files:
|
|
128
134
|
- spec/adapters/rabbitmq_adapter_spec.rb
|
129
135
|
- spec/adapters/stdout_adapter_spec.rb
|
130
136
|
- spec/logasm_spec.rb
|
137
|
+
- spec/preprocessors/blacklist_spec.rb
|
138
|
+
- spec/preprocessors/whitelist_spec.rb
|
131
139
|
- spec/spec_helper.rb
|
132
140
|
- spec/support/freddy_mock.rb
|
141
|
+
- spec/support/implement_interface.rb
|
133
142
|
- spec/utils_spec.rb
|