Repository: puma/puma (Ruby)

Issue:
Puma::ErrorLogger: missing "?" when logging requests with a query string
```diff
diff --git a/lib/puma/error_logger.rb b/lib/puma/error_logger.rb
index 792e14a6..a12fba67 100644
--- a/lib/puma/error_logger.rb
+++ b/lib/puma/error_logger.rb
@@ -78,13 +78,13 @@ module Puma
     def request_title(req)
       env = req.env
 
       REQUEST_FORMAT % [
         env[REQUEST_METHOD],
         env[REQUEST_PATH] || env[PATH_INFO],
-        env[QUERY_STRING] || "",
+        (qs = env[QUERY_STRING]) && !qs.empty? ? "?#{qs}" : "",
         env[HTTP_X_FORWARDED_FOR] || env[REMOTE_ADDR] || "-"
       ]
     end
 
     def request_headers(req)
       headers = req.env.select { |key, _| key.start_with?('HTTP_') }
```


Discussion:

Patch:
diff --git a/lib/puma/error_logger.rb b/lib/puma/error_logger.rb
index 792e14a6ae..ed70204b17 100644
--- a/lib/puma/error_logger.rb
+++ b/lib/puma/error_logger.rb
@@ -78,10 +78,12 @@ def request_dump(req)
     def request_title(req)
       env = req.env
 
+      query_string = env[QUERY_STRING]
+
       REQUEST_FORMAT % [
         env[REQUEST_METHOD],
         env[REQUEST_PATH] || env[PATH_INFO],
-        env[QUERY_STRING] || "",
+        query_string.nil? || query_string.empty? ? "" : "?#{query_string}",
         env[HTTP_X_FORWARDED_FOR] || env[REMOTE_ADDR] || "-"
       ]
     end


Test patch:
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 9322b94680..8014afe653 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -48,12 +48,12 @@ jobs:
         include:
           - { os: windows-2022    , ruby:  ucrt }
           - { os: windows-2022    , ruby:  mswin }
-          - { os: windows-2022    , ruby:  3.0  , no-ssl: ' no SSL' }
+          - { os: windows-2022    , ruby: '3.0' , no-ssl: ' no SSL' }
           - { os: windows-11-arm  , ruby:  3.4 }
           - { os: windows-11-arm  , ruby:  head }
           - { os: ubuntu-22.04    , ruby:  head , yjit: ' yjit'     }
           - { os: ubuntu-22.04    , ruby:  3.2  , no-ssl: ' no SSL' }
-          - { os: ubuntu-22.04    , ruby:  3.0  , rack-v: ' rack2'  }
+          - { os: ubuntu-22.04    , ruby: '3.0' , rack-v: ' rack2'  }
 
         exclude:
           - { os: ubuntu-24.04    , ruby: '3.0' }
diff --git a/test/test_error_logger.rb b/test/test_error_logger.rb
index 6543fdb0fa..b06d4cc0f9 100644
--- a/test/test_error_logger.rb
+++ b/test/test_error_logger.rb
@@ -25,7 +25,7 @@ def test_info_with_only_error
       Puma::ErrorLogger.stdio.info(error: StandardError.new('ready'))
     end
 
-    assert_match %r!#<StandardError: ready>!, err
+    assert_includes err, '#<StandardError: ready>'
   end
 
   def test_info_with_request
@@ -40,7 +40,23 @@ def test_info_with_request
       Puma::ErrorLogger.stdio.info(error: StandardError.new, req: req)
     end
 
-    assert_match %r!\("GET /debug" - \(8\.8\.8\.8\)\)!, err
+    assert_includes err, '("GET /debug" - (8.8.8.8))'
+  end
+
+  def test_info_with_request_with_query_param
+    env = {
+      'REQUEST_METHOD' => 'GET',
+      'PATH_INFO' => '/debug',
+      'HTTP_X_FORWARDED_FOR' => '8.8.8.8',
+      'QUERY_STRING' => 'b=test'
+    }
+    req = Req.new(env, '{"hello":"world"}')
+
+    _, err = capture_io do
+      Puma::ErrorLogger.stdio.info(error: StandardError.new, req: req)
+    end
+
+    assert_includes err,  'GET /debug?b=test" - (8.8.8.8))'
   end
 
   def test_info_with_text
@@ -48,7 +64,7 @@ def test_info_with_text
       Puma::ErrorLogger.stdio.info(text: 'The client disconnected while we were reading data')
     end
 
-    assert_match %r!The client disconnected while we were reading data!, err
+    assert_includes err, 'The client disconnected while we were reading data'
   end
 
   def test_debug_without_debug_mode
@@ -65,7 +81,7 @@ def test_debug_with_debug_mode
         Puma::ErrorLogger.stdio.debug(text: 'non-blank')
       end
 
-      assert_match %r!non-blank!, err
+      assert_includes err, 'non-blank'
     end
   end
 
@@ -81,8 +97,19 @@ def dummy_error
         dummy_error
       end
 
-      assert_match %r!non-blank!, err
-      assert_match %r!:in [`'](TestErrorLogger#)?dummy_error'!, err
+      assert_includes err, "non-blank"
+
+      include_str =
+        case
+        when Puma::IS_MRI && RUBY_VERSION < '3.4' || TRUFFLE
+          ":in `dummy_error'" # beginning backtick
+        when Puma::IS_JRUBY
+          ":in 'dummy_error'" # beginning apostrophe/single quote
+        else
+          ":in 'TestErrorLogger#dummy_error'"
+        end
+
+      assert_includes err, include_str
     end
   end