packages feed

hs-opentelemetry-exporter-handle 0.0.1.3 → 1.0.0.0

raw patch · 9 files changed

+297/−17 lines, 9 filesdep +hs-opentelemetry-exporter-handledep +hspecdep +unordered-containersdep ~hs-opentelemetry-apiPVP ok

version bump matches the API change (PVP)

Dependencies added: hs-opentelemetry-exporter-handle, hspec, unordered-containers, vector

Dependency ranges changed: hs-opentelemetry-api

API changes (from Hackage documentation)

+ OpenTelemetry.Exporter.Handle.LogRecord: defaultLogRecordFormatter :: ReadableLogRecord -> IO Text
+ OpenTelemetry.Exporter.Handle.LogRecord: makeHandleLogRecordExporter :: Handle -> (ReadableLogRecord -> IO Text) -> IO LogRecordExporter
+ OpenTelemetry.Exporter.Handle.LogRecord: stderrLogRecordExporter :: IO LogRecordExporter
+ OpenTelemetry.Exporter.Handle.LogRecord: stdoutLogRecordExporter :: IO LogRecordExporter
+ OpenTelemetry.Exporter.Handle.Metric: makeHandleMetricExporter :: Handle -> MetricExporter
+ OpenTelemetry.Exporter.Handle.Metric: stderrMetricExporter :: MonadIO m => m MetricExporter
+ OpenTelemetry.Exporter.Handle.Metric: stdoutMetricExporter :: MonadIO m => m MetricExporter
- OpenTelemetry.Exporter.Handle.Span: defaultFormatter :: ImmutableSpan -> Text
+ OpenTelemetry.Exporter.Handle.Span: defaultFormatter :: ImmutableSpan -> IO Text

Files

