event_tracer 0.3.0 → 0.4.1

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: 16cc39532664c9eb8da45f8f90a3a76ca7f180cc81da848bdf980dba5f91bfc8
4
- data.tar.gz: 713186d9e6d2de28d334f2a29db2be8e8619f7176080b5d9390f5225568db0af
3
+ metadata.gz: 335d4c810b2a3e378d826518f8e8c6c2a8a1310f029401381ac2f407437554ca
4
+ data.tar.gz: 95971a338ad15cb6f5f74bb78f10f59dfba0918c7578a3e1057f8d11e5dc7bbb
5
5
  SHA512:
6
- metadata.gz: 9a5faa6c50a2662896675686b0da3b2706ca29ec102e244be0230547e551e2da014af73954a2396870ebf20b1e55b170451e8f4863257a713776aab6dbc301d6
7
- data.tar.gz: c2b1ce96276d8821b476a21c35528c3f1c738363d960e1c709e3a1f361228fe4d0ac09ab8f13f2c6a1daef834f35f0fe22fca8b2cc328eab54a114eaa062a039
6
+ metadata.gz: 473b42b4728e7233bdddaa4d85c681b33b41c71c26eb78ee9578c332f878e78234e6007b37c6c120e0adf13eaa8dd87e18f09d2069cfe7ca578e2ef8f1fc6079
7
+ data.tar.gz: 5a58acc598909419d8164a4cfb46c2c9695b12faee98bdf2ae937648dc964c4c70e04c78e8a2395ed133a245ca86d4ac7d471972a7e72510267d2bcb754b1e56
@@ -20,7 +20,8 @@ module EventTracer
20
20
 
21
21
  # EventTracer ensures action & message is always populated
22
22
  def send_message(log_method, action:, message:, **args)
23
- data = args.merge(action: action, message: message).except(:metrics)
23
+ args.delete(:metrics)
24
+ data = args.merge(action: action, message: message)
24
25
  logger.public_send(log_method, data)
25
26
  end
26
27
 
@@ -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,13 @@
1
+ require 'dry-configurable'
2
+
3
+ module EventTracer
4
+ class Config
5
+ extend Dry::Configurable
6
+
7
+ setting :app_name, default: 'app_name'
8
+
9
+ # TODO: switch to namespace in v1.0
10
+ setting :dynamo_db_table_name, default: 'logs'
11
+ setting :dynamo_db_client
12
+ end
13
+ end
@@ -0,0 +1,14 @@
1
+ module EventTracer
2
+ module DynamoDB
3
+ class Client
4
+ class << self
5
+ extend Gem::Deprecate
6
+
7
+ def call
8
+ Aws::DynamoDB::Client.new
9
+ end
10
+ deprecate :call, 'EventTracer::Config.config.dynamo_db_client', 2021, 12
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,15 @@
1
+ module EventTracer
2
+ module DynamoDB
3
+ class DefaultProcessor
4
+ def call(log_type, action:, message:, args:)
5
+ args.merge(
6
+ timestamp: Time.now.utc.iso8601(6),
7
+ action: action,
8
+ message: message,
9
+ log_type: log_type,
10
+ app: EventTracer::Config.config.app_name
11
+ )
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'time'
4
+ require_relative 'client'
5
+ require_relative 'worker'
6
+ require_relative 'default_processor'
7
+
8
+ module EventTracer
9
+ module DynamoDB
10
+ class Logger
11
+ def initialize(buffer: Buffer.new(buffer_size: 0), log_processor: DefaultProcessor.new)
12
+ @buffer = buffer
13
+ @log_processor = log_processor
14
+ end
15
+
16
+ EventTracer::LOG_TYPES.each do |log_type|
17
+ define_method log_type do |**args|
18
+ save_message log_type, **args
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ attr_reader :buffer, :log_processor
25
+
26
+ def save_message(log_type, action:, message:, **args)
27
+ payload = log_processor.call(log_type, action: action, message: message, args: args)
28
+
29
+ unless buffer.add(payload)
30
+ all_payloads = buffer.flush + [payload]
31
+ Worker.perform_async(all_payloads)
32
+ end
33
+
34
+ LogResult.new(true)
35
+ end
36
+
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '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
+ module DynamoDB
15
+ class Worker
16
+ include ::Sidekiq::Worker
17
+
18
+ sidekiq_options retry: 1, queue: 'low'
19
+
20
+ # See https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/DynamoDB/Client.html#batch_write_item-instance_method
21
+ MAX_DYNAMO_DB_ITEM_PER_REQUEST = 25
22
+
23
+ def initialize(client = nil)
24
+ @config = EventTracer::Config.config
25
+ @client = client || @config.dynamo_db_client || Client.call
26
+ end
27
+
28
+ def perform(items)
29
+ wrap(items).each_slice(MAX_DYNAMO_DB_ITEM_PER_REQUEST) do |batch|
30
+ data = batch.map do |item|
31
+ { put_request: { item: clean_empty_values(item) } }
32
+ end
33
+
34
+ client.batch_write_item(
35
+ request_items: { config.dynamo_db_table_name => data }
36
+ )
37
+
38
+ rescue Aws::DynamoDB::Errors::ServiceError => e
39
+ EventTracer.error(
40
+ loggers: %i(base),
41
+ action: 'DynamoDBWorker',
42
+ app: EventTracer::Config.config.app_name,
43
+ error: e.class.name,
44
+ message: e.message
45
+ )
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ attr_reader :client, :config
52
+
53
+ def wrap(items)
54
+ # NOTE: This allows us to handle either buffered or unbuffered payloads
55
+ if items.is_a?(Hash)
56
+ [items]
57
+ else
58
+ Array(items)
59
+ end
60
+ end
61
+
62
+ # dynamo can't serialise empty strings/ non-zero numerics
63
+ def clean_empty_values(data)
64
+ data.delete_if do |_key, value|
65
+ case value
66
+ when Hash
67
+ clean_empty_values(value)
68
+ false
69
+ when String then value.empty?
70
+ else false
71
+ end
72
+ end
73
+ end
74
+
75
+ end
76
+ end
77
+ end
@@ -1,3 +1,3 @@
1
1
  module EventTracer
2
- VERSION = '0.3.0'.freeze
2
+ VERSION = '0.4.1'.freeze
3
3
  end
data/lib/event_tracer.rb CHANGED
@@ -3,7 +3,7 @@ require 'event_tracer/log_result'
3
3
 
4
4
  module EventTracer
5
5
 
6
- LOG_TYPES ||= %i(info warn error)
6
+ LOG_TYPES = %i(info warn error)
7
7
 
8
8
  @loggers = {}
9
9
 
@@ -70,4 +70,4 @@ module EventTracer
70
70
  end
71
71
 
72
72
  project_root = File.dirname(File.absolute_path(__FILE__))
73
- Dir.glob("#{project_root}/event_tracer/*") {|file| require file}
73
+ Dir.glob("#{project_root}/event_tracer/*.rb") {|file| require file}
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.0
4
+ version: 0.4.1
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-11 00:00:00.000000000 Z
11
+ date: 2021-11-01 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/logger.rb
101
+ - lib/event_tracer/dynamo_db/worker.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