Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added LogProcessors and implemented shutdown and forceFlush for LoggerProviders #132

Merged
merged 4 commits into from
Jul 24, 2024

Conversation

evanlauer1
Copy link
Collaborator

@evanlauer1 evanlauer1 commented Jun 13, 2024

Big Context

Logging Support is being added to hs-opentelemetry. Logging spec

Small (This PR) Context

LogProcessors are a sort of middleman between LogRecords and LogExporters that allow for modification of LogRecords after creation. This PR creates a LogProcessor type that contains onEmit, shutdown, and forceFlush methods. It also creates shutdown and forceFlush functions for LoggerProviders that call the shutdown and forceFlush methods of internal LogProcessors.

Testing

  • stack build runs
  • Test suite updated so that old tests still work with updated LoggerProvider type, but no new test written for LogProcessors. It would probable make more sense to write individual tests for each implemented LogProcessor

Copy link
Collaborator Author

@evanlauer1 evanlauer1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Finished self-review

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ShutdownResult and FlushResult are use in both OpenTelemetry.Trace.Core and OpenTelemetry.Logging.Core so they are moved here.

--
-- ForceFlush SHOULD complete or abort within some timeout. ForceFlush can be implemented as a blocking API or an asynchronous API which notifies the caller via a callback or an event. OpenTelemetry SDK authors
-- can decide if they want to make the flush timeout configurable.
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This type is similar to Processor defined in OpenTelemetry.Internal.Trace.Types. Notable differences are the type variable body because LogRecord needs it, and changing onStart and onEnd to onEmit in accordance with the spec. Maybe Processor should be renamed to SpanProcessor because there is more than one type of Processor now?

api/src/OpenTelemetry/Internal/Logging/Types.hs Outdated Show resolved Hide resolved
| -- | One or more exporters failed to successfully export one or more
-- unexported spans.
FlushError
deriving (Show)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to Common

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mirrors Processor.hs

shutdownLoggerProvider LoggerProvider {loggerProviderProcessors} = liftIO $ do
asyncShutdownResults <- V.forM loggerProviderProcessors $ \processor -> do
logProcessorShutdown processor
mapM_ wait asyncShutdownResults
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mirrors shutdownTracerProvider

jobs
case mresult of
Nothing -> pure FlushTimeout
Just res -> pure res
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mirrors forceFlushTracerProvider



instance Show (TestLogProcessor body) where
show _ = "LogProcessor {..}"
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added because shouldSatisfy has a Show constraint but (Show LogProcessor) does not make sense because of the functions that are properties. "LogProcessor {..}" is used to show that there are internal properties that exist but are not shown.

glp `shouldBe` lp
fmap TestLogProcessor (loggerProviderProcessors glp) `shouldSatisfy` null
loggerProviderResource glp `shouldBe` loggerProviderResource lp
loggerProviderAttributeLimits glp `shouldBe` loggerProviderAttributeLimits lp
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Testing each field separately is similar to testing the LoggerProvider as a whole, but it makes more sense now that LogProcessors are part of LoggerProvider.

, loggerProviderAttributeLimits :: AttributeLimits
}
deriving (Show, Eq)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cannot derive Show and Eq because Show and Eq instances do not make sense for LogProcessors because they contain functions.

@evanlauer1 evanlauer1 force-pushed the refactor-log-records-to-be-mutable branch from 9aeda12 to 62fd9ea Compare June 14, 2024 21:52
@evanlauer1 evanlauer1 force-pushed the add-log-processors branch 2 times, most recently from ea9dddb to 079c965 Compare June 15, 2024 00:27
import OpenTelemetry.Internal.Trace.Id (SpanId, TraceId)
import OpenTelemetry.LogAttributes
import OpenTelemetry.Resource (MaterializedResources)


data LogProcessor body = LogProcessor
{ logProcessorOnEmit :: IORef (ImmutableLogRecord body) -> Context -> IO ()
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly to the other PR, we should probably account for this naming style and provide a ReadWriteLogRecord here: https://opentelemetry.io/docs/specs/otel/logs/sdk/#additional-logrecord-interfaces

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that is a great idea, and I wrote a typeclass for ReadWriteLogRecord but it is difficult to constrain this function because it is part of a record/datatype. Writing

logRecordProcessorOnEmit :: (ReadWriteLogRecord r) => r body -> Context -> IO ()

does not work because r is not in scope. using GADTs, I could write

data LogRecordProcessor body where
  LogRecordProcessor :: (ReadWriteLogRecord r) 
    => { logRecordProcessorOnEmit :: r body -> Context -> IO ()
          , ..
          } 
    -> LogProcessor body

but creating a LogRecordProcessor gives the error that r is an ambiguous type variable because it could be any possible ReadWriteLogRecord. I'm not sure of how to deal with this.

@iand675 @andrewsmith

import OpenTelemetry.Internal.Trace.Id (SpanId, TraceId)
import OpenTelemetry.LogAttributes
import OpenTelemetry.Resource (MaterializedResources)


data LogProcessor body = LogProcessor
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be called LogRecordProcessor: https://opentelemetry.io/docs/specs/otel/logs/sdk/#logrecordprocessor

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. I named it LogProcessor to be concise but I agree, it is more clear and correct if it is LogRecordProcessor.

@iand675
Copy link
Owner

iand675 commented Jun 20, 2024

Approved pending the LogProcessor -> LogRecordProcessor rename

Copy link
Collaborator Author

@evanlauer1 evanlauer1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Finished self-review


-- | Logging is no-op when using this @LoggerProvider@ because it has no processors and empty options.
noOpLoggerProvider = createLoggerProvider [] emptyLoggerProviderOptions
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created to make it more clear when a LoggerProvider is no-op. There are more possible no-op LoggerProviders than this, because, to my understanding, a LoggerProvider is no-op when its processors are no-op.

import OpenTelemetry.Internal.Trace.Id (SpanId, TraceId)
import OpenTelemetry.LogAttributes
import OpenTelemetry.Resource (MaterializedResources)


data LogProcessor body = LogProcessor
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. I named it LogProcessor to be concise but I agree, it is more clear and correct if it is LogRecordProcessor.

api/src/OpenTelemetry/Internal/Logging/Types.hs Outdated Show resolved Hide resolved
api/src/OpenTelemetry/Internal/Logging/Types.hs Outdated Show resolved Hide resolved
@evanlauer1 evanlauer1 marked this pull request as ready for review July 24, 2024 00:24
@evanlauer1 evanlauer1 merged commit 967ba85 into main Jul 24, 2024
7 checks passed
@evanlauer1 evanlauer1 deleted the add-log-processors branch July 24, 2024 00:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants