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 +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
|