diff --git a/config/initializers/solid_process.rb b/config/initializers/solid_process.rb index 58789f9..7813a97 100644 --- a/config/initializers/solid_process.rb +++ b/config/initializers/solid_process.rb @@ -9,11 +9,17 @@ require "solid/process/event_logs/json_logger_listener" # require "solid/process/event_logs/basic_logger_listener" +require "solid/process/active_support_publisher" + +require "open_telemetry_tracer" +OpenTelemetryTracer.subscribe + # Solid::Process::EventLogs::BasicLoggerListener.logger = Rails.logger Solid::Result.configuration do |config| config.event_logs.listener = Solid::Result::EventLogs::Listeners[ Solid::Process::EventLogs::JsonLoggerListener, - Solid::Process::EventLogs::Record::Listener + Solid::Process::EventLogs::Record::Listener, + Solid::Process::ActiveSupportPublisher ] end diff --git a/lib/open_telemetry_tracer.rb b/lib/open_telemetry_tracer.rb new file mode 100644 index 0000000..6f21fa4 --- /dev/null +++ b/lib/open_telemetry_tracer.rb @@ -0,0 +1,53 @@ +module OpenTelemetryTracer + SolidResultTracer = ::OpenTelemetry.tracer_provider.tracer("solid-result") + + module ProcessListener + def self.start(name, _id, payload) + scope = payload[:scope] + + span = SolidResultTracer.start_span("#{scope[:name]}#call", attributes: { + "desc" => scope[:desc].inspect + }) + + token = OpenTelemetry::Context.attach(::OpenTelemetry::Trace.context_with_span(span)) + payload.merge!(__otel: {span:, token:}) + end + + def self.finish(_name, _id, payload) + otel = payload.delete(:__otel) + + otel => { span:, token: } + + span.finish + OpenTelemetry::Context.detach(token) + end + end + + module AndThenListener + def self.start(name, _id, payload) + payload => { scope:, and_then: } + + span = SolidResultTracer.start_span("#{scope[:name]}##{and_then[:method_name] || "block"}", attributes: { + "type" => and_then[:type].to_s, + "arg" => and_then[:arg].inspect + }) + + token = OpenTelemetry::Context.attach(::OpenTelemetry::Trace.context_with_span(span)) + payload.merge!(__otel: {span:, token:}) + end + + def self.finish(_name, _id, payload) + otel = payload.delete(:__otel) + + otel => { span:, token: } + + span.finish + OpenTelemetry::Context.detach(token) + end + end + + def self.subscribe + ActiveSupport::Notifications.monotonic_subscribe("start_process.solid_process", ProcessListener) + ActiveSupport::Notifications.monotonic_subscribe("and_then.solid_process", AndThenListener) + end +end diff --git a/lib/solid/process/active_support_publisher.rb b/lib/solid/process/active_support_publisher.rb new file mode 100644 index 0000000..a4967ab --- /dev/null +++ b/lib/solid/process/active_support_publisher.rb @@ -0,0 +1,19 @@ +class Solid::Process::ActiveSupportPublisher + include Solid::Result::EventLogs::Listener + + def self.around_event_logs? + true + end + + def self.around_and_then? + true + end + + def around_event_logs(scope:, &) + ActiveSupport::Notifications.instrument("start_process.solid_process", scope:, &) + end + + def around_and_then(scope:, and_then:, **, &) + ActiveSupport::Notifications.instrument("and_then.solid_process", scope:, and_then:, &) + end +end