hs-opentelemetry-api
Copyright(c) Ian Duncan 2026
LicenseBSD-3
Stabilityexperimental
Safe HaskellNone
LanguageHaskell2010

OpenTelemetry.Log.Core

Description

Overview

This module provides the Logs Bridge API for connecting existing logging systems (e.g. monad-logger, katip, co-log) to OpenTelemetry. It is not intended as a direct logging API for end users; instead, it gives logging library authors the hooks to route log records through the OpenTelemetry pipeline (processors, exporters, and correlation with traces).

Quick example

import OpenTelemetry.Log.Core

main :: IO ()
main = do
  -- Create a provider (typically done by the SDK during initialization)
  lp <- createLoggerProvider processors emptyLoggerProviderOptions
  let logger = makeLogger lp "my-app"

  -- Emit a log record
  emitLogRecord logger $ LogRecordArguments
    { severityNumber  = Just SeverityNumberInfo
    , severityText    = Just INFO
    , body            = Just (toValue ("User logged in" :: Text))
    , attributes      = mempty
    , timestamp       = Nothing
    , observedTimestamp = Nothing
    , traceContext     = Nothing
    , instrumentationAttributes = Nothing
    }

Key concepts

LoggerProvider
Factory for Loggers. Holds processors, resource info, and attribute limits. Create with createLoggerProvider.
Logger
Obtained from a LoggerProvider, scoped to an instrumentation library. Use makeLogger or getLogger.
LogRecord
A single log entry with severity, body, attributes, and optional correlation to a trace span. Emitted via emitLogRecord.

Severity filtering

You can set a minimum severity level to suppress low-priority logs:

setLoggerMinSeverity lp (Just SeverityNumberWarn)
-- Now only WARN and above are emitted

Both loggerIsEnabled and emitLogRecord respect this threshold. Use Nothing to disable filtering.

Spec reference

https://opentelemetry.io/docs/specs/otel/logs/bridge-api/

Synopsis

LoggerProvider operations

data LoggerProvider Source #

Factory for creating Logger instances. Holds processors, resource, attribute limits, and shutdown state.

All operations on LoggerProvider are safe for concurrent use from multiple threads.

Spec: https://opentelemetry.io/docs/specs/otel/logs/sdk/#loggerprovider

Since: 0.0.1.0

Constructors

LoggerProvider 

Fields

data LoggerProviderOptions Source #

Since: 0.0.1.0

Constructors

LoggerProviderOptions 

Fields

emptyLoggerProviderOptions :: LoggerProviderOptions Source #

Options for creating a LoggerProvider with no resources and default limits.

In effect, logging is a no-op when using this configuration and no-op Processors.

Since: 0.0.1.0

createLoggerProvider :: MonadIO m => [LogRecordProcessor] -> LoggerProviderOptions -> m LoggerProvider Source #

Initialize a new LoggerProvider

You should generally use getGlobalLoggerProvider for most applications.

Since: 0.0.1.0

setGlobalLoggerProvider :: MonadIO m => LoggerProvider -> m () Source #

Overwrite the globally configured LoggerProvider.

Loggers acquired from the previously installed LoggerProviders will continue to use that LoggerProviders settings.

Since: 0.0.1.0

getGlobalLoggerProvider :: MonadIO m => m LoggerProvider Source #

Access the globally configured LoggerProvider. This LoggerProvider is no-op until initialized by the SDK

Since: 0.0.1.0

shutdownLoggerProvider Source #

Arguments

:: MonadIO m 
=> LoggerProvider 
-> Maybe Int

Optional timeout in microseconds, defaults to 5,000,000 (5s)

-> m ShutdownResult 

This method provides a way for provider to do any cleanup required.

This will also trigger shutdowns on all internal processors.

Since: 0.0.1.0

forceFlushLoggerProvider Source #

Arguments

:: MonadIO m 
=> LoggerProvider 
-> Maybe Int

Optional timeout in microseconds, defaults to 5,000,000 (5s)

-> m FlushResult

Result that denotes whether the flush action succeeded, failed, or timed out.

This method provides a way for provider to immediately export all LogRecords that have not yet been exported for all the internal processors.

Since: 0.0.1.0

Logger operations

data InstrumentationLibrary Source #

An instrumentation scope identifies the library or component providing instrumentation. The OpenTelemetry specification renamed this concept from "Instrumentation Library" to "Instrumentation Scope"; this type retains the old constructor name for backwards compatibility but InstrumentationScope is the preferred type alias.

Spec: https://opentelemetry.io/docs/specs/otel/common/instrumentation-scope/

Since: 0.0.1.0

Constructors

InstrumentationLibrary 

Fields

Instances

