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")