Skip to content

LumeWeb/event

 
 

Repository files navigation

Event

GitHub go.mod Go version GoDoc Actions Status Coverage Status Go Report Card

Lightweight event management, dispatch tool library implemented by Go

  • Support for custom definition event objects
  • Support for adding multiple listeners to an event
  • Support setting the priority of the event listener, the higher the priority, the first to trigger
  • Support for a set of event listeners based on the event name prefix PREFIX.*.
    • ModeSimple(default) - app.* event listen, trigger app.run app.end, Both will fire the app.* listener
  • New match mode: ModePath
    • * Only match a segment of characters that are not ., allowing for finer monitoring and matching
    • ** matches any number of characters and can only be used at the beginning or end
  • Support for using the wildcard * to listen for triggers for all events
  • Support async trigger event by go channel consumers. use Async(), FireAsync()
  • Complete unit testing, unit coverage > 95%

中文说明请看 README.zh-CN

GoDoc

Install

go get github.com/gookit/event

Core Interfaces

The event system is organized into several focused interfaces that provide clear separation of concerns:

EventDispatcher

  • On/Listen() - Register event listeners
  • Once() - Register one-time listeners
  • Fire/Trigger() - Trigger events
  • MustFire/MustTrigger() - Trigger events with panic on error

EventBatchManager

  • FireBatch() - Trigger multiple events
  • FireEvent/TriggerEvent() - Trigger pre-built events

AsyncEventManager

  • Async/FireC() - Async trigger via channel
  • FireAsync() - Async trigger pre-built events
  • AsyncFire() - Async via goroutine
  • AwaitFire() - Async with wait
  • Close/CloseWait() - Manage async channel

ListenerManager

  • AddListener() - Register listeners
  • RemoveListener() - Remove listeners
  • HasListeners() - Check for listeners
  • Subscribe() - Bulk register listeners

EventDefinitionManager

  • AddEvent() - Register event instances
  • Get/HasEvent() - Query events
  • RemoveEvent() - Remove events

EventManager (Composite Interface)

The EventManager interface combines all core event management capabilities into a single unified API by embedding:

  • EventDispatcher - Basic event triggering
  • EventBatchManager - Batch event operations
  • AsyncEventManager - Asynchronous event handling
  • ListenerManager - Listener management
  • EventDefinitionManager - Event definition handling

This provides the complete API surface for event management in one interface. Key additional methods:

  • Reset() - Clear all listeners and events
  • Full access to all methods from embedded interfaces

Quick start

package main

import (
	"fmt"
	
	"github.com/gookit/event"
)

func main() {
	// Register event listener
	event.On("evt1", event.ListenerFunc(func(e event.Event) error {
		fmt.Printf("handle event: %s\n", e.Name())
		return nil
	}), event.Normal)

	// Register multiple listeners
	event.On("evt1", event.ListenerFunc(func(e event.Event) error {
		fmt.Printf("handle event: %s\n", e.Name())
		return nil
	}), event.High)

	// ... ...

	// Trigger event
	// Note: The second listener has a higher priority, so it will be executed first.
	event.MustFire("evt1", event.M{"arg0": "val0", "arg1": "val1"})
}

Note: The second listener has a higher priority, so it will be executed first.

Using the wildcard

Match mode ModePath

Register event listener and name end with wildcard *:

func main() {
	dbListener1 := event.ListenerFunc(func(e event.Event) error {
		fmt.Printf("handle event: %s\n", e.Name())
		return nil
	})

	event.On("app.db.*", dbListener1, event.Normal)
}

Trigger events on other logic:

func doCreate() {
	// do something ...
	// Trigger event
	event.MustFire("app.db.create", event.M{"arg0": "val0", "arg1": "val1"})
}

func doUpdate() {
	// do something ...
	// Trigger event
	event.MustFire("app.db.update", event.M{"arg0": "val0"})
}

Like the above, triggering the app.db.create app.db.update event will trigger the execution of the dbListener1 listener.

Match mode ModePath

ModePath It is a new pattern of v1.1.0, and the wildcard * matching logic has been adjusted:

  • * Only match a segment of characters that are not ., allowing for finer monitoring and matching
  • ** matches any number of characters and can only be used at the beginning or end
em := event.NewManager("test", event.UsePathMode)

// register listener
em.On("app.**", appListener)
em.On("app.db.*", dbListener)
em.On("app.*.create", createListener)
em.On("app.*.update", updateListener)

// ... ...

// fire event
// TIP: will trigger appListener, dbListener, createListener
em.Fire("app.db.create", event.M{"arg0": "val0", "arg1": "val1"})

Async fire events

Use chan fire events

You can use the Async/FireC/FireAsync method to trigger events, and the events will be written to chan for asynchronous consumption. You can use CloseWait() to close the chan and wait for all events to be consumed.

Added option configuration:

  • ChannelSize Set buffer size for chan
  • ConsumerNum Set how many coroutines to start to consume events
