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 +4 -4
- data/lib/conrad/collector.rb +168 -0
- data/lib/conrad/processor_stack.rb +50 -0
- data/lib/conrad/recorder.rb +15 -21
- data/lib/conrad/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 178acce18a6f1cd584f74d8bcf4d7d181f6d240ec7e3df10c644aa894f4e9b7b
|
4
|
+
data.tar.gz: 88bd011a135527d7068b85ebf94c9b037205622d475798ae029cf9f2786d446e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
data/lib/conrad/recorder.rb
CHANGED
@@ -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
|
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
|
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.
|
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 =
|
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
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
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
|
86
|
-
[formatter, emitter
|
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
|
data/lib/conrad/version.rb
CHANGED
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.
|
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-
|
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
|