packages feed

instana-haskell-trace-sdk 0.8.0.0 → 0.9.0.0

raw patch · 9 files changed

+296/−93 lines, 9 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Instana.SDK.SDK: addAnnotationToEntrySpan :: MonadIO m => InstanaContext -> Annotation -> m ()
+ Instana.SDK.SDK: addAnnotationToEntrySpanAt :: MonadIO m => InstanaContext -> Text -> Annotation -> m ()
+ Instana.SDK.SDK: addAnnotationValueToEntrySpanAt :: MonadIO m => InstanaContext -> Text -> AnnotationValue -> m ()
+ Instana.SDK.SDK: addJsonValueAt :: (MonadIO m, ToJSON a) => InstanaContext -> Text -> a -> m ()
+ Instana.SDK.SDK: addJsonValueToEntrySpanAt :: (MonadIO m, ToJSON a) => InstanaContext -> Text -> a -> m ()
+ Instana.SDK.Span.Span: addJsonValueAt :: ToJSON a => Text -> a -> Span -> Span
- Instana.SDK.SDK: addAnnotationAt :: (MonadIO m, ToJSON a) => InstanaContext -> Text -> a -> m ()
+ Instana.SDK.SDK: addAnnotationAt :: MonadIO m => InstanaContext -> Text -> Annotation -> m ()
- Instana.SDK.Span.Span: addAnnotationAt :: ToJSON a => Text -> a -> Span -> Span
+ Instana.SDK.Span.Span: addAnnotationAt :: Text -> Annotation -> Span -> Span

Files