ChangeLog.md view
@@ -1,5 +1,13 @@ # Changelog for hs-opentelemetry-exporter-handle +## Unreleased++## 1.0.0.0 - 2026-05-29++- `OpenTelemetry.Exporter.Handle` barrel now re-exports all three signals (Span, Metric, LogRecord); deprecated annotation removed+- New `OpenTelemetry.Exporter.Handle.LogRecord` module (console log exporter with default formatter)+- New `OpenTelemetry.Exporter.Handle.Metric` module (console metric exporter)+ ## 0.0.1.3  - Relax `hs-opentelemetry-api` bounds to support 0.3.x
LICENSE view
@@ -1,4 +1,4 @@-Copyright Ian Duncan (c) 2021+Copyright Ian Duncan (c) 2021-2026  All rights reserved. 
README.md view
@@ -1,1 +1,3 @@ # hs-opentelemetry-exporter-handle++[![hs-opentelemetry-exporter-handle](https://img.shields.io/hackage/v/hs-opentelemetry-exporter-handle?style=flat-square&logo=haskell&label=hs-opentelemetry-exporter-handle&labelColor=5D4F85)](https://hackage.haskell.org/package/hs-opentelemetry-exporter-handle)
hs-opentelemetry-exporter-handle.cabal view
@@ -1,12 +1,14 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.37.0.+-- This file has been generated from package.yaml by hpack version 0.38.3. -- -- see: https://github.com/sol/hpack  name:           hs-opentelemetry-exporter-handle-version:        0.0.1.3+version:        1.0.0.0+synopsis:       Handle-based exporter for OpenTelemetry (e.g. stdout) description:    Please see the README on GitHub at <https://github.com/iand675/hs-opentelemetry/tree/main/exporters/handle#readme>+category:       OpenTelemetry, Telemetry, Monitoring homepage:       https://github.com/iand675/hs-opentelemetry#readme bug-reports:    https://github.com/iand675/hs-opentelemetry/issues author:         Ian Duncan@@ -27,6 +29,7 @@   exposed-modules:       OpenTelemetry.Exporter.Handle       OpenTelemetry.Exporter.Handle.LogRecord+      OpenTelemetry.Exporter.Handle.Metric       OpenTelemetry.Exporter.Handle.Span   other-modules:       Paths_hs_opentelemetry_exporter_handle@@ -34,6 +37,24 @@       src   build-depends:       base >=4.7 && <5-    , hs-opentelemetry-api ==0.3.*+    , hs-opentelemetry-api ==1.0.*     , text+    , vector+  default-language: Haskell2010++test-suite hs-opentelemetry-exporter-handle-test+  type: exitcode-stdio-1.0+  main-is: Spec.hs+  other-modules:+      Paths_hs_opentelemetry_exporter_handle+  hs-source-dirs:+      test+  ghc-options: -threaded -rtsopts -with-rtsopts=-N+  build-depends:+      base >=4.7 && <5+    , hs-opentelemetry-api ==1.0.*+    , hs-opentelemetry-exporter-handle ==1.0.*+    , hspec+    , unordered-containers+    , vector   default-language: Haskell2010
src/OpenTelemetry/Exporter/Handle.hs view
@@ -1,7 +1,15 @@-module OpenTelemetry.Exporter.Handle-  {-# DEPRECATED "use OpenTelemetry.Exporter.Handle.Span instead" #-} (+{- |+Module      : OpenTelemetry.Exporter.Handle+Description : Re-exports for handle-based (stdout/stderr) exporters.+Stability   : experimental+-}+module OpenTelemetry.Exporter.Handle (   module OpenTelemetry.Exporter.Handle.Span,+  module OpenTelemetry.Exporter.Handle.Metric,+  module OpenTelemetry.Exporter.Handle.LogRecord, ) where +import OpenTelemetry.Exporter.Handle.LogRecord+import OpenTelemetry.Exporter.Handle.Metric import OpenTelemetry.Exporter.Handle.Span 
src/OpenTelemetry/Exporter/Handle/LogRecord.hs view
@@ -1,2 +1,94 @@-module OpenTelemetry.Exporter.Handle.LogRecord () where+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-} +module OpenTelemetry.Exporter.Handle.LogRecord (+  makeHandleLogRecordExporter,+  stdoutLogRecordExporter,+  stderrLogRecordExporter,+  defaultLogRecordFormatter,+) where++import qualified Data.Text as T+import qualified Data.Text.IO as T+import qualified Data.Text.Lazy as LT+import qualified Data.Text.Lazy.Builder as LB+import Data.Text.Lazy.Builder.Int (decimal)+import Data.Text.Lazy.Builder.RealFloat (realFloat)+import qualified Data.Vector as V+import OpenTelemetry.Internal.Common.Types (ExportResult (..), FlushResult (..), InstrumentationLibrary (..))+import OpenTelemetry.Internal.Log.Types+import OpenTelemetry.LogAttributes (AnyValue (..), LogAttributes (..))+import System.IO (Handle, hFlush, stderr, stdout)+++builderText :: LB.Builder -> T.Text+builderText = LT.toStrict . LB.toLazyText+++textIntegral :: (Integral n) => n -> T.Text+textIntegral = builderText . decimal+++textDouble :: Double -> T.Text+textDouble = builderText . realFloat+++makeHandleLogRecordExporter :: Handle -> (ReadableLogRecord -> IO T.Text) -> IO LogRecordExporter+makeHandleLogRecordExporter h formatter =+  mkLogRecordExporter+    LogRecordExporterArguments+      { logRecordExporterArgumentsExport = \lrs -> do+          V.mapM_ (\lr -> formatter lr >>= T.hPutStrLn h >> hFlush h) lrs+          pure Success+      , logRecordExporterArgumentsForceFlush = hFlush h >> pure FlushSuccess+      , logRecordExporterArgumentsShutdown = hFlush h+      }+++stdoutLogRecordExporter :: IO LogRecordExporter+stdoutLogRecordExporter = makeHandleLogRecordExporter stdout defaultLogRecordFormatter+++stderrLogRecordExporter :: IO LogRecordExporter+stderrLogRecordExporter = makeHandleLogRecordExporter stderr defaultLogRecordFormatter+++defaultLogRecordFormatter :: ReadableLogRecord -> IO T.Text+defaultLogRecordFormatter lr = do+  ImmutableLogRecord {..} <- readLogRecord lr+  let scope_ = readLogRecordInstrumentationScope lr+  let sevText = case logRecordSeverityText of+        UJust s -> s+        UNothing -> "UNSET"+  let bodyText = case logRecordBody of+        TextValue t -> t+        IntValue i -> textIntegral i+        DoubleValue d -> textDouble d+        BoolValue b -> if b then "true" else "false"+        NullValue -> ""+        _ -> T.pack (show logRecordBody)+  let traceInfo = case logRecordTracingDetails of+        TracingDetails tid sid _flags -> " trace=" <> T.pack (show tid) <> " span=" <> T.pack (show sid)+        NoTracingDetails -> ""+  let LogAttributes {attributesCount = attrCount, attributesDropped = droppedCount} = logRecordAttributes+  let attrInfo =+        if attrCount > 0+          then " attrs=" <> textIntegral attrCount <> if droppedCount > 0 then " dropped=" <> textIntegral droppedCount else ""+          else ""+  let eventInfo = case logRecordEventName of+        UJust en -> " event=" <> en+        UNothing -> ""+  pure $+    T.concat+      [ T.pack (show logRecordObservedTimestamp)+      , " "+      , sevText+      , " ["+      , libraryName scope_+      , "]"+      , eventInfo+      , " "+      , bodyText+      , traceInfo+      , attrInfo+      ]
+ src/OpenTelemetry/Exporter/Handle/Metric.hs view
@@ -0,0 +1,53 @@+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedStrings #-}++{- |+Module      :  OpenTelemetry.Exporter.Handle.Metric+Copyright   :  (c) Ian Duncan, 2024-2026+License     :  BSD-3+Description :  Console \/ handle metric exporter (@OTEL_METRICS_EXPORTER=console@).+Stability   :  experimental++Renders each export batch using 'OpenTelemetry.Debug.MetricExport.renderResourceMetricsExportDebug'+and writes the text to the given 'Handle'.+-}+module OpenTelemetry.Exporter.Handle.Metric (+  makeHandleMetricExporter,+  stdoutMetricExporter,+  stderrMetricExporter,+) where++import Control.Monad.IO.Class (MonadIO)+import qualified Data.Text.IO as TIO+import qualified Data.Vector as V+import OpenTelemetry.Debug.MetricExport (renderResourceMetricsExportDebug)+import OpenTelemetry.Exporter.Metric (+  MetricExporter (..),+ )+import OpenTelemetry.Internal.Common.Types (ExportResult (..), FlushResult (..), ShutdownResult (..))+import System.IO (Handle, hFlush, hPutChar, stderr, stdout)+++makeHandleMetricExporter :: Handle -> MetricExporter+makeHandleMetricExporter h =+  MetricExporter+    { metricExporterExport = \batches -> do+        TIO.hPutStr h (renderResourceMetricsExportDebug (V.toList batches))+        hPutChar h '\n'+        hFlush h+        pure Success+    , metricExporterShutdown = do+        hFlush h+        pure ShutdownSuccess+    , metricExporterForceFlush = do+        hFlush h+        pure FlushSuccess+    }+++stdoutMetricExporter :: (MonadIO m) => m MetricExporter+stdoutMetricExporter = pure (makeHandleMetricExporter stdout)+++stderrMetricExporter :: (MonadIO m) => m MetricExporter+stderrMetricExporter = pure (makeHandleMetricExporter stderr)
src/OpenTelemetry/Exporter/Handle/Span.hs view
@@ -2,6 +2,27 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} +{- |+Module      : OpenTelemetry.Exporter.Handle.Span+Copyright   : (c) Ian Duncan, 2021-2026+License     : BSD-3+Description : Export spans as text to a file handle (stdout/stderr)+Stability   : experimental++= Overview++Writes spans as human-readable text to a file handle. Useful for local+development and debugging.++= Quick example++@+import OpenTelemetry.Exporter.Handle.Span (stdoutExporter')++exporter <- stdoutExporter'+-- Spans will be printed to stdout as they complete+@+-} module OpenTelemetry.Exporter.Handle.Span (   makeHandleExporter,   -- $@@ -13,9 +34,12 @@  import Data.IORef import qualified Data.Text.Lazy as L+import Data.Text.Lazy.Builder (fromString, fromText, toLazyText) import qualified Data.Text.Lazy.IO as L import OpenTelemetry.Exporter.Span+import OpenTelemetry.Internal.Common.Types (FlushResult (..), ShutdownResult (..)) import OpenTelemetry.Trace.Core+import OpenTelemetry.Trace.Id (Base (..), spanIdBaseEncodedText, traceIdBaseEncodedText) import System.IO (Handle, hFlush, stderr, stdout)  @@ -25,7 +49,8 @@     { spanExporterExport = \fs -> do         mapM_ (mapM_ (\s -> f s >>= L.hPutStrLn h >> hFlush h)) fs         pure Success-    , spanExporterShutdown = hFlush h+    , spanExporterShutdown = hFlush h >> pure ShutdownSuccess+    , spanExporterForceFlush = hFlush h >> pure FlushSuccess     }  @@ -37,12 +62,16 @@ stderrExporter' = makeHandleExporter stderr  -defaultFormatter :: ImmutableSpan -> L.Text-defaultFormatter ImmutableSpan {..} =-  L.intercalate-    " "-    [ L.pack $ show $ traceId spanContext-    , L.pack $ show $ spanId spanContext-    , L.pack $ show spanStart-    , L.fromStrict spanName-    ]+defaultFormatter :: ImmutableSpan -> IO L.Text+defaultFormatter imm = do+  hot <- readIORef (spanHot imm)+  let ctx = spanContext imm+  pure $!+    toLazyText $+      fromText (traceIdBaseEncodedText Base16 (traceId ctx))+        <> " "+        <> fromText (spanIdBaseEncodedText Base16 (spanId ctx))+        <> " "+        <> fromString (show (spanStart imm))+        <> " "+        <> fromText (hotName hot)
+ test/Spec.hs view
@@ -0,0 +1,67 @@+{-# LANGUAGE OverloadedStrings #-}++module Main (main) where++import qualified Data.HashMap.Strict as HM+import qualified Data.Vector as V+import OpenTelemetry.Exporter.Handle.LogRecord (makeHandleLogRecordExporter)+import OpenTelemetry.Exporter.Handle.Span (makeHandleExporter)+import OpenTelemetry.Exporter.LogRecord (+  logRecordExporterExport,+  logRecordExporterForceFlush,+  logRecordExporterShutdown,+ )+import OpenTelemetry.Exporter.Span (SpanExporter (..))+import OpenTelemetry.Internal.Common.Types (ExportResult (..))+import System.IO (stdout)+import Test.Hspec+++main :: IO ()+main = hspec spec+++spec :: Spec+spec =+  -- Handle-based exporters (implementation-specific; SpanExporter / LogRecordExporter shape)+  -- https://opentelemetry.io/docs/specs/otel/trace/sdk/#span-exporter+  -- https://opentelemetry.io/docs/specs/otel/logs/sdk/#logrecordexporter+  do+    describe "Handle Span exporter" $ do+      -- SpanExporter.Export must accept empty batches (SDK contract)+      -- https://opentelemetry.io/docs/specs/otel/trace/sdk/#export-batch+      it "returns Success for empty batch" $ do+        let formatter _span = pure "test"+            exporter = makeHandleExporter stdout formatter+        result <- spanExporterExport exporter HM.empty+        case result of+          Success -> pure ()+          Failure _ -> expectationFailure "expected Success"++      it "forceFlush does not throw" $ do+        let exporter = makeHandleExporter stdout (\_ -> pure "test")+        _ <- spanExporterForceFlush exporter+        pure ()++      it "shutdown does not throw" $ do+        let exporter = makeHandleExporter stdout (\_ -> pure "test")+        _ <- spanExporterShutdown exporter+        pure ()++    describe "Handle LogRecord exporter" $ do+      it "returns Success for empty batch" $ do+        exporter <- makeHandleLogRecordExporter stdout (\_ -> pure "test")+        result <- logRecordExporterExport exporter V.empty+        case result of+          Success -> pure ()+          Failure _ -> expectationFailure "expected Success"++      it "forceFlush does not throw" $ do+        exporter <- makeHandleLogRecordExporter stdout (\_ -> pure "test")+        _ <- logRecordExporterForceFlush exporter+        pure ()++      it "shutdown does not throw" $ do+        exporter <- makeHandleLogRecordExporter stdout (\_ -> pure "test")+        _ <- logRecordExporterShutdown exporter+        pure ()