sbmt-outbox 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +440 -0
  3. data/Rakefile +3 -0
  4. data/app/interactors/sbmt/outbox/base_create_item.rb +55 -0
  5. data/app/interactors/sbmt/outbox/create_inbox_item.rb +10 -0
  6. data/app/interactors/sbmt/outbox/create_outbox_item.rb +10 -0
  7. data/app/interactors/sbmt/outbox/dry_interactor.rb +16 -0
  8. data/app/interactors/sbmt/outbox/partition_strategies/hash_partitioning.rb +20 -0
  9. data/app/interactors/sbmt/outbox/partition_strategies/number_partitioning.rb +26 -0
  10. data/app/interactors/sbmt/outbox/process_item.rb +269 -0
  11. data/app/interactors/sbmt/outbox/retry_strategies/compacted_log.rb +41 -0
  12. data/app/interactors/sbmt/outbox/retry_strategies/exponential_backoff.rb +34 -0
  13. data/app/jobs/sbmt/outbox/base_delete_stale_items_job.rb +78 -0
  14. data/app/jobs/sbmt/outbox/delete_stale_inbox_items_job.rb +15 -0
  15. data/app/jobs/sbmt/outbox/delete_stale_outbox_items_job.rb +15 -0
  16. data/app/models/sbmt/outbox/base_item.rb +165 -0
  17. data/app/models/sbmt/outbox/base_item_config.rb +106 -0
  18. data/app/models/sbmt/outbox/inbox_item.rb +38 -0
  19. data/app/models/sbmt/outbox/inbox_item_config.rb +13 -0
  20. data/app/models/sbmt/outbox/outbox_item.rb +52 -0
  21. data/app/models/sbmt/outbox/outbox_item_config.rb +13 -0
  22. data/config/initializers/schked.rb +9 -0
  23. data/config/initializers/yabeda.rb +71 -0
  24. data/config/schedule.rb +9 -0
  25. data/exe/outbox +16 -0
  26. data/lib/generators/helpers/config.rb +46 -0
  27. data/lib/generators/helpers/initializer.rb +41 -0
  28. data/lib/generators/helpers/items.rb +17 -0
  29. data/lib/generators/helpers/migration.rb +73 -0
  30. data/lib/generators/helpers/paas.rb +17 -0
  31. data/lib/generators/helpers/values.rb +49 -0
  32. data/lib/generators/helpers.rb +8 -0
  33. data/lib/generators/outbox/install/USAGE +10 -0
  34. data/lib/generators/outbox/install/install_generator.rb +33 -0
  35. data/lib/generators/outbox/install/templates/Outboxfile +3 -0
  36. data/lib/generators/outbox/install/templates/outbox.rb +32 -0
  37. data/lib/generators/outbox/install/templates/outbox.yml +51 -0
  38. data/lib/generators/outbox/item/USAGE +12 -0
  39. data/lib/generators/outbox/item/item_generator.rb +94 -0
  40. data/lib/generators/outbox/item/templates/inbox_item.rb.tt +7 -0
  41. data/lib/generators/outbox/item/templates/outbox_item.rb.tt +16 -0
  42. data/lib/generators/outbox/transport/USAGE +19 -0
  43. data/lib/generators/outbox/transport/templates/inbox_transport.yml.erb +9 -0
  44. data/lib/generators/outbox/transport/templates/outbox_transport.yml.erb +10 -0
  45. data/lib/generators/outbox/transport/transport_generator.rb +60 -0
  46. data/lib/generators/outbox.rb +23 -0
  47. data/lib/sbmt/outbox/ascii_art.rb +62 -0
  48. data/lib/sbmt/outbox/cli.rb +100 -0
  49. data/lib/sbmt/outbox/database_switcher.rb +15 -0
  50. data/lib/sbmt/outbox/engine.rb +45 -0
  51. data/lib/sbmt/outbox/error_tracker.rb +26 -0
  52. data/lib/sbmt/outbox/errors.rb +14 -0
  53. data/lib/sbmt/outbox/instrumentation/open_telemetry_loader.rb +34 -0
  54. data/lib/sbmt/outbox/logger.rb +35 -0
  55. data/lib/sbmt/outbox/middleware/builder.rb +23 -0
  56. data/lib/sbmt/outbox/middleware/open_telemetry/tracing_create_item_middleware.rb +42 -0
  57. data/lib/sbmt/outbox/middleware/open_telemetry/tracing_item_process_middleware.rb +49 -0
  58. data/lib/sbmt/outbox/middleware/runner.rb +29 -0
  59. data/lib/sbmt/outbox/middleware/sentry/tracing_batch_process_middleware.rb +48 -0
  60. data/lib/sbmt/outbox/middleware/sentry/tracing_item_process_middleware.rb +65 -0
  61. data/lib/sbmt/outbox/middleware/sentry/transaction.rb +28 -0
  62. data/lib/sbmt/outbox/probes/probe.rb +38 -0
  63. data/lib/sbmt/outbox/redis_client_factory.rb +36 -0
  64. data/lib/sbmt/outbox/tasks/delete_failed_items.rake +17 -0
  65. data/lib/sbmt/outbox/tasks/retry_failed_items.rake +20 -0
  66. data/lib/sbmt/outbox/thread_pool.rb +108 -0
  67. data/lib/sbmt/outbox/throttler.rb +52 -0
  68. data/lib/sbmt/outbox/version.rb +7 -0
  69. data/lib/sbmt/outbox/worker.rb +233 -0
  70. data/lib/sbmt/outbox.rb +136 -0
  71. data/lib/sbmt-outbox.rb +3 -0
  72. metadata +594 -0
