structured-event-logger 0.0.1 → 0.0.2

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.
@@ -1,13 +1,22 @@
1
1
  require 'logger'
2
2
  require 'active_support/json'
3
+ require 'active_support/log_subscriber'
3
4
 
4
5
  class StructuredEventLogger
5
-
6
- attr_reader :json_logger, :unstructured_logger
6
+ CLEAR = "\e[0m"
7
+ BOLD = "\e[1m"
7
8
 
8
- def initialize(json_logger, unstructured_logger = nil)
9
- @json_logger, @unstructured_logger = json_logger, unstructured_logger
9
+ # Colors
10
+ MAGENTA = "\e[35m"
11
+ CYAN = "\e[36m"
12
+ WHITE = "\e[37m"
13
+
14
+ attr_reader :json_io, :unstructured_logger, :colorize_logging
15
+
16
+ def initialize(json_io, unstructured_logger = nil)
17
+ @json_io, @unstructured_logger = json_io, unstructured_logger
10
18
  @thread_contexts = {}
19
+ @colorize_logging = ActiveSupport::LogSubscriber.colorize_logging
11
20
  end
12
21
 
13
22
  def log(msg = nil)
@@ -15,7 +24,7 @@ class StructuredEventLogger
15
24
  end
16
25
 
17
26
  def event(scope, event, content = {})
18
- log_event({:scope => scope, :event => event}.merge(flatten_hash(content)))
27
+ log_event scope, event, flatten_hash(content)
19
28
  end
20
29
 
21
30
  def context
@@ -24,8 +33,14 @@ class StructuredEventLogger
24
33
 
25
34
  private
26
35
 
27
- def format_hash(hash, separator = ', ')
28
- "[Event Logger] " + hash.map {|k, v| "#{k}=#{escape(v)}"}.join(separator)
36
+ def format_hash(scope, event, hash, separator = ', ')
37
+ @odd = !@odd
38
+ message = hash.map {|k, v| "#{k}=#{escape(v)}"}.join(separator)
39
+ if @colorize_logging
40
+ " #{@odd ? CYAN : MAGENTA}#{BOLD}[#{scope}] #{event}: #{WHITE}#{message}#{CLEAR}"
41
+ else
42
+ " [#{scope}] #{event}: #{message}"
43
+ end
29
44
  end
30
45
 
31
46
  def escape(value)
@@ -50,11 +65,12 @@ class StructuredEventLogger
50
65
  flat_hash
51
66
  end
52
67
 
53
- def log_event(hash)
54
- unstructured_logger.add(nil, format_hash(hash)) if unstructured_logger
68
+ def log_event(scope, event, hash)
69
+ unstructured_logger.add(nil, format_hash(scope, event, hash)) if unstructured_logger
70
+
55
71
  hash = hash.merge(context)
56
- hash[:timestamp] ||= Time.now.utc
57
- json_logger.add(nil, ActiveSupport::JSON.encode(hash))
72
+ hash.update(event: event, scope: scope, timestamp: Time.now.utc)
73
+ json_io.write("#{MultiJson.encode(hash)}\n")
58
74
  end
59
75
 
60
76
  def thread_key
@@ -1,3 +1,3 @@
1
1
  class StructuredEventLogger
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -19,10 +19,10 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ["lib"]
20
20
 
21
21
  spec.add_runtime_dependency "activesupport", "~> 3.2"
22
+ spec.add_runtime_dependency "multi_json"
22
23
 
23
24
  spec.add_development_dependency "bundler", "~> 1.3"
24
25
  spec.add_development_dependency "rake"
25
- spec.add_development_dependency "minitest"
26
- spec.add_development_dependency "mocha"
26
+ spec.add_development_dependency "minitest", "~> 5.0"
27
27
  spec.add_development_dependency "timecop"
28
28
  end
@@ -3,39 +3,40 @@ require 'stringio'
3
3
 
4
4
  class StructuredEventLoggerTest < Minitest::Test
5
5
  def setup
