packages feed

hs-opentelemetry-api 0.0.2.0 → 0.0.3.0

raw patch · 8 files changed

+391/−36 lines, 8 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

+ OpenTelemetry.Attributes: instance Data.String.IsString OpenTelemetry.Attributes.Attribute
+ OpenTelemetry.Attributes: instance Data.String.IsString OpenTelemetry.Attributes.PrimitiveAttribute
+ OpenTelemetry.Common: Timestamp :: TimeSpec -> Timestamp
+ OpenTelemetry.Common: TraceFlags :: Word8 -> TraceFlags
+ OpenTelemetry.Common: instance GHC.Classes.Eq OpenTelemetry.Common.Timestamp
+ OpenTelemetry.Common: instance GHC.Classes.Eq OpenTelemetry.Common.TraceFlags
+ OpenTelemetry.Common: instance GHC.Classes.Ord OpenTelemetry.Common.Timestamp
+ OpenTelemetry.Common: instance GHC.Classes.Ord OpenTelemetry.Common.TraceFlags
+ OpenTelemetry.Common: instance GHC.Read.Read OpenTelemetry.Common.Timestamp
+ OpenTelemetry.Common: instance GHC.Show.Show OpenTelemetry.Common.Timestamp
+ OpenTelemetry.Common: instance GHC.Show.Show OpenTelemetry.Common.TraceFlags
+ OpenTelemetry.Common: newtype Timestamp
+ OpenTelemetry.Common: newtype TraceFlags
+ OpenTelemetry.Logging.Core: Debug :: SeverityNumber
+ OpenTelemetry.Logging.Core: Debug2 :: SeverityNumber
+ OpenTelemetry.Logging.Core: Debug3 :: SeverityNumber
+ OpenTelemetry.Logging.Core: Debug4 :: SeverityNumber
+ OpenTelemetry.Logging.Core: Error :: SeverityNumber
+ OpenTelemetry.Logging.Core: Error2 :: SeverityNumber
+ OpenTelemetry.Logging.Core: Error3 :: SeverityNumber
+ OpenTelemetry.Logging.Core: Error4 :: SeverityNumber
+ OpenTelemetry.Logging.Core: Fatal :: SeverityNumber
+ OpenTelemetry.Logging.Core: Fatal2 :: SeverityNumber
+ OpenTelemetry.Logging.Core: Fatal3 :: SeverityNumber
+ OpenTelemetry.Logging.Core: Fatal4 :: SeverityNumber
+ OpenTelemetry.Logging.Core: Info :: SeverityNumber
+ OpenTelemetry.Logging.Core: Info2 :: SeverityNumber
+ OpenTelemetry.Logging.Core: Info3 :: SeverityNumber
+ OpenTelemetry.Logging.Core: Info4 :: SeverityNumber
+ OpenTelemetry.Logging.Core: Log :: Maybe Timestamp -> Maybe (TraceId, SpanId, TraceFlags) -> Maybe Text -> Maybe Int64 -> Maybe Text -> body -> Maybe MaterializedResources -> Maybe [(Text, Attribute)] -> Log body
+ OpenTelemetry.Logging.Core: Trace :: SeverityNumber
+ OpenTelemetry.Logging.Core: Trace2 :: SeverityNumber
+ OpenTelemetry.Logging.Core: Trace3 :: SeverityNumber
+ OpenTelemetry.Logging.Core: Trace4 :: SeverityNumber
+ OpenTelemetry.Logging.Core: Unknown :: !Int32 -> SeverityNumber
+ OpenTelemetry.Logging.Core: Warn :: SeverityNumber
+ OpenTelemetry.Logging.Core: Warn2 :: SeverityNumber
+ OpenTelemetry.Logging.Core: Warn3 :: SeverityNumber
+ OpenTelemetry.Logging.Core: Warn4 :: SeverityNumber
+ OpenTelemetry.Logging.Core: [attributes] :: Log body -> Maybe [(Text, Attribute)]
+ OpenTelemetry.Logging.Core: [body] :: Log body -> body
+ OpenTelemetry.Logging.Core: [name] :: Log body -> Maybe Text
+ OpenTelemetry.Logging.Core: [resource] :: Log body -> Maybe MaterializedResources
+ OpenTelemetry.Logging.Core: [severityNumber] :: Log body -> Maybe Int64
+ OpenTelemetry.Logging.Core: [severityText] :: Log body -> Maybe Text
+ OpenTelemetry.Logging.Core: [timestamp] :: Log body -> Maybe Timestamp
+ OpenTelemetry.Logging.Core: [tracingDetails] :: Log body -> Maybe (TraceId, SpanId, TraceFlags)
+ OpenTelemetry.Logging.Core: data Log body
+ OpenTelemetry.Logging.Core: data SeverityNumber
+ OpenTelemetry.Logging.Core: instance GHC.Base.Functor OpenTelemetry.Logging.Core.Log
+ OpenTelemetry.Logging.Core: instance GHC.Classes.Eq OpenTelemetry.Logging.Core.SeverityNumber
+ OpenTelemetry.Logging.Core: instance GHC.Classes.Ord OpenTelemetry.Logging.Core.SeverityNumber
+ OpenTelemetry.Logging.Core: instance GHC.Read.Read OpenTelemetry.Logging.Core.SeverityNumber
+ OpenTelemetry.Logging.Core: instance GHC.Show.Show OpenTelemetry.Logging.Core.SeverityNumber
+ OpenTelemetry.Trace.Core: [tracerProviderOptionsLogger] :: TracerProviderOptions -> Log Text -> IO ()
+ OpenTelemetry.Trace.Core: makeTracer :: TracerProvider -> InstrumentationLibrary -> TracerOptions -> Tracer
- OpenTelemetry.Exporter: Exporter :: (HashMap InstrumentationLibrary (Vector ImmutableSpan) -> IO ExportResult) -> IO () -> Exporter
+ OpenTelemetry.Exporter: Exporter :: (HashMap InstrumentationLibrary (Vector a) -> IO ExportResult) -> IO () -> Exporter a
- OpenTelemetry.Exporter: [exporterExport] :: Exporter -> HashMap InstrumentationLibrary (Vector ImmutableSpan) -> IO ExportResult
+ OpenTelemetry.Exporter: [exporterExport] :: Exporter a -> HashMap InstrumentationLibrary (Vector a) -> IO ExportResult
- OpenTelemetry.Exporter: [exporterShutdown] :: Exporter -> IO ()
+ OpenTelemetry.Exporter: [exporterShutdown] :: Exporter a -> IO ()
- OpenTelemetry.Exporter: data Exporter
+ OpenTelemetry.Exporter: data Exporter a
- OpenTelemetry.Trace.Core: TracerProviderOptions :: IdGenerator -> Sampler -> MaterializedResources -> AttributeLimits -> SpanLimits -> Propagator Context RequestHeaders ResponseHeaders -> TracerProviderOptions
+ OpenTelemetry.Trace.Core: TracerProviderOptions :: IdGenerator -> Sampler -> MaterializedResources -> AttributeLimits -> SpanLimits -> Propagator Context RequestHeaders ResponseHeaders -> (Log Text -> IO ()) -> TracerProviderOptions
- OpenTelemetry.Trace.Core: addAttribute :: MonadIO m => ToAttribute a => Span -> Text -> a -> m ()
+ OpenTelemetry.Trace.Core: addAttribute :: (MonadIO m, ToAttribute a) => Span -> Text -> a -> m ()

