217 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			Ruby
		
	
	
	
			
		
		
	
	
			217 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			Ruby
		
	
	
	
| require 'spec_helper'
 | |
| 
 | |
| describe Gitlab::SidekiqLogging::StructuredLogger do
 | |
|   describe '#call' do
 | |
|     let(:timestamp) { Time.iso8601('2018-01-01T12:00:00Z') }
 | |
|     let(:created_at) { timestamp - 1.second }
 | |
|     let(:scheduling_latency_s) { 1.0 }
 | |
| 
 | |
|     let(:job) do
 | |
|       {
 | |
|         "class" => "TestWorker",
 | |
|         "args" => [1234, 'hello'],
 | |
|         "retry" => false,
 | |
|         "queue" => "cronjob:test_queue",
 | |
|         "queue_namespace" => "cronjob",
 | |
|         "jid" => "da883554ee4fe414012f5f42",
 | |
|         "created_at" => created_at.to_f,
 | |
|         "enqueued_at" => created_at.to_f,
 | |
|         "correlation_id" => 'cid'
 | |
|       }
 | |
|     end
 | |
| 
 | |
|     let(:logger) { double }
 | |
|     let(:start_payload) do
 | |
|       job.merge(
 | |
|         'message' => 'TestWorker JID-da883554ee4fe414012f5f42: start',
 | |
|         'job_status' => 'start',
 | |
|         'pid' => Process.pid,
 | |
|         'created_at' => created_at.iso8601(3),
 | |
|         'enqueued_at' => created_at.iso8601(3),
 | |
|         'scheduling_latency_s' => scheduling_latency_s
 | |
|       )
 | |
|     end
 | |
|     let(:end_payload) do
 | |
|       start_payload.merge(
 | |
|         'message' => 'TestWorker JID-da883554ee4fe414012f5f42: done: 0.0 sec',
 | |
|         'job_status' => 'done',
 | |
|         'duration' => 0.0,
 | |
|         "completed_at" => timestamp.iso8601(3),
 | |
|         "system_s" => 0.0,
 | |
|         "user_s" => 0.0
 | |
|       )
 | |
|     end
 | |
|     let(:exception_payload) do
 | |
|       end_payload.merge(
 | |
|         'message' => 'TestWorker JID-da883554ee4fe414012f5f42: fail: 0.0 sec',
 | |
|         'job_status' => 'fail',
 | |
|         'error' => ArgumentError,
 | |
|         'error_message' => 'some exception'
 | |
|       )
 | |
|     end
 | |
| 
 | |
|     before do
 | |
|       allow(Sidekiq).to receive(:logger).and_return(logger)
 | |
| 
 | |
|       allow(subject).to receive(:current_time).and_return(timestamp.to_f)
 | |
| 
 | |
|       allow(Process).to receive(:times).and_return(
 | |
|         stime:  0.0,
 | |
|         utime:  0.0,
 | |
|         cutime: 0.0,
 | |
|         cstime: 0.0
 | |
|       )
 | |
|     end
 | |
| 
 | |
|     subject { described_class.new }
 | |
| 
 | |
|     context 'with SIDEKIQ_LOG_ARGUMENTS enabled' do
 | |
|       before do
 | |
|         stub_env('SIDEKIQ_LOG_ARGUMENTS', '1')
 | |
|       end
 | |
| 
 | |
|       it 'logs start and end of job' do
 | |
|         Timecop.freeze(timestamp) do
 | |
|           expect(logger).to receive(:info).with(start_payload).ordered
 | |
|           expect(logger).to receive(:info).with(end_payload).ordered
 | |
|           expect(subject).to receive(:log_job_start).and_call_original
 | |
|           expect(subject).to receive(:log_job_done).and_call_original
 | |
| 
 | |
|           subject.call(job, 'test_queue') { }
 | |
|         end
 | |
|       end
 | |
| 
 | |
|       it 'logs an exception in job' do
 | |
|         Timecop.freeze(timestamp) do
 | |
|           expect(logger).to receive(:info).with(start_payload)
 | |
|           # This excludes the exception_backtrace
 | |
|           expect(logger).to receive(:warn).with(hash_including(exception_payload))
 | |
|           expect(subject).to receive(:log_job_start).and_call_original
 | |
|           expect(subject).to receive(:log_job_done).and_call_original
 | |
| 
 | |
|           expect do
 | |
|             subject.call(job, 'test_queue') do
 | |
|               raise ArgumentError, 'some exception'
 | |
|             end
 | |
|           end.to raise_error(ArgumentError)
 | |
|         end
 | |
|       end
 | |
| 
 | |
|       context 'when the job args are bigger than the maximum allowed' do
 | |
|         it 'keeps args from the front until they exceed the limit' do
 | |
|           Timecop.freeze(timestamp) do
 | |
|             job['args'] = [
 | |
|               1,
 | |
|               2,
 | |
|               'a' * (described_class::MAXIMUM_JOB_ARGUMENTS_LENGTH / 2),
 | |
|               'b' * (described_class::MAXIMUM_JOB_ARGUMENTS_LENGTH / 2),
 | |
|               3
 | |
|             ]
 | |
