Improve this page

Events

since v0.13.0-beta.2

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 buffalo-plugins/plugins#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: github.com/fsnotify/fsnotify#Event
"buffalo:dev:build:started" a build has started event: github.com/fsnotify/fsnotify#Event
cmd: string of the go build command (example: "go build foo")
"buffalo:dev:build:finished" a build has completed event: github.com/fsnotify/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: github.com/fsnotify/fsnotify#Event
cmd: string of the go build command (example: "go build foo")