graph-trace-0.1.0.2: src/Graph/Trace/Internal/Trace.hs
{-# LANGUAGE MonoLocalBinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE CPP #-}
# if MIN_VERSION_ghc(9,0,0)
{-# LANGUAGE LinearTypes #-}
# endif
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ImplicitParams #-}
{-# LANGUAGE BangPatterns #-}
module Graph.Trace.Internal.Trace
( trace
, traceId
, traceShow
, traceShowId
, traceM
, traceShowM
, entry
, omitTraces
) where
import Control.Concurrent.MVar
import Control.Monad
import qualified Data.ByteString.Builder as BSB
import qualified Data.ByteString.Lazy.Char8 as BSL8
import GHC.Exts
import GHC.Stack (callStack, popCallStack)
import System.Environment (getProgName, lookupEnv)
import System.IO
import System.IO.Unsafe (unsafePerformIO)
import Graph.Trace.Internal.RuntimeRep (Lev)
import Graph.Trace.Internal.Types
mkTraceEvent :: DebugIP => String -> Maybe Event
mkTraceEvent !msg = do
ip <- ?_debug_ip
guard . not $ omitTraces (propagation ip)
pure $
TraceEvent
(currentTag ip)
(BSL8.pack msg)
(callStackToCallSite . popCallStack $ popCallStack callStack)
writeEventToLog :: Event -> IO ()
writeEventToLog event = seq fileLock $
withMVar fileLock $ \h ->
BSB.hPutBuilder h . (<> "\n") $ eventToLogStr event
unsafeWriteTrace :: DebugIP => String -> a -> a
-- forcing msg is required here since the file MVar could be entagled with it
unsafeWriteTrace !msg thing =
unsafePerformIO $ do
case mkTraceEvent msg of
Nothing -> pure ()
Just event -> writeEventToLog event
pure thing
{-# NOINLINE unsafeWriteTrace #-}
trace :: DebugIP => String -> a -> a
trace = unsafeWriteTrace
{-# NOINLINE trace #-}
traceId :: DebugIP => String -> String
traceId = join unsafeWriteTrace
traceShow :: (DebugIP, Show a) => a -> b -> b
traceShow = unsafeWriteTrace . show
traceShowId :: (DebugIP, Show a) => a -> a
traceShowId = join (unsafeWriteTrace . show)
traceM :: (Applicative f, DebugIP) => String -> f ()
traceM x = unsafeWriteTrace x $ pure ()
traceShowM :: (Applicative f, Show a, DebugIP) => a -> f ()
traceShowM x = unsafeWriteTrace (show x) $ pure ()
-- | Serializes access to the debug log file
fileLock :: MVar Handle
fileLock = unsafePerformIO $ do
-- check for env variable with file name
mOverrideFileName <- lookupEnv "GRAPH_TRACE_FILENAME"
logFilePath <-
case mOverrideFileName of
Nothing -> do
progName <- getProgName
pure $ progName <> ".trace"
Just n -> pure n
h <- openFile logFilePath AppendMode
hSetBuffering h NoBuffering
newMVar h
{-# NOINLINE fileLock #-}
-- | Emits a message to the log signaling a function invocation
entry
#if MIN_VERSION_ghc(9,0,0)
:: forall rep m (a :: TYPE rep). DebugIP
=> Lev a %m -> a
#else
:: forall rep (a :: TYPE rep). DebugIP
=> Lev a -> a
#endif
entry x =
case ?_debug_ip of
Nothing -> x
Just ip
| omitTraces (propagation ip) -> x
| otherwise ->
let !() = unsafePerformIO $ do
let ev = EntryEvent
(currentTag ip)
(previousTag ip)
(definitionSite ip)
-- need to call popCallStack here to get actual call site
(callStackToCallSite $ popCallStack callStack)
writeEventToLog ev
in x
{-# NOINLINE entry #-}
omitTraces :: Propagation -> Bool
omitTraces Mute = True
omitTraces Inert = True
omitTraces _ = False
-- TODO allow to apply a function to mute some specific thing
-- mute :: DebugIP => a -> a