Instances details
IsString InstrumentationLibrary Source # 
Instance details

Defined in OpenTelemetry.Internal.Common.Types

Generic InstrumentationLibrary Source # 
Instance details

Defined in OpenTelemetry.Internal.Common.Types

Associated Types

type Rep InstrumentationLibrary 
Instance details

Defined in OpenTelemetry.Internal.Common.Types

type Rep InstrumentationLibrary = D1 ('MetaData "InstrumentationLibrary" "OpenTelemetry.Internal.Common.Types" "hs-opentelemetry-api-1.0.0.0-inplace" 'False) (C1 ('MetaCons "InstrumentationLibrary" 'PrefixI 'True) ((S1 ('MetaSel ('Just "libraryName") 'SourceUnpack 'SourceStrict 'DecidedStrict) (Rec0 Text) :*: S1 ('MetaSel ('Just "libraryVersion") 'SourceUnpack 'SourceStrict 'DecidedStrict) (Rec0 Text)) :*: (S1 ('MetaSel ('Just "librarySchemaUrl") 'SourceUnpack 'SourceStrict 'DecidedStrict) (Rec0 Text) :*: S1 ('MetaSel ('Just "libraryAttributes") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 Attributes))))
Show InstrumentationLibrary Source # 
Instance details

Defined in OpenTelemetry.Internal.Common.Types

Eq InstrumentationLibrary Source # 
Instance details

Defined in OpenTelemetry.Internal.Common.Types

Ord InstrumentationLibrary Source # 
Instance details

Defined in OpenTelemetry.Internal.Common.Types

Hashable InstrumentationLibrary Source # 
Instance details

Defined in OpenTelemetry.Internal.Common.Types

Lift InstrumentationLibrary Source # 
Instance details

Defined in OpenTelemetry.Internal.Common.Types

type Rep InstrumentationLibrary Source # 
Instance details

Defined in OpenTelemetry.Internal.Common.Types

type Rep InstrumentationLibrary = D1 ('MetaData "InstrumentationLibrary" "OpenTelemetry.Internal.Common.Types" "hs-opentelemetry-api-1.0.0.0-inplace" 'False) (C1 ('MetaCons "InstrumentationLibrary" 'PrefixI 'True) ((S1 ('MetaSel ('Just "libraryName") 'SourceUnpack 'SourceStrict 'DecidedStrict) (Rec0 Text) :*: S1 ('MetaSel ('Just "libraryVersion") 'SourceUnpack 'SourceStrict 'DecidedStrict) (Rec0 Text)) :*: (S1 ('MetaSel ('Just "librarySchemaUrl") 'SourceUnpack 'SourceStrict 'DecidedStrict) (Rec0 Text) :*: S1 ('MetaSel ('Just "libraryAttributes") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 Attributes))))

instrumentationLibrary :: Text -> Text -> InstrumentationLibrary Source #

Create an InstrumentationLibrary with a name and version. Schema URL and attributes default to empty.

Prefer instrumentationScope for new code.

Since: 0.4.0.0

data Logger Source #

LogRecords can be created from Loggers. Loggers are uniquely identified by the libraryName, libraryVersion, schemaUrl fields of InstrumentationLibrary. Creating two Loggers with the same identity but different libraryAttributes is a user error.

All operations on Logger are safe for concurrent use from multiple threads.

Since: 0.0.1.0

Constructors

Logger 

Fields

makeLogger Source #

Arguments

:: LoggerProvider

The LoggerProvider holds the configuration for the Logger.

-> InstrumentationLibrary

The library that the Logger instruments. This uniquely identifies the Logger. Use a non-empty libraryName per the OpenTelemetry specification; use getLogger if you want a warning when the name is empty.

-> Logger 

Since: 0.0.1.0

getLogger :: MonadIO m => LoggerProvider -> InstrumentationLibrary -> m Logger Source #

Like makeLogger, but logs a warning when libraryName is empty.

Since: 0.0.1.0

loggerIsEnabled :: Logger -> Maybe SeverityNumber -> Maybe Text -> IO Bool Source #

Returns True if a log record with the given severity (and optional event name) would be forwarded to processors.

Checks, in order:

  1. Whether the provider has any registered processors.
  2. Whether the provider has been shut down.
  3. Whether the record's severity meets the provider's minimum severity threshold (set via LoggerProviderOptions or setLoggerMinSeverity).

When the caller passes Nothing for severity, the minimum-severity gate is skipped (the record is allowed through).

Callers SHOULD invoke this before each log emit to get the most up-to-date response, as the result may change over time.

Since: 0.1.0.0

loggerIsEnabled' :: MonadIO m => Logger -> Maybe SeverityNumber -> Maybe Text -> Maybe Context -> m Bool Source #

