Files
moreminimore-chat/config/initializers/sidekiq.rb
Aakash Bakhle a4eae9710a test: Add focused Captain response lifecycle logs (#15364)
## What changed

This pull request adds three focused log emitters for Captain V2
response jobs:

* `job_dequeued` when Sidekiq fetches the job from Redis
* `job_skipped` when the conversation is not Pending at the first job
guard
* `response_discarded` when a newer customer message exists before model
generation

The dequeue middleware identifies V2 jobs by the triggering message ID
in the third serialized Active Job argument. It does not log Captain V1
or other Sidekiq jobs.

## Why

Recent production incidents have an enqueue record but no later job
record. Active Job `Performing` and `Performed` logs are now available
temporarily, but they do not show whether Sidekiq fetched a job before a
worker disappeared.

The dequeue log closes that gap. The two application logs explain the
early exits that otherwise produce no model trace.

The production root cause remains unresolved. This pull request adds
evidence for the next occurrence and does not change Captain response
behavior.

## Log volume

A Captain V2 response job adds one dequeue line. The other two lines
occur only on an early status skip or a pre-generation burst discard.
Existing Active Job, Langfuse, completion, failure, handoff, and usage
logs cover later stages.

## Validation

* Ruby syntax checks passed for the four implementation files.
* RuboCop found no offenses in the four implementation files.
* No new specs were added because this is temporary diagnostic logging
with no response behavior change.
2026-08-09 09:01:03 +05:30

53 lines
1.9 KiB
Ruby

require Rails.root.join('lib/redis/config')
require Rails.root.join('lib/captain_response_dequeued_logger')
schedule_file = 'config/schedule.yml'
Sidekiq.configure_client do |config|
config.redis = Redis::Config.app
end
# Logs whenever a job is pulled off Redis for execution.
class ChatwootDequeuedLogger
def call(_worker, job, queue)
payload = job['args'].first
Sidekiq.logger.info("Dequeued #{job['wrapped']} #{payload['job_id']} from #{queue}")
yield
end
end
Sidekiq.configure_server do |config|
config.redis = Redis::Config.app
config.server_middleware do |chain|
chain.add CaptainResponseDequeuedLogger
chain.add ChatwootDequeuedLogger if ActiveModel::Type::Boolean.new.cast(ENV.fetch('ENABLE_SIDEKIQ_DEQUEUE_LOGGER', false))
end
# skip the default start stop logging
if Rails.env.production?
config.logger.formatter = Sidekiq::Logger::Formatters::JSON.new
config[:skip_default_job_logging] = true
config.logger.level = Logger.const_get(ENV.fetch('LOG_LEVEL', 'info').upcase.to_s)
end
end
# https://github.com/ondrejbartas/sidekiq-cron
Rails.application.reloader.to_prepare do
# load_from_hash! upserts jobs from the YAML and removes any Redis-persisted
# jobs that share the same source tag but are no longer in the file.
# This ensures deleted schedule entries are cleaned up on deploy.
if File.exist?(schedule_file) && Sidekiq.server?
schedule = YAML.load_file(schedule_file)
# Cron entries removed from schedule.yml but possibly still in Redis
# with source:'dynamic' (predating the source tag). load_from_hash!
# only cleans up source:'schedule' entries, so these need explicit removal.
# Remove names from this list once they've been through a deploy cycle.
%w[bulk_auto_assignment_job].each { |name| Sidekiq::Cron::Job.destroy(name) }
Sidekiq::Cron::Job.load_from_hash!(schedule, source: 'schedule')
end
end