event is a free, open source scheduling & event management project written in Go and released under MIT. It has 578 GitHub stars, 69 forks and 4 open issues, and was last pushed 2 months ago. On this registry it ranks #20 of 23 tracked projects in Scheduling & Event Management, with 5 head-to-head comparisons available.

What is event?

gookit/event is a lightweight, MIT-licensed event manager and dispatcher library written in Go, built for Go developers who need to register, prioritize, and fire application events inside a single process without adopting a full message broker.

What it is

gookit/event is an in-process event management and dispatch library implemented in Go, published as the importable package github.com/gookit/event. It sits within the Go module ecosystem and is maintained under the gookit organization, with documentation hosted on pkg.go.dev. Its scope is deliberately narrow: it provides the primitives for registering event listeners, organizing them by name and priority, and triggering those listeners synchronously or asynchronously from application code. The library defines its own event object type and a Listener interface, so events carry a name and a parameter map of type M, and listeners implement a single handler function.

The concrete problem it solves is the absence of a decoupled, built-in dispatch mechanism in plain Go programs. Without a library of this kind, developers wire direct function calls between components, which couples callers to every handler and makes it awkward to attach additional behavior such as logging, notifications, or cache invalidation to an existing code path. gookit/event replaces that hand-rolled wiring with a registry that keeps emitters and handlers separate. Emitters call methods such as Trigger, Fire, or MustFire with an event name and parameters, and the library routes the call to every listener registered for that name.

Key capabilities

  • Register listeners with On or Listen, passing a name, a Listener, and an optional priority; subscription of multiple handlers at once is available through Subscribe or AddSubscriber with a Subscriber.
  • Assign listener priorities using constants such as Normal and High; the higher priority listener fires first, as the README's quick-start example demonstrates.
  • Trigger events by name and parameter map with Trigger or Fire, or with a context via FireCtx(ctx context.Context, name string, params M).
  • Fire a pre-built event instance with FireEvent, several events in one call with FireBatch, and fail loudly with MustTrigger or MustFire, which panic on error.
  • Match groups of events with wildcards. ModeSimple (the default) supports prefix listening such as app.*, so firing app.run or app.end invokes the app.* listener.
  • Use ModePath, introduced in v1.1.0, for finer matching: * matches only a segment of characters that are not ., and ** matches any number of characters but may appear only at the beginning or end.
  • Listen for all events with the wildcard *, and dispatch asynchronously through a chan consumer using Async or FireC, FireAsync, or AsyncFire with the go keyword.

Who uses it and how

  • Go service and application developers who need hooks around existing logic, such as firing app.db.create and app.db.update from separate functions while a single app.db.* listener handles both.
  • Teams using path-style event namespaces, where ModePath allows one listener to cover a whole subtree of events and finer wildcards to target narrower branches.
  • Applications that need ordered side effects, where priority constants guarantee that higher-priority listeners run before lower-priority ones for the same event name.
  • Workloads that cannot block the caller, using the asynchronous chan-based dispatch so event handling happens in a separate consumer goroutine.
  • Component authors who want to publish extension points without importing every consumer, by shipping a Subscriber that consumers register during initialization.

Getting started

Install with go get github.com/gookit/event, then register a listener via event.On and fire the event with event.MustFire as shown in the README quick start. No server, container, or compose file is involved; the library is consumed directly as a Go module dependency.

How it compares

No list of paid products or comparable tools is provided in the facts, so this project stands alone in this registry. Nothing in the supplied material names an alternative library, hosted service, or commercial product that gookit/event is positioned against.

When to use it — and when not to

This is a pure in-process library, so a self-hoster operates nothing beyond a Go toolchain; there is no database, object storage, or SMTP dependency to run. It is the wrong choice for cross-process or cross-host messaging, durable queues, or delivery guarantees, since dispatch happens inside one running program. The README excerpt supplied here is also truncated in the middle of the ModePath documentation, so readers should check the full pkg.go.dev documentation before relying on the newer matching semantics.

project readme (upstream, from github) — read inline

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

Main method

  • On/Listen(name string, listener Listener, priority ...int) Register event listener
  • Subscribe/AddSubscriber(sbr Subscriber) Subscribe to support registration of multiple event listeners
  • Trigger/Fire(name string, params M) (error, Event) Trigger event by name and params
  • FireCtx(ctx context.Context, name string, params M) (error, Event) Trigger event with context
  • MustTrigger/MustFire(name string, params M) Event Trigger event, there will be panic if there is an error
  • FireEvent(e Event) (err error) Trigger an event based on a given event instance
  • FireBatch(es ...any) (ers []error) Trigger multiple events at once
  • Async/FireC(name string, params M) Push event to chan, asynchronous consumption processing
  • FireAsync(e Event) Push event to chan, asynchronous consumption processing
  • AsyncFire(e Event) Async fire event by 'go' keywords

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]any
}

Example

Implementation interface event.Subscriber

package mypgk

import (
	"fmt"

	"github.com/gookit/event"
)

type MySubscriber struct {
	// ooo
}

func (s *MySubscriber) SubscribedEvents() map[string]any {
	return map[string]any{
		"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
	Get(key string) any
	Add(key string, val any)
	Set(key string, val any)
	Data() map[string]any
	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 c

readme truncated — read the full docs on github

Frequently asked questions

Is event free to use?

event is open source under the MIT licence. There is no licence fee and no seat count — you can self-host it or, where the project offers one, pay a vendor for a managed version instead.

What does event do?

📢 Lightweight event manager and dispatcher implements by Go. Go实现的轻量级的事件管理、调度程序库, 支持设置监听器的优先级, 支持使用通配符来进行一组事件的监听

What is event written in?

event is primarily written in Go. Its source is publicly available at https://github.com/gookit/event, and it has 578 GitHub stars.