{-# LANGUAGE DataKinds #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE DefaultSignatures #-} ----------------------------------------------------------------------------- -- | -- Module : OpenTelemetry.Resource -- Copyright : (c) Ian Duncan, 2021 -- License : BSD-3 -- Description : Facilities for attaching metadata attributes to all spans in a trace -- Maintainer : Ian Duncan -- Stability : experimental -- Portability : non-portable (GHC extensions) -- -- A Resource is an immutable representation of the entity producing -- telemetry. For example, a process producing telemetry that is running in -- a container on Kubernetes has a Pod name, it is in a namespace and -- possibly is part of a Deployment which also has a name. All three of -- these attributes can be included in the Resource. -- ----------------------------------------------------------------------------- module OpenTelemetry.Resource ( -- * Creating resources directly mkResource , Resource , (.=) , (.=?) , ResourceMerge , mergeResources -- * Creating resources from data structures , ToResource(..) , materializeResources -- * Using resources with a 'OpenTelemetry.Trace.TracerProvider' , MaterializedResources , emptyMaterializedResources , getMaterializedResourcesSchema , getMaterializedResourcesAttributes ) where import Data.Proxy (Proxy(..)) import Data.Text (Text) import GHC.TypeLits import Data.Maybe (catMaybes) import OpenTelemetry.Attributes -- | A set of attributes created from one or more resources. -- -- A Resource is an immutable representation of the entity producing telemetry as Attributes. -- For example, a process producing telemetry that is running in a container on Kubernetes has a Pod name, -- it is in a namespace and possibly is part of a Deployment which also has a name. -- -- All three of these attributes can be included in the Resource. -- -- Note that there are certain that have prescribed meanings. -- -- A number of these standard resources may be found in the @OpenTelemetry.Resource.*@ modules. -- -- The primary purpose of resources as a first-class concept in the SDK is decoupling of discovery of resource information from exporters. -- This allows for independent development and easy customization for users that need to integrate with closed source environments. newtype Resource (schema :: Maybe Symbol) = Resource Attributes -- | Utility function to create a resource from a list -- of fields and attributes. See the '.=' and '.=?' functions. -- -- @since 0.0.1.0 mkResource :: [Maybe (Text, Attribute)] -> Resource r mkResource = Resource . unsafeAttributesFromListIgnoringLimits . catMaybes -- | Utility function to convert a required resource attribute -- into the format needed for 'mkResource'. (.=) :: ToAttribute a => Text -> a -> Maybe (Text, Attribute) k .= v = Just (k, toAttribute v) -- | Utility function to convert an optional resource attribute -- into the format needed for 'mkResource'. (.=?) :: ToAttribute a => Text -> Maybe a -> Maybe (Text, Attribute) k .=? mv = (\k' v -> (k', toAttribute v)) k <$> mv instance Semigroup (Resource s) where (<>) (Resource l) (Resource r) = Resource (unsafeMergeAttributesIgnoringLimits l r) instance Monoid (Resource s) where mempty = Resource emptyAttributes -- | Static checks to prevent invalid resources from being merged. -- -- Note: This is intended to be utilized for merging of resources whose attributes -- come from different sources, -- such as environment variables, or metadata extracted from the host or container. -- -- The resulting resource will have all attributes that are on any of the two input resources. -- If a key exists on both the old and updating resource, the value of the updating -- resource will be picked (even if the updated value is "empty"). -- -- The resulting resource will have the Schema URL calculated as follows: -- -- - If the old resource's Schema URL is empty then the resulting resource's Schema -- URL will be set to the Schema URL of the updating resource, -- - Else if the updating resource's Schema URL is empty then the resulting -- resource's Schema URL will be set to the Schema URL of the old resource, -- - Else if the Schema URLs of the old and updating resources are the same then -- that will be the Schema URL of the resulting resource, -- - Else this is a merging error (this is the case when the Schema URL of the old -- and updating resources are not empty and are different). The resulting resource is -- therefore statically prohibited by this type-level function. -- type family ResourceMerge schemaLeft schemaRight :: Maybe Symbol where ResourceMerge 'Nothing 'Nothing = 'Nothing ResourceMerge 'Nothing ('Just s) = 'Just s ResourceMerge ('Just s) 'Nothing = 'Just s ResourceMerge ('Just s) ('Just s) = 'Just s -- | Combine two 'Resource' values into a new 'Resource' that contains the -- attributes of the two inputs. -- -- See the 'ResourceMerge' documentation about the additional semantics of merging two resources. -- -- @since 0.0.1.0 mergeResources :: Resource old -- ^ the old resource -> Resource new -- ^ the updating resource whose attributes take precedence -> Resource (ResourceMerge old new) mergeResources (Resource l) (Resource r) = Resource (unsafeMergeAttributesIgnoringLimits l r) -- | A convenience class for converting arbitrary data into resources. class ToResource a where -- | Resource schema (if any) associated with the defined resource type ResourceSchema a :: Maybe Symbol type ResourceSchema a = 'Nothing -- | Convert the input value to a 'Resource' toResource :: a -> Resource (ResourceSchema a) class MaterializeResource schema where -- | Convert resource fields into a version that discharges the schema from the -- type level to the runtime level. materializeResources :: Resource schema -> MaterializedResources instance MaterializeResource 'Nothing where materializeResources (Resource attrs) = MaterializedResources Nothing attrs instance KnownSymbol s => MaterializeResource ('Just s) where materializeResources (Resource attrs) = MaterializedResources (Just $ symbolVal (Proxy @s)) attrs -- | A read-only resource attribute collection with an associated schema. data MaterializedResources = MaterializedResources { materializedResourcesSchema :: Maybe String , materializedResourcesAttributes :: Attributes } -- | A placeholder for 'MaterializedResources' when no resource information is -- available, needed, or required. -- -- @since 0.0.1.0 emptyMaterializedResources :: MaterializedResources emptyMaterializedResources = MaterializedResources Nothing emptyAttributes -- | Access the schema for a 'MaterializedResources' value. -- -- @since 0.0.1.0 getMaterializedResourcesSchema :: MaterializedResources -> Maybe String getMaterializedResourcesSchema = materializedResourcesSchema -- | Access the attributes for a 'MaterializedResources' value. -- -- @since 0.0.1.0 getMaterializedResourcesAttributes :: MaterializedResources -> Attributes getMaterializedResourcesAttributes = materializedResourcesAttributes