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

Update HTTP Semantic Conventions to Stable Versions #118

Merged
merged 43 commits into from
May 28, 2024
Merged
Show file tree
Hide file tree
Changes from 42 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
5330fa4
Work in Progress: Request and Response Attributes updated to match th…
evanlauer1 May 14, 2024
5e1bab4
Added Stable attributes to instrumentResponse
evanlauer1 May 15, 2024
178e3b8
Refactored to fit new httpTracerProvider return type
evanlauer1 May 16, 2024
b4bff14
Fixed missing "do" notation and removed unnecessary imports
evanlauer1 May 16, 2024
b18b623
Fix typo and update comment
evanlauer1 May 17, 2024
e5af40c
Found unchanged http name
evanlauer1 May 17, 2024
9309250
Found outdated http attribute names
evanlauer1 May 17, 2024
64c1331
Fixed cradle so HLS works
evanlauer1 May 20, 2024
39ff844
Added Settings File for application-wide environment variable configu…
evanlauer1 May 20, 2024
f9e987c
Moved semConvStabilityOptIn and related types to OpenTelemetry.Settings
evanlauer1 May 20, 2024
3e853aa
Changed code using HttpTracerProvider to work with changed return type
evanlauer1 May 20, 2024
a9520aa
Fixed exports
evanlauer1 May 20, 2024
77538ca
Replaced general Settings.hs with specific SemConvStability.hs
evanlauer1 May 20, 2024
17d2e22
Removed comment because the problem it referenced is solved
evanlauer1 May 20, 2024
32c68be
removed unused language pragma
evanlauer1 May 20, 2024
b28866f
fixed imports
evanlauer1 May 20, 2024
7d92f10
Replaced outdated http conventions based on the value of OTEL_SEMCONV…
evanlauer1 May 21, 2024
5200465
changed line about generation version
evanlauer1 May 21, 2024
1a1aba1
added unordered-containers to dependencies
evanlauer1 May 21, 2024
29bf4d7
Moved semConvStabilityOptIn so that it is in scope
evanlauer1 May 21, 2024
c2ea53f
Updated dependencies for testing
evanlauer1 May 21, 2024
6b1baaa
WIP testing for http-client
evanlauer1 May 21, 2024
524faea
Generated by newer version
evanlauer1 May 21, 2024
6da0bef
Removed WIP testing
evanlauer1 May 22, 2024
8a11e96
Updated getSemConvStabilityOptIn so it parses environment variable a…
evanlauer1 May 22, 2024
8b6f9f3
derives Show and Eq for testing purposes
evanlauer1 May 22, 2024
e76b118
Added tests for SemConvStabilityOptIn to make sure environment variab…
evanlauer1 May 22, 2024
3ca8531
Updated Haddock to include blurb about the new HTTP semantic conventions
evanlauer1 May 22, 2024
4669ae3
Removed hspec from dependencies
evanlauer1 May 22, 2024
25d1cf5
Auto-generated change. Removed hspec from build-depends
evanlauer1 May 22, 2024
d8771ed
Added newline to end of file
evanlauer1 May 22, 2024
76f062e
Split off parsing logic from getSemConvStabilityOptIn to parseSemConv…
evanlauer1 May 22, 2024
5542954
Added blank line for readability
evanlauer1 May 22, 2024
234e43e
Changed pure to Just to improve readability and contrast with Nothing
evanlauer1 May 22, 2024
266b974
Rewrote tests to eliminate boilerplate and increase readability
evanlauer1 May 23, 2024
73c5dca
Refactored and Renamed SemanticsConfig.hs to future proof
evanlauer1 May 23, 2024
591ac8c
Refactored SemanticsConfig so options are Enums for pattern matching …
evanlauer1 May 24, 2024
77b4c5f
Updated SemanticsConfig tests. NOTE: does not currently pass memoizat…
evanlauer1 May 24, 2024
e6b7fc4
Updated to match changes to SemanticsConfig.hs
evanlauer1 May 24, 2024
e0179a4
Fix infinite loop issue
evanlauer1 May 24, 2024
ac5546f
changed from . to $
evanlauer1 May 24, 2024
9523dad
Added haddocks
evanlauer1 May 24, 2024
36fd483
Formatting fixes
evanlauer1 May 28, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion api/hs-opentelemetry-api.cabal
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

