forked from goadesign/examples
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
820 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
# Tracing Example | ||
|
||
This example illustrates how to use the tracing and AWS X-Ray middleware in | ||
Goa v3. | ||
|
||
## Server Tracing Setup | ||
|
||
The tracing middleware can be mounted on the `net/http` `Handler` for HTTP | ||
transport or when initializing a gRPC server for gRPC transport. The ordering | ||
of mounting the tracing and X-Ray middleware is important as shown below. | ||
|
||
``` | ||
// HTTP | ||
var handler http.Handler = mux | ||
{ | ||
xrayHndlr, err := xray.New("calc", daemon) | ||
if err != nil { | ||
logger.Printf("[WARN] cannot connect to xray daemon %s: %s", daemon, err) | ||
} | ||
// Wrap the Xray and the tracing handler. The order is very important. | ||
handler = xrayHndlr(handler) | ||
handler = httpmdlwr.Trace()(handler) | ||
} | ||
// gRPC | ||
xm, err := xray.NewUnaryServer("calc", daemon) | ||
if err != nil { | ||
logger.Printf("[WARN] cannot connect to xray daemon %s: %s", daemon, err) | ||
} | ||
// Initialize gRPC server with the middleware. | ||
srv := grpc.NewServer( | ||
grpc.ChainUnaryInterceptor( | ||
// Mount the trace and X-Ray middleware. Order is very important. | ||
grpcmdlwr.UnaryServerTrace(), | ||
xm, | ||
), | ||
) | ||
``` | ||
|
||
## Client Tracing Setup | ||
|
||
The tracing middleware can be wrapped around a HTTP client for HTTP transport or | ||
when initializing a gRPC client connection for gRPC transport. | ||
|
||
``` | ||
// HTTP | ||
var ( | ||
doer goahttp.Doer | ||
) | ||
{ | ||
doer = &http.Client{Timeout: time.Duration(timeout) * time.Second} | ||
// Wrap doer with X-Ray and trace client middleware. Order is very important. | ||
doer = xray.WrapDoer(doer) | ||
doer = middleware.WrapDoer(doer) | ||
} | ||
// gRPC | ||
conn, err := grpc.Dial( | ||
host, | ||
grpc.WithInsecure(), | ||
grpc.WithUnaryInterceptor(grpcmiddleware.ChainUnaryClient( | ||
// Mount the X-Ray and trace client middleware. Order is very important. | ||
xray.UnaryClient(host), | ||
middleware.UnaryClientTrace(), | ||
)), | ||
) | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
|
||
grpcmiddleware "github.com/grpc-ecosystem/go-grpc-middleware" | ||
cli "goa.design/examples/basic/gen/grpc/cli/calc" | ||
"goa.design/goa/v3/grpc/middleware" | ||
"goa.design/goa/v3/grpc/middleware/xray" | ||
goa "goa.design/goa/v3/pkg" | ||
"google.golang.org/grpc" | ||
"google.golang.org/grpc/credentials/insecure" | ||
) | ||
|
||
func doGRPC(scheme, host string, timeout int, debug bool) (goa.Endpoint, interface{}, error) { | ||
conn, err := grpc.Dial( | ||
host, | ||
grpc.WithTransportCredentials(insecure.NewCredentials()), | ||
grpc.WithUnaryInterceptor(grpcmiddleware.ChainUnaryClient( | ||
// Mount the X-Ray and trace client middleware. Order is very important. | ||
xray.UnaryClient(host), | ||
middleware.UnaryClientTrace(), | ||
)), | ||
) | ||
if err != nil { | ||
fmt.Fprintf(os.Stderr, "could not connect to gRPC server at %s: %v\n", host, err) | ||
} | ||
return cli.ParseEndpoint(conn) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package main | ||
|
||
import ( | ||
"net/http" | ||
"time" | ||
|
||
cli "goa.design/examples/basic/gen/http/cli/calc" | ||
goahttp "goa.design/goa/v3/http" | ||
"goa.design/goa/v3/http/middleware" | ||
"goa.design/goa/v3/http/middleware/xray" | ||
goa "goa.design/goa/v3/pkg" | ||
) | ||
|
||
func doHTTP(scheme, host string, timeout int, debug bool) (goa.Endpoint, interface{}, error) { | ||
var ( | ||
doer goahttp.Doer | ||
) | ||
{ | ||
doer = &http.Client{Timeout: time.Duration(timeout) * time.Second} | ||
if debug { | ||
doer = goahttp.NewDebugDoer(doer) | ||
} | ||
// Wrap doer with X-Ray and trace client middleware. Order is very important. | ||
doer = xray.WrapDoer(doer) | ||
doer = middleware.WrapDoer(doer) | ||
} | ||
|
||
return cli.ParseEndpoint( | ||
scheme, | ||
host, | ||
doer, | ||
goahttp.RequestEncoder, | ||
goahttp.ResponseDecoder, | ||
debug, | ||
) | ||
} | ||
func httpUsageCommands() string { | ||
return cli.UsageCommands() | ||
} | ||
|
||
func httpUsageExamples() string { | ||
return cli.UsageExamples() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
package main | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"flag" | ||
"fmt" | ||
"net/url" | ||
"os" | ||
"strings" | ||
|
||
goa "goa.design/goa/v3/pkg" | ||
) | ||
|
||
func main() { | ||
var ( | ||
hostF = flag.String("host", "development", "Server host (valid values: development, production)") | ||
addrF = flag.String("url", "", "URL to service host") | ||
|
||
versionF = flag.String("version", "v1", "API version") | ||
verboseF = flag.Bool("verbose", false, "Print request and response details") | ||
vF = flag.Bool("v", false, "Print request and response details") | ||
timeoutF = flag.Int("timeout", 30, "Maximum number of seconds to wait for response") | ||
) | ||
flag.Usage = usage | ||
flag.Parse() | ||
var ( | ||
addr string | ||
timeout int | ||
debug bool | ||
) | ||
{ | ||
addr = *addrF | ||
if addr == "" { | ||
switch *hostF { | ||
case "development": | ||
addr = "http://localhost:8000/calc" | ||
case "production": | ||
addr = "https://{version}.goa.design/calc" | ||
addr = strings.Replace(addr, "{version}", *versionF, -1) | ||
default: | ||
fmt.Fprintf(os.Stderr, "invalid host argument: %q (valid hosts: development|production)", *hostF) | ||
os.Exit(1) | ||
} | ||
} | ||
timeout = *timeoutF | ||
debug = *verboseF || *vF | ||
} | ||
|
||
var ( | ||
scheme string | ||
host string | ||
) | ||
{ | ||
u, err := url.Parse(addr) | ||
if err != nil { | ||
fmt.Fprintf(os.Stderr, "invalid URL %#v: %s", addr, err) | ||
os.Exit(1) | ||
} | ||
scheme = u.Scheme | ||
host = u.Host | ||
} | ||
var ( | ||
endpoint goa.Endpoint | ||
payload interface{} | ||
err error | ||
) | ||
{ | ||
switch scheme { | ||
case "http", "https": | ||
endpoint, payload, err = doHTTP(scheme, host, timeout, debug) | ||
case "grpc", "grpcs": | ||
endpoint, payload, err = doGRPC(scheme, host, timeout, debug) | ||
default: | ||
fmt.Fprintf(os.Stderr, "invalid scheme: %q (valid schemes: grpc|grpcs|http|https)", scheme) | ||
os.Exit(1) | ||
} | ||
} | ||
if err != nil { | ||
if err == flag.ErrHelp { | ||
os.Exit(0) | ||
} | ||
fmt.Fprintln(os.Stderr, err.Error()) | ||
fmt.Fprintln(os.Stderr, "run '"+os.Args[0]+" --help' for detailed usage.") | ||
os.Exit(1) | ||
} | ||
|
||
data, err := endpoint(context.Background(), payload) | ||
if err != nil { | ||
fmt.Fprintln(os.Stderr, err.Error()) | ||
os.Exit(1) | ||
} | ||
|
||
if data != nil { | ||
m, _ := json.MarshalIndent(data, "", " ") | ||
fmt.Println(string(m)) | ||
} | ||
} | ||
|
||
func usage() { | ||
fmt.Fprintf(os.Stderr, `%s is a command line client for the calc API. | ||
Usage: | ||
%s [-host HOST][-url URL][-timeout SECONDS][-verbose|-v][-version VERSION] SERVICE ENDPOINT [flags] | ||
-host HOST: server host (development). valid values: development, production | ||
-url URL: specify service URL overriding host URL (http://localhost:8080) | ||
-timeout: maximum number of seconds to wait for response (30) | ||
-verbose|-v: print request and response details (false) | ||
-version: API version (v1) | ||
Commands: | ||
%s | ||
Additional help: | ||
%s SERVICE [ENDPOINT] --help | ||
Example: | ||
%s | ||
`, os.Args[0], os.Args[0], indent(httpUsageCommands()), os.Args[0], indent(httpUsageExamples())) | ||
} | ||
|
||
func indent(s string) string { | ||
if s == "" { | ||
return "" | ||
} | ||
return " " + strings.Replace(s, "\n", "\n ", -1) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
package main | ||
|
||
import ( | ||
"context" | ||
"log" | ||
"net" | ||
"net/url" | ||
"sync" | ||
|
||
calcsvc "goa.design/examples/basic/gen/calc" | ||
calcpb "goa.design/examples/basic/gen/grpc/calc/pb" | ||
calcsvcsvr "goa.design/examples/basic/gen/grpc/calc/server" | ||
grpcmdlwr "goa.design/goa/v3/grpc/middleware" | ||
"goa.design/goa/v3/grpc/middleware/xray" | ||
"goa.design/goa/v3/middleware" | ||
"google.golang.org/grpc" | ||
) | ||
|
||
// handleGRPCServer starts configures and starts a gRPC server on the given | ||
// URL. It shuts down the server if any error is received in the error channel. | ||
func handleGRPCServer(ctx context.Context, u *url.URL, calcEndpoints *calcsvc.Endpoints, wg *sync.WaitGroup, errc chan error, logger *log.Logger, debug bool, daemon string) { | ||
|
||
// Setup goa log adapter. | ||
var ( | ||
adapter middleware.Logger | ||
) | ||
{ | ||
adapter = middleware.NewLogger(logger) | ||
} | ||
|
||
// Wrap the endpoints with the transport specific layers. The generated | ||
// server packages contains code generated from the design which maps | ||
// the service input and output data structures to gRPC requests and | ||
// responses. | ||
var ( | ||
calcServer *calcsvcsvr.Server | ||
) | ||
{ | ||
calcServer = calcsvcsvr.New(calcEndpoints, nil) | ||
} | ||
|
||
xm, err := xray.NewUnaryServer("calc", daemon) | ||
if err != nil { | ||
logger.Printf("[WARN] cannot connect to xray daemon %s: %s", daemon, err) | ||
} | ||
// Initialize gRPC server with the middleware. | ||
srv := grpc.NewServer( | ||
grpc.ChainUnaryInterceptor( | ||
grpcmdlwr.UnaryRequestID(), | ||
grpcmdlwr.UnaryServerLog(adapter), | ||
// Mount the trace and X-Ray middleware. Order is very important. | ||
grpcmdlwr.UnaryServerTrace(), | ||
xm, | ||
), | ||
) | ||
|
||
// Register the servers. | ||
calcpb.RegisterCalcServer(srv, calcServer) | ||
|
||
for svc, info := range srv.GetServiceInfo() { | ||
for _, m := range info.Methods { | ||
logger.Printf("serving gRPC method %s", svc+"/"+m.Name) | ||
} | ||
} | ||
|
||
(*wg).Add(1) | ||
go func() { | ||
defer (*wg).Done() | ||
|
||
// Start gRPC server in a separate goroutine. | ||
go func() { | ||
lis, err := net.Listen("tcp", u.Host) | ||
if err != nil { | ||
errc <- err | ||
} | ||
logger.Printf("gRPC server listening on %q", u.Host) | ||
errc <- srv.Serve(lis) | ||
}() | ||
|
||
<-ctx.Done() | ||
logger.Printf("shutting down gRPC server at %q", u.Host) | ||
srv.Stop() | ||
}() | ||
} |
Oops, something went wrong.