Files

ChangeLog.md view
@@ -1,5 +1,9 @@ # Changelog for hs-opentelemetry-api +## 0.0.2.1++- Doc enhancements+ ## 0.0.2.0  - Separate `Link` and `NewLink` into two different datatypes to improve Link creation interface.
hs-opentelemetry-api.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack  name:           hs-opentelemetry-api-version:        0.0.2.0+version:        0.0.3.0 synopsis:       OpenTelemetry API for use by libraries for direct instrumentation or wrapper packages. description:    Please see the README on GitHub at <https://github.com/iand675/hs-opentelemetry/tree/main/api#readme> category:       OpenTelemetry, Telemetry, Monitoring, Observability, Metrics@@ -29,9 +29,11 @@   exposed-modules:       OpenTelemetry.Attributes       OpenTelemetry.Baggage+      OpenTelemetry.Common       OpenTelemetry.Context       OpenTelemetry.Context.ThreadLocal       OpenTelemetry.Exporter+      OpenTelemetry.Logging.Core       OpenTelemetry.Processor       OpenTelemetry.Propagator       OpenTelemetry.Resource
src/OpenTelemetry/Attributes.hs view
@@ -48,6 +48,7 @@ import GHC.Generics import Data.Data import Data.Hashable+import Data.String  -- | Default attribute limits used in the global attribute limit configuration if no environment variables are set. --@@ -118,15 +119,36 @@   deriving stock (Read, Show, Eq, Ord, Data, Generic)   deriving anyclass (Hashable) +-- | Convert a Haskell value to a 'PrimitiveAttribute' value. class ToPrimitiveAttribute a where   toPrimitiveAttribute :: a -> PrimitiveAttribute +-- | An attribute represents user-provided metadata about a span, link, or event.+--+-- Telemetry tools may use this data to support high-cardinality querying, visualization+-- in waterfall diagrams, trace sampling decisions, and more. data Attribute   = AttributeValue PrimitiveAttribute+  -- ^ An attribute representing a single primitive value   | AttributeArray [PrimitiveAttribute]+  -- ^ An attribute representing an array of primitive values.+  --+  -- All values in the array MUST be of the same primitive attribute type.   deriving stock (Read, Show, Eq, Ord, Data, Generic)   deriving anyclass (Hashable) +-- | Create a `TextAttribute` from the string value.+--+-- @since 0.0.2.1+instance IsString PrimitiveAttribute where+  fromString = TextAttribute . fromString++-- | Create a `TextAttribute` from the string value.+--+-- @since 0.0.2.1+instance IsString Attribute where+  fromString = AttributeValue . fromString+ data PrimitiveAttribute   = TextAttribute Text   | BoolAttribute Bool@@ -135,7 +157,19 @@   deriving stock (Read, Show, Eq, Ord, Data, Generic)   deriving anyclass (Hashable) -+-- | Convert a Haskell value to an 'Attribute' value.+--+-- For most values, you can define an instance of 'ToPrimitiveAttribute' and use the default 'toAttribute' implementation:+--+-- @+--+-- data Foo = Foo+--+-- instance ToPrimitiveAttribute Foo where+--   toPrimitiveAttribute Foo = TextAttribute "Foo"+-- instance ToAttribute foo+--+-- @ class ToAttribute a where   toAttribute :: a -> Attribute   default toAttribute :: ToPrimitiveAttribute a => a -> Attribute
+ src/OpenTelemetry/Common.hs view
@@ -0,0 +1,11 @@+module OpenTelemetry.Common where++import System.Clock (TimeSpec)+import Data.Word (Word8)++newtype Timestamp = Timestamp TimeSpec+  deriving (Read, Show, Eq, Ord)++-- | Contain details about the trace. Unlike TraceState values, TraceFlags are present in all traces. The current version of the specification only supports a single flag called sampled.+newtype TraceFlags = TraceFlags Word8+  deriving (Show, Eq, Ord)
src/OpenTelemetry/Context/ThreadLocal.hs view
@@ -1,6 +1,3 @@-{-# LANGUAGE MagicHash #-}-{-# LANGUAGE NumericUnderscores #-}-{-# LANGUAGE UnboxedTuples #-} {-# LANGUAGE UnliftedFFITypes #-}  -----------------------------------------------------------------------------@@ -32,21 +29,6 @@ --   means in practice that any unused contexts will cleaned up upon the next --   garbage collection and may not be actively freed when the program exits. ----- Note that this implementation of context sharing is--- mildly expensive for the garbage collector, hard to reason about without deep--- knowledge of the code you are instrumenting, and has limited guarantees of behavior --- across GHC versions due to internals usage.------ Why use this implementation, then? Depending on the structure of libraries--- that you are attempting to instrument, this may be the only way to smuggle--- a 'Context' in without significant breaking changes to the existing library--- interface.------ The rule of thumb:--- - Where possible, use OpenTelemetry.Context in a reader-esque monad, or---   pass it around directly.--- - When all else fails, use this instead.--- ----------------------------------------------------------------------------- module OpenTelemetry.Context.ThreadLocal    ( @@ -57,6 +39,8 @@   , detachContext   , adjustContext   -- ** Generalized thread-local context functions+  -- You should not use these without using some sort of specific cross-thread coordination mechanism, +  -- as there is no guarantee of what work the remote thread has done yet.   , lookupContextOnThread   , attachContextOnThread   , detachContextFromThread@@ -82,38 +66,56 @@ -- -- Warning: this can easily cause disconnected traces if libraries don't explicitly set the -- context on forked threads.+--+-- @since 0.0.1.0 getContext :: MonadIO m => m Context getContext = fromMaybe empty <$> lookupContext  -- | Retrieve a stored 'Context' for the current thread, if it exists.+--+-- @since 0.0.1.0 lookupContext :: MonadIO m => m (Maybe Context) lookupContext = lookup threadContextMap  -- | Retrieve a stored 'Context' for the provided 'ThreadId', if it exists.+--+-- @since 0.0.1.0 lookupContextOnThread :: MonadIO m => ThreadId -> m (Maybe Context) lookupContextOnThread = lookupOnThread threadContextMap  -- | Store a given 'Context' for the current thread, returning any context previously stored.+--+-- @since 0.0.1.0 attachContext :: MonadIO m => Context -> m (Maybe Context) attachContext = attach threadContextMap  -- | Store a given 'Context' for the provided 'ThreadId', returning any context previously stored.+--+-- @since 0.0.1.0 attachContextOnThread :: MonadIO m => ThreadId -> Context -> m (Maybe Context) attachContextOnThread = attachOnThread threadContextMap  -- | Remove a stored 'Context' for the current thread, returning any context previously stored.+--+-- @since 0.0.1.0 detachContext :: MonadIO m => m (Maybe Context) detachContext = detach threadContextMap  -- | Remove a stored 'Context' for the provided 'ThreadId', returning any context previously stored.+--+-- @since 0.0.1.0 detachContextFromThread :: MonadIO m => ThreadId -> m (Maybe Context) detachContextFromThread = detachFromThread threadContextMap  -- | Alter the context on the current thread using the provided function+--+-- @since 0.0.1.0 adjustContext :: MonadIO m => (Context -> Context) -> m () adjustContext = adjust threadContextMap  -- | Alter the context+--+-- @since 0.0.1.0 adjustContextOnThread :: MonadIO m => ThreadId -> (Context -> Context) -> m () adjustContextOnThread = adjustOnThread threadContextMap 
src/OpenTelemetry/Internal/Trace/Types.hs view
@@ -19,31 +19,62 @@ import GHC.Generics import Network.HTTP.Types (RequestHeaders, ResponseHeaders) import OpenTelemetry.Attributes+import OpenTelemetry.Common import OpenTelemetry.Context.Types+import OpenTelemetry.Logging.Core (Log) import OpenTelemetry.Trace.Id import OpenTelemetry.Resource import OpenTelemetry.Trace.Id.Generator import OpenTelemetry.Propagator (Propagator) import OpenTelemetry.Trace.TraceState import OpenTelemetry.Util-import System.Clock (TimeSpec)   data ExportResult   = Success   | Failure (Maybe SomeException) +-- | An identifier for the library that provides the instrumentation for a given Instrumented Library. +-- Instrumented Library and Instrumentation Library may be the same library if it has built-in OpenTelemetry instrumentation.+--+-- The inspiration of the OpenTelemetry project is to make every library and application observable out of the box by having them call OpenTelemetry API directly. +-- However, many libraries will not have such integration, and as such there is a need for a separate library which would inject such calls, using mechanisms such as wrapping interfaces,+-- subscribing to library-specific callbacks, or translating existing telemetry into the OpenTelemetry model.+--+-- A library that enables OpenTelemetry observability for another library is called an Instrumentation Library.+--+-- An instrumentation library should be named to follow any naming conventions of the instrumented library (e.g. 'middleware' for a web framework).+--+-- If there is no established name, the recommendation is to prefix packages with "hs-opentelemetry-instrumentation", followed by the instrumented library name itself.+--+-- In general, you can initialize the instrumentation library like so:+--+-- @+--+-- import qualified Data.Text as T+-- import Data.Version (showVersion)+-- import Paths_your_package_name+--+-- instrumentationLibrary :: InstrumentationLibrary+-- instrumentationLibrary = InstrumentationLibrary+--   { libraryName = "your_package_name"+--   , libraryVersion = T.pack $ showVersion version+--   }+--+-- @ data InstrumentationLibrary = InstrumentationLibrary   { libraryName :: {-# UNPACK #-} !Text+  -- ^ The name of the instrumentation library   , libraryVersion :: {-# UNPACK #-} !Text+  -- ^ The version of the instrumented library   } deriving (Ord, Eq, Generic, Show)  instance Hashable InstrumentationLibrary instance IsString InstrumentationLibrary where   fromString str = InstrumentationLibrary (fromString str) "" -data Exporter = Exporter-  { exporterExport :: HashMap InstrumentationLibrary (Vector ImmutableSpan) -> IO ExportResult+data Exporter a = Exporter+  { exporterExport :: HashMap InstrumentationLibrary (Vector a) -> IO ExportResult   , exporterShutdown :: IO ()   } @@ -77,7 +108,7 @@   }  {- | -'Tracer's can be create from a 'TracerProvider'.+'Tracer's can be created from a 'TracerProvider'. -} data TracerProvider = TracerProvider   { tracerProviderProcessors :: !(Vector Processor)@@ -87,6 +118,7 @@   , tracerProviderAttributeLimits :: !AttributeLimits   , tracerProviderSpanLimits :: !SpanLimits   , tracerProviderPropagators :: !(Propagator Context RequestHeaders ResponseHeaders)+  , tracerProviderLogger :: Log Text -> IO ()   }  -- | The 'Tracer' is responsible for creating 'Span's.@@ -95,13 +127,18 @@ -- it instruments. data Tracer = Tracer   { tracerName :: {-# UNPACK #-} !InstrumentationLibrary+  -- ^ Get the name of the 'Tracer'+  --+  -- @since 0.0.10   , tracerProvider :: !TracerProvider+  -- ^ Get the TracerProvider from which the 'Tracer' was created+  --+  -- @since 0.0.10   } -newtype Timestamp = Timestamp TimeSpec-  deriving (Show, Eq, Ord)- {- |+This is a link that is being added to a span which is going to be created.+ A @Span@ may be linked to zero or more other @Spans@ (defined by @SpanContext@) that are causally related.  @Link@s can point to Spans inside a single Trace or across different Traces. @Link@s can be used to represent  batched operations where a @Span@ was initiated by multiple initiating Spans, each representing a single incoming @@ -128,6 +165,27 @@   }   deriving (Show) +{- |+This is an immutable link for an existing span.++A @Span@ may be linked to zero or more other @Spans@ (defined by @SpanContext@) that are causally related. +@Link@s can point to Spans inside a single Trace or across different Traces. @Link@s can be used to represent +batched operations where a @Span@ was initiated by multiple initiating Spans, each representing a single incoming +item being processed in the batch.++Another example of using a Link is to declare the relationship between the originating and following trace. +This can be used when a Trace enters trusted boundaries of a service and service policy requires the generation +of a new Trace rather than trusting the incoming Trace context. The new linked Trace may also represent a long +running asynchronous data processing operation that was initiated by one of many fast incoming requests.++When using the scatter/gather (also called fork/join) pattern, the root operation starts multiple downstream +processing operations and all of them are aggregated back in a single Span. +This last Span is linked to many operations it aggregates. +All of them are the Spans from the same Trace. And similar to the Parent field of a Span. +It is recommended, however, to not set parent of the Span in this scenario as semantically the parent field +represents a single parent scenario, in many cases the parent Span fully encloses the child Span. +This is not the case in scatter/gather and batch scenarios.+-} data Link = Link   { frozenLinkContext :: !SpanContext   -- ^ @SpanContext@ of the @Span@ to link to.@@ -233,30 +291,54 @@   compare Ok (Error _) = GT   compare Ok Ok = EQ +-- | The frozen representation of a 'Span' that originates from the currently running process.+--+-- Only 'Processor's and 'Exporter's should use rely on this interface. data ImmutableSpan = ImmutableSpan   { spanName :: Text+  -- ^ A name identifying the role of the span (like function or method name).   , spanParent :: Maybe Span   , spanContext :: SpanContext+  -- ^ A `SpanContext` represents the portion of a `Span` which must be serialized and+  -- propagated along side of a distributed context. `SpanContext`s are immutable.   , spanKind :: SpanKind+  -- ^ The kind of the span. See 'SpanKind's documentation for the semantics+  -- of the various values that may be specified.   , spanStart :: Timestamp+  -- ^ A timestamp that corresponds to the start of the span   , spanEnd :: Maybe Timestamp+  -- ^ A timestamp that corresponds to the end of the span, if the span has ended.   , spanAttributes :: Attributes   , spanLinks :: FrozenBoundedCollection Link+  -- ^ Zero or more links to related spans. Links can be useful for connecting causal relationships between things like web requests that enqueue asynchronous tasks to be processed.   , spanEvents :: AppendOnlyBoundedCollection Event+  -- ^ Events, which denote a point in time occurrence. These can be useful for recording data about a span such as when an exception was thrown, or to emit structured logs into the span tree.   , spanStatus :: SpanStatus   , spanTracer :: Tracer   -- ^ Creator of the span   } +-- | A 'Span' is the fundamental type you'll work with to trace your systems.+--+-- A span is a single piece of instrumentation from a single location in your code or infrastructure. A span represents a single "unit of work" done by a service. Each span contains several key pieces of data:+--+-- - A service name identifying the service the span is from+-- - A name identifying the role of the span (like function or method name)+-- - A timestamp that corresponds to the start of the span+-- - A duration that describes how long that unit of work took to complete+-- - An ID that uniquely identifies the span+-- - A trace ID identifying which trace the span belongs to+-- - A parent ID representing the parent span that called this span. (There is no parent ID for the root span of a given trace, which denotes that it's the start of the trace.)+-- - Any additional metadata that might be helpful.+-- - Zero or more links to related spans. Links can be useful for connecting causal relationships between things like web requests that enqueue asynchronous tasks to be processed.+-- - Events, which denote a point in time occurrence. These can be useful for recording data about a span such as when an exception was thrown, or to emit structured logs into the span tree.+--+-- A trace is made up of multiple spans. Tracing vendors such as Zipkin, Jaeger, Honeycomb, Datadog, Lightstep, etc. use the metadata from each span to reconstruct the relationships between them and generate a trace diagram. data Span   = Span (IORef ImmutableSpan)   | FrozenSpan SpanContext   | Dropped SpanContext --- |  contain details about the trace. Unlike TraceState values, TraceFlags are present in all traces. The current version of the specification only supports a single flag called sampled.-newtype TraceFlags = TraceFlags Word8-  deriving (Show, Eq, Ord)- -- | TraceFlags with the @sampled@ flag not set. This means that it is up to the -- sampling configuration to decide whether or not to sample the trace. defaultTraceFlags :: TraceFlags@@ -323,6 +405,14 @@  newtype NonRecordingSpan = NonRecordingSpan SpanContext +-- | A “log” that happens as part of a span. An operation that is too fast for its own span, but too unique to roll up into its parent span.+--+-- Events contain a name, a timestamp, and an optional set of Attributes, along with a timestamp. Events represent an event that occurred at a specific time within a span’s workload.+--+-- When creating an event, this is the version that you will use. Attributes added that exceed the configured attribute limits will be dropped,+-- which is accounted for in the 'Event' structure.+--+-- @since 0.0.1.0 data NewEvent = NewEvent   { newEventName :: Text   -- ^ The name of an event. Ideally this should be a relatively unique, but low cardinality value.
+ src/OpenTelemetry/Logging/Core.hs view
@@ -0,0 +1,139 @@+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+module OpenTelemetry.Logging.Core where+import OpenTelemetry.Common+import OpenTelemetry.Trace.Id (TraceId, SpanId)+import OpenTelemetry.Attributes ( Attribute )+import Data.Text (Text)+import Data.Int (Int64, Int32)+import OpenTelemetry.Resource (MaterializedResources)++data Log body = Log+  { timestamp :: Maybe Timestamp+  -- ^ Time when the event occurred measured by the origin clock. This field is optional, it may be missing if the timestamp is unknown.+  , tracingDetails :: Maybe (TraceId, SpanId, TraceFlags)+  -- ^ Tuple contains three fields:+  -- +  -- - Request trace id as defined in W3C Trace Context. Can be set for logs that are part of request processing and have an assigned trace id.+  -- - Span id. Can be set for logs that are part of a particular processing span.+  -- - Trace flag as defined in W3C Trace Context specification. At the time of writing the specification defines one flag - the SAMPLED flag.+  , severityText :: Maybe Text+  -- ^ severity text (also known as log level). This is the original string representation of the severity as it is known at the source. If this field is missing and SeverityNumber is present then the short name that corresponds to the SeverityNumber may be used as a substitution. This field is optional.+  , severityNumber :: Maybe Int64+  -- ^ SeverityNumber is an integer number. Smaller numerical values correspond to less severe events (such as debug events), larger numerical values correspond to more severe events (such as errors and critical events). The following table defines the meaning of SeverityNumber value:+  --+  -- +-----------------------+-------------+------------------------------------------------------------------------------------------++  -- | SeverityNumber range  | Range name  | Meaning                                                                                  |+  -- +=======================+=============+==========================================================================================++  -- | 1-4                   | TRACE       | A fine-grained debugging event. Typically disabled in default configurations.            |+  -- +-----------------------+-------------+------------------------------------------------------------------------------------------++  -- | 5-8                   | DEBUG       | A debugging event.                                                                       |+  -- +-----------------------+-------------+------------------------------------------------------------------------------------------++  -- | 9-12                  | INFO        | An informational event. Indicates that an event happened.                                |+  -- +-----------------------+-------------+------------------------------------------------------------------------------------------++  -- | 13-16                 | WARN        | A warning event. Not an error but is likely more important than an informational event.  |+  -- +-----------------------+-------------+------------------------------------------------------------------------------------------++  -- | 17-20                 | ERROR       | An error event. Something went wrong.                                                    |+  -- +-----------------------+-------------+------------------------------------------------------------------------------------------++  -- | 21-24                 | FATAL       | A fatal error such as application or system crash.                                       |+  -- +-----------------------+-------------+------------------------------------------------------------------------------------------++  --+  , name :: Maybe Text+  -- ^ Short low cardinality event type that does not contain varying parts. Name describes what happened (e.g. "ProcessStarted"). Recommended to be no longer than 50 characters. Typically used for filtering and grouping purposes in backends.+  , body :: body+  {-+  Type any+    Value of type any can be one of the following:++    A scalar value: number, string or boolean,++    A byte array,++    An array (a list) of any values,++    A map<string, any>.+  -}+  -- ^ A value containing the body of the log record. Can be for example a human-readable string message (including multi-line) describing the event in a free form or it can be a structured data composed of arrays and maps of other values. First-party Applications SHOULD use a string message. However, a structured body may be necessary to preserve the semantics of some existing log formats. Can vary for each occurrence of the event coming from the same source. This field is optional.+  , resource :: Maybe MaterializedResources+  -- ^ Describes the source of the log, aka resource. Multiple occurrences of events coming from the same event source can happen across time and they all have the same value of Resource. Can contain for example information about the application that emits the record or about the infrastructure where the application runs. Data formats that represent this data model may be designed in a manner that allows the Resource field to be recorded only once per batch of log records that come from the same source. SHOULD follow OpenTelemetry semantic conventions for Resources. This field is optional.+  , attributes :: Maybe [(Text, Attribute)]+  -- ^ Additional information about the specific event occurrence. Unlike the Resource field, which is fixed for a particular source, Attributes can vary for each occurrence of the event coming from the same source. Can contain information about the request context (other than TraceId/SpanId). SHOULD follow OpenTelemetry semantic conventions for Log Attributes or semantic conventions for Span Attributes. This field is optional.+  } deriving stock (Functor)++data SeverityNumber+  = Trace+  | Trace2+  | Trace3+  | Trace4+  | Debug+  | Debug2+  | Debug3+  | Debug4+  | Info+  | Info2+  | Info3+  | Info4+  | Warn+  | Warn2+  | Warn3+  | Warn4+  | Error+  | Error2+  | Error3+  | Error4+  | Fatal+  | Fatal2+  | Fatal3+  | Fatal4+  | Unknown !Int32+  deriving (Eq, Ord, Read, Show)++-- severityTrace :: SeverityNumber+-- severityTrace = 1+-- severityTrace2 :: SeverityNumber+-- severityTrace2 = 2+-- severityTrace3 :: SeverityNumber+-- severityTrace3 = 3+-- severityTrace4 :: SeverityNumber+-- severityTrace4 = 4+-- severityDebug :: SeverityNumber+-- severityDebug = 5+-- severityDebug2 :: SeverityNumber+-- severityDebug2 = 6+-- severityDebug3 :: SeverityNumber+-- severityDebug3 = 7+-- severityDebug4 :: SeverityNumber+-- severityDebug4 = 8+-- severityInfo :: SeverityNumber+-- severityInfo = 9+-- severityInfo2 :: SeverityNumber+-- severityInfo2 = 10+-- severityInfo3 :: SeverityNumber+-- severityInfo3 = 11+-- severityInfo4 :: SeverityNumber+-- severityInfo4 = 12+-- severityWarn :: SeverityNumber+-- severityWarn = 13+-- severityWarn2 :: SeverityNumber+-- severityWarn2 = 14+-- severityWarn3 :: SeverityNumber+-- severityWarn3 = 15+-- severityWarn4 :: SeverityNumber+-- severityWarn4 = 16+-- severityError :: SeverityNumber+-- severityError = 17+-- severityError2 :: SeverityNumber+-- severityError2 = 18+-- severityError3 :: SeverityNumber+-- severityError3 = 19+-- severityError4 :: SeverityNumber+-- severityError4 = 20+-- severityFatal :: SeverityNumber+-- severityFatal = 21+-- severityFatal2 :: SeverityNumber+-- severityFatal2 = 22+-- severityFatal3 :: SeverityNumber+-- severityFatal3 = 23+-- severityFatal4 :: SeverityNumber+-- severityFatal4 = 24
src/OpenTelemetry/Trace/Core.hs view
@@ -18,7 +18,7 @@ -- -- A trace contains a single root span which encapsulates the end-to-end latency for the entire request. You can think of this as a single logical operation, such as clicking a button in a web application to add a product to a shopping cart. The root span would measure the time it took from an end-user clicking that button to the operation being completed or failing (so, the item is added to the cart or some error occurs) and the result being displayed to the user. A trace is comprised of the single root span and any number of child spans, which represent operations taking place as part of the request. Each span contains metadata about the operation, such as its name, start and end timestamps, attributes, events, and status. -- --- To create and manage spans in OpenTelemetry, the OpenTelemetry API provides the tracer interface. This object is responsible for tracking the active span in your process, and allows you to access the current span in order to perform operations on it such as adding attributes, events, and finishing it when the work it tracks is complete. One or more tracer objects can be created in a process through the tracer provider, a factory interface that allows for multiple tracers to be instantiated in a single process with different options.+-- To create and manage 'Span's in OpenTelemetry, the <https://hackage.haskell.org/package/hs-opentelemetry-api OpenTelemetry API> provides the tracer interface. This object is responsible for tracking the active span in your process, and allows you to access the current span in order to perform operations on it such as adding attributes, events, and finishing it when the work it tracks is complete. One or more tracer objects can be created in a process through the tracer provider, a factory interface that allows for multiple 'Tracer's to be instantiated in a single process with different options. --  -- Generally, the lifecycle of a span resembles the following: -- @@ -54,6 +54,7 @@   , Tracer   , tracerName   , HasTracer(..)+  , makeTracer   , getTracer   , getImmutableSpanTracer   , getTracerTracerProvider@@ -124,7 +125,7 @@ import Control.Applicative import Control.Concurrent.Async import Control.Concurrent (myThreadId)-import Control.Exception (Exception(..), try)+import Control.Exception ( Exception(..), try, SomeException(..) ) import Control.Monad import Control.Monad.IO.Class import Data.Coerce@@ -140,6 +141,7 @@ import OpenTelemetry.Attributes import qualified OpenTelemetry.Attributes as A import OpenTelemetry.Context+import OpenTelemetry.Common import OpenTelemetry.Context.ThreadLocal import OpenTelemetry.Propagator (Propagator) import OpenTelemetry.Internal.Trace.Types@@ -154,8 +156,8 @@ import System.Clock import System.IO.Unsafe import System.Timeout (timeout)-import Control.Exception (SomeException(..)) import Control.Monad.IO.Unlift+import OpenTelemetry.Logging.Core (Log) -- | Create a 'Span'.  -- -- If the provided 'Context' has a span in it (inserted via 'OpenTelemetry.Context.insertSpan'),@@ -285,12 +287,21 @@       , frozenLinkAttributes = A.addAttributes (limitBy t linkAttributeCountLimit) A.emptyAttributes linkAttributes       } +-- | The simplest function for annotating code with trace information.+--+-- @since 0.0.1.0 inSpan   :: (MonadUnliftIO m, HasCallStack)   => Tracer   -> Text+  -- ^ The name of the span. This may be updated later via 'updateName'   -> SpanArguments+  -- ^ Additional options for creating the span, such as 'SpanKind',+  -- span links, starting attributes, etc.   -> m a+  -- ^ The action to perform. 'inSpan' will record the time spent on the+  -- action without forcing strict evaluation of the result. Any uncaught+  -- exceptions will be recorded and rethrown.   -> m a inSpan t n args m = inSpan'' t callStack n args (const m) @@ -298,6 +309,7 @@   :: (MonadUnliftIO m, HasCallStack)   => Tracer   -> Text+  -- ^ The name of the span. This may be updated later via 'updateName'   -> SpanArguments   -> (Span -> m a)   -> m a@@ -307,7 +319,10 @@   :: (MonadUnliftIO m, HasCallStack)   => Tracer   -> CallStack+  -- ^ Record the location of the span in the codebase using the provided+  -- callstack for source location info.   -> Text+  -- ^ The name of the span. This may be updated later via 'updateName'   -> SpanArguments   -> (Span -> m a)   -> m a@@ -351,7 +366,8 @@ isRecording (FrozenSpan _) = pure True isRecording (Dropped _) = pure False -{- |+{- | Add an attribute to a span. Only has a useful effect on recording spans.+ As an application developer when you need to record an attribute first consult existing semantic conventions for Resources, Spans, and Metrics. If an appropriate name does not exists you will need to come up with a new name. To do that consider a few options:  The name is specific to your company and may be possibly used outside the company as well. To avoid clashes with names introduced by other companies (in a distributed system that uses applications from multiple vendors) it is recommended to prefix the new name by your company’s reverse domain name, e.g. 'com.acme.shopname'.@@ -367,8 +383,17 @@ For example, the 'otel.library.name' attribute is used to record the instrumentation library name, which is an OpenTelemetry concept that is natively represented in OTLP, but does not have an equivalent in other telemetry formats and protocols.  Any additions to the 'otel.*' namespace MUST be approved as part of OpenTelemetry specification.++@since 0.0.1.0 -}-addAttribute :: MonadIO m => A.ToAttribute a => Span -> Text -> a -> m ()+addAttribute :: (MonadIO m, A.ToAttribute a) +  => Span +  -- ^ Span to add the attribute to+  -> Text +  -- ^ Attribute name+  -> a +  -- ^ Attribute value+  -> m () addAttribute (Span s) k v = liftIO $ modifyIORef s $ \i -> i   { spanAttributes =       OpenTelemetry.Attributes.addAttribute@@ -380,6 +405,11 @@ addAttribute (FrozenSpan _) _ _ = pure () addAttribute (Dropped _) _ _ = pure () +-- | A convenience function related to 'addAttribute' that adds multiple attributes to a span at the same time.+--+-- This function may be slightly more performant than repeatedly calling 'addAttribute'.  +--+-- @since 0.0.1.0 addAttributes :: MonadIO m => Span -> [(Text, A.Attribute)] -> m () addAttributes (Span s) attrs = liftIO $ modifyIORef s $ \i -> i   { spanAttributes =@@ -391,6 +421,9 @@ addAttributes (FrozenSpan _) _ = pure () addAttributes (Dropped _) _ = pure () +-- | Add an event to a recording span. Events will not be recorded for remote spans and dropped spans.+--+-- @since 0.0.1.0 addEvent :: MonadIO m => Span -> NewEvent -> m () addEvent (Span s) NewEvent{..} = liftIO $ do   t <- maybe getTimestamp pure newEventTimestamp@@ -411,6 +444,8 @@ -- | Sets the Status of the Span. If used, this will override the default @Span@ status, which is @Unset@. -- -- These values form a total order: Ok > Error > Unset. This means that setting Status with StatusCode=Ok will override any prior or future attempts to set span Status with StatusCode=Error or StatusCode=Unset.+--+-- @since 0.0.1.0 setStatus :: MonadIO m => Span -> SpanStatus -> m () setStatus (Span s) st = liftIO $ modifyIORef s $ \i -> i   { spanStatus = if st > spanStatus i@@ -571,8 +606,14 @@   , tracerProviderOptionsAttributeLimits :: AttributeLimits   , tracerProviderOptionsSpanLimits :: SpanLimits   , tracerProviderOptionsPropagators :: Propagator Context RequestHeaders ResponseHeaders+  , tracerProviderOptionsLogger :: Log Text -> IO ()   } +-- | Options for creating a 'TracerProvider' with invalid ids, no resources, default limits, and no propagators.+--+-- In effect, tracing is a no-op when using this configuration.+--+-- @since 0.0.1.0 emptyTracerProviderOptions :: TracerProviderOptions emptyTracerProviderOptions = TracerProviderOptions   dummyIdGenerator@@ -581,6 +622,7 @@   defaultAttributeLimits   defaultSpanLimits   mempty+  (\_ -> pure ())  -- | Initialize a new tracer provider --@@ -596,10 +638,25 @@     (tracerProviderOptionsAttributeLimits opts)     (tracerProviderOptionsSpanLimits opts)     (tracerProviderOptionsPropagators opts)+    (tracerProviderOptionsLogger opts) +-- | Access the globally configured 'TracerProvider'. Once the +-- the global tracer provider is initialized via the OpenTelemetry SDK,+-- 'Tracer's created from this 'TracerProvider' will export spans to their+-- configured exporters. Prior to that, any 'Tracer's acquired from the+-- uninitialized 'TracerProvider' will create no-op spans.+--+-- @since 0.0.1.0 getGlobalTracerProvider :: MonadIO m => m TracerProvider getGlobalTracerProvider = liftIO $ readIORef globalTracer +-- | Overwrite the globally configured 'TracerProvider'.+--+-- 'Tracer's acquired from the previously installed 'TracerProvider'+-- will continue to use that 'TracerProvider's configured span processors,+-- exporters, and other settings.+--+-- @since 0.0.1.0 setGlobalTracerProvider :: MonadIO m => TracerProvider -> m () setGlobalTracerProvider = liftIO . writeIORef globalTracer @@ -609,19 +666,35 @@ getTracerProviderPropagators :: TracerProvider -> Propagator Context RequestHeaders ResponseHeaders getTracerProviderPropagators = tracerProviderPropagators +-- | Tracer configuration options. newtype TracerOptions = TracerOptions   { tracerSchema :: Maybe Text+  -- ^ OpenTelemetry provides a schema for describing common attributes so that backends can easily parse and identify relevant information. +  -- It is important to understand these conventions when writing instrumentation, in order to normalize your data and increase its utility.+  --+  -- In particular, this option is valuable to set when possible, because it allows vendors to normalize data accross releases in order to account+  -- for attribute name changes.   } +-- | Default Tracer options tracerOptions :: TracerOptions tracerOptions = TracerOptions Nothing +-- | A small utility lens for extracting a 'Tracer' from a larger data type+--+-- This will generally be most useful as a means of implementing 'OpenTelemetry.Trace.Monad.getTracer'+--+-- @since 0.0.1.0 class HasTracer s where   tracerL :: Lens' s Tracer +makeTracer :: TracerProvider -> InstrumentationLibrary -> TracerOptions -> Tracer+makeTracer tp n TracerOptions{} = Tracer n tp+ getTracer :: MonadIO m => TracerProvider -> InstrumentationLibrary -> TracerOptions -> m Tracer getTracer tp n TracerOptions{} = liftIO $ do   pure $ Tracer n tp+{-# DEPRECATED getTracer "use makeTracer" #-}  getImmutableSpanTracer :: ImmutableSpan -> Tracer getImmutableSpanTracer = spanTracer