CHANGELOG.md view
@@ -1,5 +1,16 @@ # Changelog for instana-haskell-trace-sdk +## 0.9.0.0+- Fix format of annotation span.data.http.header - it is now a JSON object instead of an array of arrays.+- BREAKING: Rename confusingly named `Instana.SDK.addAnnotationAt` to `Instana.SDK.addJsonValueAt`, because it actually takes a JSON value. `Instana.SDK.addAnnotationAt` still exists, but takes an actual `Instana.SDK.Span.SpanData.Annotation` value as its argument now, see below.+- Provide additional functions to add annotations:+  - `Instana.SDK.addAnnotationAt` which takes a path and an `Instana.SDK.Span.SpanData.Annotation` value+  - `Instana.SDK.addAnnotationToEntrySpan`, which has the same signature as `addAnnotation` but always unconditionally adds the annotation to the entry span, even if an intermediate or exit span is currently active+  - `Instana.SDK.addAnnotationToEntrySpanAt`, which has the same signature as `addAnnotationAt` but always unconditionally adds the annotation to the entry span, even if an intermediate or exit span is currently active+  - `Instana.SDK.addAnnotationValueToEntrySpanAt`, which has the same signature as `addAnnotationValueAt` but always unconditionally adds the annotation to the entry span, even if an intermediate or exit span is currently active+  - `Instana.SDK.addJsonValueToEntrySpanAt`, which has the same signature as `addAnnotationJsonValueAt` but always unconditionally adds the annotation to the entry span, even if an intermediate or exit span is currently active.+- Remove stability=experimental from cabal file.+ ## 0.8.0.0 - Fix: Apply secrets config from agent configuration. - Fix: Redact secrets from query parameters instead of removing them.
README.md view
@@ -15,7 +15,7 @@  ``` extra-deps:-- instana-haskell-trace-sdk-0.8.0.0+- instana-haskell-trace-sdk-0.9.0.0 ```  Depending on the stack resolver you use, you might also need to add `aeson-extra` to your extra-deps:@@ -155,7 +155,7 @@  #### Best Practices -Make sure you have read Instana's [docs on custom tracing](https://docs.instana.io/quick_start/custom_tracing/#tips--best-practices) and in particular the [best practices section](https://docs.instana.io/quick_start/custom_tracing/#tips--best-practices). This documentation contains a lot of useful info for integrating Instana tracing into your code; among other things, it explains which [annotations](https://docs.instana.io/quick_start/custom_tracing/#list-of-processed-tags) can be added to spans (via `InstanaSDK.addAnnotation`, `InstanaSDK.addAnnotationAt`, and `InstanaSDK.addAnnotationValueAt`).+Make sure you have read Instana's [docs on custom tracing](https://docs.instana.io/quick_start/custom_tracing/#tips--best-practices) and in particular the [best practices section](https://docs.instana.io/quick_start/custom_tracing/#tips--best-practices). This documentation contains a lot of useful info for integrating Instana tracing into your code; among other things, it explains which [annotations](https://docs.instana.io/quick_start/custom_tracing/#list-of-processed-tags) can be added to spans (via `InstanaSDK.addAnnotation`, `InstanaSDK.addAnnotationAt`, `InstanaSDK.addAnnotationValueAt`, and `InstanaSDK.addJsonValueAt`).  Instana differentiates between so-called registered spans and SDK spans. Registered spans are usually created by automatic tracing and there is specialized handling for each registered in Instana's processing pipeline. SDK spans, in contrast, are the type of spans created by using a trace SDK (like the Haskell trace SDK or other, similar SDKs for other runtime platforms). SDK span are processed in a more generic fashion by Instana's processing pipeline. 
instana-haskell-trace-sdk.cabal view
@@ -1,5 +1,5 @@ name:           instana-haskell-trace-sdk-version:        0.8.0.0+version:        0.9.0.0 synopsis:       SDK for adding custom Instana tracing support to Haskell applications. description:    Please also see the README on Github at <https://github.com/instana/haskell-trace-sdk#readme> homepage:       https://www.instana.com/@@ -11,7 +11,6 @@ license:        MIT license-file:   LICENSE build-type:     Simple-stability:      experimental cabal-version:  2.0  extra-source-files:
src/Instana/SDK/SDK.hs view
@@ -13,8 +13,13 @@     , InstanaContext     , addAnnotation     , addAnnotationAt+    , addAnnotationToEntrySpan+    , addAnnotationToEntrySpanAt     , addAnnotationValueAt+    , addAnnotationValueToEntrySpanAt     , addHttpTracingHeaders+    , addJsonValueAt+    , addJsonValueToEntrySpanAt     , addToErrorCount     , addWebsiteMonitoringBackEndCorrelation     , agentHost@@ -848,7 +853,7 @@     httpAnnotations' =       case capturedHeaders of         Just headers ->-          httpAnnotations ++ [SpanData.listAnnotation "header" headers]+          (SpanData.objectAnnotation "header" headers) : httpAnnotations         Nothing ->           httpAnnotations @@ -859,17 +864,22 @@ collectHeaders ::   [CI BSC8.ByteString]   -> [HTTPTypes.Header]-  -> Maybe [(String, String)]+  -> Maybe [Annotation] collectHeaders extraHeadersConfig allHeaders =   let-    all2 = allHeaders-    filtered = filterHeaders extraHeadersConfig all2-    serialized =+    filtered = filterHeaders extraHeadersConfig allHeaders+    listOfStringTuples =       fmap         (\(name, value) -> (BSC8.unpack $ CI.original name, BSC8.unpack value))         filtered+    headersAsAnnotations =+      fmap+        (\(name, value) -> SpanData.simpleAnnotation (T.pack name) value)+        listOfStringTuples   in-  if null serialized then Nothing else Just serialized+  if null headersAsAnnotations+    then Nothing+    else Just $ headersAsAnnotations   filterHeaders :: [CI BSC8.ByteString] -> [HTTPTypes.Header] -> [HTTPTypes.Header]@@ -970,11 +980,11 @@   let     (HTTPTypes.Status statusCode statusMessage) =       Wai.responseStatus response-  addAnnotationToEntrySpanAt context "http.status" $+  addAnnotationValueToEntrySpanAt context "http.status" $     SpanData.simpleValue statusCode   when     (statusCode >= 500 )-    (addAnnotationAt context "http.message" $+    (addAnnotationValueToEntrySpanAt context "http.message" $       SpanData.simpleValue $ BSC8.unpack statusMessage     ) @@ -1015,8 +1025,8 @@     (do        let          Just headers = capturedHeaders-       addAnnotationValueToEntrySpanAt context "http.header" $-         SpanData.listValue headers+       addAnnotationToEntrySpanAt context "http" $+         SpanData.objectAnnotation "header" headers     )  @@ -1163,16 +1173,16 @@                 collectHeaders extraHeadersConfig $                   HTTP.responseHeaders res -            addAnnotationAt context "http.status" $+            addAnnotationValueAt context "http.status" $               SpanData.simpleValue status              when               (Maybe.isJust capturedResponseHeaders)               (do                  let-                   Just responseHeaders = capturedResponseHeaders-                 addAnnotationValueAt context "http.header" $-                   SpanData.listValue responseHeaders+                   Just headers = capturedResponseHeaders+                 addAnnotationAt context "http" $+                     SpanData.objectAnnotation "header" headers               )              originalCheckResponse req res@@ -1192,8 +1202,8 @@       ]     httpAnnotations' =       case capturedRequestHeaders of-        Just requestHeaders ->-          httpAnnotations ++ [SpanData.listAnnotation "header" requestHeaders]+        Just headers ->+          (SpanData.objectAnnotation "header" headers) : httpAnnotations         Nothing ->           httpAnnotations @@ -1374,37 +1384,39 @@  -- |Adds an annotation to the currently active span. Call this between -- startEntry/startRootEntry/startExit and completeEntry/completeExit or+-- inside the IO action given to with withEntry/withExit/withRootEntry. Can be+-- called multiple times, data from multiple calls will be merged.+addAnnotation :: MonadIO m => InstanaContext -> Annotation -> m ()+addAnnotation context annotation =+  liftIO $ modifyCurrentSpan context+    (\span_ -> Span.addAnnotation annotation span_)+++-- |Adds an annotation to the currently active span. Call this between+-- startEntry/startRootEntry/startExit and completeEntry/completeExit or -- inside the IO action given to withEntry/withExit/withRootEntry. -- The given path can be a nested path, with path fragments separated by dots, -- like "http.url". This will result in -- "data": { --   ... --   "http": {---     "url": "..."+--     "url": {+--       "key_from_the_provided_annotation_value": ...+--     } --   }, --   ... -- } addAnnotationAt ::-  (MonadIO m, ToJSON a) =>+  MonadIO m =>   InstanaContext   -> Text-  -> a+  -> Annotation   -> m () addAnnotationAt context path value =   liftIO $ modifyCurrentSpan context     (\span_ -> Span.addAnnotationAt path value span_)  --- |Adds an annotation to the currently active span. Call this between--- startEntry/startRootEntry/startExit and completeEntry/completeExit or--- inside the IO action given to with withEntry/withExit/withRootEntry. Can be--- called multiple times, data from multiple calls will be merged.-addAnnotation :: MonadIO m => InstanaContext -> Annotation -> m ()-addAnnotation context annotation =-  liftIO $ modifyCurrentSpan context-    (\span_ -> Span.addAnnotation annotation span_)-- -- |Adds an annotation with the given value to the currently active span. Call -- this between startEntry/startRootEntry/startExit and -- completeEntry/completeExit or inside the IO action given to with@@ -1428,23 +1440,90 @@     (\span_ -> Span.addAnnotationValueAt path value span_)  --- |Adds an additional annotation to the currently active entry span, even if--- the currently active span is an exit child of that entry span.-addAnnotationToEntrySpanAt ::+-- |Adds a simple value (string, boolean, number) to the currently active span's+-- data section. Should not be used for objects or lists in case you intend to+-- merge them with additional values at the same path later. Call this between+-- startEntry/startRootEntry/startExit and completeEntry/completeExit or+-- inside the IO action given to withEntry/withExit/withRootEntry.+-- The given path can be a nested path, with path fragments separated by dots,+-- like "http.url". This will result in+-- "data": {+--   ...+--   "http": {+--     "url": "..."+--   },+--   ...+-- }+addJsonValueAt ::   (MonadIO m, ToJSON a) =>   InstanaContext   -> Text   -> a   -> m ()-addAnnotationToEntrySpanAt context path value =+addJsonValueAt context path value =+  liftIO $ modifyCurrentSpan context+    (\span_ -> Span.addJsonValueAt path value span_)+++-- |Adds an additional annotation to the currently active entry span, even if+-- the currently active span is an intermediate or exit child of that entry+-- span. Call this between startEntry/startRootEntry/startExit and+-- completeEntry/completeExit or inside the IO action given to with+-- withEntry/withExit/withRootEntry. Can be called multiple times, data from+-- multiple calls will be merged.+addAnnotationToEntrySpan ::+  MonadIO m =>+  InstanaContext+  -> Annotation+  -> m ()+addAnnotationToEntrySpan context annotation =   liftIO $ modifyCurrentEntrySpan context-    (\span_ -> Span.addAnnotationAt path value span_)+    (\span_ -> Span.addAnnotation annotation span_)   -- |Adds an additional annotation to the currently active entry span, even if--- the currently active span is an exit child of that entry span.+-- the currently active span is an intermediate or exit child of that entry+-- span. Call this between startEntry/startRootEntry/startExit and+-- completeEntry/completeExit or inside the IO action given to with+-- withEntry/withExit/withRootEntry. Can be called multiple times, data from+-- multiple calls will be merged. The given path can be a nested path, with path+-- fragments separated by dots, like "http.url". This will result in+-- "data": {+--   ...+--   "http": {+--     "url": {+--       "key_from_the_provided_annotation_value": ...+--     }+--   },+--   ...+-- }+addAnnotationToEntrySpanAt ::+  MonadIO m =>+  InstanaContext+  -> Text+  -> Annotation+  -> m ()+addAnnotationToEntrySpanAt context path annotation =+  liftIO $ modifyCurrentEntrySpan context+    (\span_ -> Span.addAnnotationAt path annotation span_)+++-- |Adds an additional annotation value to the currently active entry span, even+-- if the currently active span is an intermediate or exit child of that entry+-- span. Call this between startEntry/startRootEntry/startExit and+-- completeEntry/completeExit or inside the IO action given to with+-- withEntry/withExit/withRootEntry. Can be called multiple times, data from+-- multiple calls will be merged. The given path can be a nested path, with path+-- fragments separated by dots, like "http.url". This will result in+-- "data": {+--   ...+--   "http": {+--     "url": "..."+--   },+--   ...+-- } addAnnotationValueToEntrySpanAt ::-  (MonadIO m) =>+  MonadIO m =>   InstanaContext   -> Text   -> AnnotationValue@@ -1454,6 +1533,34 @@     (\span_ -> Span.addAnnotationValueAt path value span_)  +-- |Adds an additional annotation from a simple JSON value (string, number,+-- boolean) to the currently active entry span, even if+-- the currently active span is an intermediate or exit child of that entry+-- span. Should not be used for objects or lists in case you intend to merge+-- them with additional values at the same path later. Call this between+-- startEntry/startRootEntry/startExit and completeEntry/completeExit or inside+-- the IO action given to with withEntry/withExit/withRootEntry. Can be called+-- multiple times, data from  multiple calls will be merged.+-- The given path can be a nested path, with path fragments separated by dots,+-- like "http.url". This will result in+-- "data": {+--   ...+--   "http": {+--     "url": "..."+--   },+--   ...+-- }+addJsonValueToEntrySpanAt ::+  (MonadIO m, ToJSON a) =>+  InstanaContext+  -> Text+  -> a+  -> m ()+addJsonValueToEntrySpanAt context path value =+  liftIO $ modifyCurrentEntrySpan context+    (\span_ -> Span.addJsonValueAt path value span_)++ -- |Adds the Instana tracing headers -- (https://docs.instana.io/core_concepts/tracing/#http-tracing-headers) -- from the currently active span to the given HTTP client request.@@ -1729,8 +1836,8 @@   -- |Applies the given function to the currently active entry span, even if the--- currently active span is an exit child of that entry span. The entry span--- will be replaced with the result of the given function.+-- currently active span is an intermediate or exit child of that entry span.+-- The entry span will be replaced with the result of the given function. modifyCurrentEntrySpan ::   InstanaContext   -> (Span -> Span)@@ -1749,7 +1856,7 @@   -- |Applies the given function to the entry span (if present) on the given span--- stack, even if there is already an exit span on top of it.+-- stack, even if there is already an intermediate or exit span on top of it. mapCurrentEntrySpan :: (Span -> Span) -> Maybe SpanStack -> SpanStack mapCurrentEntrySpan fn stack =   Maybe.fromMaybe
src/Instana/SDK/Span/Span.hs view
@@ -10,6 +10,7 @@   , addAnnotation   , addAnnotationAt   , addAnnotationValueAt+  , addJsonValueAt   , addToErrorCount   , correlationId   , correlationType@@ -286,19 +287,16 @@     Exit exit   -> Exit $ ExitSpan.addAnnotation wrappedAnnotation exit  --- |Add a simple value (string, boolean, number) at the given path to the span's--- data section.-addAnnotationAt :: ToJSON a => Text -> a -> Span -> Span-addAnnotationAt path value span_ =+-- |Add an annotation at the given path to the span's data section.+addAnnotationAt :: Text -> Annotation -> Span -> Span+addAnnotationAt path annotation span_ =   let-    pathSegments = T.splitOn "." path-    lastPathSegment = List.last pathSegments-    pathPrefix = List.take (List.length pathSegments - 1) pathSegments+    pathPrefix = T.splitOn "." path     newData = List.foldr       (\nextPathSegment accumulated ->         SpanData.objectAnnotation nextPathSegment [accumulated]       )-      (SpanData.simpleAnnotation lastPathSegment value)+      annotation       pathPrefix   in   addAnnotation newData span_@@ -322,6 +320,14 @@       pathPrefix   in   addAnnotation newData span_+++-- |Add a simple value (string, boolean, number) at the given path to the span's+-- data section. Should not be used for objects or lists in case you intend to+-- merge them with additional values at the same path later.+addJsonValueAt :: ToJSON a => Text -> a -> Span -> Span+addJsonValueAt path value span_ =+  addAnnotationValueAt path (SpanData.simpleValue value) span_   wrapAnnotationIfNecessary :: Span -> Annotation -> Annotation
test/apps/wai/Main.hs view
@@ -163,9 +163,9 @@   threadDelay $ 10 * 1000   InstanaSDK.addToErrorCount instana 2   addAnnotations instana (moreSpanData "exit")-  InstanaSDK.addAnnotationAt instana "nested.key1" ("nested.text.value1" :: String)-  InstanaSDK.addAnnotationAt instana "nested.key2" ("nested.text.value2" :: String)-  InstanaSDK.addAnnotationAt instana "nested.key3" (1604 :: Int)+  InstanaSDK.addJsonValueAt instana "nested.key1" ("nested.text.value1" :: String)+  InstanaSDK.addJsonValueAt instana "nested.key2" ("nested.text.value2" :: String)+  InstanaSDK.addJsonValueAt instana "nested.key3" (1604 :: Int)   return "exit done"  @@ -274,7 +274,7 @@   result <- doExitCallWithTags instana   InstanaSDK.incrementErrorCount instana   addAnnotations instana (moreSpanData "entry")-  InstanaSDK.addAnnotationAt+  InstanaSDK.addJsonValueAt     instana "nested.entry.key" ("nested.entry.value" :: String)   InstanaSDK.completeEntry instana   respondWithPlainText respond result@@ -289,7 +289,7 @@   result <- simulateExitCall   InstanaSDK.incrementErrorCount instana   addAnnotations instana (moreSpanData "exit")-  InstanaSDK.addAnnotationAt instana "nested.exit.key" ("nested.exit.value" :: String)+  InstanaSDK.addJsonValueAt instana "nested.exit.key" ("nested.exit.value" :: String)   InstanaSDK.completeExit instana   return result 
test/integration/Instana/SDK/IntegrationTest/ExtraHttpHeaders.hs view
@@ -45,10 +45,10 @@           , "host"   .= ("127.0.0.1:1207" :: String)           , "url"    .= ("/http/bracket/api" :: String)           , "status" .= (200 :: Int)-          , "header" .= (-            [ ("X-Response-Header-On-Entry", "response header on entry value")-            , ("X-Request-Header-On-Entry", "request header on entry value")-            ] :: [(String, String)])+          , "header" .= (Aeson.object+            [ "X-Response-Header-On-Entry" .= ("response header on entry value" :: String)+            , "X-Request-Header-On-Entry"  .= ("request header on entry value" :: String)+            ])           ]         )       ]@@ -66,10 +66,10 @@           , "url"    .= ("http://127.0.0.1:1208/echo" :: String)           , "status" .= (200 :: Int)           , "params" .= ("some=query&parameters=2&pass=<redacted>" :: String)-          , "header" .= (-            [ ("X-Request-Header-On-Exit", "request header on exit value")-            , ("X-Response-Header-On-Exit", "response header on exit value")-            ] :: [(String, String)])+          , "header" .= (Aeson.object+            [ "X-Request-Header-On-Exit"  .= ("request header on exit value" :: String)+            , "X-Response-Header-On-Exit" .= ("response header on exit value" :: String)+            ])           ]         )       ]
test/integration/Instana/SDK/IntegrationTest/Metrics.hs view
@@ -51,7 +51,7 @@               (EntityDataRequest.arguments entityData)           , assertLabelIs               "sensorVersion"-              "0.8.0.0"+              "0.9.0.0"               (EntityDataRequest.sensorVersion entityData)           , assertCounterSatisfies               "startTime"
test/unit/Instana/SDK/SpanTest.hs view
@@ -32,15 +32,26 @@     , TestLabel "shouldAddTwoAtSameNestedPath" shouldAddTwoAtSameNestedPath     , TestLabel "shouldAddMultipleRegistered" shouldAddMultipleRegistered     , TestLabel "shouldMergeListValues" shouldMergeListValues-    , TestLabel "shouldWrapAnnotationsInSdkCustomTagsForSdkSpans" shouldWrapAnnotationsInSdkCustomTagsForSdkSpans-    , TestLabel "shouldConvertToSimpleSpanFormat" shouldConvertToSimpleSpanFormat+    , TestLabel+        "shouldWrapAnnotationsInSdkCustomTagsForSdkSpans"+        shouldWrapAnnotationsInSdkCustomTagsForSdkSpans+    , TestLabel+        "shouldConvertToSimpleSpanFormat"+        shouldConvertToSimpleSpanFormat+    , TestLabel "shouldAddAnnotationAt" shouldAddAnnotationAt+    , TestLabel+        "shouldAddAnnotationAtToExistingKey"+        shouldAddAnnotationAtToExistingKey+    , TestLabel+        "shouldAddAnnotationAtToDeeplyNestedExistingKey"+        shouldAddAnnotationAtToDeeplyNestedExistingKey     ]   shouldAddStringNotNested :: Test shouldAddStringNotNested =   let-    span_ = Span.addAnnotationAt "path" ("value" :: String) $ registeredEntrySpan+    span_ = Span.addJsonValueAt "path" ("value" :: String) $ registeredEntrySpan     spanData = Span.spanData span_   in   TestCase $@@ -55,7 +66,7 @@ shouldAddStringNested =   let     span_ =-      Span.addAnnotationAt+      Span.addJsonValueAt         "nested.path"         ("value" :: String) $           registeredEntrySpan@@ -75,7 +86,7 @@ shouldAddStringDeeplyNested =   let     span_ =-      Span.addAnnotationAt+      Span.addJsonValueAt         "really.deeply.nested.path"         ("deeplyNestedValue" :: String)         registeredEntrySpan@@ -99,7 +110,7 @@ shouldAddIntDeeplyNested =   let     span_ =-      Span.addAnnotationAt+      Span.addJsonValueAt         "really.deeply.nested.path"         (42 :: Int) $           registeredEntrySpan@@ -123,7 +134,7 @@ shouldAddDoubleDeeplyNested =   let     span_ =-      Span.addAnnotationAt+      Span.addJsonValueAt         "really.deeply.nested.path"         (28.08 :: Double) $           registeredEntrySpan@@ -146,7 +157,7 @@ shouldAddBooleanDeeplyNested :: Test shouldAddBooleanDeeplyNested =   let-    span_ = Span.addAnnotationAt "really.deeply.nested.path" True $ registeredEntrySpan+    span_ = Span.addJsonValueAt "really.deeply.nested.path" True $ registeredEntrySpan     spanData = Span.spanData span_   in   TestCase $@@ -167,8 +178,8 @@ shouldAddTwoAtDifferentPaths =   let     span_ =-      Span.addAnnotationAt "nested2.key" (2 :: Int) $-      Span.addAnnotationAt "nested1.key" (1 :: Int) $+      Span.addJsonValueAt "nested2.key" (2 :: Int) $+      Span.addJsonValueAt "nested1.key" (1 :: Int) $       registeredEntrySpan     spanData = Span.spanData span_   in@@ -188,8 +199,8 @@ shouldAddTwoAtSamePath =   let     span_ =-      Span.addAnnotationAt "nested.key2" (2 :: Int) $-      Span.addAnnotationAt "nested.key1" (1 :: Int) $+      Span.addJsonValueAt "nested.key2" (2 :: Int) $+      Span.addJsonValueAt "nested.key1" (1 :: Int) $       registeredEntrySpan     spanData = Span.spanData span_   in@@ -209,8 +220,8 @@ shouldAddTwoAtSameNestedPath =   let     span_ =-      Span.addAnnotationAt "nested.deeper.key2" (2 :: Int) $-      Span.addAnnotationAt "nested.deeper.key1" (1 :: Int) $+      Span.addJsonValueAt "nested.deeper.key2" (2 :: Int) $+      Span.addJsonValueAt "nested.deeper.key1" (1 :: Int) $       registeredEntrySpan     spanData = Span.spanData span_   in@@ -232,13 +243,13 @@ shouldAddMultipleRegistered =   let     span_ =-      Span.addAnnotationAt "nested.key3" (12.07 :: Double) $-      Span.addAnnotationAt "nested.key2" (2 :: Int) $-      Span.addAnnotationAt "nested.key1" ("value.n.1" :: String) $-      Span.addAnnotationAt "list" (["one", "two", "three"] :: [String]) $-      Span.addAnnotationAt "key3" (16.04 :: Double) $-      Span.addAnnotationAt "key2" (13 :: Int) $-      Span.addAnnotationAt "key1" ("value1" :: String) $+      Span.addJsonValueAt "nested.key3" (12.07 :: Double) $+      Span.addJsonValueAt "nested.key2" (2 :: Int) $+      Span.addJsonValueAt "nested.key1" ("value.n.1" :: String) $+      Span.addJsonValueAt "list" (["one", "two", "three"] :: [String]) $+      Span.addJsonValueAt "key3" (16.04 :: Double) $+      Span.addJsonValueAt "key2" (13 :: Int) $+      Span.addJsonValueAt "key1" ("value1" :: String) $       registeredEntrySpan     spanData = Span.spanData span_   in@@ -285,12 +296,12 @@ shouldWrapAnnotationsInSdkCustomTagsForSdkSpans =   let     span_ =-      Span.addAnnotationAt "nested.key3" (12.07 :: Double) $-      Span.addAnnotationAt "nested.key2" (2 :: Int) $-      Span.addAnnotationAt "nested.key1" ("value.n.1" :: String) $-      Span.addAnnotationAt "key3" (16.04 :: Double) $-      Span.addAnnotationAt "key2" (13 :: Int) $-      Span.addAnnotationAt "key1" ("value1" :: String) $+      Span.addJsonValueAt "nested.key3" (12.07 :: Double) $+      Span.addJsonValueAt "nested.key2" (2 :: Int) $+      Span.addJsonValueAt "nested.key1" ("value.n.1" :: String) $+      Span.addJsonValueAt "key3" (16.04 :: Double) $+      Span.addJsonValueAt "key2" (13 :: Int) $+      Span.addJsonValueAt "key1" ("value1" :: String) $       sdkEntrySpan     spanData = Span.spanData span_   in@@ -320,7 +331,7 @@ shouldConvertToSimpleSpanFormat =   let     span_ =-      Span.addAnnotationAt "really.deeply.nested.path" True registeredEntrySpan+      Span.addJsonValueAt "really.deeply.nested.path" True registeredEntrySpan     simple = SimpleSpan.convert span_     traceId = SimpleSpan.traceId simple     spanId = SimpleSpan.spanId simple@@ -351,6 +362,75 @@           ]         ]       )+      spanData+++shouldAddAnnotationAt :: Test+shouldAddAnnotationAt =+  let+    span_ =+      Span.addAnnotationAt+        "parent"+        (SpanData.simpleAnnotation "key" ("value" :: String)) $+      registeredEntrySpan+    spanData = Span.spanData span_+  in+  TestCase $+    assertEqual "shouldAddAnnotationAt"+      (SpanData [+        SpanData.objectAnnotation "parent" [+          SpanData.simpleAnnotation "key" ("value" :: String)+        ]+      ])+      spanData+++shouldAddAnnotationAtToExistingKey :: Test+shouldAddAnnotationAtToExistingKey =+  let+    span_ =+      Span.addAnnotationAt+        "nested"+        (SpanData.simpleAnnotation "key3" ("value" :: String)) $+      Span.addJsonValueAt "nested.key2" (2 :: Int) $+      Span.addJsonValueAt "nested.key1" (1 :: Int) $+      registeredEntrySpan+    spanData = Span.spanData span_+  in+  TestCase $+    assertEqual "shouldAddAnnotationAtToExistingKey"+      (SpanData [+        SpanData.objectAnnotation "nested"+          [ SpanData.simpleAnnotation "key1" (1 :: Int)+          , SpanData.simpleAnnotation "key2" (2 :: Int)+          , SpanData.simpleAnnotation "key3" ("value" :: String)+          ]+      ])+      spanData+++shouldAddAnnotationAtToDeeplyNestedExistingKey :: Test+shouldAddAnnotationAtToDeeplyNestedExistingKey =+  let+    span_ =+      Span.addAnnotationAt+        "nested.deeper"+        (SpanData.simpleAnnotation "key3" ("value" :: String)) $+      Span.addJsonValueAt "nested.deeper.key2" (2 :: Int) $+      Span.addJsonValueAt "nested.deeper.key1" (1 :: Int) $+      registeredEntrySpan+    spanData = Span.spanData span_+  in+  TestCase $+    assertEqual "shouldAddAnnotationAtToExistingKey"+      (SpanData [+        SpanData.objectAnnotation "nested" [+          SpanData.objectAnnotation "deeper"+            [ SpanData.simpleAnnotation "key1" (1 :: Int)+            , SpanData.simpleAnnotation "key2" (2 :: Int)+            , SpanData.simpleAnnotation "key3" ("value" :: String) ]+        ]+      ])       spanData