diff --git a/CHANGELOG.md b/CHANGELOG.md index 58e0d43..f3fae47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ ## Unreleased +- Fix: re-raise unhandled exceptions from `write(chunk)` (e.g. `Net::ReadTimeout`, `EOFError`) and from exhausted `send_retries` so Fluentd's own buffer retry can engage instead of silently dropping the chunk. - Switch to GitHub Actions Trusted Publishing for gem releases (replaces manual API key publishing) [#89](https://github.com/DataDog/fluent-plugin-datadog/pull/89) ## 0.15.0 diff --git a/lib/fluent/plugin/out_datadog.rb b/lib/fluent/plugin/out_datadog.rb index 8df25e4..468373e 100644 --- a/lib/fluent/plugin/out_datadog.rb +++ b/lib/fluent/plugin/out_datadog.rb @@ -177,6 +177,7 @@ def write(chunk) end rescue Exception => e log.error("Uncaught processing exception in datadog forwarder #{e.message}") + raise end end @@ -328,6 +329,7 @@ def send_retries(payload, max_retries, max_backoff) retries += 1 retry end + raise end end diff --git a/test/plugin/test_out_datadog.rb b/test/plugin/test_out_datadog.rb index 9996574..0ee4011 100644 --- a/test/plugin/test_out_datadog.rb +++ b/test/plugin/test_out_datadog.rb @@ -465,6 +465,30 @@ def create_valid_subject end end + sub_test_case "write(chunk) exception propagation" do + test "re-raises after logging so Fluentd's own buffer retry can engage" do + plugin = create_valid_subject + plugin.instance_variable_set(:@client, Object.new.tap { |c| c.define_singleton_method(:send_retries) { |*| raise Net::ReadTimeout } }) + chunk = Object.new + chunk.define_singleton_method(:msgpack_each) { |&block| block.call(["dd1"]) } + assert_raise(Net::ReadTimeout) do + plugin.write(chunk) + end + end + end + + sub_test_case "send_retries exhaustion" do + test "raises once max_retries is exhausted instead of silently returning" do + api_key = 'XXX' + stub_dd_request_with_return_code(api_key, 500, true) + payload = '{}' + client = Fluent::DatadogOutput::DatadogHTTPClient.new Logger.new(STDOUT), false, false, "datadog.com", 443, 80, nil, {}, false, api_key + assert_raise(Fluent::DatadogOutput::RetryableError) do + client.send_retries(payload, 0, 1) + end + end + end + def stub_dd_request_with_return_code(api_key, return_code, v2_routes = false) if v2_routes stub_dd_request_v2_routes(api_key).