packages feed

fb-util-0.1.0.1: Control/Trace/VLog.hs

{-
  Copyright (c) Meta Platforms, Inc. and affiliates.
  All rights reserved.

  This source code is licensed under the BSD-style license found in the
  LICENSE file in the root directory of this source tree.
-}

{-# LANGUAGE ViewPatterns, CPP #-}
module Control.Trace.VLog (
  vlogTracer,
  TraceWithPriority (..),
  vlogTracerWithPriority,
  vlogShowTracer,
  vlogTextTracer,
) where

import Control.Monad (when)
import Control.Monad.Catch (
  ExitCase (
    ExitCaseAbort,
    ExitCaseException,
    ExitCaseSuccess
  ),
 )
import Control.Monad.IO.Class
import Control.Trace.Core
import Data.Some
import Data.Text (Text)
import GHC.Stack
import TextShow (
  TextShow,
  showt,
 )
import qualified Util.Log.String as String
import Util.Log.Text

vlogShowTracer :: TextShow a => (a -> Int) -> Tracer a
vlogShowTracer =
  vlogTracer
    (\(showt -> x) -> ("BEGIN " <> x, \e -> "END" <> renderExitCase e <> x))
    showt

renderExitCase :: Some ExitCase -> Text
renderExitCase (Some ExitCaseAbort {}) = "(aborted) "
renderExitCase (Some (ExitCaseException e)) = "(" <> showt e <> ") "
renderExitCase (Some ExitCaseSuccess {}) = " "

vlogTextTracer :: Int -> Tracer Text
vlogTextTracer p =
  vlogTracer
    (\x -> ("BEGIN " <> x, \e -> "END" <> renderExitCase e <> x))
    id
    (const p)

data TraceWithPriority
  = Skip
  | T !Int !Text
  | S !Int !String

vlogTracerWithPriority :: Tracer TraceWithPriority
vlogTracerWithPriority = Tracer {..}
  where
    logMsg_ :: (HasCallStack, MonadIO m) => TraceWithPriority -> m ()
    logMsg_ Skip = pure ()
    logMsg_ x = withFrozenCallStack $ case x of
      T p t -> vlog p t
      S p s -> String.vlog p s
#if __GLASGOW_HASKELL__ < 902
      Skip -> error "unreachable"
#endif

    traceMsg_ msg =
      case msg of
        T p t -> do
            vlog p ("BEGIN " <> t)
            return ( \res -> case res of
                ExitCaseSuccess {} -> vlog p ("END " <> t)
                ExitCaseAbort {} -> vlog p ("ABORTED " <> t)
                ExitCaseException e -> vlog p ("FAILED " <> t <> ": " <> showt e)
              )
        S p t -> do
            String.vlog p ("BEGIN " <> t)
            return ( \res -> case res of
                ExitCaseSuccess {} -> String.vlog p ("END " <> t)
                ExitCaseAbort {} -> String.vlog p ("ABORTED " <> t)
                ExitCaseException e ->
                  String.vlog p ("FAILED " <> t <> ": " <> show e)
              )
        Skip -> return $ const $ return ()

vlogTracer ::
  forall a.
  -- | render BEGIN and END messages
  (a -> (Text, Some ExitCase -> Text)) ->
  -- | render LOG message
  (a -> Text) ->
  -- | Priority (use -1 to skip)
  (a -> Int) ->
  Tracer a
vlogTracer beginend log_ prio = Tracer {..}
  where
    logMsg_ :: a -> IO ()
    logMsg_ msg =
        let p = prio msg
         in when (p >= 0) $ vlog p $ log_ msg

    traceMsg_ :: a -> IO (ExitCase b -> IO ())
    traceMsg_ msg = do
      let p = prio msg
          (b, e) = beginend msg
      if p >= 0
        then vlog p b >> return (\res -> vlog p (e $ mkSome res))
        else return $ const $ return ()