Skip to content

Instantly share code, notes, and snippets.

@geberl
Last active October 15, 2023 20:59
Show Gist options
  • Save geberl/3f614bad90be1b00bad369325d3642a9 to your computer and use it in GitHub Desktop.
Save geberl/3f614bad90be1b00bad369325d3642a9 to your computer and use it in GitHub Desktop.
Example oklog/run group
package main
import (
"context"
"net/http"
"os"
"syscall"
"github.com/go-kit/kit/log"
"github.com/go-kit/kit/log/level"
"github.com/gorilla/mux"
"github.com/oklog/run"
)
func main() {
logger := MakeLogger("debug")
level.Debug(logger).Log("msg", "hello")
var (
g run.Group
ctx = context.Background()
)
ctx, cancel := context.WithCancel(ctx)
/*
Add an actor (function) to the group.
Each actor must be pre-emptable by an interrupt function.
That is, if interrupt is invoked, execute should return.
Also, it must be safe to call interrupt even after execute has returned.
The first actor (function) to return interrupts all running actors.
The error is passed to the interrupt functions, and is returned by Run.
*/
// Other func 1
{
g.Add(func() error {
level.Debug(logger).Log("msg", "other func 1 g add")
for {
if ctx.Err() != nil {
// level.Debug(logger).Log("msg", "other func 1 context cancel")
break
}
}
return nil
}, func(err error) {
level.Debug(logger).Log("msg", "other func 1 g interrupt func")
cancel()
})
}
// Other func 2
{
g.Add(func() error {
level.Debug(logger).Log("msg", "other func 2 g add")
for {
if ctx.Err() != nil {
// level.Debug(logger).Log("msg", "other func 2 context cancel")
break
}
}
return nil
}, func(err error) {
level.Debug(logger).Log("msg", "other func 2 g interrupt")
cancel()
})
}
// gorilla/mux based API
{
router := &mux.Router{}
server := &http.Server{Addr: ":8080", Handler: router}
g.Add(func() error {
level.Debug(logger).Log("msg", "api func g add func")
return server.ListenAndServe()
}, func(err error) {
level.Debug(logger).Log("msg", "api func g interrupt func")
if err := server.Shutdown(ctx); err != nil {
level.Error(logger).Log("msg", "failed to gracefully exit api", "err", err)
}
})
}
// Signal handler
{
execute, interrupt := run.SignalHandler(ctx, syscall.SIGTERM, syscall.SIGINT)
g.Add(func() error {
level.Debug(logger).Log("msg", "signal func g add")
err := execute()
if se, ok := err.(run.SignalError); ok {
level.Info(logger).Log("signal", se.Signal)
return nil
}
return err
}, func(err error) {
level.Debug(logger).Log("msg", "signal func g interrupt")
interrupt(err)
})
}
if err := g.Run(); err != nil {
level.Error(logger).Log("msg", "error running groups", "err", err)
os.Exit(1)
}
level.Debug(logger).Log("msg", "clean exit")
}
func MakeLogger(logLevel string) log.Logger {
logger := log.NewJSONLogger(log.NewSyncWriter(os.Stdout))
switch logLevel {
case "error":
logger = level.NewFilter(logger, level.AllowError())
case "warn":
logger = level.NewFilter(logger, level.AllowWarn())
case "debug":
logger = level.NewFilter(logger, level.AllowDebug())
default:
logger = level.NewFilter(logger, level.AllowInfo())
}
logger = log.With(logger, "ts", log.DefaultTimestampUTC)
logger = level.NewInjector(logger, level.InfoValue())
return logger
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment