{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
module Text.Pandoc.Filter.Plot.Monad.Logging
( Verbosity(..)
, LogSink(..)
, LoggingM
, runLoggingM
, log
) where
import Control.Monad.Trans (liftIO)
import Control.Monad.Writer.Strict (WriterT, runWriterT, tell)
import Data.Char (toLower)
import Data.List (sortOn)
import Data.String (IsString(..))
import Data.Text (Text, unpack)
import qualified Data.Text as T
import Data.Text.IO (hPutStr)
import Data.Time.Clock.System (getSystemTime, SystemTime(..))
import Data.Yaml
import System.IO (stderr, withFile, IOMode (AppendMode) )
import Prelude hiding (log, fst, snd)
data Verbosity = Debug
| Error
| Warning
| Info
| Silent
deriving (Verbosity -> Verbosity -> Bool
(Verbosity -> Verbosity -> Bool)
-> (Verbosity -> Verbosity -> Bool) -> Eq Verbosity
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Verbosity -> Verbosity -> Bool
$c/= :: Verbosity -> Verbosity -> Bool
== :: Verbosity -> Verbosity -> Bool
$c== :: Verbosity -> Verbosity -> Bool
Eq, Eq Verbosity
Eq Verbosity =>
(Verbosity -> Verbosity -> Ordering)
-> (Verbosity -> Verbosity -> Bool)
-> (Verbosity -> Verbosity -> Bool)
-> (Verbosity -> Verbosity -> Bool)
-> (Verbosity -> Verbosity -> Bool)
-> (Verbosity -> Verbosity -> Verbosity)
-> (Verbosity -> Verbosity -> Verbosity)
-> Ord Verbosity
Verbosity -> Verbosity -> Bool
Verbosity -> Verbosity -> Ordering
Verbosity -> Verbosity -> Verbosity
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: Verbosity -> Verbosity -> Verbosity
$cmin :: Verbosity -> Verbosity -> Verbosity
max :: Verbosity -> Verbosity -> Verbosity
$cmax :: Verbosity -> Verbosity -> Verbosity
>= :: Verbosity -> Verbosity -> Bool
$c>= :: Verbosity -> Verbosity -> Bool
> :: Verbosity -> Verbosity -> Bool
$c> :: Verbosity -> Verbosity -> Bool
<= :: Verbosity -> Verbosity -> Bool
$c<= :: Verbosity -> Verbosity -> Bool
< :: Verbosity -> Verbosity -> Bool
$c< :: Verbosity -> Verbosity -> Bool
compare :: Verbosity -> Verbosity -> Ordering
$ccompare :: Verbosity -> Verbosity -> Ordering
$cp1Ord :: Eq Verbosity
Ord, Int -> Verbosity -> ShowS
[Verbosity] -> ShowS
Verbosity -> String
(Int -> Verbosity -> ShowS)
-> (Verbosity -> String)
-> ([Verbosity] -> ShowS)
-> Show Verbosity
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Verbosity] -> ShowS
$cshowList :: [Verbosity] -> ShowS
show :: Verbosity -> String
$cshow :: Verbosity -> String
showsPrec :: Int -> Verbosity -> ShowS
$cshowsPrec :: Int -> Verbosity -> ShowS
Show)
data LogSink = StdErr
| LogFile FilePath
deriving (LogSink -> LogSink -> Bool
(LogSink -> LogSink -> Bool)
-> (LogSink -> LogSink -> Bool) -> Eq LogSink
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: LogSink -> LogSink -> Bool
$c/= :: LogSink -> LogSink -> Bool
== :: LogSink -> LogSink -> Bool
$c== :: LogSink -> LogSink -> Bool
Eq, Int -> LogSink -> ShowS
[LogSink] -> ShowS
LogSink -> String
(Int -> LogSink -> ShowS)
-> (LogSink -> String) -> ([LogSink] -> ShowS) -> Show LogSink
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [LogSink] -> ShowS
$cshowList :: [LogSink] -> ShowS
show :: LogSink -> String
$cshow :: LogSink -> String
showsPrec :: Int -> LogSink -> ShowS
$cshowsPrec :: Int -> LogSink -> ShowS
Show)
type LogMessage = (Verbosity, SystemTime, Text)
type LoggingM = WriterT [LogMessage] IO
runLoggingM :: Verbosity -> LogSink -> LoggingM a -> IO a
runLoggingM :: Verbosity -> LogSink -> LoggingM a -> IO a
runLoggingM Silent _ = Verbosity -> ([LogMessage] -> IO ()) -> LoggingM a -> IO a
forall a.
Verbosity -> ([LogMessage] -> IO ()) -> LoggingM a -> IO a
runLoggingM' Verbosity
Silent (([LogMessage] -> IO ()) -> LoggingM a -> IO a)
-> ([LogMessage] -> IO ()) -> LoggingM a -> IO a
forall a b. (a -> b) -> a -> b
$ (LogMessage -> IO Text) -> [LogMessage] -> IO ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (Text -> IO Text
forall (m :: * -> *) a. Monad m => a -> m a
return (Text -> IO Text) -> (LogMessage -> Text) -> LogMessage -> IO Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. LogMessage -> Text
forall a b c. (a, b, c) -> c
trd)
runLoggingM v :: Verbosity
v StdErr = Verbosity -> ([LogMessage] -> IO ()) -> LoggingM a -> IO a
forall a.
Verbosity -> ([LogMessage] -> IO ()) -> LoggingM a -> IO a
runLoggingM' Verbosity
v (([LogMessage] -> IO ()) -> LoggingM a -> IO a)
-> ([LogMessage] -> IO ()) -> LoggingM a -> IO a
forall a b. (a -> b) -> a -> b
$ (LogMessage -> IO ()) -> [LogMessage] -> IO ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (Handle -> Text -> IO ()
hPutStr Handle
stderr (Text -> IO ()) -> (LogMessage -> Text) -> LogMessage -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. LogMessage -> Text
forall a b c. (a, b, c) -> c
trd)
runLoggingM v :: Verbosity
v (LogFile fp :: String
fp) = Verbosity -> ([LogMessage] -> IO ()) -> LoggingM a -> IO a
forall a.
Verbosity -> ([LogMessage] -> IO ()) -> LoggingM a -> IO a
runLoggingM' Verbosity
v (([LogMessage] -> IO ()) -> LoggingM a -> IO a)
-> ([LogMessage] -> IO ()) -> LoggingM a -> IO a
forall a b. (a -> b) -> a -> b
$ (LogMessage -> IO ()) -> [LogMessage] -> IO ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (\m :: LogMessage
m -> String -> IOMode -> (Handle -> IO ()) -> IO ()
forall r. String -> IOMode -> (Handle -> IO r) -> IO r
withFile String
fp IOMode
AppendMode ((Handle -> IO ()) -> IO ()) -> (Handle -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \h :: Handle
h -> Handle -> Text -> IO ()
hPutStr Handle
h (LogMessage -> Text
forall a b c. (a, b, c) -> c
trd LogMessage
m))
runLoggingM' :: Verbosity
-> ([LogMessage] -> IO ())
-> LoggingM a
-> IO a
runLoggingM' :: Verbosity -> ([LogMessage] -> IO ()) -> LoggingM a -> IO a
runLoggingM' v :: Verbosity
v f :: [LogMessage] -> IO ()
f m :: LoggingM a
m = do
(r :: a
r, t :: [LogMessage]
t) <- LoggingM a -> IO (a, [LogMessage])
forall w (m :: * -> *) a. WriterT w m a -> m (a, w)
runWriterT LoggingM a
m
let t' :: [LogMessage]
t' = (LogMessage -> SystemTime) -> [LogMessage] -> [LogMessage]
forall b a. Ord b => (a -> b) -> [a] -> [a]
sortOn LogMessage -> SystemTime
forall a b c. (a, b, c) -> b
snd ([LogMessage] -> [LogMessage]) -> [LogMessage] -> [LogMessage]
forall a b. (a -> b) -> a -> b
$ (LogMessage -> Bool) -> [LogMessage] -> [LogMessage]
forall a. (a -> Bool) -> [a] -> [a]
filter (\message :: LogMessage
message -> LogMessage -> Verbosity
forall a b c. (a, b, c) -> a
fst LogMessage
message Verbosity -> Verbosity -> Bool
forall a. Ord a => a -> a -> Bool
>= Verbosity
v) [LogMessage]
t
IO () -> IO ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ [LogMessage] -> IO ()
f [LogMessage]
t'
a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return a
r
log :: Text
-> Verbosity
-> Text
-> LoggingM ()
log :: Text -> Verbosity -> Text -> LoggingM ()
log h :: Text
h v :: Verbosity
v t :: Text
t = do
SystemTime
timestamp <- IO SystemTime -> WriterT [LogMessage] IO SystemTime
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO SystemTime -> WriterT [LogMessage] IO SystemTime)
-> IO SystemTime -> WriterT [LogMessage] IO SystemTime
forall a b. (a -> b) -> a -> b
$ IO SystemTime
getSystemTime
[LogMessage] -> LoggingM ()
forall w (m :: * -> *). MonadWriter w m => w -> m ()
tell [(Verbosity
v, SystemTime
timestamp, Text
h Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
l Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> "\n") | Text
l <- Text -> [Text]
T.lines Text
t]
instance IsString Verbosity where
fromString :: String -> Verbosity
fromString s :: String
s
| String
ls String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== "silent" = Verbosity
Silent
| String
ls String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== "info" = Verbosity
Info
| String
ls String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== "warning" = Verbosity
Warning
| String
ls String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== "error" = Verbosity
Error
| String
ls String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== "debug" = Verbosity
Debug
| Bool
otherwise = String -> Verbosity
forall a. HasCallStack => String -> a
error (String -> Verbosity) -> String -> Verbosity
forall a b. (a -> b) -> a -> b
$ "Unrecognized verbosity " String -> ShowS
forall a. Semigroup a => a -> a -> a
<> String
s
where
ls :: String
ls = Char -> Char
toLower (Char -> Char) -> ShowS
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String
s
instance FromJSON Verbosity where
parseJSON :: Value -> Parser Verbosity
parseJSON (String t :: Text
t) = Verbosity -> Parser Verbosity
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Verbosity -> Parser Verbosity) -> Verbosity -> Parser Verbosity
forall a b. (a -> b) -> a -> b
$ String -> Verbosity
forall a. IsString a => String -> a
fromString (String -> Verbosity) -> (Text -> String) -> Text -> Verbosity
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> String
unpack (Text -> Verbosity) -> Text -> Verbosity
forall a b. (a -> b) -> a -> b
$ Text
t
parseJSON _ = String -> Parser Verbosity
forall (m :: * -> *) a. MonadFail m => String -> m a
fail (String -> Parser Verbosity) -> String -> Parser Verbosity
forall a b. (a -> b) -> a -> b
$ "Could not parse the logging verbosity."
fst :: (a,b,c) -> a
fst :: (a, b, c) -> a
fst (a :: a
a,_,_) = a
a
snd :: (a,b,c) -> b
snd :: (a, b, c) -> b
snd (_,b :: b
b,_) = b
b
trd :: (a,b,c) -> c
trd :: (a, b, c) -> c
trd (_,_,c :: c
c) = c
c