2020-07-17 13:22:37 +00:00
|
|
|
package journal
|
|
|
|
|
2020-07-17 17:35:15 +00:00
|
|
|
import (
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
)
|
2020-07-17 13:22:37 +00:00
|
|
|
|
2020-07-20 13:45:17 +00:00
|
|
|
// DisabledEvents is the set of event types whose journaling is suppressed.
|
|
|
|
type DisabledEvents []EventType
|
|
|
|
|
2020-07-17 13:22:37 +00:00
|
|
|
// EventType represents the signature of an event.
|
|
|
|
type EventType struct {
|
|
|
|
System string
|
|
|
|
Event string
|
2020-07-17 17:35:15 +00:00
|
|
|
|
|
|
|
// enabled stores whether this event type is enabled.
|
|
|
|
enabled bool
|
|
|
|
|
|
|
|
// safe is a sentinel marker that's set to true if this EventType was
|
|
|
|
// constructed correctly (via Journal#RegisterEventType).
|
|
|
|
safe bool
|
|
|
|
}
|
|
|
|
|
|
|
|
// Enabled returns whether this event type is enabled in the journaling
|
|
|
|
// subsystem. Users are advised to check this before actually attempting to
|
|
|
|
// add a journal entry, as it helps bypass object construction for events that
|
|
|
|
// would be discarded anyway.
|
|
|
|
//
|
|
|
|
// All event types are enabled by default, and specific event types can only
|
|
|
|
// be disabled at Journal construction time.
|
|
|
|
func (et EventType) Enabled() bool {
|
2020-08-10 14:02:15 +00:00
|
|
|
return et.safe && et.enabled
|
|
|
|
}
|
|
|
|
|
|
|
|
// EventTypeFactory is a component that constructs tracked EventType tokens,
|
|
|
|
// for usage with a Journal.
|
|
|
|
type EventTypeFactory interface {
|
|
|
|
// RegisterEventType introduces a new event type to a journal, and
|
|
|
|
// returns an EventType token that components can later use to check whether
|
|
|
|
// journalling for that type is enabled/suppressed, and to tag journal
|
|
|
|
// entries appropriately.
|
|
|
|
RegisterEventType(system, event string) EventType
|
2020-07-17 13:22:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Journal represents an audit trail of system actions.
|
|
|
|
//
|
|
|
|
// Every entry is tagged with a timestamp, a system name, and an event name.
|
|
|
|
// The supplied data can be any type, as long as it is JSON serializable,
|
|
|
|
// including structs, map[string]interface{}, or primitive types.
|
|
|
|
//
|
|
|
|
// For cleanliness and type safety, we recommend to use typed events. See the
|
|
|
|
// *Evt struct types in this package for more info.
|
|
|
|
type Journal interface {
|
2020-08-10 14:02:15 +00:00
|
|
|
EventTypeFactory
|
2020-07-17 13:22:37 +00:00
|
|
|
|
2020-07-21 16:32:01 +00:00
|
|
|
// RecordEvent records this event to the journal. See godocs on the Journal type
|
2020-07-17 13:22:37 +00:00
|
|
|
// for more info.
|
2020-07-21 16:32:01 +00:00
|
|
|
RecordEvent(evtType EventType, data interface{})
|
2020-07-17 13:22:37 +00:00
|
|
|
|
|
|
|
// Close closes this journal for further writing.
|
|
|
|
Close() error
|
|
|
|
}
|
|
|
|
|
2020-07-21 16:32:01 +00:00
|
|
|
// Event represents a journal entry.
|
2020-07-17 13:22:37 +00:00
|
|
|
//
|
|
|
|
// See godocs on Journal for more information.
|
2020-07-21 16:32:01 +00:00
|
|
|
type Event struct {
|
2020-07-17 13:22:37 +00:00
|
|
|
EventType
|
|
|
|
|
|
|
|
Timestamp time.Time
|
|
|
|
Data interface{}
|
|
|
|
}
|
|
|
|
|
2020-08-10 14:02:15 +00:00
|
|
|
// MaybeRecordEvent is a convenience function that evaluates if the EventType is
|
|
|
|
// enabled, and if so, it calls the supplier to create the event and
|
|
|
|
// subsequently journal.RecordEvent on the provided journal to record it.
|
|
|
|
//
|
|
|
|
// This is safe to call with a nil Journal, either because the value is nil,
|
|
|
|
// or because a journal obtained through NilJournal() is in use.
|
|
|
|
func MaybeRecordEvent(journal Journal, evtType EventType, supplier func() interface{}) {
|
|
|
|
if journal == nil || journal == nilj {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if !evtType.Enabled() {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
journal.RecordEvent(evtType, supplier())
|
|
|
|
}
|
|
|
|
|
2020-07-17 17:35:15 +00:00
|
|
|
// eventTypeFactory is an embeddable mixin that takes care of tracking disabled
|
|
|
|
// event types, and returning initialized/safe EventTypes when requested.
|
|
|
|
type eventTypeFactory struct {
|
|
|
|
sync.Mutex
|
|
|
|
|
|
|
|
m map[string]EventType
|
|
|
|
}
|
|
|
|
|
2020-08-10 14:02:15 +00:00
|
|
|
var _ EventTypeFactory = (*eventTypeFactory)(nil)
|
|
|
|
|
|
|
|
func NewEventTypeFactory(disabled DisabledEvents) EventTypeFactory {
|
2020-07-17 17:35:15 +00:00
|
|
|
ret := &eventTypeFactory{
|
|
|
|
m: make(map[string]EventType, len(disabled)+32), // + extra capacity.
|
|
|
|
}
|
2020-07-17 13:22:37 +00:00
|
|
|
|
|
|
|
for _, et := range disabled {
|
2020-07-17 17:35:15 +00:00
|
|
|
et.enabled, et.safe = false, true
|
|
|
|
ret.m[et.System+":"+et.Event] = et
|
2020-07-17 13:22:37 +00:00
|
|
|
}
|
2020-07-17 17:35:15 +00:00
|
|
|
|
|
|
|
return ret
|
2020-07-17 13:22:37 +00:00
|
|
|
}
|
|
|
|
|
2020-07-17 17:35:15 +00:00
|
|
|
func (d *eventTypeFactory) RegisterEventType(system, event string) EventType {
|
|
|
|
d.Lock()
|
|
|
|
defer d.Unlock()
|
|
|
|
|
|
|
|
key := system + ":" + event
|
|
|
|
if et, ok := d.m[key]; ok {
|
|
|
|
return et
|
|
|
|
}
|
|
|
|
|
|
|
|
et := EventType{
|
|
|
|
System: system,
|
|
|
|
Event: event,
|
|
|
|
enabled: true,
|
|
|
|
safe: true,
|
|
|
|
}
|
|
|
|
|
|
|
|
d.m[key] = et
|
|
|
|
return et
|
2020-07-17 13:22:37 +00:00
|
|
|
}
|