conrad 2.0.0 → 2.1.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
  SHA256:
3
- metadata.gz: fd153974202827badd3ab0b1e74ac823ac9ad67d7ac17ae627afcd0c9c9c5f93
4
- data.tar.gz: b549b8ac875f6b0654f693c751946192158538ba3d56fc29b3e4bb896e665467
3
+ metadata.gz: 178acce18a6f1cd584f74d8bcf4d7d181f6d240ec7e3df10c644aa894f4e9b7b
4
+ data.tar.gz: 88bd011a135527d7068b85ebf94c9b037205622d475798ae029cf9f2786d446e
5
5
  SHA512:
6
- metadata.gz: e304189667f80f0e6e7e8a206881d9cfa17d4289d1737ce0c7f96b655a97da5c266f8e3435586bfe9e2b3d3e8b88a112485d27bc53bf3ee5c29a72525a69d896
7
- data.tar.gz: 3c95e0f400d386782d8dd00f1ff8203e67f46799b8fa93a538c4419ca5b58fe326d1fcbb6672939a6c12b1fc7e9358e42ddb737247dfa2cb60c725770fc4b9d8
6
+ metadata.gz: 60998b354a36ac3e14160805d012f323c2362e20ffd92eee9c3a65e605af6ecd394d40f8f649e39cdec222de701faea8b6dcc266aedf6c5b3bebe063c5866dd3
7
+ data.tar.gz: 48e969b3e326cf32dc6270412840025077443abbe8a71ad62cd6f88ca3eeff6519608bbfced6851df7f3a3296a3bca7e48808eaf894003d264e23a62c9f4c659
@@ -0,0 +1,168 @@
1
+ require 'conrad/formatters/json'
2
+ require 'conrad/emitters/stdout'
3
+ require 'conrad/processor_stack'
4
+ require 'conrad/errors'
5
+
6
+ module Conrad
7
+ # Used to collect a batch of events and send them using the configured
8
+ # emitter. This is especially useful for event recording done as part of a
9
+ # request/response cycle. Events can be collected here then sent at the end of
10
+ # the request cycle.
11
+ #
12
+ # Configuration is set via the class level and individual instances are
13
+ # spawned that use this configuration. The exception to that is the
14
+ # `event_metadata` which should be per collector using:
15
+ # `Conrad::Collector.current.event_metadata = { whatever: 'you want' }`
16
+ #
17
+ # A single instance can also be created. By default it will use any values
18
+ # configured for the class, but accepts override as keyword arguments.
19
+ class Collector
20
+ class << self
21
+ # The set of Processors to use for each event added to each collector
22
+ # created, by default. Must respond to #call
23
+ attr_writer :default_processors
24
+
25
+ # The Formatter to format each event added to each collector created, by
26
+ # default. Must respond to #call
27
+ attr_writer :default_formatter
28
+
29
+ # The Emitter to use for sending events to another place, added to each
30
+ # collector by default. This should respond to #call. If `emit_as_batch`
31
+ # is set to true, this method *must* accept an Array of events. Otherwise,
32
+ # it is expected to accept a Hash.
33
+ attr_writer :default_emitter
34
+
35
+ # Boolean indicating if the events collected should be emitted as a batch
36
+ # and sent as an Array to to the configured emitter or if they should
37
+ # instead be sent one-by-one. Defaults to false.
38
+ attr_accessor :default_emit_as_batch
39
+
40
+ # @return [Conrad::Collector] the collector for a given Thread that is
41
+ # currently active
42
+ def current
43
+ Thread.current[:conrad_collector] ||= new
44
+ end
45
+
46
+ # @return the configured formatter. Defaults to Conrad::Formatters::Json
47
+ def default_formatter
48
+ @default_formatter || Conrad::Formatters::JSON.new
49
+ end
50
+
51
+ # @return the configured emitter. Defaults to Conrad::Emitters::Stdout
52
+ def default_emitter
53
+ @default_emitter || Conrad::Emitters::Stdout.new
54
+ end
55
+
56
+ # @return [Array<#call>]
57
+ def default_processors
58
+ @default_processors || []
59
+ end
60
+ end
61
+
62
+ # Used to setup metadata that will be added to every event in the collection
63
+ attr_accessor :event_metadata
64
+
65
+ # The events stored in the collector
66
+ attr_reader :events
67
+
68
+ # ProcessorStack used on each event added
69
+ attr_reader :processors
70
+
71
+ # Formatter used to generate sendable format for an Event
72
+ attr_accessor :formatter
73
+
74
+ # Emitter used to send out events
75
+ attr_accessor :emitter
76
+
77
+ # Boolean indicating if events should be sent as a batch or individually by
78
+ # default for each Collector instance
79
+ attr_accessor :emit_as_batch
80
+ alias emit_as_batch? emit_as_batch
81
+
82
+ # @param processors [Array<#call>] set of processors to run. Defaults to
83
+ # processors as configured for the class.
84
+ # @param formatter [#call] Formatter to use. Defaults to
85
+ # formatter as configured for the class.
86
+ # @param emitter [#call] emitter to send events. Defaults to
87
+ # emitter as configured for the class.
88
+ # @param emit_as_batch [Boolean] indicates how to send events. Defaults to
89
+ # value configured for class.
90
+ def initialize(
91
+ processors: self.class.default_processors,
92
+ formatter: self.class.default_formatter,
93
+ emitter: self.class.default_emitter,
94
+ emit_as_batch: self.class.default_emit_as_batch
95
+ )
96
+ @events = []
97
+ @event_metadata = {}
98
+ @processors = processors
99
+ @processor_stack = Conrad::ProcessorStack.new(processors)
100
+ @formatter = formatter
101
+ @emitter = emitter
102
+ @emit_as_batch = emit_as_batch
103
+ end
104
+
105
+ # Adds an event to the Collector to be audited at a later time. The
106
+ # current collector's event_metadata is added to the event, it is processed,
107
+ # then it is formatter before being added to the set of events.
108
+ # If `:halt_conrad_processing` is thrown during the event processing, then
109
+ # the event will not be added to the collection.
110
+ #
111
+ # @param event [Hash]
112
+ #
113
+ # @raise [ForbiddenKey] when a key is neither a Symbol nor a String
114
+ def add_event(event)
115
+ processed_event = processor_stack.call(event.merge(event_metadata))
116
+
117
+ return unless processed_event
118
+
119
+ validate_event_keys(processed_event)
120
+
121
+ events << formatter.call(processed_event)
122
+ end
123
+
124
+ # Records the events currently in the collection then clears the state of
125
+ # the Collector by emptying the events stack and clearing out the metadata.
126
+ def record_events
127
+ if emit_as_batch?
128
+ record_events_as_batch
129
+ else
130
+ record_individual_events
131
+ end
132
+
133
+ reset_state
134
+ end
135
+
136
+ # Attribute writer for changing the processors for an instance of a
137
+ # Collector
138
+ def processors=(processors)
139
+ @processor_stack = Conrad::ProcessorStack.new(processors)
140
+ @processors = processors
141
+ end
142
+
143
+ private
144
+
145
+ attr_reader :processor_stack
146
+
147
+ def validate_event_keys(event)
148
+ event.each_key do |key|
149
+ raise ForbiddenKey, key unless key.is_a?(Symbol) || key.is_a?(String)
150
+ end
151
+ end
152
+
153
+ def record_events_as_batch
154
+ emitter.call(events)
155
+ end
156
+
157
+ def record_individual_events
158
+ events.each do |event|
159
+ emitter.call(event)
160
+ end
161
+ end
162
+
163
+ def reset_state
164
+ event_metadata.clear
165
+ events.clear
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,50 @@
1
+ require 'forwardable'
2
+
3
+ module Conrad
4
+ # @api private
5
+ # Contains the main logic to share how processors are handled between the
6
+ # individual, one-off recorder and the collector.
7
+ class ProcessorStack
8
+ include Enumerable
9
+ extend Forwardable
10
+
11
+ # The processors used by this Stack
12
+ attr_reader :processors
13
+
14
+ # Delegate #each and #to_a to the processors Array to allow iterating over
15
+ # the object as if it were a collection of processors.
16
+ def_delegators :processors, :each, :to_a
17
+
18
+ # @param processors [Array<#call>] set of objects all responding to #call
19
+ # that will be used to process an event
20
+ def initialize(processors)
21
+ check_callability(processors)
22
+
23
+ @processors = processors
24
+ end
25
+
26
+ # Processes an event through the defined set of operations, returning the
27
+ # final hash. It is possible to `throw :halt_conrad_processing` to stop the
28
+ # processing stack. There should be no additional arguments to the `throw`
29
+ # call. At this point, the processing will stop and return nil.
30
+ #
31
+ # @return [nil, Hash] nil in the case that halt_conrad_processing has been
32
+ # caught, otherwise the result of all the processors which should be a
33
+ # Hash.
34
+ def call(event)
35
+ catch :halt_conrad_processing do
36
+ processors.reduce(event) do |previous_built_event, processor|
37
+ processor.call(previous_built_event)
38
+ end
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ def check_callability(processors)
45
+ processors.each do |processor|
46
+ raise ArgumentError, "#{processor} does not respond to `#call`" unless processor.respond_to?(:call)
47
+ end
48
+ end
49
+ end
50
+ end
@@ -1,6 +1,7 @@
1
1
  require 'conrad/errors'
