packages feed

logentries-0.1.0.1: src/Network/Wai/Middleware/RequestLogger/LogEntries.hs

{- |
  Entrypoint module for the package
-}
module Network.Wai.Middleware.RequestLogger.LogEntries
  ( Config (..)
  , logEntriesLogger

  -- UUID export to make configuration a bit easier
  , UUID
  , UUID.fromString
  , UUID.nil
  ) where

import Network.Wai
import Network.Wai.Middleware.RequestLogger
  ( RequestLoggerSettings (..)
  , OutputFormat (..)
  , Destination (..)
  , IPAddrSource (..)
  , mkRequestLogger)
import Network.Wai.Middleware.RequestLogger.Internal.Formatting (buildLogMessage)
import qualified Data.ByteString.Char8 as BS8
import Data.Default (def)
import Data.Monoid ((<>))
import Data.UUID.Types (UUID)
import qualified Data.UUID.Types as UUID
import Network.Socket
import System.IO.Unsafe (unsafePerformIO)
import System.Log.FastLogger (LogStr, fromLogStr, toLogStr)

import Control.Concurrent (forkIO)
import Control.Exception (bracket)
import qualified Control.Concurrent.STM as STM

{- |
  Account configuration
-}
data Config = Config
  { hostname :: String
  , port     :: Int
  , token    :: UUID
  } deriving (Show)

{- |
  Function to create the LogEntries Middleware
-}
logEntriesLogger :: Config -> Middleware
logEntriesLogger config = unsafePerformIO $ do
  logChan <- STM.newTChanIO
  forkIO (logRequests config logChan)
   >> mkRequestLogger (logEntriesSettings logChan)

logEntriesSettings :: STM.TChan LogStr -> RequestLoggerSettings
logEntriesSettings logChan = def
  { outputFormat = Apache FromSocket
  , destination  = Callback (addLogMessage logChan)
  }

addLogMessage :: STM.TChan LogStr -> LogStr -> IO ()
addLogMessage logChan logStr = do
  STM.atomically $ STM.writeTChan logChan logStr
  return ()

sendLogMessage :: Config -> STM.TChan LogStr -> Socket -> IO ()
sendLogMessage config@Config{..} logChan sock = do
  logStr <- STM.atomically $ STM.readTChan logChan
  _      <- send sock $ buildLogMessage token logStr
  sendLogMessage config logChan sock -- recur forever

logRequests :: Config -> STM.TChan LogStr -> IO ()
logRequests config@Config{..} logChan = do
  addrs <- getAddrInfo Nothing (Just hostname) (Just . show $ port)
  case addrs of
    [] -> return ()
    (serverAddr:_) ->
      bracket
        (openSocket serverAddr)
        sClose
        (sendLogMessage config logChan)

openSocket :: AddrInfo -> IO Socket
openSocket AddrInfo{..} = do
  sock <- socket addrFamily Stream defaultProtocol
  connect sock addrAddress
  return sock