event_tracer 0.3.0 → 0.4.1

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