diff options
author | Joey Hess <joeyh@joeyh.name> | 2017-04-18 13:54:16 -0400 |
---|---|---|
committer | Joey Hess <joeyh@joeyh.name> | 2017-04-18 14:04:42 -0400 |
commit | ccdb5a3c6a28cc6745d337bdb67e62d70216ef7e (patch) | |
tree | 1a63fac950bf66bba59812d8e2f6b66aa1a05c63 /Log.hs | |
parent | 88a9ce01d153ad609aa02084de0a93448c29cba4 (diff) | |
download | debug-me-ccdb5a3c6a28cc6745d337bdb67e62d70216ef7e.tar.gz |
refactor out Log
Diffstat (limited to 'Log.hs')
-rw-r--r-- | Log.hs | 79 |
1 files changed, 79 insertions, 0 deletions
@@ -0,0 +1,79 @@ +{-# LANGUAGE DeriveGeneric #-} + +module Log where + +import Types +import Hash + +import GHC.Generics (Generic) +import Data.Aeson +import Data.Char +import Data.Either +import Data.Time.Clock.POSIX +import qualified Data.ByteString.Lazy as L +import System.IO + +-- | A log of Activity both Entered and Seen, which can be recorded to +-- prove what happened in a debug-me session. +-- +-- Note that the time stamp is included to allow replaying logs, but +-- it's not part of the provable session. +-- +-- Note that changing this in ways that change the JSON serialization +-- changes debug-me's log file format. +data ActivityLog = ActivityLog + { loggedActivity :: SomeActivity + , loggedHash :: Hash + , loggedTimestamp :: Timestamp + } + deriving (Show, Generic) + +instance ToJSON (ActivityLog) +instance FromJSON (ActivityLog) + +data SomeActivity + = ActivitySeen (Activity Seen) + | ActivityEntered (Activity Entered) + deriving (Show, Generic) + +instance ToJSON SomeActivity where + toJSON = genericToJSON sumOptions + toEncoding = genericToEncoding sumOptions +instance FromJSON SomeActivity where + parseJSON = genericParseJSON sumOptions + +mkActivityLog :: SomeActivity -> POSIXTime -> ActivityLog +mkActivityLog a now = ActivityLog + { loggedActivity = a + , loggedHash = case a of + ActivitySeen s -> hash s + ActivityEntered e -> hash e + , loggedTimestamp = now + } + +type Timestamp = POSIXTime + +type Logger = SomeActivity -> IO () + +withLogger :: FilePath -> (Logger -> IO a) -> IO a +withLogger logfile a = withFile logfile WriteMode (a . mkLogger) + +mkLogger :: Handle -> Logger +mkLogger h a = do + l <- mkActivityLog a <$> getPOSIXTime + L.hPut h (encode l) + hPutStr h "\n" + hFlush h + +parseLog :: L.ByteString -> [Either String ActivityLog] +parseLog = map eitherDecode' + . filter (not . L.null) + . L.split (fromIntegral (ord '\n')) + +-- | Throws exception on unparsable log. +loadLog :: FilePath -> IO [ActivityLog] +loadLog f = do + parsed <- parseLog <$> L.readFile f + case lefts parsed of + [] -> return (rights parsed) + errs -> error $ unlines errs |