bensheldon / good_job

Multithreaded, Postgres-based, Active Job backend for Ruby on Rails.
https://goodjob-demo.herokuapp.com/
MIT License
2.69k stars 200 forks source link

[help wanted]: Preserve job logs #710

Open bensheldon opened 2 years ago

bensheldon commented 2 years ago

Preserving logs during job execution has been requested in the past.

I wanted to sketch out what this feature might look like, with the hope that someone else would implement it. I've broken it down by checkpoint because I don't want to review a huge PR all at once, but rather build up the functionality so it meets my standards for quality and maintainability. I'm happy to zoom pair on it too if you drive. I'm open to change too.

The big picture

Have an ActiveJob Extension that stores logs in the database for later review.

Checkpoint 1

Goal: A ruby harness that tees job logs to a buffer object.

Checkpoint 2

Goal: Drain logs to the database in a database-connection-efficient manner.

Checkpoint 3

Goal: surface logs in the dashboard

bensheldon commented 2 years ago

Here's a simple/naive test case for Checkpoint 1:

# spec/lib/good_job/active_job_extensions/logging_spec.rb

# frozen_string_literal: true
require 'rails_helper'

RSpec.describe GoodJob::ActiveJobExtensions::Logging do
  before do
    ActiveJob::Base.queue_adapter = GoodJob::Adapter.new(execution_mode: :external)

    stub_const 'TestJob', (Class.new(ActiveJob::Base) do
      include GoodJob::ActiveJobExtensions::Concurrency::Logging

      def perform
        logger.info "Hello, world!"
        logger.debug "Debug level!"
      end
    end)
  end

  # very naive test case, please modify based on implementation
  describe '.logs' do
    it 'stores the logs from the job in a tuple of execution ID and log' do
      active_job = TestJob.perform_later
      GoodJob.perform_inline

      job_log = described_class.logs

      # I expect this tuple would be replaced with a better object eventually, but that can be deferred to later checkpoints.
      expect(job_log).eq [[active_job.provider_job_id, 'Hello, world!', 'Debug level!']]
    end
  end
end
MarcusRiemer commented 2 months ago

Would you be open to a simpler system as "step 0" (or maybe a different issue) that captures a single result? From a UI perspective I would imagine something similar to the way errors are displayed right now, but obviously with Result instead of Error:

image