These are all auto-generated changes

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Auto-generated changes. Maybe .cabal and other auto-generated files should be in .gitignore?

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
cabal-version: 1.12

-- This file has been generated from package.yaml by hpack version 0.35.2.
-- This file has been generated from package.yaml by hpack version 0.36.0.
--
-- see: https://github.com/sol/hpack

Expand Down Expand Up @@ -52,6 +52,7 @@ library
OpenTelemetry.Resource.Service
OpenTelemetry.Resource.Telemetry
OpenTelemetry.Resource.Webengine
OpenTelemetry.SemanticsConfig
OpenTelemetry.Trace.Core
OpenTelemetry.Trace.Id
OpenTelemetry.Trace.Id.Generator
Expand Down Expand Up @@ -83,6 +84,7 @@ library
, http-types
, memory
, mtl
, safe-exceptions
, template-haskell
, text
, thread-utils-context ==0.3.*
Expand All @@ -99,6 +101,7 @@ test-suite hs-opentelemetry-api-test
main-is: Spec.hs
other-modules:
OpenTelemetry.BaggageSpec
OpenTelemetry.SemanticsConfigSpec
OpenTelemetry.Trace.SamplerSpec
OpenTelemetry.Trace.TraceFlagsSpec
Paths_hs_opentelemetry_api
Expand All @@ -124,6 +127,7 @@ test-suite hs-opentelemetry-api-test
, http-types
, memory
, mtl
, safe-exceptions
, template-haskell
, text
, thread-utils-context ==0.3.*
Expand Down
1 change: 1 addition & 0 deletions api/package.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ dependencies:
- ghc-prim
- unliftio-core
- vector-builder
- safe-exceptions