6
- @json_logger = Logger.new(StringIO.new)
7
- @buffered_logger = Logger.new(StringIO.new)
8
- @event_logger = StructuredEventLogger.new(@json_logger, @buffered_logger)
6
+ ActiveSupport::LogSubscriber.colorize_logging = false
7
+
8
+ @json_io = StringIO.new
9
+ @unstructured_logger = Logger.new(@nonstructured_io = StringIO.new)
10
+ @unstructured_logger.formatter = proc { |_, _, _, msg| "#{msg}\n" }
11
+ @event_logger = StructuredEventLogger.new(@json_io, @unstructured_logger)
12
+ @time = Time.parse('2012-01-01')
9
13
  end
10
14
 
11
15
  def test_should_log_msg_to_buffered_logger
12
- @buffered_logger.expects(:add).with(nil, "a message")
13
- @json_logger.expects(:add).never
14
-
15
16
  @event_logger.log "a message"
17
+ assert_equal "a message\n", @nonstructured_io.string
18
+ assert @json_io.string.empty?
16
19
  end
17
20
 
18
21
  def test_should_log_event_to_both_loggers
19
- Timecop.freeze(Time.now) do
20
- @buffered_logger.expects(:add).with(nil, "[Event Logger] scope=render, event=error, status=status, message=message")
21
- @json_logger.expects(:add).with(nil, "{\"scope\":\"render\",\"event\":\"error\",\"status\":\"status\",\"message\":\"message\",\"timestamp\":\"#{Time.now.utc.strftime('%FT%TZ')}\"}")
22
-
22
+ Timecop.travel(@time) do
23
23
  @event_logger.event "render", "error", {:status => "status", :message => "message"}
24
+ assert_equal "{\"status\":\"status\",\"message\":\"message\",\"event\":\"error\",\"scope\":\"render\",\"timestamp\":\"2012-01-01T05:00:00Z\"}\n", @json_io.string
25
+ assert_equal " [render] error: status=status, message=message\n", @nonstructured_io.string
24
26
  end
25
27
  end
26
28
 
27
29
  def test_should_log_flatten_hash
28
- Timecop.travel(Time.now) do
29
- @buffered_logger.expects(:add).with(nil, "[Event Logger] scope=render, event=error, status=status, message_first=first, message_second=second")
30
- @json_logger.expects(:add).with(nil, "{\"scope\":\"render\",\"event\":\"error\",\"status\":\"status\",\"message_first\":\"first\",\"message_second\":\"second\",\"timestamp\":\"#{Time.now.utc.strftime('%FT%TZ')}\"}")
30
+ Timecop.travel(@time) do
31
31
  @event_logger.event "render", "error", {:status => "status", :message => {:first => "first", :second => "second"}}
32
+
33
+ assert_equal "{\"status\":\"status\",\"message_first\":\"first\",\"message_second\":\"second\",\"event\":\"error\",\"scope\":\"render\",\"timestamp\":\"2012-01-01T05:00:00Z\"}\n", @json_io.string
34
+ assert_equal " [render] error: status=status, message_first=first, message_second=second\n", @nonstructured_io.string
32
35
  end
33
36
  end
34
37
 
35
38
  def test_should_log_to_current_context
36
- Timecop.freeze(Time.now) do
37
- @json_logger.expects(:add).with(nil, "{\"scope\":\"render\",\"event\":\"error\",\"request_id\":\"2\",\"timestamp\":\"#{Time.now.utc.strftime('%FT%TZ')}\"}")
38
-
39
+ Timecop.travel(@time) do
39
40
  Thread.new do
40
41
  @event_logger.context[:request_id] = '1'
41
42
 
@@ -45,22 +46,24 @@ class StructuredEventLoggerTest < Minitest::Test
45
46
  end.join
46
47
  end.join
47
48
  end
49
+
50
+ assert_equal "{\"request_id\":\"2\",\"event\":\"error\",\"scope\":\"render\",\"timestamp\":\"2012-01-01T05:00:00Z\"}\n", @json_io.string
48
51
  end
49
52
 
50
- def test_should_delete_context
51
-
52
- Timecop.freeze(Time.now) do
53
- order = sequence('log message order')
54
- @json_logger.expects(:add).with(nil, "{\"scope\":\"render\",\"event\":\"error\",\"request_id\":\"1\",\"timestamp\":\"#{Time.now.utc.strftime('%FT%TZ')}\"}").in_sequence(order)
55
- @json_logger.expects(:add).with(nil, "{\"scope\":\"render\",\"event\":\"error\",\"timestamp\":\"#{Time.now.utc.strftime('%FT%TZ')}\"}").in_sequence(order)
53
+ def test_should_clear_context
54
+ Timecop.travel(@time) do
56
55
 