Like loggerIsEnabled but accepts an explicit Context. When Nothing, uses the current implicit context.

Since: 0.4.0.0

setLoggerMinSeverity :: MonadIO m => LoggerProvider -> Maybe SeverityNumber -> m () Source #

Set the minimum severity for a LoggerProvider at runtime.

Log records with a severity below the threshold will be suppressed by both loggerIsEnabled and emitLogRecord. Pass Nothing to disable severity filtering (the default).

Since: 0.4.0.0

getLoggerMinSeverity :: MonadIO m => LoggerProvider -> m (Maybe SeverityNumber) Source #

Read the current minimum severity threshold for a LoggerProvider.

Returns Nothing when no severity filtering is active.

Since: 0.4.0.0

LogRecord operations

data ReadWriteLogRecord Source #

This is a data type that can represent logs from various sources: application log files, machine generated events, system logs, etc. Specification outlined here. Existing log formats can be unambiguously mapped to this data type. Reverse mapping from this data type is also possible to the extent that the target log format has equivalent capabilities.

Since: 0.0.1.0

class IsReadableLogRecord r where Source #

This is a typeclass representing LogRecords that can be read from.

A function receiving this as an argument MUST be able to access all the information added to the LogRecord. It MUST also be able to access the Instrumentation Scope and Resource information (implicitly) associated with the LogRecord.

The trace context fields MUST be populated from the resolved Context (either the explicitly passed Context or the current Context) when emitted.

Counts for attributes due to collection limits MUST be available for exporters to report as described in the transformation to non-OTLP formats specification.

Since: 0.0.1.0

Methods

readLogRecord :: r -> IO ImmutableLogRecord Source #

Reads the current state of the LogRecord from its internal IORef. The implementation mirrors readIORef.

readLogRecordInstrumentationScope :: r -> InstrumentationLibrary Source #

Reads the InstrumentationScope from the Logger that emitted the LogRecord

readLogRecordResource :: r -> MaterializedResources Source #

Reads the Resource from the LoggerProvider that emitted the LogRecord

class IsReadableLogRecord r => IsReadWriteLogRecord r where Source #

This is a typeclass representing LogRecords that can be read from or written to. All ReadWriteLogRecords are ReadableLogRecords.

A function receiving this as an argument MUST additionally be able to modify the following information added to the LogRecord:

  • Timestamp
  • ObservedTimestamp
  • SeverityText
  • SeverityNumber
  • Body
  • Attributes (addition, modification, removal)
  • EventName
  • TraceId
  • SpanId
  • TraceFlags

Since: 0.0.1.0

Methods

readLogRecordAttributeLimits :: r -> AttributeLimits Source #

Reads the attribute limits from the LoggerProvider that emitted the LogRecord. These are needed to add more attributes.

modifyLogRecord :: r -> (ImmutableLogRecord -> ImmutableLogRecord) -> IO () Source #

Atomically modifies the LogRecord using its internal IORef. Uses atomicModifyIORef' (strict) to avoid thunk buildup and ensure thread safety under concurrent mutation.

atomicModifyLogRecord :: r -> (ImmutableLogRecord -> (ImmutableLogRecord, b)) -> IO b Source #

Atomically modifies the LogRecord and returns a value. Uses atomicModifyIORef' (strict) for thread safety.

data LogRecordArguments Source #

Arguments that may be set on LogRecord creation. If observedTimestamp is not set, it will default to the current timestamp. If context is not specified it will default to the current context. Refer to the documentation of LogRecord for descriptions of the fields.

Since: 0.0.1.0

data AnyValue Source #

An attribute represents user-provided metadata about a span, link, or event.

Any values are used in place of 'Standard Attributes' in logs because third-party logs may not conform to the 'Standard Attribute' format.

Telemetry tools may use this data to support high-cardinality querying, visualization in waterfall diagrams, trace sampling decisions, and more.

Since: 0.0.1.0

Instances

Instances details
Data AnyValue Source # 
Instance details

Defined in OpenTelemetry.Internal.Common.Types

Methods

gfoldl :: (forall d b. Data d => c (d -> b) -> d -> c b) -> (forall g. g -> c g) -> AnyValue -> c AnyValue #

gunfold :: (forall b r. Data b => c (b -> r) -> c r) -> (forall r. r -> c r) -> Constr -> c AnyValue #

toConstr :: AnyValue -> Constr #

dataTypeOf :: AnyValue -> DataType #

dataCast1 :: Typeable t => (forall d. Data d => c (t d)) -> Maybe (c AnyValue) #

dataCast2 :: Typeable t => (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c AnyValue) #

