Skip to content

Commit 29af974

Browse files
authored
Fix Array message handling in parser methods (silva96#62)
Some log formats may have the 'message' field as an Array instead of a String. The parser methods were calling String methods like match?() and include?() directly on the message field, which caused 'undefined method match? for an instance of Array' errors. Changes: - Add normalize_message() helper method to handle String, Array, nil, and other types - Update all message detection methods (sql_message?, cache_message?, call_stack_message?, job_enqueue_message?) to use normalize_message() - Update Entry class to normalize messages when setting content - Update CallLineEntry to use normalized messages - Update extract_job_id_from_enqueue() to handle Array messages - Add comprehensive test coverage for Array message handling Fixes issue where log_bench would crash when encountering log entries with Array messages, particularly affecting job enqueue detection.
1 parent 54846ee commit 29af974

5 files changed

Lines changed: 114 additions & 8 deletions

File tree

‎lib/log_bench/log/call_line_entry.rb‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ def self.build(raw_line)
2323

2424
def extract_from_json(data)
2525
super
26-
message = data["message"] || ""
26+
message = Parser.normalize_message(data["message"])
2727
return unless call_line_message?(data)
2828

2929
self.content = message.strip
@@ -40,7 +40,7 @@ def extract_call_info
4040
end
4141

4242
def call_line_message?(data)
43-
message = data["message"] || ""
43+
message = Parser.normalize_message(data["message"])
4444
message.include?("↳")
4545
end
4646
end

‎lib/log_bench/log/entry.rb‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ def initialize(json_data)
99
self.json_data = json_data
1010
self.timestamp = parse_timestamp(json_data["timestamp"])
1111
self.request_id = json_data["request_id"]
12-
self.content = json_data["message"] || ""
12+
self.content = Parser.normalize_message(json_data["message"])
1313
self.type = :other
1414
end
1515

‎lib/log_bench/log/parser.rb‎

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -83,28 +83,42 @@ def self.lograge_request?(data)
8383
data["method"] && data["path"] && data["status"]
8484
end
8585

86+
def self.normalize_message(message)
87+
case message
88+
when String
89+
message
90+
when Array
91+
message.join(" ")
92+
when NilClass
93+
""
94+
else
95+
message.to_s
96+
end
97+
end
98+
8699
def self.sql_message?(data)
87-
message = data["message"] || ""
100+
message = normalize_message(data["message"])
88101
%w[SELECT INSERT UPDATE DELETE TRANSACTION BEGIN COMMIT ROLLBACK SAVEPOINT].any? { |op| message.include?(op) }
89102
end
90103

91104
def self.cache_message?(data)
92-
message = data["message"] || ""
105+
message = normalize_message(data["message"])
93106
message.include?("CACHE")
94107
end
95108

96109
def self.call_stack_message?(data)
97-
message = data["message"] || ""
110+
message = normalize_message(data["message"])
98111
message.include?("↳")
99112
end
100113

101114
def self.job_enqueue_message?(data)
102-
message = data["message"] || ""
115+
message = normalize_message(data["message"])
103116
message.match?(/Enqueued .+ \(Job ID: .+\)/)
104117
end
105118

106119
def self.extract_job_id_from_enqueue(message)
107-
match = message.match(/Job ID: ([^\)]+)/)
120+
normalized_message = normalize_message(message)
121+
match = normalized_message.match(/Job ID: ([^\)]+)/)
108122
match[1] if match
109123
end
110124

‎test/test_job_enqueue_tracking.rb‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,42 @@ def test_job_enqueue_message_detection
8484
refute LogBench::Log::Parser.job_enqueue_message?(data3)
8585
end
8686

