Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tracing-effectful package #11

Merged
merged 7 commits into from
Apr 4, 2024

Conversation

Raveline
Copy link

@Raveline Raveline commented Mar 27, 2024

This is a proposed solution to implement CORE-6093.

This has been tested on a dummy client like this:

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeOperators #-}
module Main where

import Prelude hiding (putStrLn)
import Effectful ((:>), Eff, runEff)
import Effectful.Concurrent (threadDelay, Concurrent, runConcurrent)
import Effectful.Console.ByteString (putStrLn, Console, runConsole)
import Effectful.Tracing (Trace, runZipkinTracing, withZipkin)
import Control.Monad (forever)
import Control.Monad.Base (MonadBase, liftBase)
import Monitor.Tracing
import Control.Monad.Trace.Class (rootSpanWith)
import qualified Monitor.Tracing.Zipkin as ZPK 

main :: IO ()
main = runEff . runConsole . runConcurrent . withZipkin settings $ runZipkinTracing runner

settings :: ZPK.Settings
settings = 
  ZPK.defaultSettings
    { ZPK.settingsEndpoint =
      Just $
        ZPK.defaultEndpoint
          { ZPK.endpointService = Just "test"
          }
          , ZPK.settingsPublishPeriod = 1 -- second
    }   

runner :: (Trace :> es, Concurrent :> es, Console :> es) => Eff es ()
runner = rootSpanWith (ZPK.addInheritedTag "id" "1234") alwaysSampled "example" $
  forever $ do
    childSpan "wakeup" wakeup
    threadDelay 5000000

wakeup :: (Trace :> es, Concurrent :> es, Console :> es) => Eff es ()
wakeup = do
  putStrLn $ "Woke up"
  threadDelay 3000000
  childSpan "subcall" doubleWakeup

doubleWakeup :: (Trace :> es, Concurrent :> es, Console :> es) => Eff es ()
doubleWakeup = putStrLn $ "Double wake up"

I'd reaaaaaally wish to find a less ugly solution to the implementation of traceWith.

I'll detail a bit the issue. We want to factorize code between the MonadTrace historic implementation and the Trace effect, which is why I put it in a separate, callable function. It has to be a higher-order-function, so that our MonadTrace can give the local function from its MonadReader, and so that our Trace effect can use the equivalent localStaticRep.

However, we can't have the whole traceWith function operate in MonadBaseControl IO m, MonadIO m world, because if we do, the whole call has to be wrapped in unsafeEff_... and then we can't use localStaticRep because we're not in Eff anymore.

I tried many things, and the last one I could find was to use MonadMask: since Eff has an instance, we can stay in Eff in traceWith. We "just" have to provide a lifting function: liftBase in our MonadTrace and the infamous unsafeEff_ in our Trace effect. There might be a better way to do it (there most likely is), but I'm not smart enough (or hopefully, not experienced enough with Effectful) to find it.

(Also, splitting the traceWith function in two part, to get the part that actually produces the spn on the one hand and the conditional branch on the other, would perhaps be a good improvement, but it's outside the scope of this PR).

@Raveline Raveline force-pushed the dev-raveline-core-6093-add-effectful-package branch 2 times, most recently from 3d98bc1 to def458b Compare April 2, 2024 10:53
@Raveline Raveline force-pushed the dev-raveline-core-6093-add-effectful-package branch from def458b to 0e505ba Compare April 2, 2024 12:32
@Raveline Raveline marked this pull request as ready for review April 2, 2024 12:35
@Raveline Raveline requested a review from arybczak April 2, 2024 12:35
@arybczak
Copy link

arybczak commented Apr 2, 2024

Thanks 👍 I pushed a commit with a PoC that monomorphizes traceWith to work in IO. Don't worry, these unlifts can be a bit tricky if you're not familar with them.

Can you clean it up? :)

@Raveline
Copy link
Author

Raveline commented Apr 2, 2024

Oooh, that's very cool. Will gladly integrate and cleanup, thanks !

-> Eff es a
runTracing actn = runTracing' actn . pure

runTracing'
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
runTracing'
runTracingMaybe

Do you remember our conversation about functions with ticks at the end? ;)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did, but I was just mimicking what was done in Control.Monad.Trace !

