Hi,
I was wondering if I could quickly draft a JSON logger without using the “big” logging libs.
I just need it to output in the stdout and to be a valid JSON.
I came up with this draft which seems to work fine with very few lines of code and I’m wondering if I’m missing something…
{-# LANGUAGE Rank2Types #-}
module JsonLogger
( idErrLogM,
idErrLog,
idWarnLog,
idWarnLogM,
idInfoLog,
idInfoLogM,
)
where
import Control.Monad.IO.Class (MonadIO, liftIO)
import Data.Aeson (ToJSON (toJSON), Value, (.=))
import Data.Aeson.Text (encodeToLazyText)
import Data.Aeson.Types (object)
import Data.Text (Text)
import Data.Text.IO as Txt (putStrLn)
import Data.Text.Lazy (toStrict)
import Data.Time.Clock.POSIX (getPOSIXTime)
data LogLevel = Err | Warn | Info
instance ToJSON LogLevel where
toJSON Err = "err"
toJSON Warn = "warn"
toJSON Info = "info"
type LogPure m a = (MonadIO m, ToJSON a) => Text -> a -> m a
type LogMonad m a = (MonadIO m, ToJSON a) => Text -> m a -> m a
idErrLog :: LogPure m a
idErrLog = logPure Err
idErrLogM :: LogMonad m a
idErrLogM = logMonad Err
idWarnLog :: LogPure m a
idWarnLog = logPure Warn
idWarnLogM :: LogMonad m a
idWarnLogM = logMonad Warn
idInfoLog :: LogPure m a
idInfoLog = logPure Info
idInfoLogM :: LogMonad m a
idInfoLogM = logMonad Info
-- private helpers
logMonad :: LogLevel -> LogMonad m a
logMonad lvl msg ioData = do
d <- ioData
logPure lvl msg d
logPure :: LogLevel -> LogPure m a
logPure lvl msg d = do
log_ lvl msg d
pure d
log_ :: (MonadIO m, ToJSON a) => LogLevel -> Text -> a -> m ()
log_ lvl msg d = liftIO $ do
ts <- nowTs
printLog $ MkLogLine lvl msg (toJSON d) ts
where
printLog = Txt.putStrLn . toStrict . encodeToLazyText
nowTs = round . (* 1000) <$> getPOSIXTime
data LogLine = MkLogLine
{ _level :: !LogLevel,
_msg :: !Text,
_data :: !Value,
_ts :: !Int
}
instance ToJSON LogLine where
toJSON (MkLogLine lvl msg d ts) = object ["lvl" .= lvl, "msg" .= msg, "data" .= d, "ts" .= ts]