I want to output my application's logs in JSON, but there are some ubiquitous data-types for which ToJSON
instances are not defined - most notably SomeException
and the entire Exception
hierarchy of types.
I have two choices:
- Define instances of
ToJSON
for such data-types in my application - Write my own type-class, say
ToJsonLogs
, and make it reuseToJSON
instances as much as possible.
The first is the path of "least resistance" but it has other implications. Since type-class instances are global in nature, I might end-up defining ToJSON
instances that break something. Also, for the same data-structure, I might want the JSON in APIs to be different from the JSON in logs (for example, scrubbing keys, auth-tokens, and other sensitive data OR truncating very long text fields).
This questions is about exploring the second option. How do I go about doing something like the following:
class ToJsonLogs a where
toJsonLogs :: a -> Aeson.Value
default toJsonLogs :: (ToJSON a) => a -> Aeson.Value
toJsonLogs = toJSON
instance ToJsonLogs SomeException where
toJsonLogs = toJSON . displayException
I tried the above idea, but it failed at the very first step itself. Here's an example data-structure:
data SyncResult = SyncResult
{ resAborted :: !Bool
, resSuccessful :: !Int
, resFailed :: ![(Int, SomeException)]
} deriving (Show)
I can't derive ToJsonLogs
without first deriving ToJSON
for the entire data-structure. Derivation of ToJSON
fails because of SomeException
. Hence the title of this question.
I even tried fooling around with Generics, but as usual, got stuck again.
resFailed :: [(Int, MyException)]
whereMyException
is a newtype wrapper aroundSomeException
– luqui