Guides
Events
The events package allows for Go applications, including Buffalo applications, to listen, and emit, global event messages.
Listening for Events
To start listening for events a events#Listener must first be registered with the events package.
func init() {
_, err := events.Listen(func(e events.Event) {
// do work
})
}
Once registered this new listener function will be sent all events emitted through the events package.
Emitting Events
When emitting events the Kind
attribute should be a unique, but constant, string. It is this attribute that users will use to determine how to respond to events they receive.
It is recommended to namespace this attribute like such, with error events being suffixed with :err
.
"<package-name>:<additional-names>:<optional-error>"
"myapp:foo:start"
"myapp:foo:stop"
"mypkg:workers:bar:err"
This naming pattern makes it easier for users to filter events to only those that they care about. See Filtering Events for more details.
There are multiple ways to emit an events#Event in your Go code. The events#EmitError and events#EmitPayload functions both accept a payload interface{}
argument. It is recommended to use events#Payload for payloads; any other type passed in will get converted into a events#Payload with the argument set in the payload with the key, data
.
func MyHandler(c buffalo.Context) error {
e := events.Event{
Kind: "coke:myhandler:hello",
Message: "hi!",
Payload: events.Payload{"context": c},
}
if err := events.Emit(e); err != nil {
return err
}
return c.Render(200, r.HTML("index.html"))
}
func MyHandler(c buffalo.Context) error {
if err := events.EmitError("coke:myhandler:hello:err", errors.New("boom"), c); err != nil {
return err
}
return c.Render(200, r.HTML("index.html"))
}
func MyHandler(c buffalo.Context) error {
p := events.Payload{
"message": "hi!",
}
if err := events.EmitPayload("coke:myhandler:hello", p); err != nil {
return err
}
return c.Render(200, r.HTML("index.html"))
}
Filtering Events
In the Emitting Events section the naming convention for events#Event.Kind is described. By the checking the value of events#Event.Kind.
// direct match
events.Listen(func(e events.Event) {
if e.Kind != buffalo.EvtRouteStarted {
// do nothing
return
}
// do work on the route event
})
// matching with a switch statement
events.Listen(func(e events.Event) {
switch e.Kind {
case buffalo.EvtAppStart, buffalo.EvtAppStop:
// do work
case "buffalo:dev:build:finished":
// do work
default:
// do nothing
}
})
// matching error events
func init() {
events.Listen(func(e events.Event) {
if !e.IsError() {
// do nothing
return
}
// do something with e.Error
})
}
// matching on prefix
events.Listen(func(e events.Event) {
if !strings.HasPrefix(e.Kind, "buffalo:") {
// do nothing
return
}
// do work only on events emitted by Buffalo
})
Stop Listening for Events
When registering a new events#Listener a events#DeleteFn is returned. This function should be held on to and used when you want to remove the added listener.
deleteFn, err := events.Listen(func(e events.Event) {
// do work
})
if err != nil {
return err
}
defer deleteFn()
Listening with Plugins
To enable a plugin to a receive a JSON version of emitted events, the plugin can set the events#Command.BuffaloCommand value to events
when listing the available
commands for the plugin.
// availableCmd
var availableCmd = &cobra.Command{
Use: "available",
Short: "a list of available buffalo plugins",
RunE: func(cmd *cobra.Command, args []string) error {
plugs := plugins.Commands{
{Name: "listen", UseCommand: "listen", BuffaloCommand: "events", Description: listenCmd.Short, Aliases: listenCmd.Aliases},
}
return json.NewEncoder(os.Stdout).Encode(plugs)
},
}
// listenCmd
var listenCmd = &cobra.Command{
Use: "listen",
Short: "listens to github.com/gobuffalo/events",
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return errors.New("must pass a payload")
}
e := events.Event{}
err := json.Unmarshal([]byte(args[0]), &e)
if err != nil {
return errors.WithStack(err)
}
// do work with event
return nil
},
}
Integrating a Messaging Queue
It is often desirable to take events emitted and send them to a message queue, such as Kafka or Redis, to be processed externally. The events package does not have a directhook for this sort of functionality, the most direct way of enabling this behavior is to register a events#Listener that can then hand the event over to the appropriate message queue.
events.Listen(func(e events.Event) {
myMessageQ.DoWork(e)
})
Known Events
Application Events
The following events are known to be emitted by Buffalo during the application lifecyle.
Constant | String | Emitted When | Payload |
---|---|---|---|
buffalo.EvtAppStart |
"buffalo:app:start" |
buffalo#App.Serve is called | app : buffalo#App |
buffalo.EvtAppStartErr |
"buffalo:app:start:err" |
an error occurs calling buffalo#App.Serve | app : buffalo#App |
buffalo.EvtAppStop |
"buffalo:app:stop" |
buffalo#App.Stop is called | app : buffalo#App |
buffalo.EvtAppStopErr |
"buffalo:app:stop:err" |
an error occurs calling buffalo#App.Stop | app : buffalo#App |
buffalo.EvtRouteStarted |
"buffalo:route:started" |
a requested route is being processed | route : buffalo#RouteInfo app : buffalo#App context : buffalo#Context |
buffalo.EvtRouteFinished |
"buffalo:route:finished" |
a requested route is completed | route : buffalo#RouteInfo app : buffalo#App context : buffalo#Context |
buffalo.EvtRouteErr |
"buffalo:route:err" |
there is a problem handling processing a route | route : buffalo#RouteInfo app : buffalo#App context : buffalo#Context |
buffalo.EvtWorkerStart |
"buffalo:worker:start" |
buffalo#App.Serve is called and workers are started | app : buffalo#App |
buffalo.EvtWorkerStartErr |
"buffalo:worker:start:err" |
an error occurs when starting workers | app : buffalo#App |
buffalo.EvtWorkerStop |
"buffalo:worker:stop" |
buffalo#App.Stop is called and workers are stopped | app : buffalo#App |
buffalo.EvtWorkerStopErr |
"buffalo:worker:stop:err" |
an error occurs when stopping workers | app : buffalo#App |
buffalo.EvtFailureErr |
"buffalo:failure:err" |
something can’t be processed at all. it is a bad thing | app : buffalo#App context : buffalo#Context |
Buffalo Dev Events
The following events are known to be emitted by the buffalo dev
during the development lifecyle.
String | Emitted When | Payload |
---|---|---|
"buffalo:dev:raw" |
an applicable file is modified | event : fsnotify#Event |
"buffalo:dev:build:started" |
a build has started | event : fsnotify#Event cmd : string of the go build command (example: "go build foo" ) |
"buffalo:dev:build:finished" |
a build has completed | event : fsnotify#Event pid : PID of the newly running binary build_time : the duration of the build |
"buffalo:dev:build:err" |
a build error has occurred | event : fsnotify#Event cmd : string of the go build command (example: "go build foo" ) |