katip-effectful (empty) → 0.0.1
raw patch · 6 files changed
+684/−0 lines, 6 filesdep +QuickCheckdep +basedep +containers
Dependencies added: QuickCheck, base, containers, effectful-core, effectful-plugin, katip, katip-effectful, quickcheck-instances, tasty, tasty-golden, tasty-hunit, tasty-quickcheck, template-haskell, text, time, unix
Files
- CHANGELOG.md +5/−0
- LICENSE +30/−0
- katip-effectful.cabal +113/−0
- src/Effectful/Katip.hs +424/−0
- test/Main.hs +1/−0
- test/OutputTest.hs +111/−0
+ CHANGELOG.md view
@@ -0,0 +1,5 @@+# Revision history for placeholder++## 0.1.0.0 -- 2023-10-26++* First version. Released on an unsuspecting world.
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2023, Eldritch Cookie++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Eldritch Cookie nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ katip-effectful.cabal view
@@ -0,0 +1,113 @@+cabal-version: 3.4++-- The cabal-version field refers to the version of the .cabal specification,+-- and can be different from the cabal-install (the tool) version and the+-- Cabal (the library) version you are using. As such, the Cabal (the library)+-- version used must be equal or greater than the version stated in this field.+-- Starting from the specification version 2.2, the cabal-version field must be+-- the first thing in the cabal file.++-- Initial package description 'placeholder' generated by+-- 'cabal init'. For further documentation, see:+-- http://haskell.org/cabal/users-guide/+--+-- The name of the package.+name: katip-effectful++-- The package version.+-- See the Haskell package versioning policy (PVP) for standards+-- guiding when and how versions should be incremented.+-- https://pvp.haskell.org+version: 0.0.1++-- A short (one-line) description of the package.+synopsis: Katip integration for Effectful++-- A longer description of the package.+description:+ Provides a Katip and KatipContext instance for the Eff Monad and re-exports all of Katip's API++homepage: https://github.com/eldritch-cookie/katip-effectful++-- The license under which the package is released.+license: BSD-3-Clause++-- The file containing the license text.+license-file: LICENSE++-- The package author(s).+author: Eldritch Cookie++-- An email address to which users can send suggestions, bug reports, and patches.+maintainer: eldritch.cookie@disroot.org++-- A copyright notice.+-- copyright:+build-type: Simple+bug-reports: https://github.com/eldritch-cookie/katip-effectful/issues++-- Extra doc files to be distributed with the package, such as a CHANGELOG or a README.+extra-doc-files: CHANGELOG.md+category: library, logging, data, bsd3++-- Extra source files to be distributed with the package, such as examples, or a tutorial module.+-- extra-source-files:++common deps+ ghc-options: -Wall+ build-depends:+ , base >=4.17.2 && <5+ , effectful-core >=2.2.2 && <3+ , effectful-plugin >=1.1.0.2 && <1.2+ , katip >=0.8.7 && <1+ , template-haskell >=2.19.0.0 && <2.20+ , text >=2.0.2 && <3++ default-extensions:+ DataKinds+ DerivingStrategies+ DerivingVia+ DuplicateRecordFields+ FunctionalDependencies+ NoCUSKs+ NoFieldSelectors+ NoStarIsType+ OverloadedRecordDot+ OverloadedStrings+ QuantifiedConstraints+ ScopedTypeVariables+ TypeFamilyDependencies+ TypeOperators++ default-language: GHC2021++library+ import: deps+ exposed-modules: Effectful.Katip+ hs-source-dirs: src++test-suite katip-effectful-test+ import: deps+ type: exitcode-stdio-1.0+ hs-source-dirs: test+ main-is: Main.hs+ other-modules: OutputTest+ ghc-options: -threaded+ build-depends:+ , containers >=0.6.7 && <1+ , katip-effectful+ , QuickCheck >=2.14.3 && <3+ , quickcheck-instances >=0.3.29.1 && <0.4+ , tasty >=1.4.3 && <2+ , tasty-golden >=2.3.5 && <3+ , tasty-hunit >=0.10.1 && <1+ , tasty-quickcheck >=0.10.2 && <1+ , text >=2.0.2 && <3+ , time >=1.12.2 && <2+ , unix >=2.7.3 && <3++ build-tool-depends: tasty-discover:tasty-discover++source-repository head+ type: git+ location: https://github.com/eldritch-cookie/katip-effectful
+ src/Effectful/Katip.hs view
@@ -0,0 +1,424 @@+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE UndecidableInstances #-}+{-# OPTIONS_GHC -Wno-orphans #-}++{- |+Module: Effectful.Katip+Description: Effect to use Katip+-}+module Effectful.Katip (+ -- * Framework Types+ Namespace (..),+ Environment (..),+ Severity (..),+ renderSeverity,+ textToSeverity,+ Verbosity (..),+ ToObject (..),+ LogItem (..),+ Item (..),+ ThreadIdText (..),+ PayloadSelection (..),+ Scribe (..),+ LogEnv (..),+ SimpleLogPayload,+ sl,+ defaultScribeSettings,+ ScribeSettings,+ scribeBufferSize,+ _scribeBufferSize,++ -- ** @lens@-Compatible Lenses+ itemApp,+ itemEnv,+ itemSeverity,+ itemThread,+ itemHost,+ itemProcess,+ itemPayload,+ itemMessage,+ itemTime,+ itemNamespace,+ itemLoc,+ logEnvHost,+ logEnvPid,+ logEnvApp,+ logEnvEnv,+ logEnvTimer,+ logEnvScribes,++ -- * Effect+ KatipE,++ -- ** Running The Effect+ runKatipE,+ runKatipContextE,+ startKatipE,+ startKatipContextE,++ -- * Initializing Loggers+ registerScribe,++ -- * Dropping Scribes Temporarily+ unregisterScribe,+ clearScribes,++ -- * Finalizing Scribes At Shutdown+ closeScribe,++ -- * Logging Functions+ LogStr (..),+ logStr,+ ls,+ showLS,++ -- ** Katip Logging Functions+ getLogEnv,+ localLogEnv,+ logF,+ logMsg,+ logT,+ logLoc,+ logItem,+ logKatipItem,+ logException,++ -- ** KatipContext Logging Functions+ getKatipContext,+ localKatipContext,+ getKatipNamespace,+ localKatipNamespace,+ logFM,+ logTM,+ logLocM,+ logItemM,+ logExceptionM,+ AnyLogContext,+ LogContexts,+ liftPayload,++ -- *** Temporarily Changing Log Behaviour+ katipAddNamespace,+ katipAddContext,+ katipNoLogging,++ -- * Included Scribes+ mkHandleScribe,+ mkHandleScribeWithFormatter,+ mkFileScribe,+ ColorStrategy (..),+ ItemFormatter,+ bracketFormat,+ jsonFormat,++ -- * Tools For Implementing Scribes+ PermitFunc,+ permitAND,+ permitOR,+ permitItem,+ payloadObject,+ itemJson,+) where++import Effectful+import Effectful.Dispatch.Static+import Katip (+ -- logging++ -- logContext+ AnyLogContext,+ -- formatting+ ColorStrategy (..),+ Environment (..),+ Item (..),+ ItemFormatter,+ LogContexts,+ LogEnv (..),+ LogItem (..),+ LogStr (..),+ Namespace (..),+ PayloadSelection (..),+ -- permission+ PermitFunc,+ Scribe (..),+ ScribeSettings,+ Severity (..),+ SimpleLogPayload,+ ThreadIdText (..),+ ToObject (..),+ Verbosity (..),+ bracketFormat,+ -- removing scribe++ clearScribes,+ defaultScribeSettings,+ itemApp,+ itemEnv,+ itemHost,+ itemJson,+ itemLoc,+ itemMessage,+ itemNamespace,+ itemPayload,+ itemProcess,+ itemSeverity,+ itemThread,+ itemTime,+ jsonFormat,+ liftPayload,+ logEnvApp,+ logEnvEnv,+ logEnvHost,+ logEnvPid,+ logEnvScribes,+ logEnvTimer,+ logStr,+ ls,+ payloadObject,+ permitAND,+ permitItem,+ permitOR,+ renderSeverity,+ scribeBufferSize,+ showLS,+ sl,+ textToSeverity,+ unregisterScribe,+ _scribeBufferSize,+ )++import Data.Kind+import Data.Text (Text)+import Katip qualified as K+import Katip.Core (getLocTH)+import Language.Haskell.TH (Loc)+import Language.Haskell.TH.Lib+import System.IO (Handle)+import Unsafe.Coerce (unsafeCoerce)++-- | A Effect you can use to run logging actions. there is only one effect as we can't have duplicated instances.+type KatipE :: Effect+data KatipE m a++type instance DispatchOf KatipE = Static WithSideEffects++data instance StaticRep KatipE = MkKatipE !LogEnv !LogContexts !Namespace++-- | Run a KatipE Effect without a 'Namespace' or a 'LogContexts'. This also calls closeScribes+runKatipE :: forall es a. (IOE :> es) => LogEnv -> Eff (KatipE : es) a -> Eff es a+runKatipE l act = evalStaticRep (MkKatipE l mempty mempty) act <* liftIO (K.closeScribes l)++-- | Run a KatipE Effect with a 'Namespace' and a 'LogContexts'. this also calls closeScribes+runKatipContextE :: forall es a a1. (LogItem a, IOE :> es) => LogEnv -> a -> Namespace -> Eff (KatipE : es) a1 -> Eff es a1+runKatipContextE l pl ns act = evalStaticRep (MkKatipE l (liftPayload pl) ns) act <* liftIO (K.closeScribes l)++instance forall es. (IOE :> es, KatipE :> es) => K.Katip (Eff es) where+ getLogEnv :: (IOE :> es, KatipE :> es) => Eff es LogEnv+ getLogEnv = getLogEnv+ localLogEnv ::+ (IOE :> es, KatipE :> es) =>+ (LogEnv -> LogEnv) ->+ Eff es a ->+ Eff es a+ localLogEnv = localLogEnv+instance forall es. (IOE :> es, KatipE :> es) => K.KatipContext (Eff es) where+ getKatipContext :: Eff es LogContexts+ getKatipContext = getKatipContext+ localKatipContext ::+ (IOE :> es, KatipE :> es) =>+ (LogContexts -> LogContexts) ->+ Eff es a ->+ Eff es a+ localKatipContext = localKatipContext+ getKatipNamespace :: (IOE :> es, KatipE :> es) => Eff es Namespace+ getKatipNamespace = getKatipNamespace+ localKatipNamespace ::+ (IOE :> es, KatipE :> es) =>+ (Namespace -> Namespace) ->+ Eff es a ->+ Eff es a+ localKatipNamespace = localKatipNamespace++-- | Run a KatipE Effect without a 'Namespace' or a 'LogContexts' and creating a 'LogEnv'+startKatipE :: (IOE :> es) => Namespace -> Environment -> Eff (KatipE : es) a -> Eff es a+startKatipE ns env act = liftIO (K.initLogEnv ns env) >>= \lenv -> runKatipE lenv act++-- | Run a KatipE Effect with a 'Namespace' and a 'LogContexts' and creating a 'LogEnv'+startKatipContextE :: (IOE :> es, LogItem a) => Environment -> a -> Namespace -> Eff (KatipE : es) a1 -> Eff es a1+startKatipContextE env a ns act = liftIO (K.initLogEnv ns env) >>= \e -> runKatipContextE e a ns act++{- | Add a scribe to the list.+All future log calls will go to this scribe in addition to the others.+Writes will be buffered per the ScribeSettings to prevent slow scribes from slowing down logging.+Writes will be dropped if the buffer fills.+-}+registerScribe :: (KatipE :> es) => Text -> Scribe -> ScribeSettings -> Eff es ()+registerScribe txt scrb scrbs = do+ MkKatipE lenv lctx ns <- getStaticRep+ nlenv <- unsafeEff_ $ K.registerScribe txt scrb scrbs lenv+ putStaticRep $ MkKatipE nlenv lctx ns++-- | Finalize a scribe early. Note that it isn't necessary to call this as both 'runKatipE' and 'runKatipContextE' call 'K.closeScribes'+closeScribe :: (KatipE :> es) => Text -> Eff es ()+closeScribe name = do+ MkKatipE le lc ns <- getStaticRep+ newle <- unsafeEff_ $ K.closeScribe name le+ putStaticRep $ MkKatipE newle lc ns+-- | get the 'KatipE' 'LogEnv'+getLogEnv :: forall es. (KatipE :> es) => Eff es LogEnv+getLogEnv = do+ s <- getStaticRep @KatipE+ case s of+ MkKatipE le _ _ -> return le+-- | temporarily modify the 'LogEnv'+localLogEnv :: forall es a. (KatipE :> es) => (LogEnv -> LogEnv) -> Eff es a -> Eff es a+localLogEnv f = localStaticRep @KatipE $ \(MkKatipE le lc ns) -> MkKatipE (f le) lc ns+-- | get the 'KatipE' 'LogContexts'+getKatipContext :: (KatipE :> es) => Eff es LogContexts+getKatipContext = do+ s <- getStaticRep @KatipE+ case s of+ MkKatipE _ lc _ -> return lc+-- | temporarily modify the 'LogContexts'+localKatipContext :: forall es a. (KatipE :> es) => (LogContexts -> LogContexts) -> Eff es a -> Eff es a+localKatipContext f = localStaticRep @KatipE $ \(MkKatipE le lc ns) -> MkKatipE le (f lc) ns+getKatipNamespace :: forall es. (KatipE :> es) => Eff es Namespace+-- | get the 'KatipE' 'Namespace'+getKatipNamespace = do+ s <- getStaticRep @KatipE+ case s of+ MkKatipE _ _ ns -> return ns+-- | temporarily modify the 'Namespace'+localKatipNamespace :: forall es a. (KatipE :> es) => (Namespace -> Namespace) -> Eff es a -> Eff es a+localKatipNamespace f = localStaticRep @KatipE $ \(MkKatipE le lc ns) -> MkKatipE le lc $ f ns++-- | escape hatch for implementing your own scribes+unsafeEmbedIOE :: forall es a. (KatipE :> es) => ((KatipE :> es, IOE :> es) => Eff es a) -> Eff es a+unsafeEmbedIOE act = useDict (unsafeCoerce (MkDict @(KatipE :> es)) :: Dict (KatipE :> es, IOE :> es)) act++type Dict :: Constraint -> Type+data Dict a where+ MkDict :: (a) => Dict a++useDict :: forall a r. Dict a -> ((a) => r) -> r+useDict MkDict r = r+-- | Log with full context, but without any code location.+logF :: forall a es. (LogItem a, KatipE :> es) => a -> Namespace -> Severity -> LogStr -> Eff es ()+logF a ns sev logs = unsafeEmbedIOE $ K.logF a ns sev logs++-- | Log a message without any payload/context or code location.+logMsg :: forall es. (KatipE :> es) => Namespace -> Severity -> LogStr -> Eff es ()+logMsg ns sev logs = unsafeEmbedIOE $ K.logMsg ns sev logs++{-# INLINE logT #-}+-- | Loc-tagged logging when using template-haskell. +-- @+-- $(logT) obj mempty InfoS "Hello world"+-- @+logT :: ExpQ+logT = [|\a ns sev msg -> logItem a ns (Just $(getLocTH)) sev msg|]++{-# INLINE logLoc #-}+-- | 'Loc'-tagged logging using 'GHC.Stack.Stack' when available.+-- This function does not require template-haskell as it automatically uses implicit-callstacks when the code is compiled using GHC > 7.8.+-- Using an older version of the compiler will result in the emission of a log line without any location information, so be aware of it.+-- @+-- logLoc obj mempty InfoS "Hello world"+-- @+logLoc :: (LogItem a, KatipE :> es, HasCallStack) => a -> Namespace -> Severity -> LogStr -> Eff es ()+logLoc a ns sev logs = unsafeEmbedIOE $ K.logLoc a ns sev logs+-- | Log with everything, including a source code location.+-- This is very low level and you typically can use 'logT' in its place.+logItem :: (LogItem a, KatipE :> es) => a -> Namespace -> Maybe Loc -> Severity -> LogStr -> Eff es ()+logItem a ns loc sev logs = unsafeEmbedIOE $ K.logItem a ns loc sev logs+-- | Log an already constructed 'Item'.+-- This is the lowest level function that other log* functions use.+-- It can be useful when implementing centralised logging services.+logKatipItem :: (LogItem a, KatipE :> es) => Item a -> Eff es ()+logKatipItem item = unsafeEmbedIOE $ K.logKatipItem item+-- | Perform an action while logging any exceptions that may occur.+-- @+-- >>> > logException () mempty ErrorS (error "foo")+-- @+logException :: (LogItem a, KatipE :> es) => a -> Namespace -> Severity -> Eff es b -> Eff es b+logException a ns sev act = unsafeEmbedIOE $ K.logException a ns sev act+-- | Log with full context, but without any code location. +-- Automatically supplies payload and namespace.+logFM :: (KatipE :> es) => Severity -> LogStr -> Eff es ()+logFM sev logs = unsafeEmbedIOE $ K.logFM sev logs++{-# INLINE logTM #-}+-- | 'Loc'-tagged logging when using template-haskell.+-- Automatically supplies payload and namespace.+-- @+-- $(logTM) InfoS "Hello world"+-- @+logTM :: ExpQ+logTM = [|logItemM (Just $(getLocTH))|]+{-#INLINE logLocM #-}+-- | 'Loc'-tagged logging when using 'GHC.Stack.getCallStack' implicit-callstacks.+-- Automatically supplies payload and namespace.+-- Same consideration as 'logLoc' applies+-- By default, location will be logged from the module that invokes logLocM.+-- If you want to use logLocM in a helper, +-- wrap the entire helper in withFrozenCallStack to retain the callsite of the helper in the logs.+-- This function does not require template-haskell. +-- @+-- logLocM InfoS "Hello world"+-- @+logLocM :: (KatipE :> es, HasCallStack) => Severity -> LogStr -> Eff es ()+logLocM sev logs = unsafeEmbedIOE $ K.logLocM sev logs+-- | Log with everything, including a source code location. +-- This is very low level and you typically can use 'logTM' in its place.+-- Automatically supplies payload and namespace.+logItemM :: (KatipE :> es, HasCallStack) => Maybe Loc -> Severity -> LogStr -> Eff es ()+logItemM loc sev logs = unsafeEmbedIOE $ K.logItemM loc sev logs+-- | Perform an action while logging any exceptions that may occur.+-- @+-- >>> > error "foo" `logExceptionM` ErrorS+-- @+logExceptionM :: (KatipE :> es) => Eff es a -> Severity -> Eff es a+logExceptionM act sev = unsafeEmbedIOE $ K.logExceptionM act sev++-- | Append a namespace segment to the current namespace for the given monadic action,+-- then restore the previous state afterwards.+katipAddNamespace :: (KatipE :> es) => Namespace -> Eff es a -> Eff es a+katipAddNamespace ns = localKatipNamespace (<> ns)+-- | Append some context to the current context for the given monadic action,+-- then restore the previous state afterwards. +-- Important note: be careful using this in a loop. +-- If you're using something like forever or replicateM_ that does explicit sharing to avoid a memory leak,+-- youll be fine as it will *sequence* calls to katipAddNamespace,+-- so each loop will get the same context added.+-- If you instead roll your own recursion and you're recursing in the action you provide,+-- you'll instead accumulate tons of redundant contexts and even if they all merge on log,+-- they are stored in a sequence and will leak memory.+katipAddContext :: (KatipE :> es, LogItem i) => i -> Eff es a -> Eff es a+katipAddContext ctx = localKatipContext (<> liftPayload ctx)+-- | Disable all scribes for the given monadic action,+-- then restore them afterwards.+katipNoLogging :: (KatipE :> es) => Eff es a -> Eff es a+katipNoLogging = localLogEnv $ \lenv -> lenv{_logEnvScribes = mempty}++-- | Logs to a file handle such as stdout, stderr, or a file.+-- Contexts and other information will be flattened out into bracketed fields. +-- For example:]+-- @+-- [2016-05-11 21:01:15][MyApp][Info][myhost.example.com][PID 1724][ThreadId 1154][main:Helpers.Logging Helpers/Logging.hs:32:7] Started+-- [2016-05-11 21:01:15][MyApp.confrabulation][Debug][myhost.example.com][PID 1724][ThreadId 1154][confrab_factor:42.0][main:Helpers.Logging Helpers/Logging.hs:41:9] Confrabulating widgets, with extra namespace and context+-- [2016-05-11 21:01:15][MyApp][Info][myhost.example.com][PID 1724][ThreadId 1154][main:Helpers.Logging Helpers/Logging.hs:43:7] Namespace and context are back to normal+-- @+-- Returns the newly-created 'Scribe'. The finalizer flushes the handle. Handle mode is set to 'System.IO.LineBuffering' automatically.+mkHandleScribe :: forall es. (KatipE :> es) => ColorStrategy -> Handle -> PermitFunc -> Verbosity -> Eff es Scribe+mkHandleScribe cs h pf v = unsafeEmbedIOE $ liftIO $ K.mkHandleScribe cs h pf v+-- | Logs to a file handle such as stdout, stderr, or a file.+-- Takes a custom 'ItemFormatter' that can be used to format 'Item' as needed.+-- Returns the newly-created 'Scribe'.+-- The finalizer flushes the handle.+-- Handle mode is set to 'System.IO.LineBuffering' automatically.+mkHandleScribeWithFormatter :: forall es. (KatipE :> es) => (forall a. (LogItem a) => ItemFormatter a) -> ColorStrategy -> Handle -> PermitFunc -> Verbosity -> Eff es Scribe+mkHandleScribeWithFormatter ifa cs h pf v = unsafeEmbedIOE $ liftIO $ K.mkHandleScribeWithFormatter ifa cs h pf v+-- | A specialization of 'mkHandleScribe' that takes a 'FilePath' instead of a 'Handle'. It is responsible for opening the file in 'System.IO.AppendMode' and will close the file handle on closeScribe/closeScribes. Does not do log coloring. Sets handle to 'System.IO.LineBuffering' mode.+mkFileScribe :: forall es. (KatipE :> es) => FilePath -> PermitFunc -> Verbosity -> Eff es Scribe+mkFileScribe fp pf v = unsafeEmbedIOE $ liftIO $ K.mkFileScribe fp pf v
+ test/Main.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF tasty-discover #-}
+ test/OutputTest.hs view
@@ -0,0 +1,111 @@+{-# OPTIONS_GHC -Wno-orphans #-}++module OutputTest where++import Control.Monad (replicateM)+import Data.Text.Lazy.Builder (Builder, fromText)+import Effectful (runEff)+import Effectful.Katip+import System.IO (Handle, IOMode (..), hGetContents', openFile)+import Test.QuickCheck.Instances ()+import Test.Tasty.QuickCheck++import Data.Text (Text)+import Katip (closeScribes, runKatipContextT)+import Katip qualified as K++getInteger :: Int -> Gen Int+getInteger s = chooseInt (0, s)++instance Arbitrary Namespace where+ arbitrary = do+ num <- sized getInteger+ Namespace <$> replicateM num arbitrary+ shrink = genericShrink+instance Arbitrary Environment where+ arbitrary = Environment <$> arbitrary+ shrink = genericShrink+instance Arbitrary Severity where+ arbitrary = chooseEnum (minBound, maxBound)+ shrink = genericShrink+instance Arbitrary Verbosity where+ arbitrary = chooseEnum (minBound, maxBound)+ shrink = genericShrink+instance Arbitrary LogStr where+ arbitrary = LogStr <$> arbitrary+ shrink = genericShrink+instance Arbitrary Builder where+ arbitrary = fromText <$> arbitrary+instance Arbitrary SimpleLogPayload where+ arbitrary = sl @Text <$> arbitrary <*> arbitrary++instance Show SimpleLogPayload where+ show = show . toObject++prop_EqualOutputLFM :: Severity -> Namespace -> LogStr -> Property+prop_EqualOutputLFM severity ns str = ioProperty $ do+ h1 <- openFile "scribeKCT.log" ReadWriteMode+ le1 <- logEnvWithScribe h1+ runKatipContextT le1 () ns $ K.logFM severity str+ _ <- closeScribes le1+ s1 <- hGetContents' h1+ h2 <- openFile "scribeEff.log" ReadWriteMode+ le2 <- logEnvWithScribe h2+ runEff . runKatipContextE le2 () ns $ logFM severity str+ _ <- closeScribes le2+ s2 <- hGetContents' h2+ cleanup+ return $ s1 == s2+prop_EqualOutputLF :: SimpleLogPayload -> Namespace -> Severity -> LogStr -> Property+prop_EqualOutputLF slp ns severity str = ioProperty $ do+ h1 <- openFile "scribeKCT.log" ReadWriteMode+ le1 <- logEnvWithScribe h1+ runKatipContextT le1 () ns $ K.logF slp ns severity str+ _ <- closeScribes le1+ s1 <- hGetContents' h1+ h2 <- openFile "scribeEff.log" ReadWriteMode+ le2 <- logEnvWithScribe h2+ runEff . runKatipContextE le2 () ns $ logF slp ns severity str+ _ <- closeScribes le2+ s2 <- hGetContents' h2+ cleanup+ return $ s1 == s2++prop_EqualOutputLMsg :: Namespace -> Severity -> LogStr -> Property+prop_EqualOutputLMsg ns severity str = ioProperty $ do+ h1 <- openFile "scribeKCT.log" ReadWriteMode+ le1 <- logEnvWithScribe h1+ runKatipContextT le1 () ns $ K.logMsg ns severity str+ _ <- closeScribes le1+ s1 <- hGetContents' h1+ h2 <- openFile "scribeEff.log" ReadWriteMode+ le2 <- logEnvWithScribe h2+ runEff . runKatipContextE le2 () ns $ logMsg ns severity str+ _ <- closeScribes le2+ s2 <- hGetContents' h2+ cleanup+ return $ s1 == s2+prop_EqualOutputLLoc :: SimpleLogPayload -> Namespace -> Severity -> LogStr -> Property+prop_EqualOutputLLoc slp ns severity str = ioProperty $ do+ h1 <- openFile "scribeKCT.log" ReadWriteMode+ le1 <- logEnvWithScribe h1+ runKatipContextT le1 () ns $ K.logLoc slp ns severity str+ _ <- closeScribes le1+ s1 <- hGetContents' h1+ h2 <- openFile "scribeEff.log" ReadWriteMode+ le2 <- logEnvWithScribe h2+ runEff . runKatipContextE le2 () ns $ logLoc slp ns severity str+ _ <- closeScribes le2+ s2 <- hGetContents' h2+ cleanup+ return $ s1 == s2+cleanup :: IO ()+cleanup = do+ writeFile "scribeKCT.log" []+ writeFile "scribeEff.log" []++logEnvWithScribe :: Handle -> IO LogEnv+logEnvWithScribe fp = do+ initialLE <- K.initLogEnv "testing" "testing"+ s <- K.mkHandleScribe ColorIfTerminal fp (\_ -> pure True) V3+ K.registerScribe "test" s defaultScribeSettings initialLE