gmapT :: (forall b. Data b => b -> b) -> AnyValue -> AnyValue #

gmapQl :: (r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> AnyValue -> r #

gmapQr :: forall r r'. (r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> AnyValue -> r #

gmapQ :: (forall d. Data d => d -> u) -> AnyValue -> [u] #

gmapQi :: Int -> (forall d. Data d => d -> u) -> AnyValue -> u #

gmapM :: Monad m => (forall d. Data d => d -> m d) -> AnyValue -> m AnyValue #

gmapMp :: MonadPlus m => (forall d. Data d => d -> m d) -> AnyValue -> m AnyValue #

gmapMo :: MonadPlus m => (forall d. Data d => d -> m d) -> AnyValue -> m AnyValue #

IsString AnyValue Source #

Create a TextAttribute from the string value.

Instance details

Defined in OpenTelemetry.Internal.Common.Types

Generic AnyValue Source # 
Instance details

Defined in OpenTelemetry.Internal.Common.Types

Methods

from :: AnyValue -> Rep AnyValue x #

to :: Rep AnyValue x -> AnyValue #

Read AnyValue Source # 
Instance details

Defined in OpenTelemetry.Internal.Common.Types

Show AnyValue Source # 
Instance details

Defined in OpenTelemetry.Internal.Common.Types

Eq AnyValue Source # 
Instance details

Defined in OpenTelemetry.Internal.Common.Types

Ord AnyValue Source # 
Instance details

Defined in OpenTelemetry.Internal.Common.Types

Hashable AnyValue Source # 
Instance details

Defined in OpenTelemetry.Internal.Common.Types

Methods

hashWithSalt :: Int -> AnyValue -> Int #

hash :: AnyValue -> Int #

ToValue AnyValue Source # 
Instance details

Defined in OpenTelemetry.Internal.Common.Types

type Rep AnyValue Source # 
Instance details

Defined in OpenTelemetry.Internal.Common.Types

class ToValue a where Source #

Convert a Haskell value to an Any value.

data Foo = Foo

instance ToValue Foo where
  toValue Foo = TextValue Foo

Since: 0.0.1.0

Methods

toValue :: a -> AnyValue Source #

Instances

Instances details
ToValue ByteString Source # 
Instance details

Defined in OpenTelemetry.Internal.Common.Types

ToValue Int64 Source # 
Instance details

Defined in OpenTelemetry.Internal.Common.Types

ToValue AnyValue Source # 
Instance details

Defined in OpenTelemetry.Internal.Common.Types

ToValue Text Source # 
Instance details

Defined in OpenTelemetry.Internal.Common.Types

ToValue Bool Source # 
Instance details

Defined in OpenTelemetry.Internal.Common.Types

ToValue Double Source # 
Instance details

Defined in OpenTelemetry.Internal.Common.Types

ToValue a => ToValue [a] Source # 
Instance details

Defined in OpenTelemetry.Internal.Common.Types

Methods

toValue :: [a] -> AnyValue Source #

ToValue a => ToValue (HashMap Text a) Source # 
Instance details

Defined in OpenTelemetry.Internal.Common.Types

data SeverityNumber Source #

Log severity level per the OTel log data model.

Spec: https://opentelemetry.io/docs/specs/otel/logs/data-model/#severity-fields

Since: 0.0.1.0

emitLogRecord :: MonadIO m => Logger -> LogRecordArguments -> m ReadWriteLogRecord Source #

Emits a LogRecord with properties specified by the passed in Logger and LogRecordArguments. If observedTimestamp is not set in LogRecordArguments, it will default to the current timestamp. If context is not specified in LogRecordArguments it will default to the current context.

The emitted LogRecord will be passed to any LogRecordProcessors registered on the LoggerProvider that created the Logger, provided the record's severity meets the provider's minimum severity threshold.

Since: 0.0.1.0

addAttribute :: (IsReadWriteLogRecord r, MonadIO m, ToValue a) => r -> Text -> a -> m () Source #

Add an attribute to a log record. Not an atomic modification.

See the OTel attribute naming conventions for guidance on choosing attribute names.

Since: 0.0.1.0

addAttributes :: (IsReadWriteLogRecord r, MonadIO m, ToValue a) => r -> HashMap Text a -> m () Source #

A convenience function related to addAttribute that adds multiple attributes to a LogRecord at the same time.

This function may be slightly more performant than repeatedly calling addAttribute.

This is not an atomic modification

Since: 0.0.1.0

logRecordGetAttributes :: (IsReadableLogRecord r, MonadIO m) => r -> m LogAttributes Source #

This can be useful for pulling data for attributes and using it to copy / otherwise use the data to further enrich instrumentation.

Since: 0.0.1.0