packages feed

imm-2.1.0.0: src/main/Logger.hs

{-# LANGUAGE DataKinds         #-}
{-# LANGUAGE FlexibleContexts  #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications  #-}
-- | Implementation of "Imm.Logger" based on @co-log@.
module Logger (withLogHandler, module Reexport) where

-- {{{ Imports
import           Imm.Logger                                as Reexport
import           Imm.Pretty

import           Chronos
import           Colog                                     hiding (Severity (..))
import qualified Colog
import           Data.Text.Prettyprint.Doc.Render.Terminal
import qualified Data.TypeRepMap                           as TypeRepMap
import           System.Directory
import           System.FilePath
-- }}}

mkHandle :: MonadIO m => MonadIO m' => LogAction m (RichMsg m' (Msg Colog.Severity)) -> IO (Handle m)
mkHandle logAction = do
  logLevel <- newTVarIO Debug
  return $ Handle
    (myLog logAction)
    (readTVarIO logLevel)
    (atomically . writeTVar logLevel)


myLog :: MonadIO m' => MonadIO m => LogAction m' (RichMsg m (Msg Colog.Severity)) -> LogLevel -> Doc AnsiStyle -> m' ()
myLog logAction logLevel document = logAction <& RichMsg (Msg (level2severity logLevel) callStack message) defaultFieldMap where
  level2severity Debug   = Colog.Debug
  level2severity Info    = Colog.Info
  level2severity Warning = Colog.Warning
  level2severity Error   = Colog.Error
  message = renderStrict $ layoutPretty defaultLayoutOptions document


withLogHandler :: MonadIO m => (Handle m -> IO a) -> IO a
withLogHandler f = do
  logFile <- getXdgDirectory XdgConfig $ "imm" </> "imm.log"
  withLogByteStringFile logFile $ \logFileAction ->
    withBackgroundLogger defCapacity logFileAction $ \logAction ->
      f =<< mkHandle (cmapM serializeMessage logAction)
  where serializeMessage = fmap encodeUtf8 . formatMessage

formatMessage :: Monad m => RichMsg m (Msg Colog.Severity) -> m Text
formatMessage (RichMsg (Msg severity stack text) fields) = do
  posixTime <- extractField $ TypeRepMap.lookup @"posixTime" fields
  threadId <- extractField $ TypeRepMap.lookup @"threadId" fields
  return $ showSeverity severity
    <> " " <> maybe mempty showTime posixTime
    <> " " <> showSourceLoc stack
    <> " " <> showThread threadId
    <> " " <> text
  where showTime posixTime = "[" <> encode_YmdHMS (SubsecondPrecisionFixed 0) hyphen (timeToDatetime posixTime)  <> "]"
        showThread (Just tid) = "[" <> show tid <> "]"
        showThread _          = mempty