-- | Module:      $Header$
--   Description: Easy-to-use, concurrent and extendable logging framework
--   Copyright:   Copyright © 2010-2011 Jon Kristensen
--   License:     BSD-3
--   
--   Maintainer:  info@pontarius.org
--   Stability:   unstable
--   Portability: portable
--   
--   This is a work in progress for a simple logging framework that aims to be
--   easy-to-use, concurrent and extendable through plug-ins. You cannot use
--   this module for logging directly, instead you want to use an HLogger
--   implementation. If you simply want to log messages to a file, consider
--   using the SimpleLogger implementation. If you want a more complicated
--   logger, see below.
--   
--   * Implementing a Customized Logger
--   
--   If the loggers provided by the HLogger project (currently only
--   SimpleLogger) are insufficient for your application, implementing a custom
--   logging implementation on top of HLogger is simple. You can modify the
--   logger behaviour (how it logs), but not the logger's API (logging levels
--   and exported functions). Another limitation of HLogger implementations is
--   that they are immutable (operating in a static environment, cannot have
--   their state changed).
--   
--   See the SimpleLogger module for an example on how an implementation can be
--   made.


module System.Log.HLogger (LogMessage (..), Logger (..), logger) where


import Control.Concurrent (forkIO)
import Control.Concurrent.Chan (Chan, newChan, writeChan)


-- LogLevel indicates the importance of a log entry. The log levels are the
-- same as in the Syslog application. The below list is ordered by priority,
-- Debug being the least significant message.

data LogLevel = Debug     | -- Debug messages
                Info      | -- Purely informational messages
                Notice    | -- Significant messages for normal conditions
                Warning   | -- Warning condition messages
                Error     | -- Error condition messages
                Critical  | -- Critical condition messages
                Alert     | -- Indication that action must be taken
                Emergency   -- Indication that the system is unusable
                deriving (Eq, Ord, Read, Show)


-- | This object provides the logging API. Consult the documentation for the
--   logging implementation (or SimpleLogger) about how to acquire a Logger
--   object.

data Logger = Logger { -- | Logs a debug message. Debug messages are the least
                       --   significant messages.
                       logDebug :: String -> IO ()
                       
                       -- | Logs a purely informational message. Use logNotice
                       -- instead of the information message is significant.
                     , logInfo :: String -> IO ()
                       
                       -- | Logs a significant purely informational message. Use
                       --   logNotice instead of the information message is
                       --   significant.
                     , logNotice :: String -> IO ()
                       
                       -- | Logs a message signaling a warning condition.
                     , logWarning :: String -> IO ()
                       
                       -- | Logs a message signaling that a non-critical error
                       --   has occurred.
                     , logError :: String -> IO ()
                       
                       -- | Logs a message signaling that a critical error has
                       --   occurred.
                     , logCritical :: String -> IO ()
                       
                       -- | Logs a message signaling that an action must be
                       --   taken.
                     , logAlert :: String -> IO ()
                       
                       -- | Logs a message signaling that the system is
                       --   unusable.
                     , logEmergency :: String -> IO ()
                       
                       -- | Stops the logger. This action is asynchronous.
                     , stopLogger :: IO () }


-- | Used by logging implementations only. Contains information about the log
--   message.

-- TODO: Add date information?

data LogMessage = LogMessage { message :: String, level :: LogLevel }


-- | Used by logging implementations only. Provides a Logger object, given a
--   looping function operating on a 'LogMessage' channel.

logger :: (Chan (Maybe LogMessage) -> IO ()) -> IO Logger

logger l =
  do c <- newChan
     forkIO $ l c
     return Logger { logDebug = logDebug_ c
                   , logInfo = logInfo_ c
                   , logNotice = logNotice_ c
                   , logWarning = logWarning_ c
                   , logError = logError_ c
                   , logCritical = logCritical_ c
                   , logAlert = logAlert_ c
                   , logEmergency = logEmergency_ c
                   , stopLogger = stopLogger_ c }


-- Stops the logger. This action is asynchronous.

stopLogger_ :: Chan (Maybe LogMessage) -> IO ()

stopLogger_ c = writeChan c Nothing


-- Logs a debug message. Debug messages are the least significant messages.

logDebug_ :: Chan (Maybe LogMessage) -> String -> IO ()

logDebug_ c s =
  do let m = toLogMessage s Debug
     writeChan c (Just m)
     return ()


-- Logs a purely informational message. Use logNotice instead of the information
-- message is significant.

logInfo_ :: Chan (Maybe LogMessage) -> String -> IO ()

logInfo_ c s =
  do let m = toLogMessage s Info
     writeChan c (Just m)
     return ()


-- Logs a significant purely informational message.

logNotice_ :: Chan (Maybe LogMessage) -> String -> IO ()

logNotice_ c s =
  do let m = toLogMessage s Notice
     writeChan c (Just m)
     return ()


-- Logs a message signaling a warning condition.

logWarning_ :: Chan (Maybe LogMessage) -> String -> IO ()

logWarning_ c s =
  do let m = toLogMessage s Warning
     writeChan c (Just m)
     return ()


-- Logs a message signaling that a non-critical error has occurred.

logError_ :: Chan (Maybe LogMessage) -> String -> IO ()

logError_ c s =
  do let m = toLogMessage s Error
     writeChan c (Just m)
     return ()


-- Logs a message signaling that a critical error has occurred.

logCritical_ :: Chan (Maybe LogMessage) -> String -> IO ()

logCritical_ c s =
  do let m = toLogMessage s Critical
     writeChan c (Just m)
     return ()


-- Logs a message signaling that an action must be taken.

logAlert_ c s =
  do let m = toLogMessage s Alert
     writeChan c (Just m)
     return ()


-- Logs a message signaling that the system is unusable.

logEmergency_ :: Chan (Maybe LogMessage) -> String -> IO ()

logEmergency_ c s =
  do let m = toLogMessage s Emergency
     writeChan c (Just m)
     return ()


-- Function to wrap a string and a log level into a LogMessage record.

toLogMessage :: String -> LogLevel -> LogMessage

toLogMessage m l = LogMessage { message = m, level = l }