packages feed

trace-embrace-1.2.0: trace-embrace.cabal

cabal-version:   3.0
name:            trace-embrace
version:         1.2.0
license:         BSD-3-Clause
license-file:    LICENSE
category:        Development
author:          Daniil Iaitskov <dyaitskov@gmail.com>
maintainer:      Daniil Iaitskov <dyaitskov@gmail.com>
stability:       experimental
synopsis:        Smart version of Debug.Trace module
homepage:        https://github.com/yaitskov/trace-embrace
bug-reports:     https://github.com/yaitskov/trace-embrace/issues
build-type:      Simple
description:
    
    Writing tracing code is very boring activity, especially in Haskell. The
    <https://hackage.haskell.org/package/trace-embrace trace-embrace>
    package minimizes the hassle of writing traces and maintaining them in
    codebase. Thanks to TH-driven DSL whole chunks of code containing
    function arguments could be quickly copy-pasted for tracing without
    massaging in a text editor.
    
    There are several issues with functions from standand GHC module
    <https://hackage.haskell.org/package/base/docs/Debug-Trace.html Debug.Trace>:
    
    -   no trace emitting location
    -   tracing expressions solicit to write lot of boilerplate code
    -   not possible to disable tracing without recompilation
    -   tracing is coupled with optimizaiton flag
    -   no per module granularity - all trace messages are disabled all at
        once
    
    Let’s look how trace-embrace deals with these issues.
    
    == Location
    #location#
    
    TH macros with help of GHC lib, besides the module and the line of
    exrpession emitting a trace message, find function or method name where
    the expression is.
    
    The trace message format is customizable through a config file
    (@trace-embrace.yaml@) located next to a cabal one, which is
    automatically generated if it is missing at build time. The trace line
    pattern can include location related fields such as
    <https://hackage.haskell.org/package/trace-embrace/docs/Debug-TraceEmbrace.html#v:PackageName PackageName>,
    <https://hackage.haskell.org/package/trace-embrace/docs/Debug-TraceEmbrace.html#v:FullyQualifiedModule FullyQualifiedModule>,
    <https://hackage.haskell.org/package/trace-embrace/docs/Debug-TraceEmbrace.html#v:ModuleName ModuleName>,
    <https://hackage.haskell.org/package/trace-embrace/docs/Debug-TraceEmbrace.html#v:FunctionName FunctionName>,
    and
    <https://hackage.haskell.org/package/trace-embrace/docs/Debug-TraceEmbrace.html#v:LineNumber LineNumber>.
    
    > traceMessage:
    >   traceLinePattern:
    >   - tag: FullyQualifiedModule
    >   - contents: ':'
    >     tag: Delimiter
    >   - tag: FunctionName
    >   - contents: ' '
    >     tag: Delimiter
    >   - tag: LiteralMessage
    >   - tag: Variables
    
    In a small function, a trace expression, containing only a space
    separated list of variables, is still very informative, because the rest
    is done by the library based on the expression context and
    configuration. The function name gives literal part for tracing. The
    library understands Haskell syntax very well and variables can be
    copy\/pasted in bulk “AS-IS” with comments and even pattern matching.
    
    > module Module where
    > fun x (Just y) = $(tr "/x (Just y)") $ x + y
    
    The expression and config from above following trace message is
    produced:
    
    > Module:fun ; x: 123; y: 777
    
    The argument of @tr@ consists of literal message and list of variables
    for tracing. These parts are split by right slash.
    
    == Trace control
    #trace-control#
    
    Trace control has several dimensions.
    
    === Compile\/run time
    #compilerun-time#
    
    Tracing code generation can be disabled at compile time in the
    <#configuration-file config file> or later at launch runtime via an
    environment variable. The variable name depends on configuration and by
    default it is a cabal package name (in upper case) prefixed with
    @TRACE_EMBRACE_@.
    
    > mode:
    >   # expand all tracing macros to 'id' or 'pure ()' depending on the context
    >   tag: TraceDisabled
    
    ________________________________________________________________________
    
    > mode:
    >   # use Debug.Trace.trace group of functions
    >   tag: TraceStd
    > runtimeLevelsOverrideEnvVar:
    >   # disable runtime configuration
    >   tag: Ignored
    
    ________________________________________________________________________
    
    If the environment variable is not defined then tracing is enabled. If
    the variable expands to a dash (@-@) then tracing is disabled. Otherwise
    the variable should contain a path to a file with module prefixes
    specifing trace levels. Structure of runtime config file is equal to the
    structure of @levels@ section.
    
    > levels:
    > - '!Data.Map.Strict'     # exclamation mark is warning level
    > - '|Control.Concurrent'  # bar - bottom -> is error level
    > - 'Foo.Bar'              # default is info level
    > - '-'                    # dash is trace level
    > mode:
    >   tag: TraceStd
    > runtimeLevelsOverrideEnvVar:
    >   tag: CapsPackageName   # default
    
    ________________________________________________________________________
    
    === Tracing levels
    #tracing-levels#
    
    Both Haskell modules and tracing expressions have tracing levels. If
    expression tracing level is greater or equal to thershold tracing level
    of containing module then the message is emitted. Modules by default
    have threshold trace and unprefixed literal message has tracing level
    info.
    
    > module Module where
    > yes x = $(tr "!I am emitted/") x
    > yep x $(tr "|I am emitted/") x
    > no x = $(tr "I am not emitted/") x
    > nope x = $(tr "-I am not emitted/") x
    
    ________________________________________________________________________
    
    > levels:
    > - '!Foo'
    > - '-Fo'
    > - '#Foo.Bar'
    
    Runtime tracing level for a module cannot relax compile time tracing
    level.
    
    Every cabal package uses a dedicated envirnonment variable so no
    conflict between dependencies using trace-embarce library is likely
    possible.
    
    === Trace Sink
    #trace-sink#
    
    Besides
    <https://hackage.haskell.org/package/base/docs/Debug-Trace.html#v:trace Debug.Trace.trace>
    and @\/dev\/null@,
    <https://hackage.haskell.org/package/trace-embrace/docs/Debug-TraceEmbrace.html#v:trIo trIo>,
    <https://hackage.haskell.org/package/trace-embrace/docs/Debug-TraceEmbrace.html#v:tr tr>
    <https://hackage.haskell.org/package/trace-embrace/docs/Debug-TraceEmbrace.html#v:tw tw>
    and
    <https://hackage.haskell.org/package/trace-embrace/docs/Debug-TraceEmbrace.html#v:tw-39- tw\'>
    functions can forward tracing messages to
    <https://hackage.haskell.org/package/base/docs/System-IO.html#v:hPutStrLn hPutStrLn>
    or
    <https://hackage.haskell.org/package/base/docs/Debug-Trace.html#v:traceEvent Debug.Trace.traceEvent>.
    
    > mode:
    >   tag: TraceEvent
    
    ________________________________________________________________________
    
    > mode:
    >   sink:
    >     contents: /tmp/log.log
    >     tag: FileSink
    >   tag: TraceUnsafeIo
    
    ________________________________________________________________________
    
    > mode:
    >   sink:
    >     tag: StdErrSink
    >   tag: TraceUnsafeIo
    
    ==  Configuration file
    #configuration-file#
    
    The file is generanted on build if missing.
    
    === Default compile time config file (trace-embrace.yaml)
    #default-compile-time-config-file-trace-embrace.yaml#
    
    > levels:
    > - '-'
    > mode:
    >   tag: TraceStd
    > runtimeLevelsOverrideEnvVar:
    >   tag: CapsPackageName
    > traceMessage:
    >   entrySeparator: '; '
    >   keyValueSeparator: ': '
    >   retValPrefix: ' => '
    >   traceLinePattern:
    >   - tag: FullyQualifiedModule
    >   - contents: '::'
    >     tag: Delimiter
    >   - tag: FunctionName
    >   - contents: ': '
    >     tag: Delimiter
    >   - tag: LiteralMessage
    >   - tag: Variables
    > version: 1
    
    === Sample of runtime config file
    #sample-of-runtime-config-file#
    
    Runtime config file is also in YAML format, but its structure is way
    simpler.
    
    > - '-'         # empty prefix set default thershold equal to trace level
    > - '!Foo'
    > - 'Fo'
    > - '#Foo.Bar'  # threshold higher than error - disable tracing expression
    
    Passing runtime config to @foo-bar.cabal@:
    
    > TRACE_EMBRACE_FOO_BAR=- ./foo        # disable tracing
    > TRACE_EMBRACE_FOO_BAR=rtc.yaml ./foo # override threshold levels
    
    The variable name can be specified explicitly:
    
    > runtimeLevelsOverrideEnvVar:
    >   tag: EnvironmentVariable
    >   varName: "FOO_BAR"
    
    == Examples
    #examples#
    
    === TH version of traceWith
    #th-version-of-tracewith#
    
    > {-# LANGUAGE TemplateHaskell #-}
    > module Module where
    >
    > import Debug.TraceEmbrace
    >
    > fun :: Int -> Int -> Int -> Int
    > fun x y z = $(tw "get/x y z") (x + y + z)
    
    A trace line for the snippet above would be:
    
    Module:fun: 7 get; x: 1; y: 2; z: 3 => 6
    
    === Guarded function
    #guarded-function#
    
    > {-# LANGUAGE TemplateHaskell #-}
    > module Module where
    >
    > import Debug.TraceEmbrace
    >
    > fun :: Int -> Int -> Int -> Int
    > fun $a $a_ $a | $tg = $u
    > fun a b c = a + b + c
    
    A trace line for the snippet above would be:
    
    Module:fun: 7; 1 _ 3
    
    === Trace lazy ByteString structure
    #trace-lazy-bytestring-structure#
    
    <https://hackage.haskell.org/package/bytestring/docs/Data-ByteString-Lazy.html#t:ByteString ByteString>
    @Show@ instance does not show chunks, but it can be important in parser
    debugging (attoparsec). Value of a type with not enough informative
    @Show@ instance could be wrapped into
    <https://hackage.haskell.org/package/trace-embrace/docs/Debug-TraceEmbrace.html#t:ShowTrace ShowTrace>
    and more detailed @Show@ instance should be provided.
    
    > {-# LANGUAGE OverloadedStrings #-}
    > {-# LANGUAGE TemplateHaskell #-}
    > module Module where
    >
    > import Debug.TraceEmbrace
    > import Data.ByteString.Lazy
    >
    > -- instance Show (ShowTrace ByteString) where
    > --   show ...
    >
    > fun :: ByteString -> ByteString
    > fun bs = $(tr "get/bs;bs") bs
    
    A trace line for the snippet above would be:
    
    Module:fun: 11 get; bs: “abc”; bs: [“ab”, “c”]
    
    For tracing returning values wrapped into
    <https://hackage.haskell.org/package/trace-embrace/docs/Debug-TraceEmbrace.html#t:ShowTrace ShowTrace>
    use
    <https://hackage.haskell.org/package/trace-embrace/docs/Debug-TraceEmbrace.html#v:tw-39- tw\'>.
    
    === Pattern matching syntax
    #pattern-matching-syntax#
    
    Template tracing functions support Haskell pattern syntax and comments,
    so function arguments can be quickly copy-pasted as-is:
    
    > {-# LANGUAGE TemplateHaskell #-}
    > module Module where
    >
    > import Debug.TraceEmbrace
    >
    > fun :: Maybe ([Int], Int) -> Int
    > fun v@(Just ([x], {-ignore-} _)) = $(tr "get/v@(Just ([x], {-ignore-} _))") x
    > fun _ = 0
    
    A trace line for the snippet above would be:
    
    Module:fun: 7 get; v: 1; x: 1
    
    === Unlifted vars
    #unlifted-vars#
    
    > {-# LANGUAGE TemplateHaskell #-}
    > {-# LANGUAGE MagicHash #-}
    > module Module where
    >
    > import Debug.TraceEmbrace
    > import GHC.Exts
    >
    > fun :: Int -> Int
    > fun (I# x#) = (I# ($(tr "get/x#") x#))
    
    A trace line for the snippet above would be:
    
    Module:fun: 7 get; x#: 1#

tested-with:
  GHC == 9.10.1, GHC == 9.12.2
extra-doc-files:
  changelog.md

common base
  ghc-options: -Wall
  default-language: GHC2024
  default-extensions:
    DefaultSignatures
    DuplicateRecordFields
    FunctionalDependencies
    MagicHash
    OverloadedLabels
    TemplateHaskell
    TypeFamilyDependencies
  build-depends:
    aeson < 3,
    base < 5,
    bytestring >= 0.11 && < 0.12.3,
    containers < 0.9,
    directory < 2,
    lens < 6,
    lrucache < 1.3,
    generic-lens < 3,
    refined < 1,
    template-haskell < 2.24,
    text < 3,
    yaml < 0.12

library
  import: base
  hs-source-dirs: src
  build-depends:
    cpphs < 2,
    deepseq < 1.8,
    ghc < 9.13,
    haddock-use-refs < 2,
    radix-tree < 2,
    tagged < 1,
    transformers < 1

  exposed-modules:
    Debug.TraceEmbrace
    Debug.TraceEmbrace.ByteString
    Debug.TraceEmbrace.Config
    Debug.TraceEmbrace.Config.Type
    Debug.TraceEmbrace.Config.Type.EnvVar
    Debug.TraceEmbrace.Config.Type.Level
    Debug.TraceEmbrace.Config.Type.Mode
    Debug.TraceEmbrace.Config.Type.TraceMessage
    Debug.TraceEmbrace.Config.Load
    Debug.TraceEmbrace.Config.Validation
    Debug.TraceEmbrace.FileIndex
    Debug.TraceEmbrace.Internal.Rewrap
    Debug.TraceEmbrace.Internal.TH
    Debug.TraceEmbrace.Show
    Debug.TraceEmbrace.ShowTh
    Debug.TraceEmbrace.TH

test-suite test
  import: base
  type: exitcode-stdio-1.0
  main-is: Driver.hs
  default-extensions:
    RecordWildCards
  autogen-modules:
    Paths_trace_embrace
  hs-source-dirs:
    test
  ghc-options: -Wall -rtsopts -threaded -main-is Driver
  build-depends:
    , filepath < 1.6
    , tasty
    , tasty-discover
    , tasty-hunit
    , tasty-quickcheck
    , temporary < 1.5
    , th-lock < 1
    , trace-embrace
  other-modules:
    Debug.TraceEmbrace.Test.TraceEmbrace.Config
    Debug.TraceEmbrace.Test.TraceEmbrace.DemoIndex
    Debug.TraceEmbrace.Test.TraceEmbrace.FileIndex
    Debug.TraceEmbrace.Test.TraceEmbrace.GuardedFunction
    Debug.TraceEmbrace.Test.TraceEmbrace.TH
    Debug.TraceEmbrace.Test.TraceEmbrace.TH.Event
    Debug.TraceEmbrace.Test.TraceEmbrace.TH.Format.Lifted
    Debug.TraceEmbrace.Test.TraceEmbrace.TH.Format.Unboxed
    Debug.TraceEmbrace.Test.TraceEmbrace.TH.Line
    Debug.TraceEmbrace.Test.TraceEmbrace.TH.Threshold
    Debug.TraceEmbrace.Test.TraceEmbrace.Yaml
    Demo
    Discovery
    Paths_trace_embrace

source-repository head
  type:     git
  location: https://github.com/yaitskov/trace-embrace.git