func main() {
	// Note: close event chan on program exit
	defer event.CloseWait()
	// defer event.Close()
	
    // register event listener
    event.On("app.evt1", event.ListenerFunc(func(e event.Event) error {
        fmt.Printf("handle event: %s\n", e.Name())
        return nil
    }), event.Normal)
    
    event.On("app.evt1", event.ListenerFunc(func(e event.Event) error {
        fmt.Printf("handle event: %s\n", e.Name())
        return nil
    }), event.High)
    
    // ... ...
    
    // Asynchronous consumption of events
    event.FireC("app.evt1", event.M{"arg0": "val0", "arg1": "val1"})
}

Note: The event chan should be closed when the program exits. You can use the following method:

  • event.Close() Close chan and no longer accept new events
  • event.CloseWait() Close chan and wait for all event processing to complete

Write event listeners

Using anonymous functions

You can use anonymous function for quick write an event lister.

package mypgk

import (
	"fmt"

	"github.com/gookit/event"
)

var fnHandler = func(e event.Event) error {
	fmt.Printf("handle event: %s\n", e.Name())
	return nil
}

func Run() {
	// register
	event.On("evt1", event.ListenerFunc(fnHandler), event.High)
}

Using the structure method

You can use struct write an event lister, and it should implementation interface event.Listener.

interface:

// Listener interface
type Listener interface {
	Handle(e Event) error
}

example:

Implementation interface event.Listener

package mypgk

import "github.com/gookit/event"

type MyListener struct {
	// userData string
}

func (l *MyListener) Handle(e event.Event) error {
	e.Set("result", "OK")
	return nil
}

Register multiple event listeners

Can implementation interface event.Subscriber for register multiple event listeners at once.

interface:

// Subscriber event subscriber interface.
// you can register multi event listeners in a struct func.
type Subscriber interface {
	// SubscribedEvents register event listeners
	// key: is event name
	// value: can be Listener or ListenerItem interface
	SubscribedEvents() map[string]interface{}
}

Example

Implementation interface event.Subscriber

package mypgk

import (
	"fmt"

	"github.com/gookit/event"
)

type MySubscriber struct {
	// ooo
}

func (s *MySubscriber) SubscribedEvents() map[string]interface{} {
	return map[string]interface{}{
		"e1": event.ListenerFunc(s.e1Handler),
		"e2": event.ListenerItem{
			Priority: event.AboveNormal,
			Listener: event.ListenerFunc(func(e Event) error {
				return fmt.Errorf("an error")
			}),
		},
		"e3": &MyListener{},
	}
}

func (s *MySubscriber) e1Handler(e event.Event) error {
	e.Set("e1-key", "val1")
	return nil
}

Write custom events

If you want to customize the event object or define some fixed event information in advance, you can implement the event.Event interface.

interface:

// Event interface
type Event interface {
	Name() string
	// Target() interface{}
	Get(key string) interface{}
	Add(key string, val interface{})
	Set(key string, val interface{})
	Data() map[string]interface{}
	SetData(M) Event
	Abort(bool)
	IsAborted() bool
}

examples:

package mypgk

import "github.com/gookit/event"

type MyEvent struct {
	event.BasicEvent
	customData string
}

func (e *MyEvent) CustomData() string {
	return e.customData
}

Usage:

e := &MyEvent{customData: "hello"}
e.SetName("e1")
event.AddEvent(e)

// add listener
event.On("e1", event.ListenerFunc(func(e event.Event) error {
	fmt.Printf("custom Data: %s\n", e.(*MyEvent).CustomData())
	return nil
}))

// trigger
event.Fire("e1", nil)
// OR
// event.FireEvent(e)

Note: is used to add pre-defined public event information, which is added in the initialization phase, so it is not locked. Event dynamically created in business can be directly triggered by FireEvent()

Gookit packages

  • gookit/ini Go config management, use INI files
  • gookit/rux Simple and fast request router for golang HTTP
  • gookit/gcli build CLI application, tool library, running CLI commands
  • gookit/slog Lightweight, extensible, configurable logging library written in Go
  • gookit/event Lightweight event manager and dispatcher implements by Go
  • gookit/cache Generic cache use and cache manager for golang. support File, Memory, Redis, Memcached.
  • gookit/config Go config management. support JSON, YAML, TOML, INI, HCL, ENV and Flags
  • gookit/color A command-line color library with true color support, universal API methods and Windows support
  • gookit/filter Provide filtering, sanitizing, and conversion of golang data
  • gookit/validate Use for data validation and filtering. support Map, Struct, Form data
  • gookit/goutil Some utils for the Go: string, array/slice, map, format, cli, env, filesystem, test and more
  • More, please see https://github.com/gookit

LICENSE

MIT

About

A lightweight event management and scheduling library implemented in Go. It supports setting the priority of listeners and using wildcards to monitor a group of events.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages

  • Go 100.0%