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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 312008a258504317188c1b14bd2671bbe8c1cb57
4
- data.tar.gz: 28bc444b2bb9b8001a56416c0c80860ceec0525c
3
+ metadata.gz: 6716c8a5d3446d582752238b022fe71cb1af5574
4
+ data.tar.gz: eddc2df5fa9deb630a92fecc546d575bbf62ea64
5
5
  SHA512:
6
- metadata.gz: 224caa6384c612f490e1a64a4c697224ae65f8b3faf8309c976172d22873d25be6f76c3362d19cb637cbb0817e5549c09fe8d232513f9e6631ab7366dbcf98b9
7
- data.tar.gz: 7fc9d04d683e3e6dde99604e1b25eb559240b36503f59f3220323fc6940bfaf38edcbe28c23a14298f426cdc31e8c5309510d2733efed50f38509be9f5a17393
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 )
@@ -1,7 +1,7 @@
1
1
  module Alephant
2
2
  module Logger
3
3
  class JSON
4
- VERSION = '0.4.0'.freeze
4
+ VERSION = '0.5.0'.freeze
5
5
  end
6
6
  end
7
7
  end
@@ -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
- [:debug, :info, :warn, :error].each do |level|
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
@@ -9,6 +9,7 @@ module Alephant
9
9
  def initialize(output, options = {})
10
10
  @output = output
11
11
  @nesting = options.fetch(:nesting, false)
12
+ @write_level = options.fetch(:level, :debug)
12
13
  self.class.session = -> { 'n/a' } unless self.class.session?
13
14
  end
14
15
 
@@ -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(:log_file) { instance_double File }
10
+ let(:log_output_obj) { instance_double(File) }
11
+ let(:msg) { :write }
11
12
 
12
13
  before do
13
- allow(File).to receive(:open) { log_file }
14
- allow(log_file).to receive :sync=
14
+ allow(File).to receive(:open) { log_output_obj }
15
+ allow(log_output_obj).to receive :sync=
15
16
  end
16
17
 
17
- shared_examples 'JSON logging' do
18
- let(:log_hash) do
19
- { 'foo' => 'bar', 'baz' => 'quux' }
20
- end
18
+ logging_levels = Alephant::Logger::LevelsController::LEVELS
21
19
 
22
- it 'writes JSON dump of hash to log with corresponding level key' do
23
- allow(Time).to receive(:now).and_return('foobar')
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
- expect(log_file).to receive(:write) do |json_dump|
26
- h = { 'timestamp' => 'foobar', 'uuid' => 'n/a', 'level' => level }
27
- expect(JSON.parse(json_dump)).to eq h.merge log_hash
28
- end
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
- subject.send(level, log_hash)
31
- end
30
+ context 'when message level is same as write level' do
31
+ context 'Symbol' do
32
+ let(:write_level) { level }
32
33
 
33
- it 'automatically includes a timestamp' do
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
- subject.send(level, log_hash)
40
- end
36
+ it_behaves_like 'nested JSON message flattened to strings'
41
37
 
42
- it 'outputs the timestamp first' do
43
- expect(log_file).to receive(:write) do |json_dump|
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
- subject.send(level, log_hash)
49
- end
41
+ context 'String' do
42
+ let(:write_level) { level.to_s }
50
43
 
51
- it 'displays a default session value if a custom function is not provided' do
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
- subject.send(level, log_hash)
58
- end
46
+ it_behaves_like 'nested JSON message flattened to strings'
59
47
 
60
- it 'displays a custom session value when provided a user defined function' do
61
- expect(log_file).to receive(:write) do |json_dump|
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
- described_class.session = fn
67
- subject.send(level, binding, log_hash)
68
- described_class.session = -> { 'n/a' }
69
- end
51
+ context 'Integer' do
52
+ let(:write_level) { logging_levels.index(level) }
70
53
 
71
- it 'provides a static method for checking if a session has been set' do
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
- described_class.send(:remove_instance_variable, :@session)
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
- shared_context 'nested log hash' do
81
- let(:log_hash) do
82
- { 'nest' => nest }
83
- end
58
+ it_behaves_like 'gracefully fails with string message'
59
+ end
60
+ end
84
61
 
85
- let(:nest) { { 'bird' => 'eggs' } }
86
- end
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
- shared_examples 'nests flattened to strings' do
89
- include_context 'nested log hash'
66
+ it_behaves_like 'a JSON log non writer'
90
67
 
91
- specify do
92
- expect(log_file).to receive(:write) do |json_dump|
93
- expect(JSON.parse(json_dump)['nest']).to eq nest.to_s
94
- end
68
+ it_behaves_like 'gracefully fails with string message'
69
+ end
70
+ end
95
71
 
96
- subject.send(level, log_hash)
97
- end
98
- end
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
- shared_examples 'nesting allowed' do
101
- include_context 'nested log hash'
77
+ context 'Symbol' do
78
+ let(:write_level) { logging_levels[write_level_index] }
102
79
 
103
- specify do
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
- subject.send(level, log_hash)
109
- end
110
- end
82
+ it_behaves_like 'nested JSON message flattened to strings'
111
83
 
112
- shared_examples 'gracefully fail with string arg' do
113
- let(:log_message) { 'Unable to connect to server' }
84
+ it_behaves_like 'gracefully fails with string message'
85
+ end
114
86
 