@@ -0,0 +1,114 @@
cabal-version: 3.0
-- The cabal-version field refers to the version of the .cabal specification,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can remove these cabal comments, they're quite noisy.

-- other-extensions:

-- Other library packages from which modules are imported.
build-depends: base ^>=4.17.2.1
Copy link

@arybczak arybczak Apr 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes it work with GHC 9.4 only. Also, please put upper bound for base to be 5 so we don't have to bump it when updating GHC.


-- Extra doc files to be distributed with the package, such as a CHANGELOG or a README.
extra-doc-files: CHANGELOG.md

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add the tested-with field (as is in tracing.cabal) and regenerate CI with haskell-ci (haskell-ci regenerate in the repo root will do the job).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, that "Done" was a bit optimistic ! Took me quite a while to get the CI to build everything - oddly enough, some of the build plans seemed to be different between the CI and my own machine, though I switched to the same GHC and cabal version as the ones that failed on the CI. But this is now fixed.

@Raveline Raveline force-pushed the dev-raveline-core-6093-add-effectful-package branch 2 times, most recently from 48ab66a to 82a2e34 Compare April 3, 2024 15:27
@Raveline Raveline force-pushed the dev-raveline-core-6093-add-effectful-package branch from 82a2e34 to 2ee6557 Compare April 3, 2024 15:34
instance (MonadBaseControl IO m, MonadIO m) => MonadTrace (TraceT m) where
traceWith :: Builder -> Scope -> (Scope -> IO a) -> IO a
traceWith bldr parentScope f = do
let mbParentSpn = scopeSpan parentScope
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aren't all these liftIO calls redundant?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Of course they are 🤦‍♂️ Fixed, as well as the liftBases in addSpanEntryWith.

let mbTV = scopes >>= scopeTags
for_ mbTV $ \tv -> liftBase . atomically . modifyTVar' tv $ Map.insert key val
addSpanEntryWith scopes key (LogValue val mbTime) = do
let mbTV = scopes >>= scopeLogs
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above, but liftBase.

@Raveline Raveline force-pushed the dev-raveline-core-6093-add-effectful-package branch 10 times, most recently from ed9f65b to 4c35bd6 Compare April 4, 2024 08:37
Also apply cabal-fmt to our cabal files while we're at it
@Raveline Raveline force-pushed the dev-raveline-core-6093-add-effectful-package branch from 4c35bd6 to 98c2710 Compare April 4, 2024 08:41
@Raveline Raveline merged commit 78ce72a into master Apr 4, 2024
6 checks passed
@Raveline Raveline deleted the dev-raveline-core-6093-add-effectful-package branch April 4, 2024 08:48
Copy link

@arybczak arybczak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure why you merged this since I wasn't finished with the review.

@@ -0,0 +1,3 @@
packages:
.
../
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is redundant, the cabal project file should be in the root only 🤔

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without this, tracing-effectful tries to fetch the hackage version of tracking.


tested-with: GHC == { 8.10.7, 9.0.2, 9.2.8, 9.4.8, 9.6.4, 9.8.2 }
tested-with:
GHC ==8.10.7 || ==9.0.2 || ==9.2.8 || ==9.4.8 || ==9.6.4 || ==9.8.2
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why was this changed? Did you use cabal-fmt or something?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I used cabal-fmt. Do you prefer the { } version ?

, aeson <=2.3
, base <5
, base16-bytestring <=1.1
, bytestring <=0.13
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure why you added all these upper bounds here.

Upper bounds should be done with < on major versions, which is really not the case here. Also, most of the times you can get away without adding any because they tend to be extremely annoying to update when bumping deps en masse.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1°) I added them because the CI steps generated by haskell-ci requested them. I had originally blindly followed the CI, which suggested using cabal gen-bounds, but of course this broke when testing against various GHC versions.
2°) I agreed they are extremely annoying, because I had to spend quite a while fixing them. I can remove the bound check on the CI if need be (and remove bounds in the cabal file right after). Would this be a good solution ?

@Raveline
Copy link
Author

Raveline commented Apr 4, 2024

Not sure why you merged this since I wasn't finished with the review.

I thought the PR was approved, I completely forgot there is no "approval required" before merging on this repo.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants