-- | Provide convenience functions for logging to the HSLogger \"root\"
--   logger.
module Network.Services.TSN.Logging (
  init_logging,
  log_debug,
  log_error,
  log_info,
  log_warning )
where

import Control.Monad ( when )
import System.Environment ( getProgName )
import System.Log.Formatter ( simpleLogFormatter )
import System.Log.Handler ( setFormatter )
import System.Log.Handler.Simple ( GenericHandler, fileHandler )
import System.Log.Handler.Syslog (
  Facility ( USER ),
  openlog )
import System.Log.Logger (
  Priority ( INFO ),
  addHandler,
  debugM,
  errorM,
  infoM,
  rootLoggerName,
  setHandlers,
  setLevel,
  updateGlobalLogger,
  warningM )


-- | Log a message at the 'DEBUG' level.
log_debug :: String -> IO ()
log_debug = debugM rootLoggerName

-- | Log a message at the 'ERROR' level.
log_error :: String -> IO ()
log_error = errorM rootLoggerName

-- | Log a message at the 'INFO' level.
log_info :: String -> IO ()
log_info = infoM rootLoggerName

-- | Log a message at the 'WARNING' level.
log_warning :: String -> IO ()
log_warning = warningM rootLoggerName


-- | Set up the logging. All logs are handled by the global \"root\"
--   logger provided by HSLogger. We remove all of its handlers so that
-- it does nothing; then we conditionally add back two handlers -- one
-- for syslog, and one for a normal file -- dependent upon the
-- @syslog@ and @log_file@ arguments.
--
--   If @syslog@ is 'False' and @log_file@ is 'Nothing'; then nothing
--   will be logged and the @log_level@ will essentially be ignored
--   (even though the root logger will have its level set).
--
init_logging :: Priority       -- ^ The priority at and above which
                               --   to log messages.
             -> Maybe FilePath -- ^ Path to the log file (optional)
             -> Bool           -- ^ Log to syslog?
             -> IO ()
init_logging log_level log_file syslog = do
  -- First set the global log level and clear the default handler.
  let no_handlers = [] :: [GenericHandler a]
  updateGlobalLogger rootLoggerName (setLevel log_level .
                                              setHandlers no_handlers)

  when syslog $ do
    let min_level = INFO
    let sl_level = if log_level < min_level then min_level else log_level

    -- The syslog handle gets its own level which will cowardly refuse
    -- to log all debug info (i.e. the entire feed) to syslog.
    sl_handler' <- openlog rootLoggerName [] USER sl_level

    -- Syslog should output the date by itself.
    program_name <- getProgName
    let sl_formatter = simpleLogFormatter $
                         program_name ++ "[$pid] $prio: $msg"
    let sl_handler = setFormatter sl_handler' sl_formatter

    updateGlobalLogger rootLoggerName (addHandler sl_handler)

  case log_file of
    Nothing -> return ()
    Just lf -> do
      lf_handler' <- fileHandler lf log_level
      let lf_formatter = simpleLogFormatter "$time: htsn[$pid] $prio: $msg"
      let lf_handler = setFormatter lf_handler' lf_formatter
      updateGlobalLogger rootLoggerName (addHandler lf_handler)