alephant-logger-json 0.3.1 → 0.4.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/Rakefile +3 -3
- data/alephant-logger-json.gemspec +15 -15
- data/lib/alephant/logger/dynamic_binding.rb +2 -2
- data/lib/alephant/logger/json/version.rb +1 -1
- data/lib/alephant/logger/json.rb +25 -18
- data/lib/alephant/logger/json_to_io.rb +22 -0
- data/spec/alephant/logger/json_spec.rb +44 -46
- data/spec/alephant/logger/json_to_io_spec.rb +132 -0
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 312008a258504317188c1b14bd2671bbe8c1cb57
|
4
|
+
data.tar.gz: 28bc444b2bb9b8001a56416c0c80860ceec0525c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 224caa6384c612f490e1a64a4c697224ae65f8b3faf8309c976172d22873d25be6f76c3362d19cb637cbb0817e5549c09fe8d232513f9e6631ab7366dbcf98b9
|
7
|
+
data.tar.gz: 7fc9d04d683e3e6dde99604e1b25eb559240b36503f59f3220323fc6940bfaf38edcbe28c23a14298f426cdc31e8c5309510d2733efed50f38509be9f5a17393
|
data/Rakefile
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'rake/rspec'
|
3
3
|
|
4
|
-
task :
|
4
|
+
task default: :spec
|
@@ -1,27 +1,27 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
lib = File.expand_path('../lib', __FILE__)
|
3
3
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require
|
4
|
+
require 'alephant/logger/json/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
7
|
+
spec.name = 'alephant-logger-json'
|
8
8
|
spec.version = Alephant::Logger::JSON::VERSION
|
9
|
-
spec.authors = [
|
10
|
-
spec.email = [
|
11
|
-
spec.summary =
|
12
|
-
spec.description =
|
13
|
-
spec.homepage =
|
14
|
-
spec.license =
|
9
|
+
spec.authors = ['Dan Arnould']
|
10
|
+
spec.email = ['dan@arnould.co.uk']
|
11
|
+
spec.summary = 'alephant-logger driver enabling structured logging in JSON'
|
12
|
+
spec.description = 'alephant-logger driver enabling structured logging in JSON'
|
13
|
+
spec.homepage = ''
|
14
|
+
spec.license = 'MIT'
|
15
15
|
|
16
16
|
spec.files = `git ls-files -z`.split("\x0")
|
17
17
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
-
spec.require_paths = [
|
19
|
+
spec.require_paths = ['lib']
|
20
20
|
|
21
|
-
spec.add_development_dependency
|
22
|
-
spec.add_development_dependency
|
23
|
-
spec.add_development_dependency
|
24
|
-
spec.add_development_dependency
|
25
|
-
spec.add_development_dependency
|
26
|
-
spec.add_development_dependency
|
21
|
+
spec.add_development_dependency 'bundler', '~> 1.7'
|
22
|
+
spec.add_development_dependency 'rake'
|
23
|
+
spec.add_development_dependency 'rspec'
|
24
|
+
spec.add_development_dependency 'rspec-nc'
|
25
|
+
spec.add_development_dependency 'rake-rspec'
|
26
|
+
spec.add_development_dependency 'pry'
|
27
27
|
end
|
@@ -7,7 +7,7 @@ module DynamicBinding
|
|
7
7
|
def method_missing(m, *args)
|
8
8
|
@bindings.reverse_each do |bind|
|
9
9
|
begin
|
10
|
-
method = eval(
|
10
|
+
method = eval('method(%s)' % m.inspect, bind)
|
11
11
|
rescue NameError
|
12
12
|
else
|
13
13
|
return method.call(*args)
|
@@ -18,7 +18,7 @@ module DynamicBinding
|
|
18
18
|
rescue NameError
|
19
19
|
end
|
20
20
|
end
|
21
|
-
raise NoMethodError,
|
21
|
+
raise NoMethodError, 'No such variable or method: %s' % m
|
22
22
|
end
|
23
23
|
|
24
24
|
def run_proc(p, *args)
|
data/lib/alephant/logger/json.rb
CHANGED
@@ -1,39 +1,46 @@
|
|
1
|
-
require_relative
|
2
|
-
require
|
1
|
+
require_relative './dynamic_binding.rb'
|
2
|
+
require 'json'
|
3
3
|
|
4
4
|
module Alephant
|
5
5
|
module Logger
|
6
6
|
class JSON
|
7
7
|
def initialize(log_path, options = {})
|
8
|
-
@log_file
|
9
|
-
@log_file.sync
|
10
|
-
@nesting
|
8
|
+
@log_file = File.open(log_path, 'a+')
|
9
|
+
@log_file.sync = true
|
10
|
+
@nesting = options.fetch(:nesting, false)
|
11
|
+
self.class.session = -> { 'n/a' } unless self.class.session?
|
11
12
|
end
|
12
13
|
|
13
14
|
[:debug, :info, :warn, :error].each do |level|
|
14
|
-
define_method(level) do |b=nil, hash|
|
15
|
+
define_method(level) do |b = nil, hash|
|
15
16
|
return if hash.is_a? String
|
16
|
-
|
17
|
+
|
17
18
|
h = {
|
18
|
-
:
|
19
|
-
:
|
20
|
-
:
|
21
|
-
}.merge
|
22
|
-
|
23
|
-
|
19
|
+
timestamp: Time.now.to_s,
|
20
|
+
uuid: b.nil? ? 'n/a' : self.class.session.call_with_binding(b),
|
21
|
+
level: level.to_s
|
22
|
+
}.merge(hash)
|
23
|
+
|
24
|
+
hash = flatten_values_to_s(h) unless @nesting
|
25
|
+
|
26
|
+
write(hash)
|
24
27
|
end
|
25
28
|
end
|
26
29
|
|
27
|
-
|
28
|
-
|
29
|
-
end
|
30
|
+
class << self
|
31
|
+
attr_accessor :session
|
30
32
|
|
31
|
-
|
32
|
-
|
33
|
+
def session?
|
34
|
+
defined?(@session)
|
35
|
+
end
|
33
36
|
end
|
34
37
|
|
35
38
|
private
|
36
39
|
|
40
|
+
def write(hash)
|
41
|
+
@log_file.write(::JSON.generate(hash) + "\n")
|
42
|
+
end
|
43
|
+
|
37
44
|
def flatten_values_to_s(hash)
|
38
45
|
Hash[hash.map { |k, v| [k, v.to_s] }]
|
39
46
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require_relative 'json'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Alephant
|
5
|
+
module Logger
|
6
|
+
class JSONtoIO < Alephant::Logger::JSON
|
7
|
+
attr_reader :output
|
8
|
+
|
9
|
+
def initialize(output, options = {})
|
10
|
+
@output = output
|
11
|
+
@nesting = options.fetch(:nesting, false)
|
12
|
+
self.class.session = -> { 'n/a' } unless self.class.session?
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def write(hash)
|
18
|
+
output.puts(::JSON.generate(hash))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -1,14 +1,12 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
1
|
+
require 'date'
|
2
|
+
require 'spec_helper'
|
3
|
+
require 'alephant/logger/json'
|
4
4
|
|
5
5
|
describe Alephant::Logger::JSON do
|
6
|
-
subject
|
7
|
-
described_class.new log_path
|
8
|
-
end
|
6
|
+
subject { described_class.new(log_path) }
|
9
7
|
|
10
|
-
let(:fn) { -> {
|
11
|
-
let(:log_path) {
|
8
|
+
let(:fn) { -> { 'foo' } }
|
9
|
+
let(:log_path) { '/log/path.log' }
|
12
10
|
let(:log_file) { instance_double File }
|
13
11
|
|
14
12
|
before do
|
@@ -16,32 +14,32 @@ describe Alephant::Logger::JSON do
|
|
16
14
|
allow(log_file).to receive :sync=
|
17
15
|
end
|
18
16
|
|
19
|
-
shared_examples
|
17
|
+
shared_examples 'JSON logging' do
|
20
18
|
let(:log_hash) do
|
21
|
-
{
|
19
|
+
{ 'foo' => 'bar', 'baz' => 'quux' }
|
22
20
|
end
|
23
21
|
|
24
|
-
it
|
25
|
-
allow(Time).to receive(:now).and_return(
|
22
|
+
it 'writes JSON dump of hash to log with corresponding level key' do
|
23
|
+
allow(Time).to receive(:now).and_return('foobar')
|
26
24
|
|
27
25
|
expect(log_file).to receive(:write) do |json_dump|
|
28
|
-
h = {
|
29
|
-
expect(JSON.parse
|
26
|
+
h = { 'timestamp' => 'foobar', 'uuid' => 'n/a', 'level' => level }
|
27
|
+
expect(JSON.parse(json_dump)).to eq h.merge log_hash
|
30
28
|
end
|
31
29
|
|
32
30
|
subject.send(level, log_hash)
|
33
31
|
end
|
34
32
|
|
35
|
-
it
|
33
|
+
it 'automatically includes a timestamp' do
|
36
34
|
expect(log_file).to receive(:write) do |json_dump|
|
37
|
-
t = JSON.parse(json_dump)[
|
38
|
-
expect{DateTime.parse(t)}.to_not raise_error
|
35
|
+
t = JSON.parse(json_dump)['timestamp']
|
36
|
+
expect { DateTime.parse(t) }.to_not raise_error
|
39
37
|
end
|
40
38
|
|
41
39
|
subject.send(level, log_hash)
|
42
40
|
end
|
43
41
|
|
44
|
-
it
|
42
|
+
it 'outputs the timestamp first' do
|
45
43
|
expect(log_file).to receive(:write) do |json_dump|
|
46
44
|
h = JSON.parse(json_dump)
|
47
45
|
expect(h.first[0].to_sym).to be :timestamp
|
@@ -50,69 +48,69 @@ describe Alephant::Logger::JSON do
|
|
50
48
|
subject.send(level, log_hash)
|
51
49
|
end
|
52
50
|
|
53
|
-
it
|
51
|
+
it 'displays a default session value if a custom function is not provided' do
|
54
52
|
expect(log_file).to receive(:write) do |json_dump|
|
55
53
|
h = JSON.parse(json_dump)
|
56
|
-
expect(h[
|
54
|
+
expect(h['uuid']).to eq 'n/a'
|
57
55
|
end
|
58
56
|
|
59
57
|
subject.send(level, log_hash)
|
60
58
|
end
|
61
59
|
|
62
|
-
it
|
60
|
+
it 'displays a custom session value when provided a user defined function' do
|
63
61
|
expect(log_file).to receive(:write) do |json_dump|
|
64
62
|
h = JSON.parse(json_dump)
|
65
|
-
expect(h[
|
63
|
+
expect(h['uuid']).to eq 'foo'
|
66
64
|
end
|
67
65
|
|
68
|
-
|
66
|
+
described_class.session = fn
|
69
67
|
subject.send(level, binding, log_hash)
|
70
|
-
|
68
|
+
described_class.session = -> { 'n/a' }
|
71
69
|
end
|
72
70
|
|
73
|
-
it
|
74
|
-
|
75
|
-
expect(
|
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'
|
76
74
|
|
77
|
-
|
78
|
-
expect(
|
75
|
+
described_class.send(:remove_instance_variable, :@session)
|
76
|
+
expect(described_class.session?).to eq nil
|
79
77
|
end
|
80
78
|
end
|
81
79
|
|
82
|
-
shared_context
|
80
|
+
shared_context 'nested log hash' do
|
83
81
|
let(:log_hash) do
|
84
|
-
{
|
82
|
+
{ 'nest' => nest }
|
85
83
|
end
|
86
84
|
|
87
|
-
let(:nest) { {
|
85
|
+
let(:nest) { { 'bird' => 'eggs' } }
|
88
86
|
end
|
89
87
|
|
90
|
-
shared_examples
|
91
|
-
include_context
|
88
|
+
shared_examples 'nests flattened to strings' do
|
89
|
+
include_context 'nested log hash'
|
92
90
|
|
93
91
|
specify do
|
94
92
|
expect(log_file).to receive(:write) do |json_dump|
|
95
|
-
expect(JSON.parse(json_dump)[
|
93
|
+
expect(JSON.parse(json_dump)['nest']).to eq nest.to_s
|
96
94
|
end
|
97
95
|
|
98
96
|
subject.send(level, log_hash)
|
99
97
|
end
|
100
98
|
end
|
101
99
|
|
102
|
-
shared_examples
|
103
|
-
include_context
|
100
|
+
shared_examples 'nesting allowed' do
|
101
|
+
include_context 'nested log hash'
|
104
102
|
|
105
103
|
specify do
|
106
104
|
expect(log_file).to receive(:write) do |json_dump|
|
107
|
-
expect(JSON.parse
|
105
|
+
expect(JSON.parse(json_dump)).to eq log_hash
|
108
106
|
end
|
109
107
|
|
110
108
|
subject.send(level, log_hash)
|
111
109
|
end
|
112
110
|
end
|
113
111
|
|
114
|
-
shared_examples
|
115
|
-
let(:log_message) {
|
112
|
+
shared_examples 'gracefully fail with string arg' do
|
113
|
+
let(:log_message) { 'Unable to connect to server' }
|
116
114
|
|
117
115
|
specify { expect(log_file).not_to receive(:write) }
|
118
116
|
specify do
|
@@ -124,18 +122,18 @@ describe Alephant::Logger::JSON do
|
|
124
122
|
describe "##{level}" do
|
125
123
|
let(:level) { level }
|
126
124
|
|
127
|
-
it_behaves_like
|
125
|
+
it_behaves_like 'JSON logging'
|
128
126
|
|
129
|
-
it_behaves_like
|
127
|
+
it_behaves_like 'nests flattened to strings'
|
130
128
|
|
131
|
-
it_behaves_like
|
129
|
+
it_behaves_like 'gracefully fail with string arg'
|
132
130
|
|
133
|
-
context
|
131
|
+
context 'with nesting allowed' do
|
134
132
|
subject do
|
135
|
-
described_class.new(log_path, :
|
133
|
+
described_class.new(log_path, nesting: true)
|
136
134
|
end
|
137
135
|
|
138
|
-
it_behaves_like
|
136
|
+
it_behaves_like 'nesting allowed'
|
139
137
|
end
|
140
138
|
end
|
141
139
|
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
require 'date'
|
2
|
+
require 'spec_helper'
|
3
|
+
require 'alephant/logger/json_to_io'
|
4
|
+
|
5
|
+
describe Alephant::Logger::JSONtoIO do
|
6
|
+
subject { described_class.new(logger_io) }
|
7
|
+
|
8
|
+
let(:fn) { -> { 'foo' } }
|
9
|
+
let(:logger_io) { spy }
|
10
|
+
|
11
|
+
shared_examples 'JSON logging' do
|
12
|
+
let(:log_hash) do
|
13
|
+
{ 'foo' => 'bar', 'baz' => 'quux' }
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'writes JSON dump of hash to log with corresponding level key' do
|
17
|
+
allow(Time).to receive(:now).and_return('foobar')
|
18
|
+
|
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
|
+
|
24
|
+
subject.public_send(level, log_hash)
|
25
|
+
end
|
26
|
+
|
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
|
32
|
+
|
33
|
+
subject.public_send(level, log_hash)
|
34
|
+
end
|
35
|
+
|
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
|
41
|
+
|
42
|
+
subject.public_send(level, log_hash)
|
43
|
+
end
|
44
|
+
|
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
|
50
|
+
|
51
|
+
subject.public_send(level, log_hash)
|
52
|
+
end
|
53
|
+
|
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
|
59
|
+
|
60
|
+
described_class.session = fn
|
61
|
+
subject.send(level, binding, log_hash)
|
62
|
+
described_class.session = -> { 'n/a' }
|
63
|
+
end
|
64
|
+
|
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'
|
68
|
+
|
69
|
+
described_class.send(:remove_instance_variable, :@session)
|
70
|
+
expect(described_class.session?).to eq nil
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
shared_context 'nested log hash' do
|
75
|
+
let(:log_hash) do
|
76
|
+
{ 'nest' => nest }
|
77
|
+
end
|
78
|
+
|
79
|
+
let(:nest) { { 'bird' => 'eggs' } }
|
80
|
+
end
|
81
|
+
|
82
|
+
shared_examples 'nests flattened to strings' do
|
83
|
+
include_context 'nested log hash'
|
84
|
+
|
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
|
89
|
+
|
90
|
+
subject.public_send(level, log_hash)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
shared_examples 'nesting allowed' do
|
95
|
+
include_context 'nested log hash'
|
96
|
+
|
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
|
101
|
+
|
102
|
+
subject.public_send(level, log_hash)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
shared_examples 'gracefully fail with string arg' do
|
107
|
+
let(:log_message) { 'Unable to connect to server' }
|
108
|
+
|
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
|
114
|
+
|
115
|
+
%w(debug info warn error).each do |level|
|
116
|
+
describe "##{level}" do
|
117
|
+
let(:level) { level }
|
118
|
+
|
119
|
+
it_behaves_like 'JSON logging'
|
120
|
+
|
121
|
+
it_behaves_like 'nests flattened to strings'
|
122
|
+
|
123
|
+
it_behaves_like 'gracefully fail with string arg'
|
124
|
+
|
125
|
+
context 'with nesting allowed' do
|
126
|
+
subject { described_class.new(logger_io, nesting: true) }
|
127
|
+
|
128
|
+
it_behaves_like 'nesting allowed'
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
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.4.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-
|
11
|
+
date: 2016-09-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -111,7 +111,9 @@ files:
|
|
111
111
|
- lib/alephant/logger/dynamic_binding.rb
|
112
112
|
- lib/alephant/logger/json.rb
|
113
113
|
- lib/alephant/logger/json/version.rb
|
114
|
+
- lib/alephant/logger/json_to_io.rb
|
114
115
|
- spec/alephant/logger/json_spec.rb
|
116
|
+
- spec/alephant/logger/json_to_io_spec.rb
|
115
117
|
- spec/spec_helper.rb
|
116
118
|
homepage: ''
|
117
119
|
licenses:
|
@@ -133,10 +135,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
133
135
|
version: '0'
|
134
136
|
requirements: []
|
135
137
|
rubyforge_project:
|
136
|
-
rubygems_version: 2.
|
138
|
+
rubygems_version: 2.5.1
|
137
139
|
signing_key:
|
138
140
|
specification_version: 4
|
139
141
|
summary: alephant-logger driver enabling structured logging in JSON
|
140
142
|
test_files:
|
141
143
|
- spec/alephant/logger/json_spec.rb
|
144
|
+
- spec/alephant/logger/json_to_io_spec.rb
|
142
145
|
- spec/spec_helper.rb
|