57
56
  Thread.new do
58
57
  @event_logger.context[:request_id] = '1'
59
- @event_logger.event :render, :error
58
+ @event_logger.event :render, :in_thread
60
59
  @event_logger.context.clear
61
60
  end.join
62
61
 
63
- @event_logger.event :render, :error
62
+ @event_logger.event :render, :out_thread
63
+
64
+ log_lines = @json_io.string.lines.entries
65
+ assert_equal "{\"request_id\":\"1\",\"event\":\"in_thread\",\"scope\":\"render\",\"timestamp\":\"2012-01-01T05:00:00Z\"}\n", log_lines[0]
66
+ assert_equal "{\"event\":\"out_thread\",\"scope\":\"render\",\"timestamp\":\"2012-01-01T05:00:00Z\"}\n", log_lines[1]
64
67
  end
65
68
  end
66
69
  end
@@ -1,7 +1,6 @@
1
1
  require 'bundler/setup'
2
2
  require 'minitest/autorun'
3
3
  require 'minitest/pride'
4
- require 'mocha/setup'
5
4
  require 'timecop'
6
5
 
7
6
  $LOAD_PATH.unshift File.expand_path('../lib', File.dirname(__FILE__))
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: structured-event-logger
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -30,39 +30,39 @@ dependencies:
30
30
  - !ruby/object:Gem::Version
31
31
  version: '3.2'
32
32
  - !ruby/object:Gem::Dependency
33
- name: bundler
33
+ name: multi_json
34
34
  requirement: !ruby/object:Gem::Requirement
35
35
  none: false
36
36
  requirements:
37
- - - ~>
37
+ - - ! '>='
38
38
  - !ruby/object:Gem::Version
39
- version: '1.3'
40
- type: :development
39
+ version: '0'
40
+ type: :runtime
41
41
  prerelease: false
42
42
  version_requirements: !ruby/object:Gem::Requirement
43
43
  none: false
44
44
  requirements:
45
- - - ~>
45
+ - - ! '>='
46
46
  - !ruby/object:Gem::Version
47
- version: '1.3'
47
+ version: '0'
48
48
  - !ruby/object:Gem::Dependency
49
- name: rake
49
+ name: bundler
50
50
  requirement: !ruby/object:Gem::Requirement
51
51
  none: false
52
52
  requirements:
53
- - - ! '>='
53
+ - - ~>
54
54
  - !ruby/object:Gem::Version
55
- version: '0'
55
+ version: '1.3'
56
56
  type: :development
57
57
  prerelease: false
58
58
  version_requirements: !ruby/object:Gem::Requirement
59
59
  none: false
60
60
  requirements:
61
- - - ! '>='
61
+ - - ~>
62
62
  - !ruby/object:Gem::Version
63
- version: '0'
63
+ version: '1.3'
64
64
  - !ruby/object:Gem::Dependency
65
- name: minitest
65
+ name: rake
66
66
  requirement: !ruby/object:Gem::Requirement
67
67
  none: false
68
68
  requirements:
@@ -78,21 +78,21 @@ dependencies:
78
78
  - !ruby/object:Gem::Version
79
79
  version: '0'
80
80
  - !ruby/object:Gem::Dependency
81
- name: mocha
81
+ name: minitest
82
82
  requirement: !ruby/object:Gem::Requirement
83
83
  none: false
84
84
  requirements:
85
- - - ! '>='
85
+ - - ~>
86
86
  - !ruby/object:Gem::Version
87
- version: '0'
87
+ version: '5.0'
88
88
  type: :development
89
89
  prerelease: false
90
90
  version_requirements: !ruby/object:Gem::Requirement
91
91
  none: false
92
92
  requirements:
93
- - - ! '>='
93
+ - - ~>
94
94
  - !ruby/object:Gem::Version
95
- version: '0'
95
+ version: '5.0'
96
96
  - !ruby/object:Gem::Dependency
97
97
  name: timecop
98
98
  requirement: !ruby/object:Gem::Requirement