2
2
  require 'conrad/emitters/stdout'
3
3
  require 'conrad/formatters/json'
4
+ require 'conrad/processor_stack'
4
5
 
5
6
  module Conrad
6
7
  # Provides the ability to record an event took place.
@@ -11,14 +12,14 @@ module Conrad
11
12
  # @!attribute [r] formatter
12
13
  # Configured formatter for creating the final event. Defaults to
13
14
  # JSON.
14
- # @see Conrad::JSON
15
+ # @see Conrad::Formatters::JSON
15
16
  # @!attribute [r] emitter
16
17
  # Configured emitter for sending the final event. Defaults to
17
18
  # Stdout.
18
- # @see Conrad::Stdout
19
+ # @see Conrad::Emitters::Stdout
19
20
  # @!attribute [r] processors
20
21
  # Configured processors for processing the event pre-formatting and
21
- # emission. Defaults to an empty array.
22
+ # emission. Defaults to an empty enumerable.
22
23
  class Recorder
23
24
  attr_reader :formatter, :emitter, :processors
24
25
 
@@ -36,11 +37,12 @@ module Conrad
36
37
  emitter: Conrad::Emitters::Stdout.new,
37
38
  processors: []
38
39
  )
39
- check_callability(formatter: formatter, emitter: emitter, processors: processors)
40
+ check_callability(formatter: formatter, emitter: emitter)
40
41
 
