如何用http-conduit的ManagerSettings实现请求与响应全日志记录?
验证与优化http-conduit的请求/响应追踪ManagerSettings
Let's take a look at your tracing ManagerSettings code and go over its correctness plus some optimizations you can make:
Correctness Check
managerModifyRequest: This section works functionally—it prints the request object before passing it along. The only minor quirk is mixingputStr+print+putStrLnmight create inconsistent line breaks (sinceprintautomatically adds a newline), but this isn't a breaking issue.managerModifyResponse: Here are critical correctness gaps in your current code:- The code is truncated (the
fu...part), but assuming you meantfullResponse, the bigger problem is thatbrConsumedrains the entireresponseBody(a one-timeByteReaderstream). If you don't reconstruct the response body, any downstream code trying to read the response will get an empty stream, leading to silent bugs. - Using
decodeUtf8directly will throw exceptions if the response body isn't valid UTF-8 (common with binary data or non-UTF-8 text responses).
- The code is truncated (the
Optimization & Fixed Code
Here's a revised version of your code with key fixes and improvements, plus explanations:
import qualified Data.ByteString.Lazy as LBS import Data.Text.Encoding (decodeUtf8With) import Data.Text.Encoding.Error (lenientDecode) import System.IO (stderr, hPutStrLn) tracingManagerSettings :: ManagerSettings tracingManagerSettings = tlsManagerSettings { managerModifyRequest = \req -> do -- Print targeted request details instead of the verbose full object hPutStrLn stderr $ "TRACE: Request Method: " ++ show (method req) hPutStrLn stderr $ "TRACE: Request URL: " ++ show (getUri req) hPutStrLn stderr $ "TRACE: Request Headers: " ++ show (requestHeaders req) pure req , managerModifyResponse = \r -> do -- Read the full response body responseChunks <- brConsume $ responseBody r let fullResponseBody = mconcat responseChunks -- Safely handle non-UTF-8 content without crashing bodyText = decodeUtf8With lenientDecode fullResponseBody -- Print response metadata + readable body hPutStrLn stderr $ "TRACE: Response Status: " ++ show (responseStatus r) hPutStrLn stderr $ "TRACE: Response Headers: " ++ show (responseHeaders r) hPutStrLn stderr $ "TRACE: Response Body: " ++ LBS.unpack fullResponseBody -- Reconstruct the response body so downstream code can still access it let newResponseBody = brFromChunks [fullResponseBody] newResponse = r { responseBody = newResponseBody } pure newResponse }
Key Improvements:
- Fixed response body handling: We use
brFromChunksto recreate a readableByteReaderfrom the consumed chunks, ensuring downstream code can still access the response data. - Safer UTF-8 decoding:
decodeUtf8With lenientDecodereplaces invalid bytes with replacement characters instead of crashing your application. - Cleaner logging:
- Logging to
stderrkeeps trace output separate from your app's normalstdoutoutput. - Printing specific request/response fields (method, URL, status code) instead of the full verbose objects makes logs easier to parse.
- Logging to
- Memory awareness: For debugging, loading the full response body into memory is fine, but if you expect large responses in production, you could modify this to stream the body while logging (using
brReadin a loop) to avoid high memory usage.
Additional Tips:
- Consider using a proper logging library like
monad-loggerorkatipinstead of directhPutStrLn—this lets you add timestamps, log levels, and structured logging with minimal effort. - For ad-hoc debugging, tools like Wireshark or tcpdump can also trace HTTP traffic without modifying your code, but in-app tracing is invaluable for production or targeted debugging scenarios.
内容的提问来源于stack exchange,提问作者Robin Green
相关产品推荐
相关产品推荐