87+
def test_job_enqueue_message_detection_with_array_message
88+
# Should handle Array messages
89+
data1 = {"message" => ["Enqueued", "TestJob", "(Job ID: 123-456)", "to", "Async(default)"]}
90+
assert LogBench::Log::Parser.job_enqueue_message?(data1), "Should detect job enqueue when message is an Array"
91+
92+
# Should not detect regular messages when Array
93+
data2 = {"message" => ["Regular", "log", "message"]}
94+
refute LogBench::Log::Parser.job_enqueue_message?(data2), "Should not detect job enqueue for regular Array message"
95+
96+
# Should extract job ID from Array message
97+
job_id = LogBench::Log::Parser.extract_job_id_from_enqueue(data1["message"])
98+
assert_equal "123-456", job_id, "Should extract job ID from Array message"
99+
end
100+
101+
def test_parse_job_enqueue_with_array_message
102+
# Test parsing a job enqueue log entry where message is an Array
103+
request_log = '{"method":"GET","path":"/users","status":200,"duration":45.2,"controller":"UsersController","action":"index","request_id":"d72f06fa-71f1-4fb4-a27f-d9b36fe17593","timestamp":"2025-01-01T10:00:00Z"}'
104+
enqueue_log = '{"message":["Enqueued","TestJob","(Job ID: 8afaa702-7b0d-4d20-91ad-65bbf78ee0c8)","to","Async(default)","at","2025-10-17","14:02:51","UTC"],"level":"INFO","timestamp":"2025-10-17T14:02:49.976Z","time":1760709769.9763,"request_id":"d72f06fa-71f1-4fb4-a27f-d9b36fe17593","tags":["ActiveJob"]}'
105+
106+
collection = LogBench::Log::Collection.new([request_log, enqueue_log])
107+
requests = collection.requests
108+
109+
assert_equal 1, requests.size
110+
request = requests.first
111+
112+
# Should have one job enqueue entry in related logs
113+
job_enqueue_entries = request.related_logs.select { |log| log.is_a?(LogBench::Log::JobEnqueueEntry) }
114+
assert_equal 1, job_enqueue_entries.size
115+
116+
job_enqueue = job_enqueue_entries.first
117+
assert_equal "8afaa702-7b0d-4d20-91ad-65bbf78ee0c8", job_enqueue.job_id
118+
assert_equal "d72f06fa-71f1-4fb4-a27f-d9b36fe17593", job_enqueue.request_id
119+
# Content should be normalized (joined Array)
120+
assert_match(/Enqueued.*TestJob.*Job ID: 8afaa702-7b0d-4d20-91ad-65bbf78ee0c8/, job_enqueue.content)
121+
end
122+
87123
def test_full_workflow_example
88124
# This test demonstrates the complete workflow:
89125
# 1. HTTP request comes in

‎test/test_log_bench.rb‎

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,4 +497,60 @@ def test_parse_entries_with_null_or_missing_message
497497
assert_equal :other, request.related_logs[0].type
498498
assert_equal :other, request.related_logs[1].type
499499
end
500+
501+
def test_normalize_message_handles_string
502+
assert_equal "test message", LogBench::Log::Parser.normalize_message("test message")
503+
end
504+
505+
def test_normalize_message_handles_array
506+
assert_equal "test message", LogBench::Log::Parser.normalize_message(["test", "message"])
507+
end
508+
509+
def test_normalize_message_handles_nil
510+
assert_equal "", LogBench::Log::Parser.normalize_message(nil)
511+
end
512+
513+
def test_normalize_message_handles_other_types
514+
assert_equal "123", LogBench::Log::Parser.normalize_message(123)
515+
assert_equal "true", LogBench::Log::Parser.normalize_message(true)
516+
end
517+
518+
def test_sql_message_detection_with_array
519+
# Should detect SQL messages when message is an Array
520+
data1 = {"message" => ["SELECT", "*", "FROM", "users"]}
521+
assert LogBench::Log::Parser.sql_message?(data1), "Should detect SQL when message is an Array"
522+
523+
data2 = {"message" => ["INSERT", "INTO", "posts"]}
524+
assert LogBench::Log::Parser.sql_message?(data2), "Should detect INSERT when message is an Array"
525+
526+
data3 = {"message" => ["Regular", "log", "message"]}
527+
refute LogBench::Log::Parser.sql_message?(data3), "Should not detect SQL for regular Array message"
528+
end
529+
530+
def test_cache_message_detection_with_array
531+
# Should detect cache messages when message is an Array
532+
data1 = {"message" => ["CACHE", "hit", "for", "key"]}
533+
assert LogBench::Log::Parser.cache_message?(data1), "Should detect CACHE when message is an Array"
534+
535+
data2 = {"message" => ["Regular", "log", "message"]}
536+
refute LogBench::Log::Parser.cache_message?(data2), "Should not detect CACHE for regular Array message"
537+
end
538+
539+
def test_call_stack_message_detection_with_array
540+
# Should detect call stack messages when message is an Array
541+
data1 = {"message" => ["↳", "app/controllers/users_controller.rb:10"]}
542+
assert LogBench::Log::Parser.call_stack_message?(data1), "Should detect call stack when message is an Array"
543+
544+
data2 = {"message" => ["Regular", "log", "message"]}
545+
refute LogBench::Log::Parser.call_stack_message?(data2), "Should not detect call stack for regular Array message"
546+
end
547+
548+
def test_entry_content_with_array_message
549+
# Entry should normalize Array messages to strings
550+
json_data = {"message" => ["Test", "message", "with", "array"], "timestamp" => "2025-01-01T10:00:00Z"}
551+
entry = LogBench::Log::Entry.new(json_data)
552+
553+
assert_equal "Test message with array", entry.content
554+
assert_instance_of String, entry.content
555+
end
500556
end

0 commit comments

Comments
 (0)