@@ -0,0 +1,51 @@
1
+ default: &default
2
+ owner: owner-team
3
+ bucket_size: 16
4
+
5
+ probes:
6
+ port: SET-UP-YOUR-HEALTHCHECK-PORT-HERE
7
+
8
+ kafka:
9
+ required_acks: -1
10
+ max_retries: 1
11
+
12
+ # outbox_items:
13
+ # outbox_item:
14
+ # owner: outbox-item-owner-team
15
+ # partition_size: 2
16
+ # partition_strategy: number
17
+ # retention: P1W
18
+ # retry_strategies:
19
+ # - exponential_backoff
20
+ # - compacted_log
21
+ # transports:
22
+ # kafka_producer:
23
+ # topic: "outbox_item_topic"
24
+ # kafka:
25
+ # required_acks: -1
26
+
27
+ # inbox_items:
28
+ # inbox_item:
29
+ # owner: inbox-item-owner-team
30
+ # partition_size: 2
31
+ # partition_strategy: number
32
+ # retention: P1W
33
+ # retry_strategies:
34
+ # - exponential_backoff
35
+ # transports:
36
+ # import_order:
37
+ # source: "kafka_consumer"
38
+
39
+ development:
40
+ <<: *default
41
+
42
+ test:
43
+ <<: *default
44
+ bucket_size: 2
45
+
46
+ staging:
47
+ <<: *default
48
+
49
+ production:
50
+ <<: *default
51
+ bucket_size: 256
@@ -0,0 +1,12 @@
1
+ Description:
2
+ Generates Outbox item (model, migration, patches outbox.yml, etc)
3
+
4
+ Example:
5
+ bin/rails generate outbox:item Model::InboxItem --kind inbox
6
+
7
+ This will create:
8
+ app/models/model/inbox_item.rb
9
+ db/migrations/XXX_create_model_inbox_items.rb
10
+ This will modify:
11
+ configs/values.yml
12
+ config/outbox.yml
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "generators/outbox"
4
+
5
+ module Outbox
6
+ module Generators
7
+ class ItemGenerator < NamedBase
8
+ source_root File.expand_path("templates", __dir__)
9
+
10
+ class_option :kind, type: :string, desc: "Either inbox or outbox", banner: "inbox/outbox", required: true
11
+ class_option :skip_migration, type: :boolean, default: false, desc: "Skip creating migration"
12
+ class_option :skip_model, type: :boolean, default: false, desc: "Skip creating model class"
13
+ class_option :skip_config, type: :boolean, default: false, desc: "Skip modifying config/outbox.yml"
14
+ class_option :skip_values, type: :boolean, default: false, desc: "Skip modifying configs/values.yaml"
15
+
16
+ def check_kind!
17
+ return if options[:kind].in?(%w[inbox outbox])
18
+
19
+ raise Rails::Generators::Error, "Unknown item kind. " \
20
+ "Please specify `--kind inbox` or `--kind outbox`"
21
+ end
22
+
23
+ def check!
24
+ check_config!
25
+ end
26
+
27
+ def validate_item_name
28
+ return if file_name.underscore.match?(%r{#{options[:kind]}_item$})
29
+
30
+ continue = yes?(
31
+ "Warning: your item's name doesn't match the conventional" \
32
+ " name format (i.e. Some#{options[:kind].camelize}Item). Continue?"
33
+ )
34
+ return if continue
35
+
36
+ raise Rails::Generators::Error, "Aborting"
37
+ end
38
+
39
+ def create_migration
40
+ return if options[:skip_migration]
41
+
42
+ create_migration_file(migration_class_name, migration_table_name)
43
+ end
44
+
45
+ def create_model
46
+ return if options[:skip_model]
47
+
48
+ template "#{options[:kind]}_item.rb", File.join("app/models", "#{item_path}.rb")
49
+ end
50
+
51
+ def patch_config
52
+ return if options[:skip_config]
53
+
54
+ item_template_data = if options[:kind] == "inbox"
55
+ <<~RUBY
56
+ #{item_path}:
57
+ partition_size: 1
58
+ partition_strategy: hash
59
+ retention: P1W
60
+ max_retries: 7
61
+ retry_strategies:
62
+ - exponential_backoff
63
+ # # see README to learn more about transport configuration
64
+ # transports: {}
65
+ RUBY
66
+ else
67
+ <<~RUBY
68
+ #{item_path}:
69
+ partition_size: 1
70
+ partition_strategy: number
71
+ retention: P3D
72
+ max_retries: 7
73
+ retry_strategies:
74
+ - exponential_backoff
75
+ # # see README to learn more about transport configuration
76
+ # transports: {}
77
+ RUBY
78
+ end
79
+
80
+ add_item_to_config("#{options[:kind]}_items", item_template_data)
81
+ end
82
+
83
+ def patch_values
84
+ return if options[:skip_values]
85
+ return unless paas_app?
86
+
87
+ # e.g. order/inbox_item => inbox-order-inbox-items
88
+ deployment_name = "#{options[:kind]}-" + dasherize_item(item_path)
89
+
90
+ add_item_to_values(deployment_name.pluralize, item_path)
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ <% module_namespacing do -%>
4
+ class <%= namespaced_item_class_name %> < Sbmt::Outbox::InboxItem
5
+ self.table_name = "<%= migration_table_name %>"
6
+ end
7
+ <% end -%>
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ <% module_namespacing do -%>
4
+ class <%= namespaced_item_class_name %> < Sbmt::Outbox::OutboxItem
5
+ self.table_name = "<%= migration_table_name %>"
6
+
7
+ private
8
+
9
+ def extra_options
10
+ {
11
+ # Kafka partition key
12
+ key: event_key
13
+ }
14
+ end
15
+ end
16
+ <% end -%>
@@ -0,0 +1,19 @@
1
+ Description:
2
+ Configures a transport inside the config/outbox.yml file
3
+ Arguments:
4
+ - item name, either CamelCased or under_scored, required
5
+ - transport name, i.e. sbmt/kafka_producer or import_order, required
6
+ Options:
7
+ - --kind inbox/outbox - transport type, required
8
+ - --topic, i.e. my-topic-name, optional
9
+ - --event_name, i.e. order_created, affects only outbox transports, optional
10
+ - --source, i.e. kafka, affects only inbox transports, optional
11
+ - --target, i.e order, affects only inbox transports, optional
12
+
13
+ Example:
14
+ bin/rails g outbox:transport MyOutboxItem sbmt/kafka_producer --kind outbox
15
+
16
+ The following lines will be inserted to config/outbox.yml under the `outbox_items/my_outbox_item` section:
17
+ transports:
18
+ sbmt/kafka_producer:
19
+ topic: "need to add a topic"
@@ -0,0 +1,9 @@
1
+ transports:
2
+ <%= transport.underscore %>:
3
+ <%- if options[:source].present? -%>
4
+ source: "<%= options[:source] %>"
5
+ <%- elsif options[:target].present? -%>
6
+ target: "<%= options[:target] %>"
7
+ <%- else -%>
8
+ # configure source, target and other options here
9
+ <%- end -%>
@@ -0,0 +1,10 @@
1
+ <%- topic = options[:topic].presence || "need to add a topic" -%>
2
+ transports:
3
+ <%- if options[:event_name].present? -%>
4
+ - class: "<%= transport.underscore %>"
5
+ event_name: "<%= options[:event_name] %>"
6
+ topic: "<%= topic %>"
7
+ <%- else -%>
8
+ <%= transport.underscore %>:
9
+ topic: "<%= topic %>"
10
+ <%- end -%>
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "generators/outbox"
4
+
5
+ module Outbox
6
+ module Generators
7
+ class TransportGenerator < NamedBase
8
+ source_root File.expand_path("templates", __dir__)
9
+
10
+ argument :transport, required: true, type: :string, banner: "sbmt/kafka_producer"
11
+
12
+ class_option :kind, type: :string, desc: "Either inbox or outbox", banner: "inbox/outbox", required: true
13
+ class_option :topic, type: :string, banner: "my-topic-name"
14
+ class_option :source, type: :string, banner: "kafka"
15
+ class_option :target, type: :string, banner: "order"
16
+ class_option :event_name, type: :string, default: "", banner: "order_created"
17
+
18
+ def check_name!
19
+ return if options[:kind].in?(%w[inbox outbox])
20
+
21
+ raise Rails::Generators::Error, "Unknown transport type. Should be inbox or outbox"
22
+ end
23
+
24
+ def check!
25
+ check_config!
26
+ end
27
+
28
+ def check_item_exists!
29
+ return if item_exists?
30
+
31
+ raise Rails::Generators::Error, "Item `#{item_name}` does not exist in the `#{options[:kind]}_items` " \
32
+ "section of #{CONFIG_PATH}. You may insert this item by running " \
33
+ "`bin/rails g outbox:item #{item_name} --kind #{options[:kind]}`"
34
+ end
35
+
36
+ def insert_transport
37
+ data = optimize_indentation(template, 6)
38
+ insert_into_file CONFIG_PATH, data, after: "#{item_name.underscore}:\n", force: true
39
+ end
40
+
41
+ private
42
+
43
+ def item_name
44
+ name
45
+ end
46
+
47
+ def item_exists?
48
+ File.binread(CONFIG_PATH).match?(%r{#{options[:kind]}_items:.*#{item_name.underscore}:}m)
49
+ end
50
+
51
+ def template_path
52
+ File.join(TransportGenerator.source_root, "#{options[:kind]}_transport.yml.erb")
53
+ end
54
+
55
+ def template
56
+ ERB.new(File.read(template_path), trim_mode: "%-").result(binding)
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+ require_relative "helpers"
5
+
6
+ module Outbox
7
+ module Generators
8
+ class Base < Rails::Generators::Base
9
+ include Helpers::Config
10
+ include Helpers::Initializer
11
+ include Helpers::Paas
12
+ end
13
+
14
+ class NamedBase < Rails::Generators::NamedBase
15
+ include Helpers::Config
16
+ include Helpers::Initializer
17
+ include Helpers::Items
18
+ include Helpers::Migration
19
+ include Helpers::Paas
20
+ include Helpers::Values
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sbmt
4
+ module Outbox
5
+ module AsciiArt
6
+ module_function
7
+
8
+ def logo
9
+ <<~'TEXT'
10
+
11
+ ______________________
12
+ /\ _______ _______ \
13
+ / \ /\ \/\ \ \
14
+ / _ \ \ \ \ \ \
15
+ / /\ \ \ \ \ \ \
16
+ / / \ \ \______\ \______\ \
17
+ / / \ \ / _____/_/ _____/_ \
18
+ / / \_ \ /\ \/\ \ \
19
+ / _\ / _ \ \ \ \ \ \
20
+ / /\ \ / /\ \ \ \ \ \ \
21
+ / / \ \ / / \ \ \______\ \______\ \
22
+ / / \ \/_/ \ \ / / / / \
23
+ / / \_ / \_ \_____________________\
24
+ \ \ / _\ / / /
25
+ \ \ / /\ \ / / \______\ \______\ /
26
+ \ \ / / \ \ / / / / / / /
27
+ \ \/_/ \ \/_ / / / / / /
28
+ \ / \_ / / / / / /
29
+ \ \ / / \/______/\/______/ /
30
+ \ \ / / \______\ \______\ /
31
+ \ \ / / / / / / /
32
+ \ \/_ / / / / / /
33
+ \ / / / / / /
34
+ \ / \/______/\/______/ /
35
+ \/_____________________/
36
+
37
+ TEXT
38
+ end
39
+
40
+ def shutdown
41
+ <<~'TEXT'
42
+
43
+ _
44
+ | |
45
+ | |===( ) //////
46
+ |_| ||| | o o|
47
+ ||| ( c ) ____
48
+ ||| \= / || \_
49
+ |||||| || |
50
+ |||||| ...||__/|-"
51
+ |||||| __|________|__
52
+ ||| |______________|
53
+ ||| || || || ||
54
+ ||| || || || ||
55
+ -------------------|||-------------||-||------||-||-------
56
+ |__> || || || ||
57
+
58
+ TEXT
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "thor"
4
+ require "sbmt/outbox/ascii_art"
5
+
6
+ module Sbmt
7
+ module Outbox
8
+ class CLI < Thor
9
+ def self.exit_on_failure?
10
+ true
11
+ end
12
+
13
+ default_command :start
14
+
15
+ desc "start", "Start outbox worker"
16
+ option :box,
17
+ aliases: "-b",
18
+ repeatable: true,
19
+ desc: "Outbox/Inbox processors to start in format foo_name:1,2,n bar_name:1,2,n"
20
+ option :concurrency,
21
+ aliases: "-c",
22
+ type: :numeric,
23
+ default: 10,
24
+ desc: "Number of threads"
25
+ def start
26
+ load_environment
27
+
28
+ worker = Sbmt::Outbox::Worker.new(
29
+ boxes: format_boxes(options[:box]),
30
+ concurrency: options[:concurrency]
31
+ )
32
+
33
+ Sbmt::Outbox.current_worker = worker
34
+
35
+ watch_signals(worker)
36
+
37
+ $stdout.puts AsciiArt.logo
38
+ $stdout.puts "Outbox/Inbox worker has been started"
39
+ $stdout.puts "Version: #{VERSION}"
40
+ $stdout.puts "Starting probes..."
41
+ Sbmt::Outbox::Probes::Probe.run_probes
42
+
43
+ worker.start
44
+ end
45
+
46
+ private
47
+
48
+ def load_environment
49
+ load(lookup_outboxfile)
50
+
51
+ require "sbmt/outbox"
52
+ require "sbmt/outbox/worker"
53
+ end
54
+
55
+ def lookup_outboxfile
56
+ file_path = ENV["OUTBOXFILE"] || "#{Dir.pwd}/Outboxfile"
57
+
58
+ raise "Cannot locate Outboxfile at #{file_path}" unless File.exist?(file_path)
59
+
60
+ file_path
61
+ end
62
+
63
+ def format_boxes(val)
64
+ if val.nil?
65
+ fetch_all_boxes
66
+ else
67
+ extract_boxes(val)
68
+ end
69
+ end
70
+
71
+ def fetch_all_boxes
72
+ Outbox.outbox_item_classes + Outbox.inbox_item_classes
73
+ end
74
+
75
+ def extract_boxes(boxes)
76
+ boxes.map do |name, acc|
77
+ item_class = Sbmt::Outbox.item_classes_by_name[name]
78
+ raise "Cannot locate box #{name}" unless item_class
79
+ item_class
80
+ end
81
+ end
82
+
83
+ def watch_signals(worker)
84
+ # ctrl+c
85
+ Signal.trap("INT") do
86
+ $stdout.puts AsciiArt.shutdown
87
+ $stdout.puts "Going to shut down..."
88
+ worker.stop
89
+ end
90
+
91
+ # normal kill with number 15
92
+ Signal.trap("TERM") do
93
+ $stdout.puts AsciiArt.shutdown
94
+ $stdout.puts "Going to shut down..."
95
+ worker.stop
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sbmt
4
+ module Outbox
5
+ class DatabaseSwitcher
6
+ def self.use_slave
7
+ yield
8
+ end
9
+
10
+ def self.use_master
11
+ yield
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/engine"
4
+
5
+ module Sbmt
6
+ module Outbox
7
+ class Engine < Rails::Engine
8
+ isolate_namespace Sbmt::Outbox
9
+
10
+ config.outbox = ActiveSupport::OrderedOptions.new.tap do |c|
11
+ c.active_record_base_class = "ApplicationRecord"
12
+ c.active_job_base_class = "ApplicationJob"
13
+ c.error_tracker = "Sbmt::Outbox::ErrorTracker"
14
+ c.outbox_item_classes = []
15
+ c.inbox_item_classes = []
16
+ c.paths = []
17
+ c.redis = {url: ENV.fetch("REDIS_URL", "redis://127.0.0.1:6379")}
18
+ c.process_items = ActiveSupport::OrderedOptions.new.tap do |c|
19
+ c.general_timeout = 120
20
+ c.cutoff_timeout = 60
21
+ c.batch_size = 200
22
+ end
23
+ c.worker = ActiveSupport::OrderedOptions.new.tap do |c|
24
+ c.rate_limit = 20
25
+ c.rate_interval = 60
26
+ c.shuffle_jobs = true
27
+ end
28
+ c.database_switcher = "Sbmt::Outbox::DatabaseSwitcher"
29
+ c.batch_process_middlewares = []
30
+ c.item_process_middlewares = []
31
+ c.create_item_middlewares = []
32
+
33
+ if defined?(::Sentry)
34
+ c.batch_process_middlewares.push("Sbmt::Outbox::Middleware::Sentry::TracingBatchProcessMiddleware")
35
+ c.item_process_middlewares.push("Sbmt::Outbox::Middleware::Sentry::TracingItemProcessMiddleware")
36
+ end
37
+ end
38
+
39
+ rake_tasks do
40
+ load "sbmt/outbox/tasks/retry_failed_items.rake"
41
+ load "sbmt/outbox/tasks/delete_failed_items.rake"
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sbmt
4
+ module Outbox
5
+ class ErrorTracker
6
+ class << self
7
+ def error(message, params = {})
8
+ unless defined?(Sentry)
9
+ Outbox.logger.log_error(message, params)
10
+ return
11
+ end
12
+
13
+ Sentry.with_scope do |scope|
14
+ scope.set_contexts(contexts: params)
15
+
16
+ if message.is_a?(Exception)
17
+ Sentry.capture_exception(message, level: :error)
18
+ else
19
+ Sentry.capture_message(message, level: :error)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sbmt
4
+ module Outbox
5
+ class Error < StandardError
6
+ end
7
+
8
+ class ConfigError < Error
9
+ end
10
+
11
+ class DatabaseError < Error
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "opentelemetry"
4
+ require "opentelemetry-common"
5
+ require "opentelemetry-instrumentation-base"
6
+
7
+ require_relative "../middleware/open_telemetry/tracing_create_item_middleware"
8
+ require_relative "../middleware/open_telemetry/tracing_item_process_middleware"
9
+
10
+ module Sbmt
11
+ module Outbox
12
+ module Instrumentation
13
+ class OpenTelemetryLoader < ::OpenTelemetry::Instrumentation::Base
14
+ install do |_config|
15
+ require_dependencies
16
+
17
+ ::Sbmt::Outbox.config.create_item_middlewares.push("Sbmt::Outbox::Middleware::OpenTelemetry::TracingCreateItemMiddleware")
18
+ ::Sbmt::Outbox.config.item_process_middlewares.push("Sbmt::Outbox::Middleware::OpenTelemetry::TracingItemProcessMiddleware")
19
+ end
20
+
21
+ present do
22
+ true
23
+ end
24
+
25
+ private
26
+
27
+ def require_dependencies
28
+ require_relative "../middleware/open_telemetry/tracing_create_item_middleware"
29
+ require_relative "../middleware/open_telemetry/tracing_item_process_middleware"
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sbmt
4
+ module Outbox
5
+ class Logger
6
+ delegate :logger, to: :Rails
7
+
8
+ def log_info(message, **params)
9
+ with_tags(**params) do
10
+ logger.info(message)
11
+ end
12
+ end
13
+
14
+ def log_error(message, **params)
15
+ with_tags(**params) do
16
+ logger.error(message)
17
+ end
18
+ end
19
+
20
+ def log_success(message, **params)
21
+ log_info(message, status: "success", **params)
22
+ end
23
+
24
+ def log_failure(message, **params)
25
+ log_error(message, status: "failure", **params)
26
+ end
27
+
28
+ def with_tags(**params)
29
+ logger.tagged(**params) do
30
+ yield
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sbmt
4
+ module Outbox
5
+ module Middleware
6
+ class Builder
7
+ def initialize(middlewares)
8
+ middlewares.each { |middleware| stack << middleware }
9
+ end
10
+
11
+ def call(...)
12
+ Runner.new(stack.dup).call(...)
13
+ end
14
+
15
+ private
16
+
17
+ def stack
18
+ @stack ||= []
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end