Skip to content

Commit c7e5462

Browse files
authored
fix: incorrect created at timestamp in a request (#5)
1 parent 37cd565 commit c7e5462

12 files changed

Lines changed: 200 additions & 19 deletions

.rubocop.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@ Metrics/PerceivedComplexity:
5353
Metrics/AbcSize:
5454
Max: 26
5555

56+
RSpec/ExampleLength:
57+
Max: 10
58+
59+
RSpec/MultipleMemoizedHelpers:
60+
Max: 10
61+
5662
Metrics/MethodLength:
5763
Exclude:
5864
- "lib/logdash/metrics.rb"

CHANGELOG.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,14 @@
22

33
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
44

5+
## [1.0.2] - 2025-05-22
6+
7+
### Fixed
8+
- Ensure `createdAt` parameter is sent correctly in other environments
9+
510
## [1.0.1] - 2025-05-22
611

7-
### Added
12+
### Fixed
813
- Ensure iso8601 timestamp format is available in other environments
914

1015
## [1.0.0] - 2025-05-21

Gemfile.lock

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
PATH
22
remote: .
33
specs:
4-
logdash (1.0.0)
5-
json
4+
logdash (1.0.2)
5+
json (~> 2.0)
66

77
GEM
88
remote: https://rubygems.org/
@@ -15,6 +15,7 @@ GEM
1515
bigdecimal
1616
rexml
1717
diff-lcs (1.6.2)
18+
docile (1.4.1)
1819
hashdiff (1.2.0)
1920
json (2.12.0)
2021
language_server-protocol (3.17.0.5)
@@ -71,6 +72,12 @@ GEM
7172
rubocop-rspec_rails (2.29.1)
7273
rubocop (~> 1.61)
7374
ruby-progressbar (1.13.0)
75+
simplecov (0.22.0)
76+
docile (~> 1.1)
77+
simplecov-html (~> 0.11)
78+
simplecov_json_formatter (~> 0.1)
79+
simplecov-html (0.13.1)
80+
simplecov_json_formatter (0.1.4)
7481
timecop (0.9.10)
7582
unicode-display_width (3.1.4)
7683
unicode-emoji (~> 4.0, >= 4.0.4)
@@ -85,14 +92,15 @@ PLATFORMS
8592
ruby
8693

8794
DEPENDENCIES
88-
bundler
95+
bundler (~> 2.0)
8996
logdash!
9097
rake (~> 13.0)
9198
rspec (~> 3.0)
9299
rubocop (~> 1.62)
93100
rubocop-rspec (~> 2.25)
94-
timecop
95-
webmock
101+
simplecov (~> 0.22)
102+
timecop (~> 0.9)
103+
webmock (~> 3.18)
96104

97105
BUNDLED WITH
98-
2.6.6
106+
2.6.9

lib/logdash/logger.rb

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# frozen_string_literal: true
22

33
require 'json'
4+
require 'time'
45

56
module Logdash
67
module Types
@@ -59,19 +60,26 @@ def colorize(text, level)
5960
"\e[38;2;#{rgb[0]};#{rgb[1]};#{rgb[2]}m#{text}\e[0m"
6061
end
6162

63+
def iso8601_timestamp
64+
Time.now.utc.strftime('%Y-%m-%dT%H:%M:%S.%3NZ')
65+
end
66+
6267
def default_prefix_proc
63-
lambda do |level|
64-
timestamp = colorize("[#{Time.now.utc.strftime('%Y-%m-%dT%H:%M:%SZ')}]", Logdash::Types::LogLevel::SILLY)
68+
lambda do |level, timestamp|
69+
timestamp = colorize("[#{timestamp}]", Logdash::Types::LogLevel::SILLY)
6570
level_tag = colorize("[#{level.to_s.upcase}]", level)
6671
"#{timestamp} #{level_tag} "
6772
end
6873
end
6974

7075
def _log(level, message)
71-
prefix = @prefix_proc.call(level)
76+
timestamp = iso8601_timestamp
77+
prefix = @prefix_proc.call(level, timestamp)
78+
7279
puts "#{prefix}#{message}"
80+
7381
@on_log_proc&.call(level, message)
74-
@log_sync&.send(message, level, Time.now.utc)
82+
@log_sync&.send(message, level, timestamp)
7583
end
7684
end
7785
end

lib/logdash/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# frozen_string_literal: true
22

33
module Logdash
4-
VERSION = '1.0.1'
4+
VERSION = '1.0.2'
55
end

logdash-1.0.0.gem

-8.5 KB
Binary file not shown.

logdash-1.0.1.gem

-9 KB
Binary file not shown.

logdash-1.0.2.gem

9 KB
Binary file not shown.

logdash.gemspec

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,15 @@ Gem::Specification.new do |spec|
2525
spec.executables = []
2626
spec.require_paths = ['lib']
2727

28-
spec.add_dependency 'json'
28+
spec.add_dependency 'json', '~> 2.0'
2929

30-
spec.add_development_dependency 'bundler'
30+
spec.add_development_dependency 'bundler', '~> 2.0'
3131
spec.add_development_dependency 'rake', '~> 13.0'
3232
spec.add_development_dependency 'rspec', '~> 3.0'
3333
spec.add_development_dependency 'rubocop', '~> 1.62'
3434
spec.add_development_dependency 'rubocop-rspec', '~> 2.25'
35-
spec.add_development_dependency 'timecop'
36-
spec.add_development_dependency 'webmock'
35+
spec.add_development_dependency 'simplecov', '~> 0.22'
36+
spec.add_development_dependency 'timecop', '~> 0.9'
37+
spec.add_development_dependency 'webmock', '~> 3.18'
3738
spec.metadata['rubygems_mfa_required'] = 'true'
3839
end

spec/logdash/http_log_sync_spec.rb

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
# frozen_string_literal: true
2+
3+
require 'spec_helper'
4+
require 'logdash/http_log_sync'
5+
require 'webmock/rspec'
6+
7+
RSpec.describe Logdash::HttpLogSync do
8+
let(:api_key) { 'test_api_key' }
9+
let(:host) { 'https://api.logdash.com' }
10+
let(:verbose) { false }
11+
let(:log_sync) { described_class.new(api_key: api_key, host: host, verbose: verbose) }
12+
13+
describe '#send' do
14+
let(:message) { 'Test log message' }
15+
let(:level) { 'INFO' }
16+
let(:created_at) { Time.now.utc.iso8601 }
17+
let(:thread) { instance_spy(Thread) }
18+
19+
before do
20+
stub_request(:post, "#{host}/logs")
21+
allow(Thread).to receive(:new).and_yield
22+
end
23+
24+
it 'sends a log message in a new thread' do
25+
log_sync.send(message, level, created_at)
26+
sleep 0.01
27+
28+
expect(a_request(:post, "#{host}/logs")).to have_been_made.once
29+
end
30+
31+
context 'when api_key is nil' do
32+
let(:log_sync_no_key) { described_class.new(api_key: nil, host: host, verbose: verbose) }
33+
34+
it 'does not send a log message' do
35+
log_sync_no_key.send(message, level, created_at)
36+
37+
expect(a_request(:post, "#{host}/logs")).not_to have_been_made
38+
end
39+
40+
it 'does not create a new thread' do
41+
log_sync_no_key.send(message, level, created_at)
42+
43+
expect(Thread).not_to have_received(:new)
44+
end
45+
end
46+
47+
it 'sends the correct payload body' do
48+
expected_body = {
49+
message: message,
50+
level: level,
51+
createdAt: created_at,
52+
sequenceNumber: 0
53+
}.to_json
54+
55+
log_sync.send(message, level, created_at)
56+
sleep 0.01
57+
58+
expect(a_request(:post, "#{host}/logs")
59+
.with(body: expected_body)).to have_been_made.once
60+
end
61+
62+
it 'sends the correct headers' do
63+
log_sync.send(message, level, created_at)
64+
sleep 0.01
65+
66+
expect(a_request(:post, "#{host}/logs")
67+
.with(headers: {
68+
'Content-Type' => 'application/json',
69+
'Project-Api-Key' => api_key
70+
})).to have_been_made.once
71+
end
72+
73+
it 'increments the sequence number for each log' do
74+
log_sync.send(message, level, created_at)
75+
sleep 0.01
76+
log_sync.send(message, level, created_at)
77+
sleep 0.01
78+
79+
expect(a_request(:post, "#{host}/logs")
80+
.with(body: hash_including(sequenceNumber: 1))).to have_been_made.once
81+
end
82+
83+
it 'sends createdAt in iso8601 format' do
84+
formatted_time = Time.now.utc.iso8601
85+
log_sync.send(message, level, formatted_time)
86+
sleep 0.01
87+
88+
expect(a_request(:post, "#{host}/logs")
89+
.with(body: hash_including(createdAt: formatted_time))).to have_been_made.once
90+
end
91+
92+
context 'when verbose is true' do
93+
let(:verbose) { true }
94+
95+
it 'outputs status on successful send' do
96+
stub_request(:post, "#{host}/logs").to_return(status: 200)
97+
expect do
98+
log_sync.send(message, level, created_at)
99+
sleep 0.01
100+
end
101+
.to output(a_string_starting_with('[LogDash BG] Sent log (seq: 0), status: 200')).to_stdout_from_any_process
102+
end
103+
end
104+
105+
context 'when verbose is false' do
106+
let(:verbose) { false }
107+
108+
it 'does not output status on successful send' do
109+
stub_request(:post, "#{host}/logs").to_return(status: 200)
110+
expect do
111+
log_sync.send(message, level, created_at)
112+
sleep 0.01
113+
end.not_to output.to_stdout_from_any_process
114+
end
115+
end
116+
end
117+
118+
describe 'NoopLogSync' do
119+
let(:noop_log_sync) { Logdash::NoopLogSync.new }
120+
121+
it 'does not make any HTTP requests' do
122+
noop_log_sync.send('Test log message', 'INFO', Time.now.utc.iso8601)
123+
124+
expect(a_request(:any, /.*/)).not_to have_been_made
125+
end
126+
127+
it 'does not create a new thread' do
128+
thread = instance_spy(Thread)
129+
allow(Thread).to receive(:new).and_return(thread)
130+
131+
noop_log_sync.send('Test log message', 'INFO', Time.now.utc.iso8601)
132+
133+
expect(Thread).not_to have_received(:new)
134+
end
135+
136+
it 'does not output anything' do
137+
expect do
138+
noop_log_sync.send('Test log message', 'INFO', Time.now.utc.iso8601)
139+
end.not_to output.to_stdout_from_any_process
140+
end
141+
end
142+
end

0 commit comments

Comments
 (0)