event_tracer 0.3.1 → 0.3.2

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: c5207c9165b5c41811f3128fdf9d22c1dd21f805131aab1d2dfa2861bc55a43b
4
- data.tar.gz: 3cda4e4b5dcb3c994797e9cae5ef3d31253e49d09a5d7527b49f107fc430e1df
3
+ metadata.gz: '002520584662d6b31ce2f05310a70c4fb8af37c3c865f6bdf2b8ebc44bf16654'
4
+ data.tar.gz: daa3fc169b3daa2771adb7d832872c055b723dbe25d28fff4ba24c618f2b54a2
5
5
  SHA512:
6
- metadata.gz: 6333e12e55d047a9d4b6f5a6cb8af0781218a5be884b06a1b96fa3e994a73901f72d14e47269929df96284c0a8bb5ffc8f7aa6677413d2952a87b1b54b9f81c4
7
- data.tar.gz: 8a27bbcf11291c1ca89c39944b784fb44661b00496f10412da4efbe933c7a014bac7a7fa186d55580e0fd2e766b1fcbe18565c13475091d1a01e6c1d0369c653
6
+ metadata.gz: '038e91c1d03b17070b97c3c170d73992d4050b9cee39f3529fc712a2cfda324d8afdfb10013f6a6838176966f94d6dd17b5f4851a62af6bdc0e6c8ea2b2e95b1'
7
+ data.tar.gz: fdeb28420163369840b82b0a56e6bd83a638fe4c5e7069f280c6807d8f9ad00b9044c642263fc89391628c110af8e527596c2e86c2b759a50691c3d13a7390c5
@@ -0,0 +1,65 @@
1
+ require 'concurrent'
2
+
3
+ module EventTracer
4
+ # This is an implementation of buffer storage. We use Concurrent::Array underneath
5
+ # to ensure thread-safe behavior. Data is stored until certain size / interval
6
+ # before flushing.
7
+ #
8
+ # Caveats: We should only store non-important data like logs in this buffer
9
+ # because if a process is killed, the data in this buffer is lost.
10
+ class Buffer
11
+ # Buffer can store maximum 10 items.
12
+ # Bigger size requires more memory to store, so choose a reasonable number
13
+ DEFAULT_BUFFER_SIZE = 10
14
+ # An item can live in buffer for at least 10s between each `Buffer#add` if the buffer is not full
15
+ # If there are larger interval between the calls, it can live longer.
16
+ DEFAULT_FLUSH_INTERVAL = 10
17
+
18
+ def initialize(
19
+ buffer_size: DEFAULT_BUFFER_SIZE,
20
+ flush_interval: DEFAULT_FLUSH_INTERVAL
21
+ )
22
+ @buffer_size = buffer_size
23
+ @flush_interval = flush_interval
24
+ @buffer = Concurrent::Array.new
25
+ end
26
+
27
+ # Add an item to buffer
28
+ #
29
+ # @param item: data to be added to buffer
30
+ # @return true if the item can be added, otherwise false
31
+ def add(item)
32
+ if add_item?
33
+ buffer.push({ item: item, created_at: Time.now })
34
+ true
35
+ else
36
+ false
37
+ end
38
+ end
39
+
40
+ # Remove all existing items from buffer
41
+ #
42
+ # @return all items in buffer
43
+ def flush
44
+ data = []
45
+
46
+ data << buffer.shift[:item] until buffer.empty?
47
+
48
+ data
49
+ end
50
+
51
+ # This method is only used to facilitate testing
52
+ def size
53
+ buffer.size
54
+ end
55
+
56
+ private
57
+
58
+ attr_reader :buffer_size, :flush_interval, :buffer
59
+
60
+ def add_item?
61
+ buffer.size < buffer_size &&
62
+ (buffer.empty? || buffer.first[:created_at] > Time.now - flush_interval)
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,10 @@
1
+ require 'dry-configurable'
2
+
3
+ module EventTracer
4
+ class Config
5
+ extend Dry::Configurable
6
+
7
+ setting :app_name, default: 'app_name'
8
+ setting :dynamo_db_table_name, default: 'logs'
9
+ end
10
+ end
@@ -0,0 +1,7 @@
1
+ module EventTracer
2
+ class DynamoDBClient
3
+ def self.call
4
+ Aws::DynamoDB::Client.new
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,13 @@
1
+ module EventTracer
2
+ class DynamoDBDefaultProcessor
3
+ def call(log_type, action:, message:, args:)
4
+ args.merge(
5
+ timestamp: Time.now.utc.iso8601(6),
6
+ action: action,
7
+ message: message,
8
+ log_type: log_type,
9
+ app: EventTracer::Config.config.app_name
10
+ )
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'dynamo_db_client'
4
+
5
+ begin
6
+ require 'sidekiq'
7
+ require 'aws-sdk-dynamodb'
8
+ rescue LoadError => e
9
+ puts "Please add the missing gem into your app Gemfile: #{e.message}"
10
+ raise
11
+ end
12
+
13
+ module EventTracer
14
+ class DynamoDBLogWorker
15
+ include ::Sidekiq::Worker
16
+
17
+ sidekiq_options retry: 1, queue: 'low'
18
+
19
+ # See https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/DynamoDB/Client.html#batch_write_item-instance_method
20
+ MAX_DYNAMO_DB_ITEM_PER_REQUEST = 25
21
+
22
+ def perform(items)
23
+ wrap(items).each_slice(MAX_DYNAMO_DB_ITEM_PER_REQUEST) do |batch|
24
+ data = batch.map do |item|
25
+ { put_request: { item: clean_empty_values(item) } }
26
+ end
27
+
28
+ EventTracer::DynamoDBClient.call.batch_write_item(
29
+ request_items: { EventTracer::Config.config.dynamo_db_table_name => data }
30
+ )
31
+
32
+ rescue Aws::DynamoDB::Errors::ServiceError => e
33
+ EventTracer.error(
34
+ loggers: %i(base),
35
+ action: 'DynamoDBLogWorker',
36
+ app: EventTracer::Config.config.app_name,
37
+ error: e.class.name,
38
+ message: e.message
39
+ )
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ def wrap(items)
46
+ # NOTE: This allows us to handle either buffered or unbuffered payloads
47
+ if items.is_a?(Hash)
48
+ [items]
49
+ else
50
+ Array(items)
51
+ end
52
+ end
53
+
54
+ # dynamo can't serialise empty strings/ non-zero numerics
55
+ def clean_empty_values(data)
56
+ data.delete_if do |_key, value|
57
+ case value
58
+ when Hash
59
+ clean_empty_values(value)
60
+ false
61
+ when String then value.empty?
62
+ else false
63
+ end
64
+ end
65
+ end
66
+
67
+ end
68
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'time'
4
+ require_relative 'dynamo_db_client'
5
+
6
+ module EventTracer
7
+ class DynamoDBLogger
8
+ def initialize(buffer: Buffer.new(buffer_size: 0), log_processor: EventTracer::DynamoDBDefaultProcessor.new)
9
+ @buffer = buffer
10
+ @log_processor = log_processor
11
+ end
12
+
13
+ EventTracer::LOG_TYPES.each do |log_type|
14
+ define_method log_type do |**args|
15
+ save_message log_type, **args
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ attr_reader :buffer, :log_processor
22
+
23
+ def save_message(log_type, action:, message:, **args)
24
+ payload = log_processor.call(log_type, action: action, message: message, args: args)
25
+
26
+ unless buffer.add(payload)
27
+ all_payloads = buffer.flush + [payload]
28
+ DynamoDBLogWorker.perform_async(all_payloads)
29
+ end
30
+
31
+ LogResult.new(true)
32
+ end
33
+
34
+ end
35
+ end
@@ -1,3 +1,3 @@
1
1
  module EventTracer
2
- VERSION = '0.3.1'.freeze
2
+ VERSION = '0.3.2'.freeze
3
3
  end
metadata CHANGED
@@ -1,15 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: event_tracer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - melvrickgoh
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-08-19 00:00:00.000000000 Z
11
+ date: 2021-10-11 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: concurrent-ruby
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: dry-configurable
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
13
41
  - !ruby/object:Gem::Dependency
14
42
  name: bundler
15
43
  requirement: !ruby/object:Gem::Requirement
@@ -53,7 +81,7 @@ dependencies:
53
81
  - !ruby/object:Gem::Version
54
82
  version: '3.0'
55
83
  description: 'Thin wrapper for formatted logging/ metric services to be used as a
56
- single service. External service(s) supported: Appsignal'
84
+ single service. External service(s) supported: Appsignal, Datadog, DynamoDB'
57
85
  email:
58
86
  - melvrickgoh@hotmail.com
59
87
  executables: []
@@ -64,7 +92,13 @@ files:
64
92
  - lib/event_tracer/appsignal_logger.rb
65
93
  - lib/event_tracer/base_logger.rb
66
94
  - lib/event_tracer/basic_decorator.rb
95
+ - lib/event_tracer/buffer.rb
96
+ - lib/event_tracer/config.rb
67
97
  - lib/event_tracer/datadog_logger.rb
98
+ - lib/event_tracer/dynamo_db_client.rb
99
+ - lib/event_tracer/dynamo_db_default_processor.rb
100
+ - lib/event_tracer/dynamo_db_log_worker.rb
101
+ - lib/event_tracer/dynamo_db_logger.rb
68
102
  - lib/event_tracer/log_result.rb
69
103
  - lib/event_tracer/version.rb
70
104
  homepage: https://github.com/melvrickgoh/event_tracer
@@ -87,7 +121,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
87
121
  - !ruby/object:Gem::Version
88
122
  version: '0'
89
123
  requirements: []
90
- rubygems_version: 3.2.3
124
+ rubygems_version: 3.2.28
91
125
  signing_key:
92
126
  specification_version: 4
93
127
  summary: Thin wrapper for formatted logging/ metric services to be used as a single