structured-event-logger 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # StructuredEventLogger
2
2
 
3
- Structured event logger that writes events to both a human readable log and a JSON formatted log
3
+ Structured event logger that submits events to a list of listeners, including
4
+ human readable logs, json formatted event streams, and other endpoints.
4
5
 
5
6
  ## Installation
6
7
 
@@ -18,10 +19,13 @@ Or install it yourself as:
18
19
 
19
20
  ## Usage
20
21
 
21
- # Creating an instance
22
- json_logger = File.open(Rails.root.join("log", "event.log"), "a")
23
- human_readable_logger = Rails.logger
24
- event_logger = StructuredEventLogger.new(json_logger, human_readable_logger)
22
+ # Creating an instance of a StructuredEventLogger with several endpoints.
23
+ # The listeners provided to the constructors should respond to #log_event.
24
+ json_io = File.open(Rails.root.join("log", "event.log"), "a")
25
+ event_logger = StructuredEventLogger.new([
26
+ StructuredEventLogger::HumanReadableLogger.new(Rails.logger),
27
+ StructuredEventLogger::JsonWriter.new(json_io)
28
+ ])
25
29
 
26
30
  # Basic usage
27
31
  event_logger.event('scope', event, field: 'value', other_field: 'other value')
@@ -36,6 +40,18 @@ Or install it yourself as:
36
40
  # later, while processing a request inside that filter
37
41
  event_logger.event('scope', 'event', other_value: 'blah') # will also include { my_value: 'whatever' }
38
42
 
43
+ ## Fields
44
+
45
+ The default event fields that this library sets are prefixed with `event_`:
46
+
47
+ - `event_scope`: scope of the event, the first parameter to the `event` call.
48
+ - `event_name`: name of the event, the second parameter to the `event` call.
49
+ - `event_uuid`: A unique identifier for the event generated using `SecureRandom.uuid`.
50
+ - `event_timestamp`: The timestamp of the event, set to `Time.now.utc`.
51
+
52
+ All these fields can be overriden by passing new values to the context hash, i.e. the
53
+ third parameter to the `event` call.
54
+
39
55
  ## Contributing
40
56
 
41
57
  1. Fork it
@@ -1,28 +1,13 @@
1
- require 'logger'
2
1
  require 'securerandom'
3
- require 'active_support/json'
4
- require 'active_support/log_subscriber'
5
2
 
6
3
  class StructuredEventLogger
7
- CLEAR = "\e[0m"
8
- BOLD = "\e[1m"
4
+ attr_reader :endpoints, :default_context
9
5
 
10
- # Colors
11
- MAGENTA = "\e[35m"
12
- CYAN = "\e[36m"
13
- WHITE = "\e[37m"
6
+ def initialize(endpoints = [])
7
+ @endpoints = endpoints
14
8
 
15
- attr_reader :json_io, :unstructured_logger, :colorize_logging, :default_context
16
-
17
- def initialize(json_io, unstructured_logger = nil)
18
- @json_io, @unstructured_logger = json_io, unstructured_logger
19
9
  @thread_contexts = {}
20
10
  @default_context = {}
21
- @colorize_logging = ActiveSupport::LogSubscriber.colorize_logging
22
- end
23
-
24
- def log(msg = nil)
25
- unstructured_logger.add(nil, msg)
26
11
  end
27
12
 
28
13
  def event(scope, event, content = {})
@@ -35,25 +20,6 @@ class StructuredEventLogger
35
20
 
36
21
  private
37
22
 
