conrad 2.0.0 → 2.1.0

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