Copyright | Copyright 2022 Shea Levy. |
---|---|
License | Apache-2.0 |
Maintainer | shea@shealevy.com |
Safe Haskell | Safe-Inferred |
Language | Haskell2010 |
MonadEvent
and EventT
-based instrumentation
implicitly track the underlying EventBackend
for you. This module is for those
who would rather pass around EventBackend
s explicitly.
Synopsis
- data Event m r f
- hoistEvent :: (forall x. m x -> n x) -> Event m r f -> Event n r f
- addField :: Event m r f -> f -> m ()
- reference :: Event m r f -> r
- data NewEventArgs r s f = NewEventArgs {
- newEventSelector :: !(s f)
- newEventParent :: !(Maybe r)
- newEventCauses :: ![r]
- newEventInitialFields :: ![f]
- emitImmediateEvent :: EventBackend m r s -> forall f. NewEventArgs r s f -> m r
- allocateEvent :: (Monad m, Exceptable e) => EventBackend m r s -> forall f. s f -> GeneralAllocate m e () releaseArg (Event m r f)
- allocateEventArgs :: (Monad m, Exceptable e) => EventBackend m r s -> forall f. NewEventArgs r s f -> GeneralAllocate m e () releaseArg (Event m r f)
- withEvent :: MonadWithExceptable m => EventBackend m r s -> forall f. s f -> (Event m r f -> m a) -> m a
- withEventArgs :: MonadWithExceptable m => EventBackend m r s -> forall f. NewEventArgs r s f -> (Event m r f -> m a) -> m a
- data EventBackend m r s
- subEventBackend :: Functor m => InjectSelector s t -> Event m r f -> EventBackend m r t -> EventBackend m r s
- causedEventBackend :: Functor m => InjectSelector s t -> [Event m r f] -> EventBackend m r t -> EventBackend m r s
- hoistEventBackend :: Functor m => (forall x. m x -> n x) -> EventBackend m r s -> EventBackend n r s
- narrowEventBackend :: Functor m => InjectSelector s t -> EventBackend m r t -> EventBackend m r s
- type InjectSelector s t = forall f. s f -> forall a. (forall g. t g -> (f -> g) -> a) -> a
- injectSelector :: (forall f. s f -> t f) -> InjectSelector s t
- idInjectSelector :: InjectSelector s s
- setAncestorEventBackend :: r -> EventBackend m r s -> EventBackend m r s
- setInitialCauseEventBackend :: [r] -> EventBackend m r s -> EventBackend m r s
- unitEventBackend :: Applicative m => EventBackend m () s
- pairEventBackend :: Applicative m => EventBackend m a s -> EventBackend m b s -> EventBackend m (a, b) s
- noopEventBackend :: Applicative m => r -> EventBackend m r s
- finalize :: Event m r f -> Maybe SomeException -> m ()
- newEvent :: EventBackend m r s -> forall f. NewEventArgs r s f -> m (Event m r f)
Documentation
hoistEvent :: (forall x. m x -> n x) -> Event m r f -> Event n r f Source #
Hoist an Event
along a given natural transformation into a new monad.
Event manipulation
addField :: Event m r f -> f -> m () Source #
Add a field to an Event
.
Fields make up the basic data captured in an event. They should be added
to an Event
as the code progresses through various phases of work, and can
be both milestone markers ("we got this far in the process") or more detailed
instrumentation ("we've processed N records").
They are intended to be of a domain specific type per unit of functionality within an instrumented codebase (but see DynamicField for a generic option).
reference :: Event m r f -> r Source #
Obtain a reference to an Event
.
References are used to link Event
s together, via the newEventParent
and newEventCauses
fields of NewEventArgs
.
References can live past when an event has been finalize
d.
Code being instrumented should always have r
as an unconstrained
type parameter, both because it is an implementation concern for
EventBackend
s and because references are backend-specific and it
would be an error to reference an event in one backend from an event
in a different backend.
Resource-safe event allocation
data NewEventArgs r s f Source #
Arguments specifying how an Event
should be created.
See simpleNewEventArgs
for a simple case.
NewEventArgs | |
|
emitImmediateEvent :: EventBackend m r s -> forall f. NewEventArgs r s f -> m r Source #
Create an event which has no duration and is immediately finalized successfully.
Returns a reference to the event.
allocateEvent :: (Monad m, Exceptable e) => EventBackend m r s -> forall f. s f -> GeneralAllocate m e () releaseArg (Event m r f) Source #
Allocate a new Event
, selected by the given selector.
The selector specifies the category of new event we're creating, as well
as the type of fields that can be added to it (with addField
).
Selectors are intended to be of a domain specific type per unit of functionality within an instrumented codebase, implemented as a GADT (but see DynamicEventSelector for a generic option).
allocateEventArgs :: (Monad m, Exceptable e) => EventBackend m r s -> forall f. NewEventArgs r s f -> GeneralAllocate m e () releaseArg (Event m r f) Source #
Allocate a new Event
, based on given NewEventArgs
.
:: MonadWithExceptable m | |
=> EventBackend m r s | |
-> forall f. s f | The event selector. |
-> (Event m r f -> m a) | |
-> m a |
Run an action with a new Event
, selected by the given selector.
The selector specifies the category of new event we're creating, as well
as the type of fields that can be added to it (with addField
).
Selectors are intended to be of a domain specific type per unit of functionality within an instrumented codebase, implemented as a GADT (but see DynamicEventSelector for a generic option).
The Event
is automatically finalize
d at the end of the function it's passed to.
:: MonadWithExceptable m | |
=> EventBackend m r s | |
-> forall f. NewEventArgs r s f | The event selector. |
-> (Event m r f -> m a) | |
-> m a |
Run an action with a new Event
, based on the given NewEventArgs
.
The Event
is automatically finalize
d at the end of the function it's passed to.
EventBackend
s
data EventBackend m r s Source #
A backend for creating Event
s.
Different EventBackend
s will be used to emit instrumentation to
different systems. Multiple backends can be combined with
pairEventBackend
.
A simple EventBackend
for logging to a Handle
can be
created with jsonHandleBackend.
From an EventBackend
, new events can be created via selectors
(of type s f
for some field type f
), typically with the
resource-safe allocation functions.
Selectors are values which designate the general category of event
being created, as well as the type of fields that can be added to it.
For example, a web service's selector type may have a ServicingRequest
constructor, whose field type includes a ResponseCode
constructor which
records the HTTP status code.
Selectors are intended to be of a domain specific type per unit of functionality within an instrumented codebase, implemented as a GADT (but see DynamicEventSelector for a generic option).
Implementations must ensure that EventBackend
s and their underlying Event
s
are safe to use across threads.
m
- The monad we're instrumenting in.
r
- The type of event references used in this
EventBackend
. Seereference
. s
- The type of event selectors. See
newEventSelector
.
Backend transformation
:: Functor m | |
=> InjectSelector s t | Bring selectors from the new backend into the parent event's backend. |
-> Event m r f | The parent event. |
-> EventBackend m r t | |
-> EventBackend m r s |
An EventBackend
where every otherwise parentless event will be marked
as a child of the given Event
.
:: Functor m | |
=> InjectSelector s t | Bring selectors from the new backend into the causing event's backend. |
-> [Event m r f] | The causing events. |
-> EventBackend m r t | |
-> EventBackend m r s |
An EventBackend
where every otherwise causeless event will be marked
as caused by the given Event
s.
hoistEventBackend :: Functor m => (forall x. m x -> n x) -> EventBackend m r s -> EventBackend n r s Source #
Hoist an EventBackend
along a given natural transformation into a new monad.
narrowEventBackend :: Functor m => InjectSelector s t -> EventBackend m r t -> EventBackend m r s Source #
Narrow an EventBackend
to a new selector type via a given injection function.
A typical usage, where component A calls component B, would be to have A's selector
type have a constructor to take any value of B's selector type (and preserve the field)
and then call narrowEventBackend
with that constructor when invoking functions in B.
type InjectSelector s t = forall f. s f -> forall a. (forall g. t g -> (f -> g) -> a) -> a Source #
Inject a narrower selector and its fields into a wider selector.
See injectSelector
for a simple way to construct one of these.
injectSelector :: (forall f. s f -> t f) -> InjectSelector s t Source #
Construct an InjectSelector
with a straightforward injection from s
to t
idInjectSelector :: InjectSelector s s Source #
The identity InjectSelector
setAncestorEventBackend :: r -> EventBackend m r s -> EventBackend m r s Source #
Transform an EventBackend
so all of its Event
s have a given parent, if they
are not given another parent.
setInitialCauseEventBackend :: [r] -> EventBackend m r s -> EventBackend m r s Source #
Transform an EventBackend
so all of its Event
s have the given causes,
if they are not given another set of causes.
Backend composition
unitEventBackend :: Applicative m => EventBackend m () s Source #
A no-op EventBackend
.
This can be used if calling instrumented code from an un-instrumented context, or to purposefully ignore instrumentation from some call.
unitEventBackend
is the algebraic unit of pairEventBackend
.
pairEventBackend :: Applicative m => EventBackend m a s -> EventBackend m b s -> EventBackend m (a, b) s Source #
An EventBackend
which sequentially generates Event
s in the two given EventBackend
s.
This can be used to emit instrumentation in multiple ways (e.g. logs to grafana and metrics on a prometheus HTML page).
noopEventBackend :: Applicative m => r -> EventBackend m r s Source #
A no-op EventBackend
that can be integrated with other backends.
This can be used to purposefully ignore instrumentation from some call.
All events will have the given reference, so can be connected to appropriate events in non-no-op backends, but not in a way that can distinguish between different events from the same no-op backend.
Primitive Event
resource management.
Prefer the resource-safe event allocation functions to these when possible.
finalize :: Event m r f -> Maybe SomeException -> m () Source #
Mark an Event
as finished, perhaps due to an Exception
.
In normal usage, this should be automatically called via the use of the resource-safe event allocation functions.
This is a no-op if the Event
has already been finalize
d.
As a result, it is likely pointless to call
addField
after this call, though it still may be reasonable to call
reference
.
newEvent :: EventBackend m r s -> forall f. NewEventArgs r s f -> m (Event m r f) Source #
Create a new Event
, specified by the given arguments.
Consider the resource-safe event allocation functions instead of calling this directly.