38
- def format_hash(scope, event, hash, separator = ', ')
39
- @odd = !@odd
40
- message = hash.map {|k, v| "#{k}=#{escape(v)}"}.join(separator)
41
- if @colorize_logging
42
- " #{@odd ? CYAN : MAGENTA}#{BOLD}[#{scope}] #{event}: #{WHITE}#{message}#{CLEAR}"
43
- else
44
- " [#{scope}] #{event}: #{message}"
45
- end
46
- end
47
-
48
- def escape(value)
49
- output = value.to_s
50
- if output =~ /[\s"\\]/
51
- '"' + output.gsub('\\', '\\\\\\').gsub('"', '\\"') + '"'
52
- else
53
- output
54
- end
55
- end
56
-
57
23
  def flatten_hash(hash, keys = nil, separator = "_")
58
24
  flat_hash = {}
59
25
  hash.each_pair do |key, val|
@@ -68,14 +34,23 @@ class StructuredEventLogger
68
34
  end
69
35
 
70
36
  def log_event(scope, event, hash)
71
- unstructured_logger.add(nil, format_hash(scope, event, hash)) if unstructured_logger
72
-
73
- hash = hash.merge(@default_context.merge(context)).merge(event_name: event, event_scope: scope, event_uuid: SecureRandom.uuid)
74
- hash = { event_timestamp: Time.now.utc }.merge(hash)
75
- json_io.write("#{MultiJson.encode(hash)}\n")
37
+ record = @default_context.merge(context)
38
+ record.update(event_name: event, event_scope: scope, event_uuid: SecureRandom.uuid, event_timestamp: Time.now.utc)
39
+ record.update(hash)
40
+
41
+ endpoints.each do |endpoint|
42
+ begin
43
+ endpoint.call(scope, event, hash, record)
44
+ rescue => e
45
+ $stderr.write("Failed to submit event #{scope}/#{event} to #{endpoint.inspect}: #{e.message}.\n")
46
+ end
47
+ end
76
48
  end
77
49
 
78
50
  def thread_key
79
51
  Thread.current
80
52
  end
81
53
  end
54
+
55
+ require 'structured_event_logger/json_writer'
56
+ require 'structured_event_logger/human_readable_logger'
@@ -0,0 +1,40 @@
1
+ require 'active_support/log_subscriber'
2
+
3
+ class StructuredEventLogger::HumanReadableLogger
4
+
5
+ CLEAR = "\e[0m"
6
+ BOLD = "\e[1m"
7
+
8
+ # Colors
9
+ MAGENTA = "\e[35m"
10
+ CYAN = "\e[36m"
11
+ WHITE = "\e[37m"
12
+
13
+ attr_accessor :logger, :colorize, :log_level
14
+
15
+ def initialize(logger, colorize = ActiveSupport::LogSubscriber.colorize_logging, log_level = nil)
16
+ @logger, @colorize, @log_level = logger, colorize, log_level
17
+ end
18
+
19
+
20
+ def call(scope, event, hash, decorated_hash)
21
+ logger.add(log_level, format_hash(scope, event, hash))
22
+ end
23
+
24
+ private
25
+
26
+ def format_hash(scope, event, hash, separator = ', ')
27
+ @odd = !@odd
28
+ message = hash.map {|k, v| "#{k}=#{escape(v)}"}.join(separator)
29
+ if @colorize
30
+ " #{@odd ? CYAN : MAGENTA}#{BOLD}[#{scope}] #{event}: #{WHITE}#{message}#{CLEAR}"
31
+ else
32
+ " [#{scope}] #{event}: #{message}"
33
+ end
34
+ end
35
+
36
+ def escape(value)
37
+ output = value.to_s
38
+ output =~ /[\s"\\]/ ? output.inspect : output
39
+ end
40
+ end
@@ -0,0 +1,15 @@
1
+ require 'multi_json'
2
+ require 'active_support/json'
3
+
4
+ class StructuredEventLogger::JsonWriter
5
+
6
+ attr_reader :io
7
+
8
+ def initialize(io)
9
+ @io = io
10
+ end
11
+
12
+ def call(scope, event, hash, record)
13
+ io.write(MultiJson.encode(record) + "\n")
14
+ end
15
+ end
@@ -1,3 +1,3 @@
1
1
  class StructuredEventLogger
2
- VERSION = "0.0.4"
2
+ VERSION = "0.0.5"
3
3
  end
@@ -4,22 +4,19 @@ require 'stringio'
4
4
  class StructuredEventLoggerTest < Minitest::Test
5
5
  def setup
6
6
  ActiveSupport::LogSubscriber.colorize_logging = false
7
-
8
- @json_io = StringIO.new
7
+
9
8
  @unstructured_logger = Logger.new(@nonstructured_io = StringIO.new)
10
9
  @unstructured_logger.formatter = proc { |_, _, _, msg| "#{msg}\n" }
11
- @event_logger = StructuredEventLogger.new(@json_io, @unstructured_logger)
10
+
11
+ @event_logger = StructuredEventLogger.new([
12
+ StructuredEventLogger::HumanReadableLogger.new(@unstructured_logger),
13
+ StructuredEventLogger::JsonWriter.new(@json_io = StringIO.new)
14
+ ])
12
15
 
13
16
  Time.stubs(:now).returns(Time.parse('2012-01-01T05:00:00Z'))
14
17
  SecureRandom.stubs(:uuid).returns('aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee')
15
18
  end
16
19
 
17
- def test_should_log_msg_to_buffered_logger
18
- @event_logger.log "a message"
19
- assert_equal "a message\n", @nonstructured_io.string
20
- assert @json_io.string.empty?
21
- end
22
-
23
20
  def test_should_log_event_to_both_loggers
24
21
  @event_logger.event "render", "error", {:status => "status", :message => "message"}
25
22
 
@@ -37,14 +34,25 @@ class StructuredEventLoggerTest < Minitest::Test
37
34
  assert_last_event_contains_value '2012-01-01T05:00:00Z', :event_timestamp
38
35
  end
39
36
 
40
- def test_overwriting_default_properties
37
+ def test_overwriting_default_properties_using_hash
41
38
  @event_logger.event :original, :original, :event_scope => 'overwritten', :event_name => 'overwritten',
42
39
  :event_timestamp => Time.parse('1912-01-01T04:00:00Z'), :event_uuid => 'overwritten'
43
40
 
41
+ assert_last_event_contains_value 'overwritten', :event_scope
42
+ assert_last_event_contains_value 'overwritten', :event_name
43
+ assert_last_event_contains_value 'overwritten', :event_uuid
44
+ assert_last_event_contains_value '1912-01-01T04:00:00Z', :event_timestamp
45
+ end
46
+
47
+ def test_overwriting_default_properties_using_context
48
+ @event_logger.default_context[:event_name] = 'overwritten'
49
+ Thread.new do
50
+ @event_logger.context[:event_scope] = 'overwritten'
51
+ @event_logger.event :original, :original
52
+ end.join
53
+
44
54
  assert_last_event_contains_value 'original', :event_scope
45
55
  assert_last_event_contains_value 'original', :event_name
46
- assert_last_event_contains_value 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee', :event_uuid
47
- assert_last_event_contains_value '1912-01-01T04:00:00Z', :event_timestamp
48
56
  end
49
57
 
50
58
  def test_should_log_flatten_hash
data/test/test_helper.rb CHANGED
@@ -2,6 +2,7 @@ require 'bundler/setup'
2
2
  require 'minitest/autorun'
3
3
  require 'minitest/pride'
4
4
  require 'mocha/setup'
5
+ require 'logger'
5
6
 
6
7
  $LOAD_PATH.unshift File.expand_path('../lib', File.dirname(__FILE__))
7
8
  require 'structured_event_logger'
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.4
4
+ version: 0.0.5
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -12,7 +12,7 @@ authors:
12
12
  autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
- date: 2013-08-09 00:00:00.000000000 Z
15
+ date: 2013-08-13 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: activesupport
@@ -125,6 +125,8 @@ files:
125
125
  - Rakefile
126
126
  - lib/structured-event-logger.rb
127
127
  - lib/structured_event_logger.rb
128
+ - lib/structured_event_logger/human_readable_logger.rb
129
+ - lib/structured_event_logger/json_writer.rb
128
130
  - lib/structured_event_logger/version.rb
129
131
  - structured-event-logger.gemspec
130
132
  - test/structured_event_logger_test.rb