| 
 | |
|             expected_args = job['args'].take(3) + ['...']
 | |
| 
 | |
|             expect(logger).to receive(:info).with(start_payload.merge('args' => expected_args)).ordered
 | |
|             expect(logger).to receive(:info).with(end_payload.merge('args' => expected_args)).ordered
 | |
|             expect(subject).to receive(:log_job_start).and_call_original
 | |
|             expect(subject).to receive(:log_job_done).and_call_original
 | |
| 
 | |
|             subject.call(job, 'test_queue') { }
 | |
|           end
 | |
|         end
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     context 'with SIDEKIQ_LOG_ARGUMENTS disabled' do
 | |
|       it 'logs start and end of job without args' do
 | |
|         Timecop.freeze(timestamp) do
 | |
|           expect(logger).to receive(:info).with(start_payload.except('args')).ordered
 | |
|           expect(logger).to receive(:info).with(end_payload.except('args')).ordered
 | |
|           expect(subject).to receive(:log_job_start).and_call_original
 | |
|           expect(subject).to receive(:log_job_done).and_call_original
 | |
| 
 | |
|           subject.call(job, 'test_queue') { }
 | |
|         end
 | |
|       end
 | |
| 
 | |
|       it 'logs without created_at and enqueued_at fields' do
 | |
|         Timecop.freeze(timestamp) do
 | |
|           excluded_fields = %w(created_at enqueued_at args scheduling_latency_s)
 | |
| 
 | |
|           expect(logger).to receive(:info).with(start_payload.except(*excluded_fields)).ordered
 | |
|           expect(logger).to receive(:info).with(end_payload.except(*excluded_fields)).ordered
 | |
|           expect(subject).to receive(:log_job_start).and_call_original
 | |
|           expect(subject).to receive(:log_job_done).and_call_original
 | |
| 
 | |
|           subject.call(job.except("created_at", "enqueued_at"), 'test_queue') { }
 | |
|         end
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     context 'with latency' do
 | |
|       let(:created_at) { Time.iso8601('2018-01-01T10:00:00Z') }
 | |
|       let(:scheduling_latency_s) { 7200.0 }
 | |
| 
 | |
|       it 'logs with scheduling latency' do
 | |
|         Timecop.freeze(timestamp) do
 | |
|           expect(logger).to receive(:info).with(start_payload.except('args')).ordered
 | |
|           expect(logger).to receive(:info).with(end_payload.except('args')).ordered
 | |
|           expect(subject).to receive(:log_job_start).and_call_original
 | |
|           expect(subject).to receive(:log_job_done).and_call_original
 | |
| 
 | |
|           subject.call(job, 'test_queue') { }
 | |
|         end
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     context 'with Gitaly and Rugged calls' do
 | |
|       let(:timing_data) do
 | |
|         {
 | |
|           gitaly_calls: 10,
 | |
|           gitaly_duration: 10000,
 | |
|           rugged_calls: 1,
 | |
|           rugged_duration_ms: 5000
 | |
|         }
 | |
|       end
 | |
| 
 | |
|       before do
 | |
|         job.merge!(timing_data)
 | |
|       end
 | |
| 
 | |
|       it 'logs with Gitaly and Rugged timing data' do
 | |
|         Timecop.freeze(timestamp) do
 | |
|           expect(logger).to receive(:info).with(start_payload.except('args')).ordered
 | |
|           expect(logger).to receive(:info).with(end_payload.except('args')).ordered
 | |
| 
 | |
|           subject.call(job, 'test_queue') { }
 | |
|         end
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     def ctime(times)
 | |
|       times[:cstime] + times[:cutime]
 | |
|     end
 | |
| 
 | |
|     context 'with ctime value greater than 0' do
 | |
|       let(:times_start) { { stime: 0.04999, utime: 0.0483, cstime: 0.0188, cutime: 0.0188 } }
 | |
|       let(:times_end)   { { stime: 0.0699, utime: 0.0699, cstime: 0.0399, cutime: 0.0399 } }
 | |
| 
 | |
|       before do
 | |
|         end_payload['system_s'] = 0.02
 | |
|         end_payload['user_s'] = 0.022
 | |
|         end_payload['child_s'] = 0.042
 | |
| 
 | |
|         allow(Process).to receive(:times).and_return(times_start, times_end)
 | |
|       end
 | |
| 
 | |
|       it 'logs with ctime data and other cpu data' do
 | |
|         Timecop.freeze(timestamp) do
 | |
|           expect(logger).to receive(:info).with(start_payload.except('args')).ordered
 | |
|           expect(logger).to receive(:info).with(end_payload.except('args')).ordered
 | |
| 
 | |
|           subject.call(job, 'test_queue') { }
 | |
|         end
 | |
|       end
 | |
|     end
 | |
|   end
 | |
| end
 |