41
42
  @formatter = formatter
42
43
  @emitter = emitter
43
44
  @processors = processors
45
+ @processor_stack = Conrad::ProcessorStack.new(processors)
44
46
  end
45
47
 
46
48
  # Processes the given event, formats it, then emits it. It is possible
@@ -51,13 +53,11 @@ module Conrad
51
53
  #
52
54
  # @param event [Hash] the set of key value pairs to be emitted
53
55
  # as a single audit event. It is expected that all keys will be given as
54
- # Symbols or Strings. All values should be of a type that matches the
55
- # SCALAR_TYPES or an array once the processor cycle is complete but before
56
- # final formatting.
56
+ # Symbols or Strings.
57
57
  #
58
58
  # @raise [ForbiddenKey] when a key is neither a Symbol nor a String
59
59
  def audit_event(event)
60
- processed_event = process_event(event)
60
+ processed_event = processor_stack.call(event)
61
61
 
62
62
  return unless processed_event
63
63
 
@@ -68,11 +68,11 @@ module Conrad
68
68
 
69
69
  private
70
70
 
71
- def process_event(event)
72
- catch :halt_conrad_processing do
73
- processors.reduce(event) do |previous_built_event, processor|
74
- processor.call(previous_built_event)
75
- end
71
+ attr_reader :processor_stack
72
+
73
+ def validate_event_keys(event)
74
+ event.each_key do |key|
75
+ raise ForbiddenKey, key unless key.is_a?(Symbol) || key.is_a?(String)
76
76
  end
77
77
  end
78
78
 
@@ -82,16 +82,10 @@ module Conrad
82
82
  )
83
83
  end
84
84
 
85
- def check_callability(formatter:, emitter:, processors:)
86
- [formatter, emitter, *processors].each do |callable|
85
+ def check_callability(formatter:, emitter:)
86
+ [formatter, emitter].each do |callable|
87
87
  raise ArgumentError, "#{callable} does not respond to `#call`" unless callable.respond_to?(:call)
88
88
  end
89
89
  end
90
-
91
- def validate_event_keys(event)
92
- event.each_key do |key|
93
- raise ForbiddenKey, key unless key.is_a?(Symbol) || key.is_a?(String)
94
- end
95
- end
96
90
  end
97
91
  end
@@ -1,5 +1,5 @@
1
1
  # :nodoc:
2
2
  module Conrad
3
3
  # :nodoc:
4
- VERSION = '2.0.0'.freeze
4
+ VERSION = '2.1.0'.freeze
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: conrad
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jonathon Anderson
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-01-04 00:00:00.000000000 Z
11
+ date: 2019-01-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-sdk
@@ -74,10 +74,12 @@ extensions: []
74
74
  extra_rdoc_files: []
75
75
  files:
76
76
  - lib/conrad.rb
77
+ - lib/conrad/collector.rb
77
78
  - lib/conrad/emitters/sqs.rb
78
79
  - lib/conrad/emitters/stdout.rb
79
80
  - lib/conrad/errors.rb
80
81
  - lib/conrad/formatters/json.rb
82
+ - lib/conrad/processor_stack.rb
81
83
  - lib/conrad/processors.rb
82
84
  - lib/conrad/processors/add_timestamp.rb
83
85
  - lib/conrad/processors/add_uuid.rb