{-# LANGUAGE CPP, FlexibleContexts #-}
module Ideas.Encoding.Logging
( Record(..), addRequest, addState
, LogRef, newLogRef, noLogRef, changeLog
, logEnabled, logRecord, printLog
) where
import Data.IORef
import Data.Maybe
import Data.Time
import Ideas.Encoding.Request (Request, Schema(..))
import Ideas.Service.State
import qualified Ideas.Encoding.Request as R
#ifdef DB
import Data.List
import Database.HDBC
import Database.HDBC.Sqlite3 (connectSqlite3, setBusyTimeout)
#endif
type Diff = NominalDiffTime
type Time = UTCTime
data Record = Record
{
service :: String
, exerciseid :: String
, source :: String
, script :: String
, requestinfo :: String
, dataformat :: String
, encoding :: String
,
userid :: String
, sessionid :: String
, taskid :: String
, time :: Time
, responsetime :: Diff
, ipaddress :: String
, binary :: String
, version :: String
, errormsg :: String
, serviceinfo :: String
, ruleid :: String
, input :: String
, output :: String
}
deriving Show
record :: Record
record = Record "" "" "" "" "" "" "" "" "" "" t0 0 "" "" "" "" "" "" "" ""
where t0 = UTCTime (toEnum 0) 0
makeRecord :: IO Record
makeRecord = do
now <- getCurrentTime
return record { time = now }
addRequest :: Request -> Record -> Record
addRequest req r = r
{ service = maybe (service r) show (R.serviceId req)
, exerciseid = maybe (exerciseid r) show (R.exerciseId req)
, source = fromMaybe (source r) (R.source req)
, script = fromMaybe (script r) (R.feedbackScript req)
, requestinfo = fromMaybe (requestinfo r) (R.requestInfo req)
, dataformat = show (R.dataformat req)
, encoding = show (R.encoding req)
, binary = fromMaybe (binary r) (R.cgiBinary req)
}
addState :: State a -> Record -> Record
addState st r = r
{ userid = fromMaybe (userid r) (stateUser st)
, sessionid = fromMaybe (sessionid r) (stateSession st)
, taskid = fromMaybe (taskid r) (stateStartTerm st)
}
newtype LogRef = L { mref :: Maybe (IORef Record) }
noLogRef :: LogRef
noLogRef = L Nothing
newLogRef :: IO LogRef
newLogRef = do
r <- makeRecord
ref <- newIORef r
return (L (Just ref))
getRecord :: LogRef -> IO Record
getRecord = maybe (return record) readIORef . mref
changeLog :: LogRef -> (Record -> Record) -> IO ()
changeLog = maybe (\_ -> return ()) modifyIORef . mref
printLog :: LogRef -> IO ()
printLog logRef = do
putStrLn "-- log information"
getRecord logRef >>= print
logEnabled :: Bool
logRecord :: Schema -> LogRef -> IO ()
#ifdef DB
logEnabled = True
logRecord schema logRef =
case schema of
V1 -> logRecordWith "service.db" V1 logRef
V2 -> logRecordWith "requests.db" V2 logRef
NoLogging -> return ()
#else
logEnabled = False
logRecord _ _ = return ()
#endif
#ifdef DB
nameOfTable :: Schema -> String
nameOfTable V1 = "log"
nameOfTable _ = "requests"
columnsInTable :: Schema -> Record -> [SqlValue]
columnsInTable V1 = values_v1
columnsInTable _ = values_v2
values_v1 :: Record -> [SqlValue]
values_v1 r =
let get f = toSql (f r)
in [ get service, get exerciseid, get source, get dataformat, get encoding
, get input, get output, get ipaddress, get time, get responsetime
]
values_v2 :: Record -> [SqlValue]
values_v2 r =
let get f = toSql (f r)
in [ get service, get exerciseid, get source, get script, get requestinfo
, get dataformat, get encoding, get userid, get sessionid, get taskid
, get time, get responsetime, get ipaddress, get binary, get version
, get errormsg, get serviceinfo, get ruleid, get input, get output
]
logRecordWith :: FilePath -> Schema -> LogRef -> IO ()
logRecordWith file schema logRef = do
conn <- connectSqlite3 file
setBusyTimeout conn 200
r <- getRecord logRef
end <- getCurrentTime
let diff = diffUTCTime end (time r)
insertRecord schema r {responsetime = diff} conn
disconnect conn
`catchSql` \_ ->
return ()
insertRecord :: IConnection c => Schema -> Record -> c -> IO ()
insertRecord schema r conn =
let cols = columnsInTable schema r
pars = "(" ++ intercalate "," (replicate (length cols) "?") ++ ")"
stm = "INSERT INTO " ++ nameOfTable schema ++ " VALUES " ++ pars
in run conn stm cols >> commit conn
#endif