115
- specify { expect(log_file).not_to receive(:write) }
116
- specify do
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
- %w(debug info warn error).each do |level|
122
- describe "##{level}" do
123
- let(:level) { level }
90
+ it_behaves_like 'a JSON log writer'
124
91
 
125
- it_behaves_like 'JSON logging'
92
+ it_behaves_like 'nested JSON message flattened to strings'
126
93
 
127
- it_behaves_like 'nests flattened to strings'
94
+ it_behaves_like 'gracefully fails with string message'
95
+ end
128
96
 
129
- it_behaves_like 'gracefully fail with string arg'
97
+ context 'Integer' do
98
+ let(:write_level) { write_level_index }
130
99
 
131
- context 'with nesting allowed' do
132
- subject do
133
- described_class.new(log_path, nesting: true)
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
- subject { described_class.new(logger_io) }
7
+ let(:fn) { -> { 'foo' } }
8
+ let(:log_output_obj) { spy }
9
+ let(:msg) { :puts }
7
10
 
8
- let(:fn) { -> { 'foo' } }
9
- let(:logger_io) { spy }
11
+ logging_levels = Alephant::Logger::LevelsController::LEVELS
10
12
 
11
- shared_examples 'JSON logging' do
12
- let(:log_hash) do
13
- { 'foo' => 'bar', 'baz' => 'quux' }
14
- end
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
- it 'writes JSON dump of hash to log with corresponding level key' do
17
- allow(Time).to receive(:now).and_return('foobar')
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
- expect(logger_io).to receive(:puts) do |json_dump|
20
- h = { 'timestamp' => 'foobar', 'uuid' => 'n/a', 'level' => level }
21
- expect(JSON.parse(json_dump)).to eq h.merge log_hash
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
- subject.public_send(level, log_hash)
25
- end
27
+ it_behaves_like 'a JSON log writer'
26
28
 
27
- it 'automatically includes a timestamp' do
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
- subject.public_send(level, log_hash)
34
- end
31
+ it_behaves_like 'gracefully fails with string message'
32
+ end
35
33
 
36
- it 'outputs the timestamp first' do
37
- expect(logger_io).to receive(:puts) do |json_dump|
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
- subject.public_send(level, log_hash)
43
- end
37
+ it_behaves_like 'a JSON log writer'
44
38
 
45
- it 'displays a default session value if a custom function is not provided' do
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
- subject.public_send(level, log_hash)
52
- end
41
+ it_behaves_like 'gracefully fails with string message'
42
+ end
53
43
 
54
- it 'displays a custom session value when provided a user defined function' do
55
- expect(logger_io).to receive(:puts) do |json_dump|
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
- described_class.session = fn
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
- it 'provides a static method for checking if a session has been set' do
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
- described_class.send(:remove_instance_variable, :@session)
70
- expect(described_class.session?).to eq nil
71
- end
72
- end
51
+ it_behaves_like 'gracefully fails with string message'
52
+ end
53
+ end
73
54
 
74
- shared_context 'nested log hash' do
75
- let(:log_hash) do
76
- { 'nest' => nest }
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
- let(:nest) { { 'bird' => 'eggs' } }
80
- end
59
+ it_behaves_like 'a JSON log non writer'
81
60
 
82
- shared_examples 'nests flattened to strings' do
83
- include_context 'nested log hash'
61
+ it_behaves_like 'gracefully fails with string message'
62
+ end
63
+ end
84
64
 
85
- specify do
86
- expect(logger_io).to receive(:puts) do |json_dump|
87
- expect(JSON.parse(json_dump)['nest']).to eq nest.to_s
88
- end
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
- subject.public_send(level, log_hash)
91
- end
92
- end
70
+ context 'Symbol' do
71
+ let(:write_level) { logging_levels[write_level_index] }
93
72
 
94
- shared_examples 'nesting allowed' do
95
- include_context 'nested log hash'
73
+ it_behaves_like 'a JSON log writer'
96
74
 
97
- specify do
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
- subject.public_send(level, log_hash)
103
- end
104
- end
77
+ it_behaves_like 'gracefully fails with string message'
78
+ end
105
79
 
106
- shared_examples 'gracefully fail with string arg' do
107
- let(:log_message) { 'Unable to connect to server' }
80
+ context 'String' do
81
+ let(:write_level) { logging_levels[write_level_index].to_s }
108
82
 
109
- specify { expect(logger_io).not_to receive(:puts) }
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
- %w(debug info warn error).each do |level|
116
- describe "##{level}" do
117
- let(:level) { level }
85
+ it_behaves_like 'nested JSON message flattened to strings'
118
86
 
119
- it_behaves_like 'JSON logging'
87
+ it_behaves_like 'gracefully fails with string message'
88
+ end
120
89
 
121
- it_behaves_like 'nests flattened to strings'
90
+ context 'Symbol' do
91
+ let(:write_level) { write_level_index }
122
92
 
123
- it_behaves_like 'gracefully fail with string arg'
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(logger_io, nesting: true) }
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
@@ -0,0 +1,11 @@
1
+ shared_examples 'a loggable level' do
2
+ it 'returns true' do
3
+ expect(loggable?).to be(true)
4
+ end
5
+ end
6
+
7
+ shared_examples 'a non loggable level' do
8
+ it 'returns false' do
9
+ expect(loggable?).to be(false)
10
+ end
11
+ 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.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: 2016-09-23 00:00:00.000000000 Z
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.5.1
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