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

[http-client] using hooks #144

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

kakkun61
Copy link
Collaborator

@kakkun61 kakkun61 commented Aug 9, 2024

What

  • to use managerModifyRequest and managerModifyResponse to implement this instrumentation
    • instead of wrapping original http-client's Manager

Why

  • a lot of wrapping and unwrapping are necessary because the wrapper type is different from the original one of course when using http-client instrumentation

How

  • ManagerSetting provides hooks for requests and responses
  • to call createSpan in a hook for a request and to call endSpan in a hook for a response
  • ThreadLocalStorage is used to transport span data created in a hook for a request to a hook for a response

Note

Cachix @sandydoo seems to implement a similar instrumentation.

https://github.com/cachix/hs-opentelemetry-instrumentation-http-client/

@kakkun61 kakkun61 requested review from iand675 and evanlauer1 August 9, 2024 06:30
@kakkun61 kakkun61 self-assigned this Aug 9, 2024
@kakkun61 kakkun61 changed the title Use managerModifyRequest and managerModifyResponse to implement this instrumentation http-client instrumentation using hooks Aug 9, 2024
@kakkun61 kakkun61 marked this pull request as draft December 6, 2024 09:18
@kakkun61 kakkun61 force-pushed the http-client-modifier branch from a192ebe to b0bfa6b Compare December 24, 2024 08:57
@kakkun61 kakkun61 marked this pull request as ready for review December 24, 2024 09:11
@kakkun61
Copy link
Collaborator Author

This is ready to be review!

@kakkun61 kakkun61 changed the title http-client instrumentation using hooks [http-client] using hooks Dec 24, 2024
import qualified OpenTelemetry.Trace.Core as Otel


appendModifierToSettings :: (MonadIO m, HasCallStack) => Otel.TracerProvider -> Orig.ManagerSettings -> m Orig.ManagerSettings
Copy link
Collaborator

Choose a reason for hiding this comment

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

haddock?



appendModifierToSettings :: (MonadIO m, HasCallStack) => Otel.TracerProvider -> Orig.ManagerSettings -> m Orig.ManagerSettings
appendModifierToSettings tracerProvider settings = withFrozenCallStack $ liftIO $ do
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't think freezing the call stack helps here?


requestModifier :: HasCallStack => Otel.Tracer -> ThreadLocalStorage -> Orig.Request -> IO Orig.Request
requestModifier tracer tls request = do
spanRef <- TLS.getTLS tls
Copy link
Collaborator

Choose a reason for hiding this comment

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

So, there's some kind of important set of assumptions here about how requests and responses are handled. It looks to me like you're assuming that a response to a request will always be exactly the next response processed by the thread that sent the response. Is that true? It could do with some explanation as to why we think it is true if so!

(That assumption is important because it means we can use a thread-local storage to effectively identify a request: if a response comes back and there is something in the storage, it must be for the request that we sent.)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That point is certainly very important. I had made that assumption unconsciously. I will check it out. Maybe I need to think of another way.

case maybeSpan of
Just span_ -> do
Otel.addAttributes span_ $ makeResponseAttributes response
Otel.endSpan span_ Nothing
Copy link
Collaborator

Choose a reason for hiding this comment

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

What happens if there is no response? or if an exception is thrown? I think this version is less safe in the presence of failures than the old one. The old one used the inSpan functions, so should never leave us with unterminated spans, but I think this one can do?

makeThreadLocalStorage = TLS.mkTLS $ newIORef Nothing


makeRequestAttributes :: Orig.Request -> Otel.AttributeMap
Copy link
Collaborator

Choose a reason for hiding this comment

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

These changes look useful, though!

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