Copyright | Copyright 2022 Shea Levy. |
---|---|
License | Apache-2.0 |
Maintainer | shea@shealevy.com |
Safe Haskell | Safe-Inferred |
Language | Haskell2010 |
This is the primary module needed to instrument code with eventuo11y.
Instrumentors should first define selector types and field types appropriate to the unit of code they're instrumenting:
Selectors are values which designate the general category of event
being created, parameterized by 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).
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).
Instrumentation then centers around Event
s, populated using the
event manipulation functions. Event
s are initialized
with MonadEvent
functions, typically via the
resource-safe event allocation functions. For an
explicit alternative to MonadEvent
, see Observe.Event.Explicit.
Depending on which EventBackend
s may end up consuming the Event
s,
instrumentors will also need to define renderers for their selectors
and fields. For example, they may need to implement values of types
RenderSelectorJSON
to use JSON rendering EventBackend
s.
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
- class (forall r s. Monad (em r s), Monad (BackendMonad em)) => MonadEvent em
- type EnvEvent em r s = Event (em r s) r
- data NewEventArgs r s f = NewEventArgs {
- newEventSelector :: !(s f)
- newEventParent :: !(Maybe r)
- newEventCauses :: ![r]
- newEventInitialFields :: ![f]
- emitImmediateEvent' :: MonadEvent em => NewEventArgs r s f -> em r s r
- withEvent :: MonadWithEvent em r s => forall f. s f -> (EnvEvent em r s f -> em r s a) -> em r s a
- withEventArgs :: MonadWithEvent em r s => forall f. NewEventArgs r s f -> (EnvEvent em r s f -> em r s a) -> em r s a
- withNarrowingEvent :: MonadWithEvent em r t => InjectSelector s t -> forall f. t f -> (EnvEvent em r s f -> em r s x) -> em r t x
- withNarrowingEventArgs :: MonadWithEvent em r t => InjectSelector s t -> forall f. NewEventArgs r t f -> (EnvEvent em r s f -> em r s x) -> em r t x
- 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
- type MonadWithEvent em r s = (MonadEvent em, MonadWithExceptable (em r s))
- allocateEvent :: (MonadEvent em, Exceptable e) => forall f. s f -> GeneralAllocate (em r s) e () releaseArg (EnvEvent em r s f)
- allocateEventArgs :: (MonadEvent em, Exceptable e) => forall f. NewEventArgs r s f -> GeneralAllocate (em r s) e () releaseArg (EnvEvent em r s f)
- data EventT m r s a
- runEventT :: Monad m => EventT m r s a -> EventBackend m r s -> m a
- eventLift :: forall m r s. Applicative m => StatelessControlTransformation m (EventT m r s)
- newtype TransEventMonad t em r s a = TransEventMonad {
- unTransEventMonad :: t (em r s) a
- type family BackendMonad em :: Type -> Type
- type EnvBackend em = EventBackend (BackendMonad em)
- data EventBackend m r s
- liftBackendMonad :: MonadEvent em => BackendMonad em a -> em r s a
- backend :: MonadEvent em => em r s (EnvBackend em r s)
- withModifiedBackend :: MonadEvent em => (EnvBackend em r s -> EnvBackend em r' s') -> em r' s' a -> em r s a
- finalize :: Event m r f -> Maybe SomeException -> m ()
- newEvent' :: MonadEvent em => forall f. s f -> em r s (EnvEvent em r s f)
- newEventArgs :: MonadEvent em => forall f. NewEventArgs r s f -> em r s (EnvEvent em r s f)
- type BackendEvent em = Event (BackendMonad em)
- hoistBackendEvent :: MonadEvent em => BackendEvent em r f -> EnvEvent em r s f
- allocateBackendEvent :: (MonadEvent em, Exceptable e) => forall f. NewEventArgs r s f -> GeneralAllocate (em r s) e () releaseArg (BackendEvent em r f)
- withBackendEvent :: (MonadEvent em, MonadWithExceptable (em r s)) => forall f. NewEventArgs r s f -> (BackendEvent em r f -> em r s a) -> em r s a
- newBackendEvent :: MonadEvent em => forall f. NewEventArgs r s f -> em r s (BackendEvent em 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.
MonadEvent
class (forall r s. Monad (em r s), Monad (BackendMonad em)) => MonadEvent em Source #
Monads suitable for Event
-based instrumentation, with implicit EventBackend
management.
See Observe.Event.Explicit for Event
-based instrumentation with explicit EventBackend
passing.
Note that em
is an indexed monad of EventMonadKind
.
Instances
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' :: MonadEvent em => NewEventArgs r s f -> em r s r Source #
Create an event which has no duration and is immediately finalized successfully.
Returns a reference to the event.
withEvent :: MonadWithEvent em r s => forall f. s f -> (EnvEvent em r s f -> em r s a) -> em r s a Source #
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).
Within the nested action, all new parentless Event
s will be
made children of the new Event
.
The Event
will be finalize
d at the end of the nested action.
withEventArgs :: MonadWithEvent em r s => forall f. NewEventArgs r s f -> (EnvEvent em r s f -> em r s a) -> em r s a Source #
withNarrowingEvent :: MonadWithEvent em r t => InjectSelector s t -> forall f. t f -> (EnvEvent em r s f -> em r s x) -> em r t x Source #
Run an action with a new Event
, selected by a given selector, with a narrower sub-selector type.
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).
Within the nested action, all new parentless Event
s will be
made children of the new Event
, and all new Event
s will
be selected by the narrower selector type.
The Event
will be finalize
d at the end of the nested action.
withNarrowingEventArgs :: MonadWithEvent em r t => InjectSelector s t -> forall f. NewEventArgs r t f -> (EnvEvent em r s f -> em r s x) -> em r t x Source #
Run an action with a new Event
, specified by the given NewEventArgs
, with a narrower sub-selector type.
Within the nested action, all new parentless Event
s will be
made children of the new Event
, and all new Event
s will
be selected by the narrower selector type.
The Event
will be finalize
d at the end of the nested action.
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
type MonadWithEvent em r s = (MonadEvent em, MonadWithExceptable (em r s)) Source #
A MonadEvent
suitable for running the withEvent
family of functions
allocateEvent :: (MonadEvent em, Exceptable e) => forall f. s f -> GeneralAllocate (em r s) e () releaseArg (EnvEvent em r s 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 :: (MonadEvent em, Exceptable e) => forall f. NewEventArgs r s f -> GeneralAllocate (em r s) e () releaseArg (EnvEvent em r s f) Source #
Allocate a new Event
, specified by the given NewEventArgs
.
EventT
Make a monad into a MonadEvent
.
Instances
runEventT :: Monad m => EventT m r s a -> EventBackend m r s -> m a Source #
Run an EventT
with an initial EventBackend
.
eventLift :: forall m r s. Applicative m => StatelessControlTransformation m (EventT m r s) Source #
Lift m
into EventT
m
.
TransEventMonad
newtype TransEventMonad t em r s a Source #
Apply a MonadTrans
former to an EventMonadKind
to get a transformed EventMonadKind
When t
is MonadTransControl
and em
is MonadEvent
, TransEventMonad
t
em
is
MonadEvent
and has all of the relevant instances conferred by t
.
TransEventMonad | |
|
Instances
Primitives
type family BackendMonad em :: Type -> Type Source #
The monad of the implicitly carried EventBackend
Instances
type BackendMonad (EventT m) Source # | |
Defined in Observe.Event.Class | |
type BackendMonad (TransEventMonad t em) Source # | |
Defined in Observe.Event.Class |
type EnvBackend em = EventBackend (BackendMonad em) Source #
The type of the implicit EventBackend
of a MonadEvent
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
.
liftBackendMonad :: MonadEvent em => BackendMonad em a -> em r s a Source #
backend :: MonadEvent em => em r s (EnvBackend em r s) Source #
Access the implicitly carried EventBackend
:: MonadEvent em | |
=> (EnvBackend em r s -> EnvBackend em r' s') | Modify the Note that the modification may change the reference and selector types. |
-> em r' s' a | Action to run with the modified backend available. |
-> em r s a |
Run an instrumented action with a modified EventBackend
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' :: MonadEvent em => forall f. s f -> em r s (EnvEvent em r s f) Source #
Create 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).
Consider the resource-safe event allocation functions instead of calling this directly.
newEventArgs :: MonadEvent em => forall f. NewEventArgs r s f -> em r s (EnvEvent em r s f) Source #
Create a new Event
, specified by the given NewEventArgs
.
Consider the resource-safe event allocation functions instead of calling this directly.
Backend Events
Event
s within the BackendMonad
of a MonadEvent
These are low-level primitives that can be used if the existing higher-level event allocation/backend modification combinators are insufficient
type BackendEvent em = Event (BackendMonad em) Source #
An Event
in the BackendMonad
of a MonadEvent
hoistBackendEvent :: MonadEvent em => BackendEvent em r f -> EnvEvent em r s f Source #
Bring a BackendEvent
into the MonadEvent
allocateBackendEvent :: (MonadEvent em, Exceptable e) => forall f. NewEventArgs r s f -> GeneralAllocate (em r s) e () releaseArg (BackendEvent em r f) Source #
A BackendMonad
variant of allocateEventArgs
.
withBackendEvent :: (MonadEvent em, MonadWithExceptable (em r s)) => forall f. NewEventArgs r s f -> (BackendEvent em r f -> em r s a) -> em r s a Source #
Run an action with a new BackendEvent
.
newBackendEvent :: MonadEvent em => forall f. NewEventArgs r s f -> em r s (BackendEvent em r f) Source #
A BackendMonad
variant of newEventArgs