eventuo11y-otel (empty) → 0.1.0.0
raw patch · 4 files changed
+160/−0 lines, 4 filesdep +basedep +eventuo11ydep +hs-opentelemetry-api
Dependencies added: base, eventuo11y, hs-opentelemetry-api, text
Files
- CHANGELOG.md +5/−0
- LICENSE +13/−0
- eventuo11y-otel.cabal +34/−0
- src/Observe/Event/Render/OpenTelemetry.hs +108/−0
+ CHANGELOG.md view
@@ -0,0 +1,5 @@+# Revision history for eventuo11y-otel++## 0.1.0.0 -- 2023-01-13++Initial release
+ LICENSE view
@@ -0,0 +1,13 @@+Copyright 2023 Shea Levy.++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this project except in compliance with the License.+You may obtain a copy of the License at++ http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.
+ eventuo11y-otel.cabal view
@@ -0,0 +1,34 @@+cabal-version: 3.0+name: eventuo11y-otel+version: 0.1.0.0+synopsis: OpenTelemetry-based rendering for eventuo11y+description:+ Render [eventuo11y](https://hackage.haskell.org/package/eventuo11y) 'Observe.Event.Event's+ to OpenTelemetry traces.++bug-reports: https://github.com/shlevy/eventuo11y/issues+license: Apache-2.0+license-file: LICENSE+author: Shea Levy+maintainer: shea@shealevy.com+copyright: Copyright 2023 Shea Levy.+category: Observability+extra-source-files: CHANGELOG.md+tested-with: GHC == { 8.10.7, 9.2.4 }++source-repository head+ type: git+ location: https://github.com/shlevy/eventuo11y++library+ exposed-modules:+ Observe.Event.Render.OpenTelemetry++ build-depends:+ , base ^>= { 4.14, 4.16, 4.17 }+ , eventuo11y ^>= { 0.7 }+ , hs-opentelemetry-api ^>= { 0.0.3 }+ , text ^>= { 1.2, 2.0 }++ hs-source-dirs: src+ default-language: Haskell2010
+ src/Observe/Event/Render/OpenTelemetry.hs view
@@ -0,0 +1,108 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE RecordWildCards #-}++-- |+-- Description : EventBackend for rendering events as OpenTelemetry traces+-- Copyright : Copyright 2023 Shea Levy.+-- License : Apache-2.0+-- Maintainer : shea@shealevy.com+module Observe.Event.Render.OpenTelemetry where++import Control.Monad.IO.Class+import Data.Text (Text, pack)+import Observe.Event.Backend+import OpenTelemetry.Context+import OpenTelemetry.Context.ThreadLocal+import OpenTelemetry.Trace.Core hiding (Event)+import OpenTelemetry.Trace.Id++-- | An 'EventBackend' built on a 'Tracer'.+--+-- When no explicit parent is set, the backend will try to find a parent in the "OpenTelemetry.Context.ThreadLocal" 'Context'.+-- However, it will never update that 'Context', as the primitive 'EventBackend' API has no way to determine if it's being+-- consumed in a scoped context or one allowing for general interleaving.+--+-- When possible, events created with 'emitImmediateEvent' will use the span event API. However, this requires a parent event+-- (explicitly specified or found in the thread-local 'Context'), so the backend will fallback to creating and 'finalize'ing a new+-- 'Span'. If a span event is created, the resulting 'reference' will be to its parent, as span events cannot be parents/links. Span+-- events do not allow for non-parent links, so any `newEventCauses` are dropped; in the future, we may either add them as custom+-- 'Attribute's or fall back to a full span if any are specified.+--+-- Event 'Link's are currently not given any attributes. In the future, arbitrary link metadata could be added to the core 'EventBackend'+-- API, in which case we could add a renderer for the link metadata type.+--+-- Currently the backend lets the underlying 'Tracer' set all timestamps. In the future, 'RenderSelectorOTel' could be allowed to run in+-- @m@ and have a timestamp field.+--+-- Exceptions passed to 'finalize' are 'recordException'ed without any custom attributes. In the future, an @Exception -> [Text, Attribute]@+-- argument could be added, or arbitrary exception metadata added to 'finalize'.+tracerEventBackend :: (MonadIO m) => Tracer -> RenderSelectorOTel s -> EventBackend m Span s+tracerEventBackend tracer render = backend+ where+ backend =+ EventBackend+ { newEvent = \args@(NewEventArgs {..}) -> do+ ctx <- maybe empty id <$> lookupContext+ let ctx' = case newEventParent of+ Just s -> insertSpan s ctx+ Nothing -> ctx+ OTelRendered {..} = render newEventSelector+ links <- traverse (fmap (flip NewLink []) . getSpanContext) newEventCauses+ s <-+ createSpanWithoutCallStack tracer ctx' eventName $+ SpanArguments+ { kind = eventKind,+ attributes = concatMap renderField newEventInitialFields,+ links = links,+ startTime = Nothing+ }+ pure $+ Event+ { reference = s,+ addField = addAttributes s . renderField,+ finalize = \me -> do+ let recordError e = do+ recordException s [("exception.escaped", toAttribute True)] Nothing e+ setStatus s . Error . pack $ show e+ maybe (setStatus s Ok) recordError me+ endSpan s Nothing+ },+ emitImmediateEvent = \args@(NewEventArgs {..}) -> case newEventParent of+ Nothing -> do+ m_ctx <- lookupContext+ case m_ctx >>= lookupSpan of+ Just s ->+ emitImmediateEvent backend $+ args+ { newEventParent = Just s+ }+ Nothing -> do+ ev <- newEvent backend args+ finalize ev Nothing+ pure $ reference ev+ Just s -> do+ let OTelRendered {..} = render newEventSelector+ addEvent s $+ NewEvent+ { newEventName = eventName,+ newEventAttributes = concatMap renderField newEventInitialFields,+ newEventTimestamp = Nothing+ }+ pure s+ }++-- | Render a given selector (and all of its fields) to OpenTelemetry+type RenderSelectorOTel s = forall f. s f -> OTelRendered f++-- | The result of rendering a specific selector with field type @f@+data OTelRendered f = OTelRendered+ { -- | The name of the event. See section on "span name" at <https://opentelemetry.io/docs/reference/specification/trace/api/#span>+ eventName :: !Text,+ -- | See the specification on [SpanKind](https://opentelemetry.io/docs/reference/specification/trace/api/#spankind)+ eventKind :: !SpanKind,+ -- | Render a field to a set of span [attributes](https://opentelemetry.io/docs/reference/specification/common/#attribute).+ --+ -- Note especially the [attribute naming guidelines](https://opentelemetry.io/docs/reference/specification/common/attribute-naming/)+ renderField :: !(f -> [(Text, Attribute)])+ }