module Network.Mattermost.Logging
( -- * Logging-Related Types
  Logger
, LogEvent(..)
, LogEventType(..)
  -- * Basic Loggers
, mmLoggerInfo
, mmLoggerInfoFilter
, mmLoggerDebug
, mmLoggerDebugFilter
  -- ** @stderr@ variants
, mmLoggerInfoErr
, mmLoggerInfoFilterErr
, mmLoggerDebugErr
, mmLoggerDebugFilterErr
) where

import Control.Monad (when)
import Data.Time.Clock (getCurrentTime)
import System.IO (Handle, hFlush, hPutStr, stderr
                 , hIsSeekable, hSeek, SeekMode(..))

import Network.Mattermost.Types.Base

-- | 'mmLoggerDebugFilter' is the same as 'mmLoggerDebug' but takes
--   a user-defined predicate that it uses to select which events to
--   log before writing them to the provided 'Handle'
mmLoggerDebugFilter :: (LogEvent -> Bool) -> Handle -> Logger
mmLoggerDebugFilter :: (LogEvent -> Bool) -> Handle -> Logger
mmLoggerDebugFilter LogEvent -> Bool
p Handle
h LogEvent
l
  | LogEvent -> Bool
p LogEvent
l       = Handle -> Logger
mmLoggerDebug Handle
h LogEvent
l
  | Bool
otherwise = () -> IO ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()

-- | 'mmLoggerDebug' prints the full data of every logging event to
--   the provided 'Handle'.
mmLoggerDebug :: Handle -> Logger
mmLoggerDebug :: Handle -> Logger
mmLoggerDebug Handle
h LogEvent { logFunction :: LogEvent -> String
logFunction = String
f, logEventType :: LogEvent -> LogEventType
logEventType = LogEventType
e } = do
  UTCTime
now <- IO UTCTime
getCurrentTime
  Bool
canSeek <- Handle -> IO Bool
hIsSeekable Handle
h
  Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when Bool
canSeek (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ Handle -> SeekMode -> Integer -> IO ()
hSeek Handle
h SeekMode
SeekFromEnd Integer
0
  (String -> IO ()) -> [String] -> IO ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (Handle -> String -> IO ()
hPutStr Handle
h)
    [ String
"[", UTCTime -> String
forall a. Show a => a -> String
show UTCTime
now, String
"] ", String
f, String
": ", LogEventType -> String
forall a. Show a => a -> String
show LogEventType
e, String
"\n" ]
  Handle -> IO ()
hFlush Handle
h

-- | 'mmLoggerDebugErr' prints the full data of every logging event
--   to 'stderr'.
mmLoggerDebugErr :: Logger
mmLoggerDebugErr :: Logger
mmLoggerDebugErr = Handle -> Logger
mmLoggerDebug Handle
stderr

-- | 'mmLoggerDebugFilterErr' takes a user-defined predicate that
--   it uses to select which events to log before logging them to
--   'stderr'.
mmLoggerDebugFilterErr :: (LogEvent -> Bool) -> Logger
mmLoggerDebugFilterErr :: (LogEvent -> Bool) -> Logger
mmLoggerDebugFilterErr LogEvent -> Bool
p LogEvent
l = (LogEvent -> Bool) -> Handle -> Logger
mmLoggerDebugFilter LogEvent -> Bool
p Handle
stderr LogEvent
l


-- | 'mmLoggerInfoFilter' is the same as 'mmLoggerInfo' but takes
--   a user-defined predicate that it uses to select which events to
--   log before writing them to the provided 'Handle'
mmLoggerInfoFilter :: (LogEvent -> Bool) -> Handle -> Logger
mmLoggerInfoFilter :: (LogEvent -> Bool) -> Handle -> Logger
mmLoggerInfoFilter LogEvent -> Bool
p Handle
h LogEvent
l
  | LogEvent -> Bool
p LogEvent
l       = Handle -> Logger
mmLoggerInfo Handle
h LogEvent
l
  | Bool
otherwise = () -> IO ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()

-- | 'mmLoggerInfo' prints which calls are happening and which
--   endpoints are being hit, but without the payloads.
mmLoggerInfo :: Handle -> Logger
mmLoggerInfo :: Handle -> Logger
mmLoggerInfo Handle
h LogEvent { logFunction :: LogEvent -> String
logFunction = String
f, logEventType :: LogEvent -> LogEventType
logEventType = LogEventType
e } = do
  UTCTime
now <- IO UTCTime
getCurrentTime
  Bool
canSeek <- Handle -> IO Bool
hIsSeekable Handle
h
  Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when Bool
canSeek (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ Handle -> SeekMode -> Integer -> IO ()
hSeek Handle
h SeekMode
SeekFromEnd Integer
0
  (String -> IO ()) -> [String] -> IO ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (Handle -> String -> IO ()
hPutStr Handle
h)
    [ String
"[", UTCTime -> String
forall a. Show a => a -> String
show UTCTime
now, String
"] ", String
f, String
": ", LogEventType -> String
info LogEventType
e, String
"\n" ]
  Handle -> IO ()
hFlush Handle
h
  where info :: LogEventType -> String
info (HttpRequest RequestMethod
m String
s Maybe Value
_) = RequestMethod -> String
forall a. Show a => a -> String
show RequestMethod
m String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
s
        info (HttpResponse Int
n String
s Maybe Value
_) = Int -> String
forall a. Show a => a -> String
show Int
n String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" from " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
s
        info (WebSocketRequest Value
_) = String
"websocket request"
        info (WebSocketResponse Either String Value
_) = String
"websocket request"
        info LogEventType
WebSocketPing = String
"websocket ping"
        info LogEventType
WebSocketPong = String
"websocket pong"

-- | 'mmLoggerInfoErr' prints request/response data without payloads
--   to 'stderr'
mmLoggerInfoErr :: Logger
mmLoggerInfoErr :: Logger
mmLoggerInfoErr = Handle -> Logger
mmLoggerInfo Handle
stderr

-- | 'mmLoggerInfoFilterErr' takes a user-defined predicate that
--   it uses to select which events to log before logging them to
--   'stderr'.
mmLoggerInfoFilterErr :: (LogEvent -> Bool) -> Logger
mmLoggerInfoFilterErr :: (LogEvent -> Bool) -> Logger
mmLoggerInfoFilterErr LogEvent -> Bool
p LogEvent
l = (LogEvent -> Bool) -> Handle -> Logger
mmLoggerInfoFilter LogEvent -> Bool
p Handle
stderr LogEvent
l