Skip to content

Commit

Permalink
Migrate from xlog to zerolog in examples
Browse files Browse the repository at this point in the history
Switch to zerolog as xlog is no longer maintained.
  • Loading branch information
rs committed Jul 10, 2017
1 parent 5b33327 commit 64b19cc
Show file tree
Hide file tree
Showing 8 changed files with 223 additions and 178 deletions.
80 changes: 29 additions & 51 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1189,72 +1189,50 @@ See [schema.IP](https://godoc.org/github.com/rs/rest-layer/schema#IP) validator

## Timeout and Request Cancellation

REST Layer respects [context](https://godoc.org/context) deadline from end to end. Timeout and request cancellation are thus handled throught `context`. By default no cancellation handling or per request timeout are defined. You can easily add them using [xhandler](https://github.com/rs/xhandler) provided handlers as follow:

```go
// Init a xhandler chain (see http://github.com/rs/xhandler)
c := xhandler.Chain{}

// Add close notifier handler so context is cancelled when the client closes
// the connection
c.UseC(xhandler.CloseHandler)

// Add timeout handler
c.UseC(xhandler.TimeoutHandler(2 * time.Second))

// Add other handlers like xlog, xaccess, cors (see examples)

// Bind the API under /api/ path
http.Handle("/api/", http.StripPrefix("/api/", c.Handler(api)))
```
REST Layer respects [context](https://godoc.org/context) deadline from end to end. Timeout and request cancellation are thus handled throught `context`. Since Go 1.8, context is cancelled automatically if the user closes the connection.

When a request is stopped because the client closed the connection (context cancelled), the response HTTP status is set to `499 Client Closed Request` (for logging purpose). When a timeout is set and the request has reached this timeout, the response HTTP status is set to `509 Gateway Timeout`.

## Logging

You can customize REST Layer logger by changing the `resource.Logger` function to call any logging framework you want.

We recommend using [xlog](https://github.com/rs/xlog). To configure REST Layer with `xlog`, proceed as follow:
We recommend using [zerolog](https://github.com/rs/zerolog). To configure REST Layer with `zerolog`, proceed as follow:

```go
// Init an alice handler chain (use your preferred one)
c := alice.New()

// Install a logger
c = c.Append(hlog.NewHandler(log.With().Logger()))

// Log API accesses
c = c.Append(hlog.AccessHandler(func(r *http.Request, status, size int, duration time.Duration) {
hlog.FromRequest(r).Info().
Str("method", r.Method).
Str("url", r.URL.String()).
Int("status", status).
Int("size", size).
Dur("duration", duration).
Msg("")
}))

// Add some fields to per-request logger context
c = c.Append(hlog.RequestHandler("req"))
c = c.Append(hlog.RemoteAddrHandler("ip"))
c = c.Append(hlog.UserAgentHandler("ua"))
c = c.Append(hlog.RefererHandler("ref"))
c = c.Append(hlog.RequestIDHandler("req_id", "Request-Id"))

// Install zerolog/rest-layer adapter
resource.LoggerLevel = resource.LogLevelDebug
resource.Logger = func(ctx context.Context, level resource.LogLevel, msg string, fields map[string]interface{}) {
xlog.FromContext(ctx).OutputF(xlog.Level(level), 2, msg, fields)
zerolog.Ctx(ctx).WithLevel(zerolog.Level(level)).Fields(fields).Msg(msg)
}

// Install xlog logger with a complex routing configuration
c.UseC(xlog.NewHandler(xlog.Config{
// Log info level and higher
Level: xlog.Level(resource.LoggerLevel),
// Set some global env fields
Fields: xlog.F{
"role": "my-service",
"host": host,
},
// Output everything on console
Output: xlog.NewOutputChannel(xlog.MultiOutput{
// Send all logs with field type=mymodule to a remote syslog
xlog.FilterOutput{
Cond: func(fields map[string]interface{}) bool {
return fields["type"] == "mymiddleware"
},
Output: xlog.NewSyslogOutput("tcp", "1.2.3.4:1234", "mymiddleware"),
},
// Setup different output per log level
xlog.LevelOutput{
// Send errors to the console
Error: xlog.NewConsoleOutput(),
// Send syslog output for error level
Info: xlog.NewSyslogOutput("", "", ""),
},
}),
}))

// Log API access using xlog
c.UseC(xaccess.NewHandler())
```

See [xlog](https://github.com/rs/xlog) documentation for more info.
See [zerolog](https://github.com/rs/zerolog) documentation for more info.

## CORS

Expand Down
39 changes: 24 additions & 15 deletions example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ package restlayer

import (
"context"
"log"
"net/http"
"net/url"
"time"

"github.com/justinas/alice"
"github.com/rs/cors"
Expand All @@ -15,7 +15,9 @@ import (
"github.com/rs/rest-layer/rest"
"github.com/rs/rest-layer/schema"
"github.com/rs/xaccess"
"github.com/rs/xlog"
"github.com/rs/zerolog"
"github.com/rs/zerolog/hlog"
"github.com/rs/zerolog/log"
)

func Example() {
Expand Down Expand Up @@ -137,24 +139,31 @@ func Example() {
// Create API HTTP handler for the resource graph
api, err := rest.NewHandler(index)
if err != nil {
log.Fatalf("Invalid API configuration: %s", err)
log.Fatal().Err(err).Msg("Invalid API configuration")
}

// Init an alice handler chain (use your preferred one)
c := alice.New()

// Add close notifier handler so context is cancelled when the client closes
// the connection
//c.Append(xhandler.CloseHandler)

// Add timeout handler
//c.Append(xhandler.TimeoutHandler(2 * time.Second))

// Install a logger (see https://github.com/rs/xlog)
c = c.Append(xlog.NewHandler(xlog.Config{}))
// Install a logger
c = c.Append(hlog.NewHandler(log.With().Logger()))
c = c.Append(hlog.AccessHandler(func(r *http.Request, status, size int, duration time.Duration) {
hlog.FromRequest(r).Info().
Str("method", r.Method).
Str("url", r.URL.String()).
Int("status", status).
Int("size", size).
Dur("duration", duration).
Msg("")
}))
c = c.Append(hlog.RequestHandler("req"))
c = c.Append(hlog.RemoteAddrHandler("ip"))
c = c.Append(hlog.UserAgentHandler("ua"))
c = c.Append(hlog.RefererHandler("ref"))
c = c.Append(hlog.RequestIDHandler("req_id", "Request-Id"))
resource.LoggerLevel = resource.LogLevelDebug
resource.Logger = func(ctx context.Context, level resource.LogLevel, msg string, fields map[string]interface{}) {
xlog.FromContext(ctx).OutputF(xlog.Level(level), 2, msg, fields)
zerolog.Ctx(ctx).WithLevel(zerolog.Level(level)).Fields(fields).Msg(msg)
}

// Log API access
Expand All @@ -168,8 +177,8 @@ func Example() {
http.Handle("/api/", http.StripPrefix("/api/", c.Then(api)))

// Serve it
log.Print("Serving API on http://localhost:8080")
log.Info().Msg("Serving API on http://localhost:8080")
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatal(err)
log.Fatal().Err(err).Msg("")
}
}
64 changes: 36 additions & 28 deletions examples/auth-jwt/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ package main
import (
"context"
"flag"
"log"
"fmt"
"net/http"
"time"

Expand All @@ -17,8 +17,9 @@ import (
"github.com/rs/rest-layer/rest"
"github.com/rs/rest-layer/schema"
"github.com/rs/rest-layer/schema/query"
"github.com/rs/xaccess"
"github.com/rs/xlog"
"github.com/rs/zerolog"
"github.com/rs/zerolog/hlog"
"github.com/rs/zerolog/log"
)

// NOTE: this example show how to integrate REST Layer with JWT. No authentication is performed
Expand Down Expand Up @@ -80,9 +81,9 @@ func NewJWTHandler(users *resource.Resource, jwtKeyFunc jwt.Keyfunc) func(next h
}
// Store it into the request's context
ctx = NewContextWithUser(ctx, user)
// Add the user to log context (using zerolog)
ctx = hlog.FromRequest(r).With().Interface("user_id", user.ID).Logger().WithContext(ctx)
r = r.WithContext(ctx)
// If xlog is setup, store the user as logger field
xlog.FromContext(ctx).SetField("user_id", user.ID)
next.ServeHTTP(w, r)
})
}
Expand Down Expand Up @@ -292,21 +293,29 @@ func main() {
// Create API HTTP handler for the resource graph
api, err := rest.NewHandler(index)
if err != nil {
log.Fatalf("Invalid API configuration: %s", err)
log.Fatal().Msgf("Invalid API configuration: %s", err)
}

// Setup logger
c := alice.New()
c = c.Append(xlog.NewHandler(xlog.Config{}))
c = c.Append(xaccess.NewHandler())
c = c.Append(xlog.RequestHandler("req"))
c = c.Append(xlog.RemoteAddrHandler("ip"))
c = c.Append(xlog.UserAgentHandler("ua"))
c = c.Append(xlog.RefererHandler("ref"))
c = c.Append(xlog.RequestIDHandler("req_id", "Request-Id"))
c = c.Append(hlog.NewHandler(log.With().Logger()))
c = c.Append(hlog.AccessHandler(func(r *http.Request, status, size int, duration time.Duration) {
hlog.FromRequest(r).Info().
Str("method", r.Method).
Str("url", r.URL.String()).
Int("status", status).
Int("size", size).
Dur("duration", duration).
Msg("")
}))
c = c.Append(hlog.RequestHandler("req"))
c = c.Append(hlog.RemoteAddrHandler("ip"))
c = c.Append(hlog.UserAgentHandler("ua"))
c = c.Append(hlog.RefererHandler("ref"))
c = c.Append(hlog.RequestIDHandler("req_id", "Request-Id"))
resource.LoggerLevel = resource.LogLevelDebug
resource.Logger = func(ctx context.Context, level resource.LogLevel, msg string, fields map[string]interface{}) {
xlog.FromContext(ctx).OutputF(xlog.Level(level), 2, msg, fields)
zerolog.Ctx(ctx).WithLevel(zerolog.Level(level)).Fields(fields).Msg(msg)
}

// Setup auth middleware
Expand All @@ -327,29 +336,28 @@ func main() {
jackClaims["user_id"] = "jack"
jackTokenString, err := jackToken.SignedString(jwtSecretBytes)
if err != nil {
log.Fatal(err)
log.Fatal().Err(err).Msg("")
}
johnToken := jwt.New(jwt.SigningMethodHS256)
johnClaims := johnToken.Claims.(jwt.MapClaims)
johnClaims["user_id"] = "john"
johnTokenString, err := johnToken.SignedString(jwtSecretBytes)
if err != nil {
log.Fatal(err)
log.Fatal().Err(err).Msg("")
}

// Serve it
log.Print("Serving API on http://localhost:8080")
log.Printf("Your token secret is %q, change it with the `-jwt-secret' flag", *jwtSecret)
log.Print("Play with tokens:\n",
"\n",
"- http :8080/posts access_token==", johnTokenString, " title=\"John's post\"\n",
"- http :8080/posts access_token==", johnTokenString, "\n",
"- http :8080/posts\n",
"\n",
"- http :8080/posts access_token==", jackTokenString, " title=\"Jack's post\"\n",
"- http :8080/posts access_token==", jackTokenString, "\n",
)
fmt.Println("Serving API on http://localhost:8080")
fmt.Printf("Your token secret is %q, change it with the `-jwt-secret' flag\n", *jwtSecret)
fmt.Print("Play with tokens:\n" +
"\n" +
"- http :8080/posts access_token==" + johnTokenString + " title=\"John's post\"\n" +
"- http :8080/posts access_token==" + johnTokenString + "\n" +
"- http :8080/posts\n" +
"\n" +
"- http :8080/posts access_token==" + jackTokenString + " title=\"Jack's post\"\n" +
"- http :8080/posts access_token==" + jackTokenString + "\n")
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatal(err)
log.Fatal().Err(err).Msg("")
}
}
36 changes: 22 additions & 14 deletions examples/auth/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ package main

import (
"context"
"log"
"net/http"
"time"

Expand All @@ -14,8 +13,9 @@ import (
"github.com/rs/rest-layer/rest"
"github.com/rs/rest-layer/schema"
"github.com/rs/rest-layer/schema/query"
"github.com/rs/xaccess"
"github.com/rs/xlog"
"github.com/rs/zerolog"
"github.com/rs/zerolog/hlog"
"github.com/rs/zerolog/log"
)

// NOTE: this example demonstrates how to implement basic authentication/authorization with REST Layer.
Expand Down Expand Up @@ -270,21 +270,29 @@ func main() {
// Create API HTTP handler for the resource graph
api, err := rest.NewHandler(index)
if err != nil {
log.Fatalf("Invalid API configuration: %s", err)
log.Fatal().Err(err).Msg("Invalid API configuration")
}

// Setup logger
c := alice.New()
c = c.Append(xlog.NewHandler(xlog.Config{}))
c = c.Append(xaccess.NewHandler())
c = c.Append(xlog.RequestHandler("req"))
c = c.Append(xlog.RemoteAddrHandler("ip"))
c = c.Append(xlog.UserAgentHandler("ua"))
c = c.Append(xlog.RefererHandler("ref"))
c = c.Append(xlog.RequestIDHandler("req_id", "Request-Id"))
c = c.Append(hlog.NewHandler(log.With().Logger()))
c = c.Append(hlog.AccessHandler(func(r *http.Request, status, size int, duration time.Duration) {
hlog.FromRequest(r).Info().
Str("method", r.Method).
Str("url", r.URL.String()).
Int("status", status).
Int("size", size).
Dur("duration", duration).
Msg("")
}))
c = c.Append(hlog.RequestHandler("req"))
c = c.Append(hlog.RemoteAddrHandler("ip"))
c = c.Append(hlog.UserAgentHandler("ua"))
c = c.Append(hlog.RefererHandler("ref"))
c = c.Append(hlog.RequestIDHandler("req_id", "Request-Id"))
resource.LoggerLevel = resource.LogLevelDebug
resource.Logger = func(ctx context.Context, level resource.LogLevel, msg string, fields map[string]interface{}) {
xlog.FromContext(ctx).OutputF(xlog.Level(level), 2, msg, fields)
zerolog.Ctx(ctx).WithLevel(zerolog.Level(level)).Fields(fields).Msg(msg)
}

// Setup auth middleware
Expand All @@ -294,8 +302,8 @@ func main() {
http.Handle("/", c.Then(api))

// Serve it
log.Print("Serving API on http://localhost:8080")
log.Info().Msg("Serving API on http://localhost:8080")
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatal(err)
log.Fatal().Err(err).Msg("")
}
}
Loading

0 comments on commit 64b19cc

Please sign in to comment.