structured-event-logger 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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