library:
source-dirs: src
Expand Down
90 changes: 90 additions & 0 deletions api/src/OpenTelemetry/SemanticsConfig.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
{-# LANGUAGE OverloadedStrings #-}

module OpenTelemetry.SemanticsConfig (
SemanticsOptions (httpOption),
HttpOption (..),
getSemanticsOptions,
getSemanticsOptions',
) where

import Control.Exception.Safe (throwIO, tryAny)
import Data.IORef (newIORef, readIORef, writeIORef)
import qualified Data.Text as T
import System.Environment (lookupEnv)
import System.IO.Unsafe (unsafePerformIO)


{- | This is a record that contains options for whether the new stable semantics conventions should be emitted.
Semantics conventions that have been declared stable:
- [http](https://opentelemetry.io/blog/2023/http-conventions-declared-stable/#migration-plan)
-}
data SemanticsOptions = SemanticsOptions {httpOption :: HttpOption}


-- | This option determines whether stable, old, or both kinds of http attributes are emitted.
data HttpOption
= Stable
| StableAndOld
| Old
deriving (Show, Eq)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I used this instead of set membership because I did not like that if the pattern was to use when (useStableHttpSemantics options) useStableAttributes it would be easy to forget to check both useStable and useOld because the compiler does not enforce it. This way, the compiler will say "missing cases in pattern match".


-- | These are the default values emitted if OTEL_SEM_CONV_STABILITY_OPT_IN is unset or does not contain values for a specific category of option.
defaultOptions :: SemanticsOptions
defaultOptions = SemanticsOptions {httpOption = Old}


-- | Detects the presence of "http/dup" or "http" in OTEL_SEMCONV_STABILITY_OPT_IN or uses the default option if they are not there.
parseHttpOption :: (Foldable t) => t T.Text -> HttpOption
parseHttpOption envs
| "http/dup" `elem` envs = StableAndOld
| "http" `elem` envs = Stable
| otherwise = httpOption defaultOptions
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Using httpOption defaultOptions means that the value if the environment variable is unset will always be the same as if it is set but missing configuration for http.



-- | Detects the presence of semantics options in OTEL_SEMCONV_STABILITY_OPT_IN or uses the defaultOptions if they are not present.
parseSemanticsOptions :: Maybe String -> SemanticsOptions
parseSemanticsOptions Nothing = defaultOptions
parseSemanticsOptions (Just env) = SemanticsOptions {..}
where
envs = fmap T.strip $ T.splitOn "," $ T.pack env
httpOption = parseHttpOption envs
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The compiler will give a warning if one of the fields is unset.



{- | Version of getSemanticsOptions that is not memoized. It is recommended to use getSemanticsOptions for efficiency purposes
unless it is necessary to retrieve the value of OTEL_SEMCONV_STABILITY_OPT_IN every time getSemanticsOptions' is called.
-}
getSemanticsOptions' :: IO SemanticsOptions
getSemanticsOptions' = parseSemanticsOptions <$> lookupEnv "OTEL_SEMCONV_STABILITY_OPT_IN"
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I used function' to indicate a different version of function. I'm not sure if that is the best way to indicate the difference. I want function to be the default and function' to only be used in necessary circumstances.



{- | Create a new memoized IO action using an 'IORef' under the surface. Note that
the action may be run in multiple threads simultaneously, so this may not be
thread safe (depending on the underlying action). For the sake of reading an environment
variable and parsing some stuff, we don't have to be concerned about thread-safety.
-}
memoize :: IO a -> IO (IO a)
memoize action = do
ref <- newIORef Nothing
pure $ do
mres <- readIORef ref
res <- case mres of
Just res -> pure res
Nothing -> do
res <- tryAny action
writeIORef ref $ Just res
pure res
either throwIO pure res


{- | Retrieves OTEL_SEMCONV_STABILITY_OPT_IN and parses it into SemanticsOptions.

This uses the [global IORef trick](https://www.parsonsmatt.org/2021/04/21/global_ioref_in_template_haskell.html)
to memoize the settings for efficiency. Note that getSemanticsOptions stores and returns the
value of the first time it was called and will not change when OTEL_SEMCONV_STABILITY_OPT_IN
is updated. Use getSemanticsOptions' to read OTEL_SEMCONV_STABILITY_OPT_IN every time the
function is called.
-}
getSemanticsOptions :: IO SemanticsOptions
getSemanticsOptions = unsafePerformIO $ memoize getSemanticsOptions'
{-# NOINLINE getSemanticsOptions #-}
44 changes: 44 additions & 0 deletions api/test/OpenTelemetry/SemanticsConfigSpec.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
module OpenTelemetry.SemanticsConfigSpec where

import OpenTelemetry.SemanticsConfig
import System.Environment
import Test.Hspec


envVarName :: String
envVarName = "OTEL_SEMCONV_STABILITY_OPT_IN"


spec :: Spec
spec = do
describe "SemanticsConfig" $ do
describe "HttpOption" $ do
it "defaults to 'Old' when env var has no value" $ do
unsetEnv envVarName
semanticsOptions <- getSemanticsOptions'
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Uses getSemanticsOptions' so that the tests are not affected by memoization. Memoization is tested later.

httpOption semanticsOptions `shouldBe` Old
mapM_
( \(envVarVal, expectedVal) ->
it ("returns " ++ show expectedVal ++ " when env var is " ++ show envVarVal) $ do
setEnv envVarName envVarVal
semanticsOptions <- getSemanticsOptions'
httpOption semanticsOptions `shouldBe` expectedVal
)
[ ("http", Stable)
, ("http/du", Old) -- intentionally similar to both "http/dup" and "http"
, ("http/dup", StableAndOld)
, ("http/dup,http", StableAndOld)
, ("http,http/dup", StableAndOld)
, ("http,something-random,http/dup", StableAndOld)
]
context "memoization" $ do
it "works" $ do
setEnv envVarName "http"
semanticsOptions <- getSemanticsOptions
httpOption semanticsOptions `shouldBe` Stable
it ("does not change when " ++ envVarName ++ " changes") $ do
setEnv envVarName "http"
semanticsOptions <- getSemanticsOptions
setEnv envVarName "http/dup"
semanticsOptions <- getSemanticsOptions
httpOption semanticsOptions `shouldBe` Stable -- and not StableAndOld because of memoization
2 changes: 2 additions & 0 deletions api/test/Spec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import OpenTelemetry.Attributes (lookupAttribute)

import qualified OpenTelemetry.BaggageSpec as Baggage
import OpenTelemetry.Context
import qualified OpenTelemetry.SemanticsConfigSpec as SemanticsConfigSpec
import OpenTelemetry.Trace.Core
import qualified OpenTelemetry.Trace.SamplerSpec as Sampler
import qualified OpenTelemetry.Trace.TraceFlagsSpec as TraceFlags
Expand Down Expand Up @@ -54,3 +55,4 @@ main = hspec $ do
Baggage.spec
Sampler.spec
TraceFlags.spec
SemanticsConfigSpec.spec
31 changes: 23 additions & 8 deletions hie.yaml
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Auto-generated using the command gen-hie > hie.yaml. This allows HLS to work in VSCode for this repository.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This file was generated using the command gen-hie > hie.yaml. These changes allow HLS to work well with VsCode in this repository.

Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ cradle:
- path: "api/test"
component: "hs-opentelemetry-api:test:hs-opentelemetry-api-test"

- path: "examples/hspec/src"
component: "hspec-example:lib"

- path: "examples/hspec/test"
component: "hspec-example:test:test"

- path: "examples/yesod-minimal/src"
component: "yesod-minimal:lib"

Expand Down Expand Up @@ -90,8 +96,8 @@ cradle:
- path: "propagators/b3/src"
component: "hs-opentelemetry-propagator-b3:lib"

- path: "propagators/b3/test/spec"
component: "hs-opentelemetry-propagator-b3:test:spec"
- path: "propagators/b3/test"
component: "hs-opentelemetry-propagator-b3:test:hs-opentelemetry-propagator-b3-test"

- path: "propagators/datadog/src"
component: "hs-opentelemetry-propagator-datadog:lib"
Expand All @@ -105,6 +111,21 @@ cradle:
- path: "propagators/datadog/benchmark/header-codec/main.hs"
component: "hs-opentelemetry-propagator-datadog:bench:header-codec"

- path: "propagators/datadog/old-src/main.hs"
component: "hs-opentelemetry-propagator-datadog:bench:header-codec"

- path: "propagators/datadog/benchmark/header-codec/Raw.hs"
component: "hs-opentelemetry-propagator-datadog:bench:header-codec"

- path: "propagators/datadog/benchmark/header-codec/String.hs"
component: "hs-opentelemetry-propagator-datadog:bench:header-codec"

- path: "propagators/datadog/old-src/Raw.hs"
component: "hs-opentelemetry-propagator-datadog:bench:header-codec"

- path: "propagators/datadog/old-src/String.hs"
component: "hs-opentelemetry-propagator-datadog:bench:header-codec"

- path: "propagators/w3c/src"
component: "hs-opentelemetry-propagator-w3c:lib"

Expand All @@ -116,9 +137,3 @@ cradle:

- path: "sdk/test"
component: "hs-opentelemetry-sdk:test:hs-opentelemetry-sdk-test"

- path: "utils/exceptions/src"
component: "hs-opentelemetry-utils-exceptions:lib"

- path: "utils/exceptions/test"
component: "hs-opentelemetry-utils-exceptions:test:exceptions-test"
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

These are all auto-generated changes

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Auto-generated change

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
cabal-version: 1.12

-- This file has been generated from package.yaml by hpack version 0.35.2.
-- This file has been generated from package.yaml by hpack version 0.36.0.
--
-- see: https://github.com/sol/hpack

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
- Use internals to instrument a particular callsite using modifyRequest, modifyResponse (Next best)
- Provide a middleware to pull from the thread-local state (okay)
- Modify the global manager to pull from the thread-local state (least good, can't be helped sometimes)

[New HTTP semantic conventions have been declared stable.](https://opentelemetry.io/blog/2023/http-conventions-declared-stable/#migration-plan) Opt-in by setting the environment variable OTEL_SEMCONV_STABILITY_OPT_IN to
- "http" - to use the stable conventions
- "http/dup" - to emit both the old and the stable conventions
Otherwise, the old conventions will be used. The stable conventions will replace the old conventions in the next major release of this library.
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This haddock blurb documents the change so that other programmers using the library can opt-in.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Excellent

-}
module OpenTelemetry.Instrumentation.HttpClient (
withResponse,
Expand Down Expand Up @@ -68,8 +73,8 @@ withResponse
-> (Client.Response Client.BodyReader -> m a)
-> m a
withResponse httpConf req man f = do
t <- httpTracerProvider
inSpan'' t "withResponse" (addAttributesToSpanArguments callerAttributes spanArgs) $ \_wrSpan -> do
tracer <- httpTracerProvider
inSpan'' tracer "withResponse" (addAttributesToSpanArguments callerAttributes spanArgs) $ \_wrSpan -> do
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This variable was renamed for clarity. Interestingly, httpTracerProvider returns a Tracer and not a TracerProvider. I wonder if it should be renamed

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think the variable rename is reasonable. Haskellers can lean on the side of being terse and I think that partly stems from mathematics backgrounds, where single letter variable naming is common.

ctxt <- getContext
-- TODO would like to capture the req/resp time specifically
-- inSpan "http.request" (defaultSpanArguments { startingKind = Client }) $ \httpReqSpan -> do
Expand All @@ -88,8 +93,8 @@ withResponse httpConf req man f = do
-}
httpLbs :: (MonadUnliftIO m, HasCallStack) => HttpClientInstrumentationConfig -> Client.Request -> Client.Manager -> m (Client.Response L.ByteString)
httpLbs httpConf req man = do
t <- httpTracerProvider
inSpan'' t "httpLbs" (addAttributesToSpanArguments callerAttributes spanArgs) $ \_ -> do
tracer <- httpTracerProvider
inSpan'' tracer "httpLbs" (addAttributesToSpanArguments callerAttributes spanArgs) $ \_ -> do
ctxt <- getContext
req' <- instrumentRequest httpConf ctxt req
resp <- liftIO $ Client.httpLbs req' man
Expand All @@ -102,8 +107,8 @@ httpLbs httpConf req man = do
-}
httpNoBody :: (MonadUnliftIO m, HasCallStack) => HttpClientInstrumentationConfig -> Client.Request -> Client.Manager -> m (Client.Response ())
httpNoBody httpConf req man = do
t <- httpTracerProvider
inSpan'' t "httpNoBody" (addAttributesToSpanArguments callerAttributes spanArgs) $ \_ -> do
tracer <- httpTracerProvider
inSpan'' tracer "httpNoBody" (addAttributesToSpanArguments callerAttributes spanArgs) $ \_ -> do
ctxt <- getContext
req' <- instrumentRequest httpConf ctxt req
resp <- liftIO $ Client.httpNoBody req' man
Expand Down Expand Up @@ -141,8 +146,8 @@ httpNoBody httpConf req man = do
-}
responseOpen :: (MonadUnliftIO m, HasCallStack) => HttpClientInstrumentationConfig -> Client.Request -> Client.Manager -> m (Client.Response Client.BodyReader)
responseOpen httpConf req man = do
t <- httpTracerProvider
inSpan'' t "responseOpen" (addAttributesToSpanArguments callerAttributes spanArgs) $ \_ -> do
tracer <- httpTracerProvider
inSpan'' tracer "responseOpen" (addAttributesToSpanArguments callerAttributes spanArgs) $ \_ -> do
ctxt <- getContext
req' <- instrumentRequest httpConf ctxt req
resp <- liftIO $ Client.responseOpen req' man
Expand Down
Loading
Loading