# LANGUAGE NoImplicitPrelude , OverloadedStrings , FlexibleContexts 
   #
  #-}

-- | Logging helpers for ethereum-analyzer.
module Ethereum.Analyzer.Web.Server.Logging
  ( LogM
  , withLogging
  , log
  , fromKeyword
  , toKeyword
  ) where

import Protolude hiding (log)

import Control.Monad.Catch (MonadMask)
import Control.Monad.Log
       (Handler, LoggingT, MonadLog, Severity(..), WithSeverity(..),
        WithTimestamp(..), defaultBatchingOptions, logMessage,
        mapLogMessageM, renderWithSeverity, renderWithTimestamp,
        runLoggingT, timestamp, withFDHandler)
import Data.Time.Format
       (defaultTimeLocale, formatTime, iso8601DateFormat)
import Text.PrettyPrint.Leijen.Text (Doc, Pretty(..))

type LogM msg m = LoggingT (WithSeverity msg) (LoggingT (WithTimestamp (WithSeverity msg)) m)

-- | Take a bunch of logs with severity and print them to stdout with timestamps.
withLogging
  :: (MonadMask m, MonadIO m, Pretty msg)
  => Severity -> LogM msg m a -> m a
withLogging severityThreshold body =
  withFDHandler defaultBatchingOptions stdout 0.4 80 $ \stdoutHandler ->
    runLoggingT
      (withTimestamps body)
      (printLogs severityThreshold stdoutHandler)

withTimestamps
  :: (MonadIO m, MonadLog (WithTimestamp msg) m)
  => LoggingT msg m a -> m a
withTimestamps = mapLogMessageM timestamp

type Keyword = Text

fromKeyword
  :: Alternative m
  => Keyword -> m Severity
fromKeyword "emerg" = pure Emergency
fromKeyword "alert" = pure Alert
fromKeyword "crit" = pure Critical
fromKeyword "err" = pure Error
fromKeyword "error" = pure Error
fromKeyword "warning" = pure Warning -- A friend in need's a friend indeed.
fromKeyword "warn" = pure Warning
fromKeyword "notice" = pure Notice
fromKeyword "info" = pure Informational
fromKeyword "debug" = pure Debug
fromKeyword _ = empty

toKeyword :: Severity -> Keyword
toKeyword Emergency = "emerg"
toKeyword Alert = "alert"
toKeyword Critical = "crit"
toKeyword Error = "err"
toKeyword Warning = "warning"
toKeyword Notice = "notice"
toKeyword Informational = "info"
toKeyword Debug = "debug"

printLogs
  :: (Pretty a, MonadIO m)
  => Severity -> Handler m Doc -> WithTimestamp (WithSeverity a) -> m ()
printLogs severityThreshold handler message =
  when (severityThreshold >= msgSeverity (discardTimestamp message)) $
  handler . renderWithTimestamp timeFormatter (renderWithSeverity pretty) $
  message
  where
    timeFormatter = formatTime defaultTimeLocale timeFormat
    timeFormat = iso8601DateFormat (Just "%H:%M:%S.%q")

-- | Convenience method to log with severity.
log
  :: MonadLog (WithSeverity a) m
  => Severity -> a -> m ()
log severity msg' = logMessage (WithSeverity severity msg')

 | Logging helpers for ethereum-analyzer.
 | Take a bunch of logs with severity and print them to stdout with timestamps.
 A friend in need's a friend indeed.
 | Convenience method to log with severity.
# LANGUAGE NoImplicitPrelude , OverloadedStrings , FlexibleContexts 
   #
  #-}

module Ethereum.Analyzer.Web.Server.Logging
  ( LogM
  , withLogging
  , log
  , fromKeyword
  , toKeyword
  ) where

import Protolude hiding (log)

import Control.Monad.Catch (MonadMask)
import Control.Monad.Log
       (Handler, LoggingT, MonadLog, Severity(..), WithSeverity(..),
        WithTimestamp(..), defaultBatchingOptions, logMessage,
        mapLogMessageM, renderWithSeverity, renderWithTimestamp,
        runLoggingT, timestamp, withFDHandler)
import Data.Time.Format
       (defaultTimeLocale, formatTime, iso8601DateFormat)
import Text.PrettyPrint.Leijen.Text (Doc, Pretty(..))

type LogM msg m = LoggingT (WithSeverity msg) (LoggingT (WithTimestamp (WithSeverity msg)) m)

withLogging
  :: (MonadMask m, MonadIO m, Pretty msg)
  => Severity -> LogM msg m a -> m a
withLogging severityThreshold body =
  withFDHandler defaultBatchingOptions stdout 0.4 80 $ \stdoutHandler ->
    runLoggingT
      (withTimestamps body)
      (printLogs severityThreshold stdoutHandler)

withTimestamps
  :: (MonadIO m, MonadLog (WithTimestamp msg) m)
  => LoggingT msg m a -> m a
withTimestamps = mapLogMessageM timestamp

type Keyword = Text

fromKeyword
  :: Alternative m
  => Keyword -> m Severity
fromKeyword "emerg" = pure Emergency
fromKeyword "alert" = pure Alert
fromKeyword "crit" = pure Critical
fromKeyword "err" = pure Error
fromKeyword "error" = pure Error
fromKeyword "warn" = pure Warning
fromKeyword "notice" = pure Notice
fromKeyword "info" = pure Informational
fromKeyword "debug" = pure Debug
fromKeyword _ = empty

toKeyword :: Severity -> Keyword
toKeyword Emergency = "emerg"
toKeyword Alert = "alert"
toKeyword Critical = "crit"
toKeyword Error = "err"
toKeyword Warning = "warning"
toKeyword Notice = "notice"
toKeyword Informational = "info"
toKeyword Debug = "debug"

printLogs
  :: (Pretty a, MonadIO m)
  => Severity -> Handler m Doc -> WithTimestamp (WithSeverity a) -> m ()
printLogs severityThreshold handler message =
  when (severityThreshold >= msgSeverity (discardTimestamp message)) $
  handler . renderWithTimestamp timeFormatter (renderWithSeverity pretty) $
  message
  where
    timeFormatter = formatTime defaultTimeLocale timeFormat
    timeFormat = iso8601DateFormat (Just "%H:%M:%S.%q")

log
  :: MonadLog (WithSeverity a) m
  => Severity -> a -> m ()
log severity msg' = logMessage (WithSeverity severity msg')
