alephant-logger-json 0.4.0 → 0.5.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 +32 -1
- data/lib/alephant/logger/json/version.rb +1 -1
- data/lib/alephant/logger/json.rb +12 -3
- data/lib/alephant/logger/json_to_io.rb +1 -0
- data/lib/alephant/logger/levels_controller.rb +41 -0
- data/spec/alephant/logger/json_spec.rb +80 -94
- data/spec/alephant/logger/json_to_io_spec.rb +77 -90
- data/spec/alephant/logger/levels_controller_spec.rb +145 -0
- data/spec/alephant/logger/support/json_shared_examples.rb +119 -0
- data/spec/alephant/logger/support/levels_controller_shared_examples.rb +11 -0
- metadata +10 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6716c8a5d3446d582752238b022fe71cb1af5574
|
4
|
+
data.tar.gz: eddc2df5fa9deb630a92fecc546d575bbf62ea64
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 36f9a992d64486a1152cc6ef974e4e634d78dee69e5918b7eee6382fcd052280ec86e2db677926bb2851369f2a950a0895c8847c530d16fe685030321eed9bf9
|
7
|
+
data.tar.gz: 573d6232f79aa04207fb8eb7c1525cc5004ec7728a9c8aa2638798addc1596b6af58c5a703e430571ddc5459c0fd803c7956f7a2a033853b07cc65a4f2cdcbca
|
data/README.md
CHANGED
@@ -51,7 +51,7 @@ There are two methods available to help you:
|
|
51
51
|
|
52
52
|
When using tracing, you'll need to provide a binding context as the first argument to your log level method calls.
|
53
53
|
|
54
|
-
This is to resolve issues with lambda's scope availability. See `Kernal#binding` for more details.
|
54
|
+
This is to resolve issues with lambda's scope availability. See `Kernal#binding` for more details.
|
55
55
|
|
56
56
|
Example usage:
|
57
57
|
|
@@ -63,6 +63,37 @@ If no `binding` is provided then tracing is ignored and the logger falls back to
|
|
63
63
|
|
64
64
|
> Note: you can hide the binding necessity behind an abstraction layer if you prefer
|
65
65
|
|
66
|
+
### Logging Levels
|
67
|
+
|
68
|
+
The logger includes an option to define a desired logging level. Only log levels that are equal to or higher than the desired level will be logged.
|
69
|
+
The logger defaults to the _lowest_ level `0` i.e. `:debug` when a desired level is undefined.
|
70
|
+
|
71
|
+
Example
|
72
|
+
|
73
|
+
```ruby
|
74
|
+
# Hierarchical Log levels
|
75
|
+
# 0 => debug
|
76
|
+
# 1 => info
|
77
|
+
# 2 => warn
|
78
|
+
# 3 => error
|
79
|
+
|
80
|
+
# When Default level :debug
|
81
|
+
json_logger = Alephant::Logger::JSON.new("path/to/logfile.log")
|
82
|
+
|
83
|
+
# Log all levels >= 0
|
84
|
+
json_logger.info "This will log"
|
85
|
+
|
86
|
+
# When log level is defined
|
87
|
+
json_logger = Alephant::Logger::JSON.new("path/to/logfile.log", level: :info)
|
88
|
+
|
89
|
+
# log all levels >= 1
|
90
|
+
|
91
|
+
json_logger.debug "This will NOT log"
|
92
|
+
json_logger.info "This will log"
|
93
|
+
```
|
94
|
+
|
95
|
+
> Note: The logger expects the desired level to be defined as a Symbol, String or Integer type.
|
96
|
+
|
66
97
|
## Contributing
|
67
98
|
|
68
99
|
1. Fork it ( https://github.com/BBC-News/alephant-logger-json/fork )
|
data/lib/alephant/logger/json.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
-
require_relative './dynamic_binding.rb'
|
2
1
|
require 'json'
|
2
|
+
require_relative 'dynamic_binding'
|
3
|
+
require_relative 'levels_controller'
|
3
4
|
|
4
5
|
module Alephant
|
5
6
|
module Logger
|
@@ -8,10 +9,11 @@ module Alephant
|
|
8
9
|
@log_file = File.open(log_path, 'a+')
|
9
10
|
@log_file.sync = true
|
10
11
|
@nesting = options.fetch(:nesting, false)
|
12
|
+
@write_level = options.fetch(:level, :debug)
|
11
13
|
self.class.session = -> { 'n/a' } unless self.class.session?
|
12
14
|
end
|
13
15
|
|
14
|
-
|
16
|
+
LevelsController::LEVELS.each do |level|
|
15
17
|
define_method(level) do |b = nil, hash|
|
16
18
|
return if hash.is_a? String
|
17
19
|
|
@@ -23,7 +25,7 @@ module Alephant
|
|
23
25
|
|
24
26
|
hash = flatten_values_to_s(h) unless @nesting
|
25
27
|
|
26
|
-
write(hash)
|
28
|
+
write(hash) if writeable?(level)
|
27
29
|
end
|
28
30
|
end
|
29
31
|
|
@@ -41,6 +43,13 @@ module Alephant
|
|
41
43
|
@log_file.write(::JSON.generate(hash) + "\n")
|
42
44
|
end
|
43
45
|
|
46
|
+
def writeable?(message_level)
|
47
|
+
LevelsController.should_log?(
|
48
|
+
message_level: message_level,
|
49
|
+
desired_level: @write_level
|
50
|
+
)
|
51
|
+
end
|
52
|
+
|
44
53
|
def flatten_values_to_s(hash)
|
45
54
|
Hash[hash.map { |k, v| [k, v.to_s] }]
|
46
55
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Alephant
|
2
|
+
module Logger
|
3
|
+
class LevelsController
|
4
|
+
LEVELS = %i(debug info warn error).freeze
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def should_log?(message_level:, desired_level:)
|
8
|
+
message_level_index = level_index(message_level)
|
9
|
+
|
10
|
+
return false unless message_level_index
|
11
|
+
|
12
|
+
message_level_index >= desired_level_index(desired_level)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def desired_level_index(desired_level)
|
18
|
+
case desired_level
|
19
|
+
when Symbol then level_index_with_default(desired_level)
|
20
|
+
when String then level_index_with_default(desired_level.to_sym)
|
21
|
+
when Integer then desired_level
|
22
|
+
else
|
23
|
+
raise(
|
24
|
+
ArgumentError,
|
25
|
+
'wrong type of argument: expected Integer, Symbol or String. '\
|
26
|
+
"got #{desired_level.class}"
|
27
|
+
)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def level_index_with_default(desired_level)
|
32
|
+
level_index(desired_level) || 0
|
33
|
+
end
|
34
|
+
|
35
|
+
def level_index(level)
|
36
|
+
LEVELS.index(level)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -1,137 +1,123 @@
|
|
1
1
|
require 'date'
|
2
2
|
require 'spec_helper'
|
3
3
|
require 'alephant/logger/json'
|
4
|
+
require 'alephant/logger/levels_controller'
|
5
|
+
require_relative 'support/json_shared_examples'
|
4
6
|
|
5
7
|
describe Alephant::Logger::JSON do
|
6
|
-
subject { described_class.new(log_path) }
|
7
|
-
|
8
8
|
let(:fn) { -> { 'foo' } }
|
9
9
|
let(:log_path) { '/log/path.log' }
|
10
|
-
let(:
|
10
|
+
let(:log_output_obj) { instance_double(File) }
|
11
|
+
let(:msg) { :write }
|
11
12
|
|
12
13
|
before do
|
13
|
-
allow(File).to receive(:open) {
|
14
|
-
allow(
|
14
|
+
allow(File).to receive(:open) { log_output_obj }
|
15
|
+
allow(log_output_obj).to receive :sync=
|
15
16
|
end
|
16
17
|
|
17
|
-
|
18
|
-
let(:log_hash) do
|
19
|
-
{ 'foo' => 'bar', 'baz' => 'quux' }
|
20
|
-
end
|
18
|
+
logging_levels = Alephant::Logger::LevelsController::LEVELS
|
21
19
|
|
22
|
-
|
23
|
-
|
20
|
+
logging_levels.each_with_index do |level, i|
|
21
|
+
describe "##{level}" do
|
22
|
+
let(:level) { level }
|
23
|
+
let(:log_hash) { { 'foo' => 'bar', 'baz' => 'quux' } }
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
25
|
+
context 'when write level is specified' do
|
26
|
+
subject(:logger) do
|
27
|
+
described_class.new(log_path, level: write_level)
|
28
|
+
end
|
29
29
|
|
30
|
-
|
31
|
-
|
30
|
+
context 'when message level is same as write level' do
|
31
|
+
context 'Symbol' do
|
32
|
+
let(:write_level) { level }
|
32
33
|
|
33
|
-
|
34
|
-
expect(log_file).to receive(:write) do |json_dump|
|
35
|
-
t = JSON.parse(json_dump)['timestamp']
|
36
|
-
expect { DateTime.parse(t) }.to_not raise_error
|
37
|
-
end
|
34
|
+
it_behaves_like 'a JSON log writer'
|
38
35
|
|
39
|
-
|
40
|
-
end
|
36
|
+
it_behaves_like 'nested JSON message flattened to strings'
|
41
37
|
|
42
|
-
|
43
|
-
|
44
|
-
h = JSON.parse(json_dump)
|
45
|
-
expect(h.first[0].to_sym).to be :timestamp
|
46
|
-
end
|
38
|
+
it_behaves_like 'gracefully fails with string message'
|
39
|
+
end
|
47
40
|
|
48
|
-
|
49
|
-
|
41
|
+
context 'String' do
|
42
|
+
let(:write_level) { level.to_s }
|
50
43
|
|
51
|
-
|
52
|
-
expect(log_file).to receive(:write) do |json_dump|
|
53
|
-
h = JSON.parse(json_dump)
|
54
|
-
expect(h['uuid']).to eq 'n/a'
|
55
|
-
end
|
44
|
+
it_behaves_like 'a JSON log writer'
|
56
45
|
|
57
|
-
|
58
|
-
end
|
46
|
+
it_behaves_like 'nested JSON message flattened to strings'
|
59
47
|
|
60
|
-
|
61
|
-
|
62
|
-
h = JSON.parse(json_dump)
|
63
|
-
expect(h['uuid']).to eq 'foo'
|
64
|
-
end
|
48
|
+
it_behaves_like 'gracefully fails with string message'
|
49
|
+
end
|
65
50
|
|
66
|
-
|
67
|
-
|
68
|
-
described_class.session = -> { 'n/a' }
|
69
|
-
end
|
51
|
+
context 'Integer' do
|
52
|
+
let(:write_level) { logging_levels.index(level) }
|
70
53
|
|
71
|
-
|
72
|
-
described_class.session = fn
|
73
|
-
expect(described_class.session?).to eq 'instance-variable'
|
54
|
+
it_behaves_like 'a JSON log writer'
|
74
55
|
|
75
|
-
|
76
|
-
expect(described_class.session?).to eq nil
|
77
|
-
end
|
78
|
-
end
|
56
|
+
it_behaves_like 'nested JSON message flattened to strings'
|
79
57
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
end
|
58
|
+
it_behaves_like 'gracefully fails with string message'
|
59
|
+
end
|
60
|
+
end
|
84
61
|
|
85
|
-
|
86
|
-
|
62
|
+
context 'when message level is lower than write level' do
|
63
|
+
context 'Integer' do
|
64
|
+
let(:write_level) { logging_levels.index(level) + 1 }
|
87
65
|
|
88
|
-
|
89
|
-
include_context 'nested log hash'
|
66
|
+
it_behaves_like 'a JSON log non writer'
|
90
67
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
end
|
68
|
+
it_behaves_like 'gracefully fails with string message'
|
69
|
+
end
|
70
|
+
end
|
95
71
|
|
96
|
-
|
97
|
-
|
98
|
-
|
72
|
+
context 'when message level is higher than write level' do
|
73
|
+
let(:write_level_index) do
|
74
|
+
i > 0 ? logging_levels.index(level) - 1 : 0
|
75
|
+
end
|
99
76
|
|
100
|
-
|
101
|
-
|
77
|
+
context 'Symbol' do
|
78
|
+
let(:write_level) { logging_levels[write_level_index] }
|
102
79
|
|
103
|
-
|
104
|
-
expect(log_file).to receive(:write) do |json_dump|
|
105
|
-
expect(JSON.parse(json_dump)).to eq log_hash
|
106
|
-
end
|
80
|
+
it_behaves_like 'a JSON log writer'
|
107
81
|
|
108
|
-
|
109
|
-
end
|
110
|
-
end
|
82
|
+
it_behaves_like 'nested JSON message flattened to strings'
|
111
83
|
|
112
|
-
|
113
|
-
|
84
|
+
it_behaves_like 'gracefully fails with string message'
|
85
|
+
end
|
114
86
|
|
115
|
-
|
116
|
-
|
117
|
-
expect { subject.debug log_message }.not_to raise_error
|
118
|
-
end
|
119
|
-
end
|
87
|
+
context 'String' do
|
88
|
+
let(:write_level) { logging_levels[write_level_index].to_s }
|
120
89
|
|
121
|
-
|
122
|
-
describe "##{level}" do
|
123
|
-
let(:level) { level }
|
90
|
+
it_behaves_like 'a JSON log writer'
|
124
91
|
|
125
|
-
|
92
|
+
it_behaves_like 'nested JSON message flattened to strings'
|
126
93
|
|
127
|
-
|
94
|
+
it_behaves_like 'gracefully fails with string message'
|
95
|
+
end
|
128
96
|
|
129
|
-
|
97
|
+
context 'Integer' do
|
98
|
+
let(:write_level) { write_level_index }
|
130
99
|
|
131
|
-
|
132
|
-
|
133
|
-
|
100
|
+
it_behaves_like 'a JSON log writer'
|
101
|
+
|
102
|
+
it_behaves_like 'nested JSON message flattened to strings'
|
103
|
+
|
104
|
+
it_behaves_like 'gracefully fails with string message'
|
105
|
+
end
|
134
106
|
end
|
107
|
+
end
|
108
|
+
|
109
|
+
context 'when write level is not specified' do
|
110
|
+
subject(:logger) { described_class.new(log_path) }
|
111
|
+
|
112
|
+
it_behaves_like 'a JSON log writer'
|
113
|
+
|
114
|
+
it_behaves_like 'nested JSON message flattened to strings'
|
115
|
+
|
116
|
+
it_behaves_like 'gracefully fails with string message'
|
117
|
+
end
|
118
|
+
|
119
|
+
context 'with nesting allowed' do
|
120
|
+
subject(:logger) { described_class.new(log_path, nesting: true) }
|
135
121
|
|
136
122
|
it_behaves_like 'nesting allowed'
|
137
123
|
end
|
@@ -1,129 +1,116 @@
|
|
1
1
|
require 'date'
|
2
2
|
require 'spec_helper'
|
3
3
|
require 'alephant/logger/json_to_io'
|
4
|
+
require_relative 'support/json_shared_examples'
|
4
5
|
|
5
6
|
describe Alephant::Logger::JSONtoIO do
|
6
|
-
|
7
|
+
let(:fn) { -> { 'foo' } }
|
8
|
+
let(:log_output_obj) { spy }
|
9
|
+
let(:msg) { :puts }
|
7
10
|
|
8
|
-
|
9
|
-
let(:logger_io) { spy }
|
11
|
+
logging_levels = Alephant::Logger::LevelsController::LEVELS
|
10
12
|
|
11
|
-
|
12
|
-
|
13
|
-
{
|
14
|
-
|
13
|
+
logging_levels.each_with_index do |level, i|
|
14
|
+
describe "##{level}" do
|
15
|
+
let(:level) { level }
|
16
|
+
let(:log_hash) { { 'foo' => 'bar', 'baz' => 'quux' } }
|
15
17
|
|
16
|
-
|
17
|
-
|
18
|
+
context 'when write level is specified' do
|
19
|
+
subject(:logger) do
|
20
|
+
described_class.new(log_output_obj, level: write_level)
|
21
|
+
end
|
18
22
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
end
|
23
|
+
context 'when message level is same as write level' do
|
24
|
+
context 'Symbol' do
|
25
|
+
let(:write_level) { level }
|
23
26
|
|
24
|
-
|
25
|
-
end
|
27
|
+
it_behaves_like 'a JSON log writer'
|
26
28
|
|
27
|
-
|
28
|
-
expect(logger_io).to receive(:puts) do |json_dump|
|
29
|
-
t = JSON.parse(json_dump)['timestamp']
|
30
|
-
expect { DateTime.parse(t) }.to_not raise_error
|
31
|
-
end
|
29
|
+
it_behaves_like 'nested JSON message flattened to strings'
|
32
30
|
|
33
|
-
|
34
|
-
|
31
|
+
it_behaves_like 'gracefully fails with string message'
|
32
|
+
end
|
35
33
|
|
36
|
-
|
37
|
-
|
38
|
-
h = JSON.parse(json_dump)
|
39
|
-
expect(h.first[0].to_sym).to be :timestamp
|
40
|
-
end
|
34
|
+
context 'String' do
|
35
|
+
let(:write_level) { level.to_s }
|
41
36
|
|
42
|
-
|
43
|
-
end
|
37
|
+
it_behaves_like 'a JSON log writer'
|
44
38
|
|
45
|
-
|
46
|
-
expect(logger_io).to receive(:puts) do |json_dump|
|
47
|
-
h = JSON.parse(json_dump)
|
48
|
-
expect(h['uuid']).to eq 'n/a'
|
49
|
-
end
|
39
|
+
it_behaves_like 'nested JSON message flattened to strings'
|
50
40
|
|
51
|
-
|
52
|
-
|
41
|
+
it_behaves_like 'gracefully fails with string message'
|
42
|
+
end
|
53
43
|
|
54
|
-
|
55
|
-
|
56
|
-
h = JSON.parse(json_dump)
|
57
|
-
expect(h['uuid']).to eq 'foo'
|
58
|
-
end
|
44
|
+
context 'Integer' do
|
45
|
+
let(:write_level) { logging_levels.index(level) }
|
59
46
|
|
60
|
-
|
61
|
-
subject.send(level, binding, log_hash)
|
62
|
-
described_class.session = -> { 'n/a' }
|
63
|
-
end
|
47
|
+
it_behaves_like 'a JSON log writer'
|
64
48
|
|
65
|
-
|
66
|
-
described_class.session = fn
|
67
|
-
expect(described_class.session?).to eq 'instance-variable'
|
49
|
+
it_behaves_like 'nested JSON message flattened to strings'
|
68
50
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
end
|
51
|
+
it_behaves_like 'gracefully fails with string message'
|
52
|
+
end
|
53
|
+
end
|
73
54
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
end
|
55
|
+
context 'when message level is lower than write level' do
|
56
|
+
context 'Integer' do
|
57
|
+
let(:write_level) { logging_levels.index(level) + 1 }
|
78
58
|
|
79
|
-
|
80
|
-
end
|
59
|
+
it_behaves_like 'a JSON log non writer'
|
81
60
|
|
82
|
-
|
83
|
-
|
61
|
+
it_behaves_like 'gracefully fails with string message'
|
62
|
+
end
|
63
|
+
end
|
84
64
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
65
|
+
context 'when message level is higher than write level' do
|
66
|
+
let(:write_level_index) do
|
67
|
+
i > 0 ? logging_levels.index(level) - 1 : 0
|
68
|
+
end
|
89
69
|
|
90
|
-
|
91
|
-
|
92
|
-
end
|
70
|
+
context 'Symbol' do
|
71
|
+
let(:write_level) { logging_levels[write_level_index] }
|
93
72
|
|
94
|
-
|
95
|
-
include_context 'nested log hash'
|
73
|
+
it_behaves_like 'a JSON log writer'
|
96
74
|
|
97
|
-
|
98
|
-
expect(logger_io).to receive(:puts) do |json_dump|
|
99
|
-
expect(JSON.parse(json_dump)).to eq log_hash
|
100
|
-
end
|
75
|
+
it_behaves_like 'nested JSON message flattened to strings'
|
101
76
|
|
102
|
-
|
103
|
-
|
104
|
-
end
|
77
|
+
it_behaves_like 'gracefully fails with string message'
|
78
|
+
end
|
105
79
|
|
106
|
-
|
107
|
-
|
80
|
+
context 'String' do
|
81
|
+
let(:write_level) { logging_levels[write_level_index].to_s }
|
108
82
|
|
109
|
-
|
110
|
-
specify do
|
111
|
-
expect { subject.debug log_message }.not_to raise_error
|
112
|
-
end
|
113
|
-
end
|
83
|
+
it_behaves_like 'a JSON log writer'
|
114
84
|
|
115
|
-
|
116
|
-
describe "##{level}" do
|
117
|
-
let(:level) { level }
|
85
|
+
it_behaves_like 'nested JSON message flattened to strings'
|
118
86
|
|
119
|
-
|
87
|
+
it_behaves_like 'gracefully fails with string message'
|
88
|
+
end
|
120
89
|
|
121
|
-
|
90
|
+
context 'Symbol' do
|
91
|
+
let(:write_level) { write_level_index }
|
122
92
|
|
123
|
-
|
93
|
+
it_behaves_like 'a JSON log writer'
|
94
|
+
|
95
|
+
it_behaves_like 'nested JSON message flattened to strings'
|
96
|
+
|
97
|
+
it_behaves_like 'gracefully fails with string message'
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
context 'when write level is not specified' do
|
103
|
+
subject(:logger) { described_class.new(log_output_obj) }
|
104
|
+
|
105
|
+
it_behaves_like 'a JSON log writer'
|
106
|
+
|
107
|
+
it_behaves_like 'nested JSON message flattened to strings'
|
108
|
+
|
109
|
+
it_behaves_like 'gracefully fails with string message'
|
110
|
+
end
|
124
111
|
|
125
112
|
context 'with nesting allowed' do
|
126
|
-
subject { described_class.new(
|
113
|
+
subject(:logger) { described_class.new(log_output_obj, nesting: true) }
|
127
114
|
|
128
115
|
it_behaves_like 'nesting allowed'
|
129
116
|
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'alephant/logger/levels_controller'
|
3
|
+
require_relative 'support/levels_controller_shared_examples'
|
4
|
+
|
5
|
+
RSpec.describe Alephant::Logger::LevelsController do
|
6
|
+
describe '.should_log?' do
|
7
|
+
subject(:loggable?) do
|
8
|
+
described_class.should_log?(
|
9
|
+
message_level: message_level,
|
10
|
+
desired_level: desired_level
|
11
|
+
)
|
12
|
+
end
|
13
|
+
|
14
|
+
describe 'Message level' do
|
15
|
+
let(:message_level) { :info }
|
16
|
+
|
17
|
+
context 'when message level is higher than desired level' do
|
18
|
+
context 'Symbol' do
|
19
|
+
let(:desired_level) { :debug }
|
20
|
+
|
21
|
+
it_behaves_like 'a loggable level'
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'String' do
|
25
|
+
let(:desired_level) { 'debug' }
|
26
|
+
|
27
|
+
it_behaves_like 'a loggable level'
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'Integer' do
|
31
|
+
let(:desired_level) { 0 }
|
32
|
+
|
33
|
+
it_behaves_like 'a loggable level'
|
34
|
+
|
35
|
+
context 'when out of range' do
|
36
|
+
let(:desired_level) { -200 }
|
37
|
+
|
38
|
+
it_behaves_like 'a loggable level'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'when message level is lower than desired level' do
|
44
|
+
context 'Symbol' do
|
45
|
+
let(:desired_level) { :warn }
|
46
|
+
|
47
|
+
it_behaves_like 'a non loggable level'
|
48
|
+
end
|
49
|
+
|
50
|
+
context 'String' do
|
51
|
+
let(:desired_level) { 'warn' }
|
52
|
+
|
53
|
+
it_behaves_like 'a non loggable level'
|
54
|
+
end
|
55
|
+
|
56
|
+
context 'Integer' do
|
57
|
+
let(:desired_level) { 3 }
|
58
|
+
|
59
|
+
it_behaves_like 'a non loggable level'
|
60
|
+
|
61
|
+
context 'when out of range' do
|
62
|
+
let(:desired_level) { 100 }
|
63
|
+
|
64
|
+
it_behaves_like 'a non loggable level'
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context 'when message level is equal to desired level' do
|
70
|
+
context 'Symbol' do
|
71
|
+
let(:desired_level) { message_level }
|
72
|
+
|
73
|
+
it_behaves_like 'a loggable level'
|
74
|
+
end
|
75
|
+
|
76
|
+
context 'String' do
|
77
|
+
let(:desired_level) { message_level.to_s }
|
78
|
+
|
79
|
+
it_behaves_like 'a loggable level'
|
80
|
+
end
|
81
|
+
|
82
|
+
context 'Integer' do
|
83
|
+
let(:desired_level) { 1 }
|
84
|
+
|
85
|
+
it_behaves_like 'a loggable level'
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
context 'when message level is invalid' do
|
90
|
+
let(:desired_level) { :debug }
|
91
|
+
|
92
|
+
context 'when message level is not in LEVELS' do
|
93
|
+
let(:message_level) { :foobar }
|
94
|
+
|
95
|
+
it_behaves_like 'a non loggable level'
|
96
|
+
end
|
97
|
+
|
98
|
+
context 'when message level is nil' do
|
99
|
+
let(:message_level) { nil }
|
100
|
+
|
101
|
+
it_behaves_like 'a non loggable level'
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
describe 'Desired level' do
|
107
|
+
context 'when desired level is not in LEVELS' do
|
108
|
+
let(:message_level) { :error }
|
109
|
+
let(:desired_level) { :foobar }
|
110
|
+
|
111
|
+
it 'defaults to debug' do
|
112
|
+
expect(loggable?).to be(true)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
context 'when desired level is an unsupported type' do
|
117
|
+
context 'Hash' do
|
118
|
+
let(:message_level) { :error }
|
119
|
+
let(:desired_level) { {} }
|
120
|
+
|
121
|
+
it 'raises an argument error' do
|
122
|
+
expect { loggable? }.to raise_error(
|
123
|
+
ArgumentError,
|
124
|
+
'wrong type of argument: expected Integer, '\
|
125
|
+
'Symbol or String. got Hash'
|
126
|
+
)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
context 'Nil' do
|
131
|
+
let(:message_level) { :error }
|
132
|
+
let(:desired_level) { nil }
|
133
|
+
|
134
|
+
it 'raises an argument error' do
|
135
|
+
expect { loggable? }.to raise_error(
|
136
|
+
ArgumentError,
|
137
|
+
'wrong type of argument: expected Integer, '\
|
138
|
+
'Symbol or String. got NilClass'
|
139
|
+
)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
shared_examples 'a JSON log writer' do
|
2
|
+
let(:log_hash) do
|
3
|
+
{ 'foo' => 'bar', 'baz' => 'quux' }
|
4
|
+
end
|
5
|
+
|
6
|
+
it 'writes JSON dump of hash to log with corresponding level key' do
|
7
|
+
allow(Time).to receive(:now).and_return('foobar')
|
8
|
+
|
9
|
+
expect(log_output_obj).to receive(msg) do |json_dump|
|
10
|
+
h = { 'timestamp' => 'foobar', 'uuid' => 'n/a', 'level' => level.to_s }
|
11
|
+
expect(JSON.parse(json_dump)).to eq(h.merge(log_hash))
|
12
|
+
end
|
13
|
+
|
14
|
+
logger.send(level, log_hash)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'automatically includes a timestamp' do
|
18
|
+
expect(log_output_obj).to receive(msg) do |json_dump|
|
19
|
+
t = JSON.parse(json_dump)['timestamp']
|
20
|
+
expect { DateTime.parse(t) }.not_to raise_error
|
21
|
+
end
|
22
|
+
|
23
|
+
logger.send(level, log_hash)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'outputs the timestamp first' do
|
27
|
+
expect(log_output_obj).to receive(msg) do |json_dump|
|
28
|
+
h = JSON.parse(json_dump)
|
29
|
+
expect(h.first[0].to_sym).to be(:timestamp)
|
30
|
+
end
|
31
|
+
|
32
|
+
logger.send(level, log_hash)
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'when a session is set' do
|
36
|
+
it 'provides a static reader method' do
|
37
|
+
described_class.session = fn
|
38
|
+
expect(described_class.session?).to eq('instance-variable')
|
39
|
+
|
40
|
+
described_class.send(:remove_instance_variable, :@session)
|
41
|
+
expect(described_class.session?).to be(nil)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'when a user defined function is provided' do
|
46
|
+
it 'displays a custom session value' do
|
47
|
+
expect(log_output_obj).to receive(msg) do |json_dump|
|
48
|
+
h = JSON.parse(json_dump)
|
49
|
+
expect(h['uuid']).to eq('foo')
|
50
|
+
end
|
51
|
+
|
52
|
+
described_class.session = fn
|
53
|
+
logger.send(level, binding, log_hash)
|
54
|
+
described_class.session = -> { 'n/a' }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'when a user defined function is not provided' do
|
59
|
+
it 'displays a default session value' do
|
60
|
+
expect(log_output_obj).to receive(msg) do |json_dump|
|
61
|
+
h = JSON.parse(json_dump)
|
62
|
+
expect(h['uuid']).to eq('n/a')
|
63
|
+
end
|
64
|
+
|
65
|
+
logger.send(level, log_hash)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
shared_examples 'a JSON log non writer' do
|
71
|
+
before do
|
72
|
+
allow(Time).to receive(:now).and_return('foobar')
|
73
|
+
logger.send(level, log_hash)
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'does not write' do
|
77
|
+
expect(log_output_obj).not_to receive(msg)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
shared_context 'nested log hash' do
|
82
|
+
let(:log_hash) { { 'nest' => nest } }
|
83
|
+
|
84
|
+
let(:nest) { { 'bird' => 'eggs' } }
|
85
|
+
end
|
86
|
+
|
87
|
+
shared_examples 'nested JSON message flattened to strings' do
|
88
|
+
include_context 'nested log hash'
|
89
|
+
|
90
|
+
specify do
|
91
|
+
expect(log_output_obj).to receive(msg) do |json_dump|
|
92
|
+
expect(JSON.parse(json_dump)['nest']).to eq(nest.to_s)
|
93
|
+
end
|
94
|
+
|
95
|
+
logger.send(level, log_hash)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
shared_examples 'nesting allowed' do
|
100
|
+
include_context 'nested log hash'
|
101
|
+
|
102
|
+
specify do
|
103
|
+
expect(log_output_obj).to receive(msg) do |json_dump|
|
104
|
+
expect(JSON.parse(json_dump)).to eq(log_hash)
|
105
|
+
end
|
106
|
+
|
107
|
+
logger.send(level, log_hash)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
shared_examples 'gracefully fails with string message' do
|
112
|
+
let(:log_message) { 'Unable to connect to server' }
|
113
|
+
|
114
|
+
specify { expect(log_output_obj).not_to receive(msg) }
|
115
|
+
|
116
|
+
specify do
|
117
|
+
expect { logger.debug(log_message) }.not_to raise_error
|
118
|
+
end
|
119
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: alephant-logger-json
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dan Arnould
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-09-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -112,8 +112,12 @@ files:
|
|
112
112
|
- lib/alephant/logger/json.rb
|
113
113
|
- lib/alephant/logger/json/version.rb
|
114
114
|
- lib/alephant/logger/json_to_io.rb
|
115
|
+
- lib/alephant/logger/levels_controller.rb
|
115
116
|
- spec/alephant/logger/json_spec.rb
|
116
117
|
- spec/alephant/logger/json_to_io_spec.rb
|
118
|
+
- spec/alephant/logger/levels_controller_spec.rb
|
119
|
+
- spec/alephant/logger/support/json_shared_examples.rb
|
120
|
+
- spec/alephant/logger/support/levels_controller_shared_examples.rb
|
117
121
|
- spec/spec_helper.rb
|
118
122
|
homepage: ''
|
119
123
|
licenses:
|
@@ -135,11 +139,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
135
139
|
version: '0'
|
136
140
|
requirements: []
|
137
141
|
rubyforge_project:
|
138
|
-
rubygems_version: 2.
|
142
|
+
rubygems_version: 2.6.8
|
139
143
|
signing_key:
|
140
144
|
specification_version: 4
|
141
145
|
summary: alephant-logger driver enabling structured logging in JSON
|
142
146
|
test_files:
|
143
147
|
- spec/alephant/logger/json_spec.rb
|
144
148
|
- spec/alephant/logger/json_to_io_spec.rb
|
149
|
+
- spec/alephant/logger/levels_controller_spec.rb
|
150
|
+
- spec/alephant/logger/support/json_shared_examples.rb
|
151
|
+
- spec/alephant/logger/support/levels_controller_shared_examples.rb
|
145
152
|
- spec/spec_helper.rb
|