Commit f40c4c06 authored by Mayra Cabrera's avatar Mayra Cabrera

Merge branch 'sh-fix-sidekiq-timestamps' into 'master'

Make Sidekiq timestamps consistently ISO 8601

Closes #194116

See merge request gitlab-org/gitlab!22750
parents fe4f5d6e f89237f0
---
title: Make Sidekiq timestamps consistently ISO 8601
merge_request: 22750
author:
type: fixed
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
module Gitlab module Gitlab
module SidekiqLogging module SidekiqLogging
class JSONFormatter class JSONFormatter
TIMESTAMP_FIELDS = %w[created_at enqueued_at started_at retried_at failed_at completed_at].freeze
def call(severity, timestamp, progname, data) def call(severity, timestamp, progname, data)
output = { output = {
severity: severity, severity: severity,
...@@ -13,11 +15,27 @@ module Gitlab ...@@ -13,11 +15,27 @@ module Gitlab
when String when String
output[:message] = data output[:message] = data
when Hash when Hash
convert_to_iso8601!(data)
output.merge!(data) output.merge!(data)
end end
output.to_json + "\n" output.to_json + "\n"
end end
private
def convert_to_iso8601!(payload)
TIMESTAMP_FIELDS.each do |key|
value = payload[key]
payload[key] = format_time(value) if value.present?
end
end
def format_time(timestamp)
return timestamp unless timestamp.is_a?(Numeric)
Time.at(timestamp).utc.iso8601(3)
end
end end
end end
end end
...@@ -6,8 +6,6 @@ require 'active_record/log_subscriber' ...@@ -6,8 +6,6 @@ require 'active_record/log_subscriber'
module Gitlab module Gitlab
module SidekiqLogging module SidekiqLogging
class StructuredLogger class StructuredLogger
START_TIMESTAMP_FIELDS = %w[created_at enqueued_at].freeze
DONE_TIMESTAMP_FIELDS = %w[started_at retried_at failed_at completed_at].freeze
MAXIMUM_JOB_ARGUMENTS_LENGTH = 10.kilobytes MAXIMUM_JOB_ARGUMENTS_LENGTH = 10.kilobytes
def call(job, queue) def call(job, queue)
...@@ -65,8 +63,6 @@ module Gitlab ...@@ -65,8 +63,6 @@ module Gitlab
payload['job_status'] = 'done' payload['job_status'] = 'done'
end end
convert_to_iso8601(payload, DONE_TIMESTAMP_FIELDS)
payload['db_duration'] = ActiveRecord::LogSubscriber.runtime payload['db_duration'] = ActiveRecord::LogSubscriber.runtime
payload['db_duration_s'] = payload['db_duration'] / 1000 payload['db_duration_s'] = payload['db_duration'] / 1000
...@@ -79,7 +75,7 @@ module Gitlab ...@@ -79,7 +75,7 @@ module Gitlab
# ignore `cpu_s` if the platform does not support Process::CLOCK_THREAD_CPUTIME_ID (time[:cputime] == 0) # ignore `cpu_s` if the platform does not support Process::CLOCK_THREAD_CPUTIME_ID (time[:cputime] == 0)
# supported OS version can be found at: https://www.rubydoc.info/stdlib/core/2.1.6/Process:clock_gettime # supported OS version can be found at: https://www.rubydoc.info/stdlib/core/2.1.6/Process:clock_gettime
payload['cpu_s'] = time[:cputime].round(6) if time[:cputime] > 0 payload['cpu_s'] = time[:cputime].round(6) if time[:cputime] > 0
payload['completed_at'] = Time.now.utc payload['completed_at'] = Time.now.utc.to_f
end end
def parse_job(job) def parse_job(job)
...@@ -91,17 +87,9 @@ module Gitlab ...@@ -91,17 +87,9 @@ module Gitlab
job.delete('args') unless ENV['SIDEKIQ_LOG_ARGUMENTS'] job.delete('args') unless ENV['SIDEKIQ_LOG_ARGUMENTS']
job['args'] = limited_job_args(job['args']) if job['args'] job['args'] = limited_job_args(job['args']) if job['args']
convert_to_iso8601(job, START_TIMESTAMP_FIELDS)
job job
end end
def convert_to_iso8601(payload, keys)
keys.each do |key|
payload[key] = format_time(payload[key]) if payload[key]
end
end
def elapsed(t0) def elapsed(t0)
t1 = get_time t1 = get_time
{ {
...@@ -121,12 +109,6 @@ module Gitlab ...@@ -121,12 +109,6 @@ module Gitlab
Gitlab::Metrics::System.monotonic_time Gitlab::Metrics::System.monotonic_time
end end
def format_time(timestamp)
return timestamp if timestamp.is_a?(String)
Time.at(timestamp).utc.iso8601(3)
end
def limited_job_args(args) def limited_job_args(args)
return unless args.is_a?(Array) return unless args.is_a?(Array)
......
...@@ -3,28 +3,53 @@ ...@@ -3,28 +3,53 @@
require 'spec_helper' require 'spec_helper'
describe Gitlab::SidekiqLogging::JSONFormatter do describe Gitlab::SidekiqLogging::JSONFormatter do
let(:hash_input) { { foo: 1, bar: 'test' } }
let(:message) { 'This is a test' } let(:message) { 'This is a test' }
let(:timestamp) { Time.now } let(:now) { Time.now }
let(:timestamp) { now.utc.to_f }
it 'wraps a Hash' do let(:timestamp_iso8601) { now.iso8601(3) }
result = subject.call('INFO', timestamp, 'my program', hash_input)
describe 'with a Hash' do
data = JSON.parse(result) let(:hash_input) do
expected_output = hash_input.stringify_keys {
expected_output['severity'] = 'INFO' foo: 1,
expected_output['time'] = timestamp.utc.iso8601(3) 'bar' => 'test',
'created_at' => timestamp,
expect(data).to eq(expected_output) 'enqueued_at' => timestamp,
'started_at' => timestamp,
'retried_at' => timestamp,
'failed_at' => timestamp,
'completed_at' => timestamp_iso8601
}
end
it 'properly formats timestamps into ISO 8601 form' do
result = subject.call('INFO', now, 'my program', hash_input)
data = JSON.parse(result)
expected_output = hash_input.stringify_keys.merge!(
{
'severity' => 'INFO',
'time' => timestamp_iso8601,
'created_at' => timestamp_iso8601,
'enqueued_at' => timestamp_iso8601,
'started_at' => timestamp_iso8601,
'retried_at' => timestamp_iso8601,
'failed_at' => timestamp_iso8601,
'completed_at' => timestamp_iso8601
}
)
expect(data).to eq(expected_output)
end
end end
it 'wraps a String' do it 'wraps a String' do
result = subject.call('DEBUG', timestamp, 'my string', message) result = subject.call('DEBUG', now, 'my string', message)
data = JSON.parse(result) data = JSON.parse(result)
expected_output = { expected_output = {
severity: 'DEBUG', severity: 'DEBUG',
time: timestamp.utc.iso8601(3), time: timestamp_iso8601,
message: message message: message
} }
......
...@@ -30,8 +30,8 @@ describe Gitlab::SidekiqLogging::StructuredLogger do ...@@ -30,8 +30,8 @@ describe Gitlab::SidekiqLogging::StructuredLogger do
'message' => 'TestWorker JID-da883554ee4fe414012f5f42: start', 'message' => 'TestWorker JID-da883554ee4fe414012f5f42: start',
'job_status' => 'start', 'job_status' => 'start',
'pid' => Process.pid, 'pid' => Process.pid,
'created_at' => created_at.iso8601(3), 'created_at' => created_at.to_f,
'enqueued_at' => created_at.iso8601(3), 'enqueued_at' => created_at.to_f,
'scheduling_latency_s' => scheduling_latency_s 'scheduling_latency_s' => scheduling_latency_s
) )
end end
...@@ -40,7 +40,7 @@ describe Gitlab::SidekiqLogging::StructuredLogger do ...@@ -40,7 +40,7 @@ describe Gitlab::SidekiqLogging::StructuredLogger do
'message' => 'TestWorker JID-da883554ee4fe414012f5f42: done: 0.0 sec', 'message' => 'TestWorker JID-da883554ee4fe414012f5f42: done: 0.0 sec',
'job_status' => 'done', 'job_status' => 'done',
'duration' => 0.0, 'duration' => 0.0,
'completed_at' => timestamp.iso8601(3), 'completed_at' => timestamp.to_f,
'cpu_s' => 1.111112, 'cpu_s' => 1.111112,
'db_duration' => 0, 'db_duration' => 0,
'db_duration_s' => 0 'db_duration_s' => 0
...@@ -227,17 +227,17 @@ describe Gitlab::SidekiqLogging::StructuredLogger do ...@@ -227,17 +227,17 @@ describe Gitlab::SidekiqLogging::StructuredLogger do
describe '#add_time_keys!' do describe '#add_time_keys!' do
let(:time) { { duration: 0.1231234, cputime: 1.2342345 } } let(:time) { { duration: 0.1231234, cputime: 1.2342345 } }
let(:payload) { { 'class' => 'my-class', 'message' => 'my-message', 'job_status' => 'my-job-status' } } let(:payload) { { 'class' => 'my-class', 'message' => 'my-message', 'job_status' => 'my-job-status' } }
let(:current_utc_time) { '2019-09-23 10:00:58 UTC' } let(:current_utc_time) { Time.now.utc }
let(:payload_with_time_keys) { { 'class' => 'my-class', 'message' => 'my-message', 'job_status' => 'my-job-status', 'duration' => 0.123123, 'cpu_s' => 1.234235, 'completed_at' => current_utc_time } } let(:payload_with_time_keys) { { 'class' => 'my-class', 'message' => 'my-message', 'job_status' => 'my-job-status', 'duration' => 0.123123, 'cpu_s' => 1.234235, 'completed_at' => current_utc_time.to_f } }
subject { described_class.new } subject { described_class.new }
it 'update payload correctly' do it 'update payload correctly' do
expect(Time).to receive_message_chain(:now, :utc).and_return(current_utc_time) Timecop.freeze(current_utc_time) do
subject.send(:add_time_keys!, time, payload)
subject.send(:add_time_keys!, time, payload) expect(payload).to eq(payload_with_time_keys)
end
expect(payload).to eq(payload_with_time_keys)
end end
end end
end end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment