Skip to content

Commit bb23ce9

Browse files
mergify[bot]yaauie
andauthored
pipeline logging: add cause chain when logging (#16677) (#17935)
Often an exception is caught and a wrapper of that exception is re-thrown; logging only the outermost exception can make it difficult to chase down the root cause. This commit adds a helper to normalize an exception and its cause chain for logging, and uses that helper to add context whenever logging an exception from the pipeline implementation (cherry picked from commit 9b9790c) Co-authored-by: Rye Biesemeyer <[email protected]>
1 parent ea8c987 commit bb23ce9

File tree

1 file changed

+43
-19
lines changed

1 file changed

+43
-19
lines changed

logstash-core/lib/logstash/java_pipeline.rb

Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -137,9 +137,7 @@ def start
137137

138138
@thread = Thread.new do
139139
error_log_params = ->(e) {
140-
default_logging_keys(
141-
:exception => e,
142-
:backtrace => e.backtrace,
140+
exception_logging_keys(e,
143141
"pipeline.sources" => pipeline_source_details
144142
)
145143
}
@@ -312,7 +310,7 @@ def start_workers
312310
@crash_detected.make_true
313311
@logger.error(
314312
"Pipeline worker error, the pipeline will be stopped",
315-
default_logging_keys(:error => e.cause.message, :exception => e.cause.class, :backtrace => e.cause.backtrace)
313+
exception_logging_keys(e.cause)
316314
)
317315
end
318316
end
@@ -422,21 +420,14 @@ def inputworker(plugin)
422420
if plugin.stop?
423421
@logger.debug(
424422
"Input plugin raised exception during shutdown, ignoring it.",
425-
default_logging_keys(
426-
:plugin => plugin.class.config_name,
427-
:exception => e.message,
428-
:backtrace => e.backtrace))
423+
exception_logging_keys(e, :plugin => plugin.class.config_name))
429424
return
430425
end
431426

432427
# otherwise, report error and restart
433428
@logger.error(I18n.t(
434429
"logstash.pipeline.worker-error-debug",
435-
**default_logging_keys(
436-
:plugin => plugin.inspect,
437-
:error => e.message,
438-
:exception => e.class,
439-
:stacktrace => e.backtrace.join("\n"))))
430+
**exception_logging_keys(e, :plugin => plugin.inspect)))
440431

441432
# Assuming the failure that caused this exception is transient,
442433
# let's sleep for a bit and execute #run again
@@ -580,10 +571,7 @@ def close_plugin_and_ignore(plugin)
580571
rescue => e
581572
@logger.warn(
582573
"plugin raised exception while closing, ignoring",
583-
default_logging_keys(
584-
:plugin => plugin.class.config_name,
585-
:exception => e.message,
586-
:backtrace => e.backtrace))
574+
exception_logging_keys(e, :plugin => plugin.class.config_name))
587575
end
588576
end
589577

@@ -607,12 +595,12 @@ def init_worker_loop
607595
rescue => e
608596
@logger.error(
609597
"Worker loop initialization error",
610-
default_logging_keys(:error => e.message, :exception => e.class, :stacktrace => e.backtrace.join("\n")))
598+
exception_logging_keys(e))
611599
nil
612600
rescue Java::java.lang.StackOverflowError => se
613601
@logger.error(
614602
"Stack overflow error while compiling Pipeline. Please increase thread stack size using -Xss",
615-
default_logging_keys())
603+
exception_logging_keys(se))
616604
nil
617605
end
618606
end
@@ -630,6 +618,42 @@ def default_logging_keys(other_keys = {})
630618
keys
631619
end
632620

621+
def exception_logging_keys(active_exception = $!, other_keys = {})
622+
base = active_exception ? unwind_cause_chain(active_exception) : {}
623+
default_logging_keys(base.merge(other_keys))
624+
end
625+
626+
##
627+
# Yields once per exception in the provided exception's cause chain
628+
# @param exception [Exception]
629+
# @yield_param [Exception]
630+
def cause_chain(exception, &block)
631+
return enum_for(:cause_chain, exception) unless block_given?
632+
633+
current = exception
634+
while !current.nil?
635+
yield current
636+
cause = current.cause
637+
current = (cause == current ? nil : cause)
638+
end
639+
end
640+
641+
##
642+
# unwinds the provided exception to create a deeply-nested structure
643+
# representing the exception and its cause chain
644+
# @param [Exception] exception
645+
# @return [Hash{Symbol=>[String|Hash]}]
646+
def unwind_cause_chain(exception)
647+
cause_chain(exception).reverse_each.reduce(nil) do |cause, exception|
648+
{
649+
:exception => exception.class.name,
650+
:error => exception.message,
651+
:stacktrace => exception.backtrace.join("\n"),
652+
:cause => cause,
653+
}.compact
654+
end
655+
end
656+
633657
def preserve_event_order?(pipeline_workers)
634658
case settings.get("pipeline.ordered")
635659
when "auto"

0 commit comments

Comments
 (0)