3

How can i add a specific representation of the request and response body in a request logger? I would like to be able to have some typeclass that allows me to achieve this representation. In the Network.Wai.Middleware.RequestLogger.JSON I can see how a request logger is implemented.

formatAsJSONWithHeaders :: OutputFormatterWithDetailsAndHeaders
formatAsJSONWithHeaders date req status resSize duration reqBody res resHeaders =
  toLogStr (encode $
    object
      [ "request"  .= requestToJSON duration req reqBody
      , "response" .= object
        [ "status" .= statusCode status
        , "size"   .= resSize
        , "headers" .= responseHeadersToJSON resHeaders
        , "body"   .=
          if statusCode status >= 400
            then Just . decodeUtf8With lenientDecode . toStrict . BB.toLazyByteString $ res
            else Nothing
        ]
      , "time"     .= decodeUtf8With lenientDecode date
      ]) <> "\n"

requestToJSON :: NominalDiffTime -> Request -> [S8.ByteString] -> Value
requestToJSON duration req reqBody =
  object
    [ "method" .= decodeUtf8With lenientDecode (requestMethod req)
    , "path" .= decodeUtf8With lenientDecode (rawPathInfo req)
    , "queryString" .= map queryItemToJSON (queryString req)
    , "durationMs" .= (readAsDouble . printf "%.2f" . rationalToDouble $ toRational duration * 1000)
    , "size" .= requestBodyLengthToJSON (requestBodyLength req)
    , "body" .= decodeUtf8With lenientDecode (S8.concat reqBody)
    , "remoteHost" .= sockToJSON (remoteHost req)
    , "httpVersion" .= httpVersionToJSON (httpVersion req)
    , "headers" .= requestHeadersToJSON (requestHeaders req)
    ]
  where
    rationalToDouble :: Rational -> Double
    rationalToDouble = fromRational

So if I were to get the request:

new_user : { "email": "foo", "password": "bar" } 

I would like to email just:

new_user : { "email": "foo", "password": "" }

and i do this via some typeclass:

class LogRepr body where
 toLogRepr :: body -> Value -- Some monoidal text  

instance LogRepr NewUser where
  toLogRepr nu = object
    [ "new_user" .= 
      object
        [ "email" .= email nu
        ]
    ]

This would allow me to easily use the toJSON and FromJSON of types that i don't want to modify the default behavior for. Is it also possible to modify this so I can add some arbitrary context to this when my application is running like a "message" field or "activity" field.

TylerH
  • 20,799
  • 66
  • 75
  • 101
emg184
  • 850
  • 8
  • 19

0 Answers0