mirror of
https://github.com/coredns/coredns.git
synced 2026-04-25 21:35:33 -04:00
plugin/metrics: Switch to using promhttp instead of deprecated Handler (#1312)
prometheus.Handler is deprecated according to the godoc for the package so instead we're using promhttp. Additionally, we are exposing the Registry that metrics is using so other plugins that are not inside of coredns can read the registry. Otherwise, if we kept using the Default one, there's no way to access that from outside of the coredns repo since it is vendored.
This commit is contained in:
committed by
Miek Gieben
parent
1919913c98
commit
671d170619
43
vendor/github.com/openzipkin/zipkin-go-opentracing/events/event_nettrace.go
generated
vendored
Normal file
43
vendor/github.com/openzipkin/zipkin-go-opentracing/events/event_nettrace.go
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
package events
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/net/trace"
|
||||
|
||||
"github.com/openzipkin/zipkin-go-opentracing"
|
||||
)
|
||||
|
||||
// NetTraceIntegrator can be passed into a zipkintracer as NewSpanEventListener
|
||||
// and causes all traces to be registered with the net/trace endpoint.
|
||||
var NetTraceIntegrator = func() func(zipkintracer.SpanEvent) {
|
||||
var tr trace.Trace
|
||||
return func(e zipkintracer.SpanEvent) {
|
||||
switch t := e.(type) {
|
||||
case zipkintracer.EventCreate:
|
||||
tr = trace.New("tracing", t.OperationName)
|
||||
tr.SetMaxEvents(1000)
|
||||
case zipkintracer.EventFinish:
|
||||
tr.Finish()
|
||||
case zipkintracer.EventTag:
|
||||
tr.LazyPrintf("%s:%v", t.Key, t.Value)
|
||||
case zipkintracer.EventLogFields:
|
||||
var buf bytes.Buffer
|
||||
for i, f := range t.Fields {
|
||||
if i > 0 {
|
||||
buf.WriteByte(' ')
|
||||
}
|
||||
fmt.Fprintf(&buf, "%s:%v", f.Key(), f.Value())
|
||||
}
|
||||
|
||||
tr.LazyPrintf("%s", buf.String())
|
||||
case zipkintracer.EventLog:
|
||||
if t.Payload != nil {
|
||||
tr.LazyPrintf("%s (payload %v)", t.Event, t.Payload)
|
||||
} else {
|
||||
tr.LazyPrintf("%s", t.Event)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
10
vendor/github.com/openzipkin/zipkin-go-opentracing/examples/cli_with_2_services/Makefile
generated
vendored
Normal file
10
vendor/github.com/openzipkin/zipkin-go-opentracing/examples/cli_with_2_services/Makefile
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
# builds the Cli and Svc1 & Svc2 projects
|
||||
|
||||
all: clean
|
||||
@go get -v ./...
|
||||
@go build -v -o build/svc1 ./svc1/cmd
|
||||
@go build -v -o build/svc2 ./svc2/cmd
|
||||
@go build -v -o build/cli ./cli
|
||||
|
||||
clean:
|
||||
@rm -rf build
|
||||
35
vendor/github.com/openzipkin/zipkin-go-opentracing/examples/cli_with_2_services/README.md
generated
vendored
Normal file
35
vendor/github.com/openzipkin/zipkin-go-opentracing/examples/cli_with_2_services/README.md
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
## Zipkin tracing using OpenTracing API
|
||||
|
||||
This directory contains a super simple command line client and two HTTP services
|
||||
which are instrumented using the OpenTracing API using the
|
||||
[zipkin-go-opentracing](https://github.com/openzipkin/zipkin-go-opentracing)
|
||||
tracer.
|
||||
|
||||
The code is a quick hack to solely demonstrate the usage of
|
||||
[OpenTracing](http://opentracing.io) with a [Zipkin](http://zipkin.io) backend.
|
||||
|
||||
```
|
||||
note: the examples will only compile with Go 1.7 or higher
|
||||
```
|
||||
|
||||
## Usage:
|
||||
|
||||
Build `svc1`, `svc2` and `cli` with `make` and start both compiled services
|
||||
found in the newly created `build` subdirectory.
|
||||
|
||||
When you call the `cli` program it will trigger two calls to `svc1` of which one
|
||||
call will be proxied from `svc1` over to `svc2` to handle the method and by that
|
||||
generating a couple of spans across services.
|
||||
|
||||
Methods have been instrumented with some examples of
|
||||
[OpenTracing](http://opentracing.io) Tags which will be transformed into
|
||||
[Zipkin](http://zipkin.io) binary annotations and
|
||||
[OpenTracing](http://opentracing.io) LogEvents which will be transformed into
|
||||
[Zipkin](http://zipkin.io) annotations.
|
||||
|
||||
The most interesting piece of code is found in `examples/middleware` which is
|
||||
kind of the missing link for people looking for a tracing framework. I advise
|
||||
you to seriously look into using [Go kit](https://gokit.io) and use its
|
||||
abstractions and OpenTracing middleware with which this Tracer is fully
|
||||
compatible, instead of rolling your own. If you still want to roll your own you
|
||||
can use `examples/middleware` as a starting point.
|
||||
89
vendor/github.com/openzipkin/zipkin-go-opentracing/examples/cli_with_2_services/cli/main.go
generated
vendored
Normal file
89
vendor/github.com/openzipkin/zipkin-go-opentracing/examples/cli_with_2_services/cli/main.go
generated
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
// +build go1.7
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
opentracing "github.com/opentracing/opentracing-go"
|
||||
|
||||
zipkin "github.com/openzipkin/zipkin-go-opentracing"
|
||||
"github.com/openzipkin/zipkin-go-opentracing/examples/cli_with_2_services/svc1"
|
||||
)
|
||||
|
||||
const (
|
||||
// Our service name.
|
||||
serviceName = "cli"
|
||||
|
||||
// Host + port of our service.
|
||||
hostPort = "0.0.0.0:0"
|
||||
|
||||
// Endpoint to send Zipkin spans to.
|
||||
zipkinHTTPEndpoint = "http://localhost:9411/api/v1/spans"
|
||||
|
||||
// Debug mode.
|
||||
debug = false
|
||||
|
||||
// Base endpoint of our SVC1 service.
|
||||
svc1Endpoint = "http://localhost:61001"
|
||||
|
||||
// same span can be set to true for RPC style spans (Zipkin V1) vs Node style (OpenTracing)
|
||||
sameSpan = true
|
||||
|
||||
// make Tracer generate 128 bit traceID's for root spans.
|
||||
traceID128Bit = true
|
||||
)
|
||||
|
||||
//ci
|
||||
func main() {
|
||||
// Create our HTTP collector.
|
||||
collector, err := zipkin.NewHTTPCollector(zipkinHTTPEndpoint)
|
||||
if err != nil {
|
||||
fmt.Printf("unable to create Zipkin HTTP collector: %+v\n", err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
|
||||
// Create our recorder.
|
||||
recorder := zipkin.NewRecorder(collector, debug, hostPort, serviceName)
|
||||
|
||||
// Create our tracer.
|
||||
tracer, err := zipkin.NewTracer(
|
||||
recorder,
|
||||
zipkin.ClientServerSameSpan(sameSpan),
|
||||
zipkin.TraceID128Bit(traceID128Bit),
|
||||
)
|
||||
if err != nil {
|
||||
fmt.Printf("unable to create Zipkin tracer: %+v\n", err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
|
||||
// Explicitly set our tracer to be the default tracer.
|
||||
opentracing.InitGlobalTracer(tracer)
|
||||
|
||||
// Create Client to svc1 Service
|
||||
client := svc1.NewHTTPClient(tracer, svc1Endpoint)
|
||||
|
||||
// Create Root Span for duration of the interaction with svc1
|
||||
span := opentracing.StartSpan("Run")
|
||||
|
||||
// Put root span in context so it will be used in our calls to the client.
|
||||
ctx := opentracing.ContextWithSpan(context.Background(), span)
|
||||
|
||||
// Call the Concat Method
|
||||
span.LogEvent("Call Concat")
|
||||
res1, err := client.Concat(ctx, "Hello", " World!")
|
||||
fmt.Printf("Concat: %s Err: %+v\n", res1, err)
|
||||
|
||||
// Call the Sum Method
|
||||
span.LogEvent("Call Sum")
|
||||
res2, err := client.Sum(ctx, 10, 20)
|
||||
fmt.Printf("Sum: %d Err: %+v\n", res2, err)
|
||||
|
||||
// Finish our CLI span
|
||||
span.Finish()
|
||||
|
||||
// Close collector to ensure spans are sent before exiting.
|
||||
collector.Close()
|
||||
}
|
||||
78
vendor/github.com/openzipkin/zipkin-go-opentracing/examples/cli_with_2_services/svc1/cmd/main.go
generated
vendored
Normal file
78
vendor/github.com/openzipkin/zipkin-go-opentracing/examples/cli_with_2_services/svc1/cmd/main.go
generated
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
// +build go1.7
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
opentracing "github.com/opentracing/opentracing-go"
|
||||
|
||||
zipkin "github.com/openzipkin/zipkin-go-opentracing"
|
||||
"github.com/openzipkin/zipkin-go-opentracing/examples/cli_with_2_services/svc1"
|
||||
"github.com/openzipkin/zipkin-go-opentracing/examples/cli_with_2_services/svc2"
|
||||
)
|
||||
|
||||
const (
|
||||
// Our service name.
|
||||
serviceName = "svc1"
|
||||
|
||||
// Host + port of our service.
|
||||
hostPort = "127.0.0.1:61001"
|
||||
|
||||
// Endpoint to send Zipkin spans to.
|
||||
zipkinHTTPEndpoint = "http://localhost:9411/api/v1/spans"
|
||||
|
||||
// Debug mode.
|
||||
debug = false
|
||||
|
||||
// Base endpoint of our SVC2 service.
|
||||
svc2Endpoint = "http://localhost:61002"
|
||||
|
||||
// same span can be set to true for RPC style spans (Zipkin V1) vs Node style (OpenTracing)
|
||||
sameSpan = true
|
||||
|
||||
// make Tracer generate 128 bit traceID's for root spans.
|
||||
traceID128Bit = true
|
||||
)
|
||||
|
||||
//svc1
|
||||
func main() {
|
||||
// create collector.
|
||||
collector, err := zipkin.NewHTTPCollector(zipkinHTTPEndpoint)
|
||||
if err != nil {
|
||||
fmt.Printf("unable to create Zipkin HTTP collector: %+v\n", err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
|
||||
// create recorder.
|
||||
recorder := zipkin.NewRecorder(collector, debug, hostPort, serviceName)
|
||||
|
||||
// create tracer.
|
||||
tracer, err := zipkin.NewTracer(
|
||||
recorder,
|
||||
zipkin.ClientServerSameSpan(sameSpan),
|
||||
zipkin.TraceID128Bit(traceID128Bit),
|
||||
)
|
||||
if err != nil {
|
||||
fmt.Printf("unable to create Zipkin tracer: %+v\n", err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
|
||||
// explicitly set our tracer to be the default tracer.
|
||||
opentracing.InitGlobalTracer(tracer)
|
||||
|
||||
// create the client to svc2
|
||||
svc2Client := svc2.NewHTTPClient(tracer, svc2Endpoint)
|
||||
|
||||
// create the service implementation
|
||||
service := svc1.NewService(svc2Client)
|
||||
|
||||
// create the HTTP Server Handler for the service
|
||||
handler := svc1.NewHTTPHandler(tracer, service)
|
||||
|
||||
// start the service
|
||||
fmt.Printf("Starting %s on %s\n", serviceName, hostPort)
|
||||
http.ListenAndServe(hostPort, handler)
|
||||
}
|
||||
125
vendor/github.com/openzipkin/zipkin-go-opentracing/examples/cli_with_2_services/svc1/httpclient.go
generated
vendored
Normal file
125
vendor/github.com/openzipkin/zipkin-go-opentracing/examples/cli_with_2_services/svc1/httpclient.go
generated
vendored
Normal file
@@ -0,0 +1,125 @@
|
||||
// +build go1.7
|
||||
|
||||
package svc1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
opentracing "github.com/opentracing/opentracing-go"
|
||||
|
||||
"github.com/openzipkin/zipkin-go-opentracing/examples/middleware"
|
||||
)
|
||||
|
||||
// client is our actual client implementation
|
||||
type client struct {
|
||||
baseURL string
|
||||
httpClient *http.Client
|
||||
tracer opentracing.Tracer
|
||||
traceRequest middleware.RequestFunc
|
||||
}
|
||||
|
||||
// Concat implements our Service interface.
|
||||
func (c *client) Concat(ctx context.Context, a, b string) (string, error) {
|
||||
// create new span using span found in context as parent (if none is found,
|
||||
// our span becomes the trace root).
|
||||
span, ctx := opentracing.StartSpanFromContext(ctx, "Concat")
|
||||
defer span.Finish()
|
||||
|
||||
// assemble URL query
|
||||
url := fmt.Sprintf(
|
||||
"%s/concat/?a=%s&b=%s", c.baseURL, url.QueryEscape(a), url.QueryEscape(b),
|
||||
)
|
||||
|
||||
// create the HTTP request
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// use our middleware to propagate our trace
|
||||
req = c.traceRequest(req.WithContext(ctx))
|
||||
|
||||
// execute the HTTP request
|
||||
resp, err := c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
// annotate our span with the error condition
|
||||
span.SetTag("error", err.Error())
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// read the http response body
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
// annotate our span with the error condition
|
||||
span.SetTag("error", err.Error())
|
||||
return "", err
|
||||
}
|
||||
|
||||
// return the result
|
||||
return string(data), nil
|
||||
}
|
||||
|
||||
// Sum implements our Service interface.
|
||||
func (c *client) Sum(ctx context.Context, a, b int64) (int64, error) {
|
||||
// create new span using span found in context as parent (if none is found,
|
||||
// our span becomes the trace root).
|
||||
span, ctx := opentracing.StartSpanFromContext(ctx, "Sum")
|
||||
defer span.Finish()
|
||||
|
||||
// assemble URL query
|
||||
url := fmt.Sprintf("%s/sum/?a=%d&b=%d", c.baseURL, a, b)
|
||||
|
||||
// create the HTTP request
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// use our middleware to propagate our trace
|
||||
req = c.traceRequest(req.WithContext(ctx))
|
||||
|
||||
// execute the HTTP request
|
||||
resp, err := c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
// annotate our span with the error condition
|
||||
span.SetTag("error", err.Error())
|
||||
return 0, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// read the http response body
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
// annotate our span with the error condition
|
||||
span.SetTag("error", err.Error())
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// convert html response to expected result type (int64)
|
||||
result, err := strconv.ParseInt(string(data), 10, 64)
|
||||
if err != nil {
|
||||
// annotate our span with the error condition
|
||||
span.SetTag("error", err.Error())
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// return the result
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// NewHTTPClient returns a new client instance to our svc1 using the HTTP
|
||||
// transport.
|
||||
func NewHTTPClient(tracer opentracing.Tracer, baseURL string) Service {
|
||||
return &client{
|
||||
baseURL: baseURL,
|
||||
httpClient: &http.Client{},
|
||||
tracer: tracer,
|
||||
traceRequest: middleware.ToHTTPRequest(tracer),
|
||||
}
|
||||
}
|
||||
84
vendor/github.com/openzipkin/zipkin-go-opentracing/examples/cli_with_2_services/svc1/httpserver.go
generated
vendored
Normal file
84
vendor/github.com/openzipkin/zipkin-go-opentracing/examples/cli_with_2_services/svc1/httpserver.go
generated
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
// +build go1.7
|
||||
|
||||
package svc1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
opentracing "github.com/opentracing/opentracing-go"
|
||||
|
||||
"github.com/openzipkin/zipkin-go-opentracing/examples/middleware"
|
||||
)
|
||||
|
||||
type httpService struct {
|
||||
service Service
|
||||
}
|
||||
|
||||
// concatHandler is our HTTP HandlerFunc for a Concat request.
|
||||
func (s *httpService) concatHandler(w http.ResponseWriter, req *http.Request) {
|
||||
// parse query parameters
|
||||
v := req.URL.Query()
|
||||
result, err := s.service.Concat(req.Context(), v.Get("a"), v.Get("b"))
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
// return the result
|
||||
w.Write([]byte(result))
|
||||
}
|
||||
|
||||
// sumHandler is our HTTP Handlerfunc for a Sum request.
|
||||
func (s *httpService) sumHandler(w http.ResponseWriter, req *http.Request) {
|
||||
// parse query parameters
|
||||
v := req.URL.Query()
|
||||
a, err := strconv.ParseInt(v.Get("a"), 10, 64)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
b, err := strconv.ParseInt(v.Get("b"), 10, 64)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
// call our Sum binding
|
||||
result, err := s.service.Sum(req.Context(), a, b)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
// return the result
|
||||
w.Write([]byte(fmt.Sprintf("%d", result)))
|
||||
}
|
||||
|
||||
// NewHTTPHandler returns a new HTTP handler our svc1.
|
||||
func NewHTTPHandler(tracer opentracing.Tracer, service Service) http.Handler {
|
||||
// Create our HTTP Service.
|
||||
svc := &httpService{service: service}
|
||||
|
||||
// Create the mux.
|
||||
mux := http.NewServeMux()
|
||||
|
||||
// Create the Concat handler.
|
||||
var concatHandler http.Handler
|
||||
concatHandler = http.HandlerFunc(svc.concatHandler)
|
||||
|
||||
// Wrap the Concat handler with our tracing middleware.
|
||||
concatHandler = middleware.FromHTTPRequest(tracer, "Concat")(concatHandler)
|
||||
|
||||
// Create the Sum handler.
|
||||
var sumHandler http.Handler
|
||||
sumHandler = http.HandlerFunc(svc.sumHandler)
|
||||
|
||||
// Wrap the Sum handler with our tracing middleware.
|
||||
sumHandler = middleware.FromHTTPRequest(tracer, "Sum")(sumHandler)
|
||||
|
||||
// Wire up the mux.
|
||||
mux.Handle("/concat/", concatHandler)
|
||||
mux.Handle("/sum/", sumHandler)
|
||||
|
||||
// Return the mux.
|
||||
return mux
|
||||
}
|
||||
50
vendor/github.com/openzipkin/zipkin-go-opentracing/examples/cli_with_2_services/svc1/implementation.go
generated
vendored
Normal file
50
vendor/github.com/openzipkin/zipkin-go-opentracing/examples/cli_with_2_services/svc1/implementation.go
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
// +build go1.7
|
||||
|
||||
package svc1
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
opentracing "github.com/opentracing/opentracing-go"
|
||||
|
||||
"github.com/openzipkin/zipkin-go-opentracing/examples/cli_with_2_services/svc2"
|
||||
)
|
||||
|
||||
// svc1 is our actual service implementation
|
||||
type svc1 struct {
|
||||
svc2Client svc2.Service
|
||||
}
|
||||
|
||||
func (s *svc1) Concat(ctx context.Context, a, b string) (string, error) {
|
||||
// test for length overflow
|
||||
if len(a)+len(b) > StrMaxSize {
|
||||
// pull span from context (has already been created by our middleware)
|
||||
span := opentracing.SpanFromContext(ctx)
|
||||
span.SetTag("error", ErrMaxSize.Error())
|
||||
return "", ErrMaxSize
|
||||
}
|
||||
|
||||
return a + b, nil
|
||||
}
|
||||
|
||||
func (s *svc1) Sum(ctx context.Context, a, b int64) (int64, error) {
|
||||
// pull span from context (has already been created by our middleware)
|
||||
span := opentracing.SpanFromContext(ctx)
|
||||
span.SetTag("proxy-to", "svc2")
|
||||
|
||||
// proxy request to svc2
|
||||
result, err := s.svc2Client.Sum(ctx, a, b)
|
||||
if err != nil {
|
||||
span.SetTag("error", err.Error())
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// NewService returns a new implementation of our Service.
|
||||
func NewService(svc2Client svc2.Service) Service {
|
||||
return &svc1{
|
||||
svc2Client: svc2Client,
|
||||
}
|
||||
}
|
||||
24
vendor/github.com/openzipkin/zipkin-go-opentracing/examples/cli_with_2_services/svc1/service.go
generated
vendored
Normal file
24
vendor/github.com/openzipkin/zipkin-go-opentracing/examples/cli_with_2_services/svc1/service.go
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
// +build go1.7
|
||||
|
||||
package svc1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
)
|
||||
|
||||
// Service constants
|
||||
const (
|
||||
StrMaxSize = 1024
|
||||
)
|
||||
|
||||
// Service errors
|
||||
var (
|
||||
ErrMaxSize = errors.New("maximum size of 1024 bytes exceeded")
|
||||
)
|
||||
|
||||
// Service interface
|
||||
type Service interface {
|
||||
Concat(ctx context.Context, a, b string) (string, error)
|
||||
Sum(ctx context.Context, a, b int64) (int64, error)
|
||||
}
|
||||
71
vendor/github.com/openzipkin/zipkin-go-opentracing/examples/cli_with_2_services/svc2/cmd/main.go
generated
vendored
Normal file
71
vendor/github.com/openzipkin/zipkin-go-opentracing/examples/cli_with_2_services/svc2/cmd/main.go
generated
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
// +build go1.7
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
opentracing "github.com/opentracing/opentracing-go"
|
||||
|
||||
zipkin "github.com/openzipkin/zipkin-go-opentracing"
|
||||
"github.com/openzipkin/zipkin-go-opentracing/examples/cli_with_2_services/svc2"
|
||||
)
|
||||
|
||||
const (
|
||||
// Our service name.
|
||||
serviceName = "svc2"
|
||||
|
||||
// Host + port of our service.
|
||||
hostPort = "127.0.0.1:61002"
|
||||
|
||||
// Endpoint to send Zipkin spans to.
|
||||
zipkinHTTPEndpoint = "http://localhost:9411/api/v1/spans"
|
||||
|
||||
// Debug mode.
|
||||
debug = false
|
||||
|
||||
// same span can be set to true for RPC style spans (Zipkin V1) vs Node style (OpenTracing)
|
||||
sameSpan = true
|
||||
|
||||
// make Tracer generate 128 bit traceID's for root spans.
|
||||
traceID128Bit = true
|
||||
)
|
||||
|
||||
//svc2
|
||||
func main() {
|
||||
// create collector.
|
||||
collector, err := zipkin.NewHTTPCollector(zipkinHTTPEndpoint)
|
||||
if err != nil {
|
||||
fmt.Printf("unable to create Zipkin HTTP collector: %+v\n", err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
|
||||
// create recorder.
|
||||
recorder := zipkin.NewRecorder(collector, debug, hostPort, serviceName)
|
||||
|
||||
// create tracer.
|
||||
tracer, err := zipkin.NewTracer(
|
||||
recorder,
|
||||
zipkin.ClientServerSameSpan(sameSpan),
|
||||
zipkin.TraceID128Bit(traceID128Bit),
|
||||
)
|
||||
if err != nil {
|
||||
fmt.Printf("unable to create Zipkin tracer: %+v\n", err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
|
||||
// explicitly set our tracer to be the default tracer.
|
||||
opentracing.InitGlobalTracer(tracer)
|
||||
|
||||
// create the service implementation
|
||||
service := svc2.NewService()
|
||||
|
||||
// create the HTTP Server Handler for the service
|
||||
handler := svc2.NewHTTPHandler(tracer, service)
|
||||
|
||||
// start the service
|
||||
fmt.Printf("Starting %s on %s\n", serviceName, hostPort)
|
||||
http.ListenAndServe(hostPort, handler)
|
||||
}
|
||||
82
vendor/github.com/openzipkin/zipkin-go-opentracing/examples/cli_with_2_services/svc2/httpclient.go
generated
vendored
Normal file
82
vendor/github.com/openzipkin/zipkin-go-opentracing/examples/cli_with_2_services/svc2/httpclient.go
generated
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
// +build go1.7
|
||||
|
||||
package svc2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
opentracing "github.com/opentracing/opentracing-go"
|
||||
|
||||
"github.com/openzipkin/zipkin-go-opentracing/examples/middleware"
|
||||
)
|
||||
|
||||
// client is our actual client implementation
|
||||
type client struct {
|
||||
baseURL string
|
||||
httpClient *http.Client
|
||||
tracer opentracing.Tracer
|
||||
traceRequest middleware.RequestFunc
|
||||
}
|
||||
|
||||
// Sum implements our Service interface.
|
||||
func (c *client) Sum(ctx context.Context, a int64, b int64) (int64, error) {
|
||||
// create new span using span found in context as parent (if none is found,
|
||||
// our span becomes the trace root).
|
||||
span, ctx := opentracing.StartSpanFromContext(ctx, "Sum")
|
||||
defer span.Finish()
|
||||
|
||||
// assemble URL query
|
||||
url := fmt.Sprintf("%s/sum/?a=%d&b=%d", c.baseURL, a, b)
|
||||
|
||||
// create the HTTP request
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// use our middleware to propagate our trace
|
||||
req = c.traceRequest(req.WithContext(ctx))
|
||||
|
||||
// execute the HTTP request
|
||||
resp, err := c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
// annotate our span with the error condition
|
||||
span.SetTag("error", err.Error())
|
||||
return 0, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// read the http response body
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
// annotate our span with the error condition
|
||||
span.SetTag("error", err.Error())
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// convert html response to expected result type (int64)
|
||||
result, err := strconv.ParseInt(string(data), 10, 64)
|
||||
if err != nil {
|
||||
// annotate our span with the error condition
|
||||
span.SetTag("error", err.Error())
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// return the result
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// NewHTTPClient returns a new client instance to our svc2 using the HTTP
|
||||
// transport.
|
||||
func NewHTTPClient(tracer opentracing.Tracer, baseURL string) Service {
|
||||
return &client{
|
||||
baseURL: baseURL,
|
||||
httpClient: &http.Client{},
|
||||
tracer: tracer,
|
||||
traceRequest: middleware.ToHTTPRequest(tracer),
|
||||
}
|
||||
}
|
||||
63
vendor/github.com/openzipkin/zipkin-go-opentracing/examples/cli_with_2_services/svc2/httpserver.go
generated
vendored
Normal file
63
vendor/github.com/openzipkin/zipkin-go-opentracing/examples/cli_with_2_services/svc2/httpserver.go
generated
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
// +build go1.7
|
||||
|
||||
package svc2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
opentracing "github.com/opentracing/opentracing-go"
|
||||
|
||||
"github.com/openzipkin/zipkin-go-opentracing/examples/middleware"
|
||||
)
|
||||
|
||||
type httpService struct {
|
||||
service Service
|
||||
}
|
||||
|
||||
// sumHandler is our HTTP Handlerfunc for a Sum request.
|
||||
func (s *httpService) sumHandler(w http.ResponseWriter, req *http.Request) {
|
||||
// parse query parameters
|
||||
v := req.URL.Query()
|
||||
a, err := strconv.ParseInt(v.Get("a"), 10, 64)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
b, err := strconv.ParseInt(v.Get("b"), 10, 64)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
// call our Sum binding
|
||||
result, err := s.service.Sum(req.Context(), a, b)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
// return the result
|
||||
w.Write([]byte(fmt.Sprintf("%d", result)))
|
||||
}
|
||||
|
||||
// NewHTTPHandler returns a new HTTP handler our svc2.
|
||||
func NewHTTPHandler(tracer opentracing.Tracer, service Service) http.Handler {
|
||||
// Create our HTTP Service.
|
||||
svc := &httpService{service: service}
|
||||
|
||||
// Create the mux.
|
||||
mux := http.NewServeMux()
|
||||
|
||||
// Create the Sum handler.
|
||||
var sumHandler http.Handler
|
||||
sumHandler = http.HandlerFunc(svc.sumHandler)
|
||||
|
||||
// Wrap the Sum handler with our tracing middleware.
|
||||
sumHandler = middleware.FromHTTPRequest(tracer, "Sum")(sumHandler)
|
||||
|
||||
// Wire up the mux.
|
||||
mux.Handle("/sum/", sumHandler)
|
||||
|
||||
// Return the mux.
|
||||
return mux
|
||||
}
|
||||
78
vendor/github.com/openzipkin/zipkin-go-opentracing/examples/cli_with_2_services/svc2/implementation.go
generated
vendored
Normal file
78
vendor/github.com/openzipkin/zipkin-go-opentracing/examples/cli_with_2_services/svc2/implementation.go
generated
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
// +build go1.7
|
||||
|
||||
package svc2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
opentracing "github.com/opentracing/opentracing-go"
|
||||
"github.com/opentracing/opentracing-go/ext"
|
||||
)
|
||||
|
||||
// svc2 is our actual service implementation.
|
||||
type svc2 struct{}
|
||||
|
||||
// NewService returns a new implementation of our Service.
|
||||
func NewService() Service {
|
||||
return &svc2{}
|
||||
}
|
||||
|
||||
// Sum implements our Service interface.
|
||||
func (s *svc2) Sum(ctx context.Context, a int64, b int64) (int64, error) {
|
||||
// We love starting up slow
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
|
||||
// Pull span from context.
|
||||
span := opentracing.SpanFromContext(ctx)
|
||||
|
||||
// Example binary annotations.
|
||||
span.SetTag("service", "svc2")
|
||||
span.SetTag("string", "some value")
|
||||
span.SetTag("int", 123)
|
||||
span.SetTag("bool", true)
|
||||
|
||||
// Example annotation
|
||||
span.LogEvent("MyEventAnnotation")
|
||||
|
||||
// Let's wait a little so it shows up nicely in our tracing graphics.
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
|
||||
// Let's assume we want to trace a call we do to a database.
|
||||
s.fakeDBCall(span)
|
||||
|
||||
// Check for Int overflow condition.
|
||||
if (b > 0 && a > (Int64Max-b)) || (b < 0 && a < (Int64Min-b)) {
|
||||
span.SetTag("error", ErrIntOverflow.Error())
|
||||
return 0, ErrIntOverflow
|
||||
}
|
||||
|
||||
// calculate and return the result (all that boilerplate for this?) ;)
|
||||
return a + b, nil
|
||||
}
|
||||
|
||||
func (s *svc2) fakeDBCall(span opentracing.Span) {
|
||||
resourceSpan := opentracing.StartSpan(
|
||||
"myComplexQuery",
|
||||
opentracing.ChildOf(span.Context()),
|
||||
)
|
||||
defer resourceSpan.Finish()
|
||||
// mark span as resource type
|
||||
ext.SpanKind.Set(resourceSpan, "resource")
|
||||
// name of the resource we try to reach
|
||||
ext.PeerService.Set(resourceSpan, "PostgreSQL")
|
||||
// hostname of the resource
|
||||
ext.PeerHostname.Set(resourceSpan, "localhost")
|
||||
// port of the resource
|
||||
ext.PeerPort.Set(resourceSpan, 3306)
|
||||
// let's binary annotate the query we run
|
||||
resourceSpan.SetTag(
|
||||
"query", "SELECT recipes FROM cookbook WHERE topic = 'world domination'",
|
||||
)
|
||||
|
||||
// Let's assume the query is going to take some time. Finding the right
|
||||
// world domination recipes is like searching for a needle in a haystack.
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
|
||||
// sweet... all done
|
||||
}
|
||||
24
vendor/github.com/openzipkin/zipkin-go-opentracing/examples/cli_with_2_services/svc2/service.go
generated
vendored
Normal file
24
vendor/github.com/openzipkin/zipkin-go-opentracing/examples/cli_with_2_services/svc2/service.go
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
// +build go1.7
|
||||
|
||||
package svc2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
)
|
||||
|
||||
// Service constants
|
||||
const (
|
||||
Int64Max = 1<<63 - 1
|
||||
Int64Min = -(Int64Max + 1)
|
||||
)
|
||||
|
||||
// Service errors
|
||||
var (
|
||||
ErrIntOverflow = errors.New("integer overflow occurred")
|
||||
)
|
||||
|
||||
// Service interface to our svc2 service.
|
||||
type Service interface {
|
||||
Sum(ctx context.Context, a int64, b int64) (int64, error)
|
||||
}
|
||||
101
vendor/github.com/openzipkin/zipkin-go-opentracing/examples/middleware/http.go
generated
vendored
Normal file
101
vendor/github.com/openzipkin/zipkin-go-opentracing/examples/middleware/http.go
generated
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
// +build go1.7
|
||||
|
||||
// Package middleware provides some usable transport middleware to deal with
|
||||
// propagating Zipkin traces across service boundaries.
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
opentracing "github.com/opentracing/opentracing-go"
|
||||
"github.com/opentracing/opentracing-go/ext"
|
||||
|
||||
"github.com/openzipkin/zipkin-go-opentracing/thrift/gen-go/zipkincore"
|
||||
)
|
||||
|
||||
// RequestFunc is a middleware function for outgoing HTTP requests.
|
||||
type RequestFunc func(req *http.Request) *http.Request
|
||||
|
||||
// ToHTTPRequest returns a RequestFunc that injects an OpenTracing Span found in
|
||||
// context into the HTTP Headers. If no such Span can be found, the RequestFunc
|
||||
// is a noop.
|
||||
func ToHTTPRequest(tracer opentracing.Tracer) RequestFunc {
|
||||
return func(req *http.Request) *http.Request {
|
||||
// Retrieve the Span from context.
|
||||
if span := opentracing.SpanFromContext(req.Context()); span != nil {
|
||||
|
||||
// We are going to use this span in a client request, so mark as such.
|
||||
ext.SpanKindRPCClient.Set(span)
|
||||
|
||||
// Add some standard OpenTracing tags, useful in an HTTP request.
|
||||
ext.HTTPMethod.Set(span, req.Method)
|
||||
span.SetTag(zipkincore.HTTP_HOST, req.URL.Host)
|
||||
span.SetTag(zipkincore.HTTP_PATH, req.URL.Path)
|
||||
ext.HTTPUrl.Set(
|
||||
span,
|
||||
fmt.Sprintf("%s://%s%s", req.URL.Scheme, req.URL.Host, req.URL.Path),
|
||||
)
|
||||
|
||||
// Add information on the peer service we're about to contact.
|
||||
if host, portString, err := net.SplitHostPort(req.URL.Host); err == nil {
|
||||
ext.PeerHostname.Set(span, host)
|
||||
if port, err := strconv.Atoi(portString); err != nil {
|
||||
ext.PeerPort.Set(span, uint16(port))
|
||||
}
|
||||
} else {
|
||||
ext.PeerHostname.Set(span, req.URL.Host)
|
||||
}
|
||||
|
||||
// Inject the Span context into the outgoing HTTP Request.
|
||||
if err := tracer.Inject(
|
||||
span.Context(),
|
||||
opentracing.TextMap,
|
||||
opentracing.HTTPHeadersCarrier(req.Header),
|
||||
); err != nil {
|
||||
fmt.Printf("error encountered while trying to inject span: %+v\n", err)
|
||||
}
|
||||
}
|
||||
return req
|
||||
}
|
||||
}
|
||||
|
||||
// HandlerFunc is a middleware function for incoming HTTP requests.
|
||||
type HandlerFunc func(next http.Handler) http.Handler
|
||||
|
||||
// FromHTTPRequest returns a Middleware HandlerFunc that tries to join with an
|
||||
// OpenTracing trace found in the HTTP request headers and starts a new Span
|
||||
// called `operationName`. If no trace could be found in the HTTP request
|
||||
// headers, the Span will be a trace root. The Span is incorporated in the
|
||||
// HTTP Context object and can be retrieved with
|
||||
// opentracing.SpanFromContext(ctx).
|
||||
func FromHTTPRequest(tracer opentracing.Tracer, operationName string,
|
||||
) HandlerFunc {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
// Try to join to a trace propagated in `req`.
|
||||
wireContext, err := tracer.Extract(
|
||||
opentracing.TextMap,
|
||||
opentracing.HTTPHeadersCarrier(req.Header),
|
||||
)
|
||||
if err != nil {
|
||||
fmt.Printf("error encountered while trying to extract span: %+v\n", err)
|
||||
}
|
||||
|
||||
// create span
|
||||
span := tracer.StartSpan(operationName, ext.RPCServerOption(wireContext))
|
||||
defer span.Finish()
|
||||
|
||||
// store span in context
|
||||
ctx := opentracing.ContextWithSpan(req.Context(), span)
|
||||
|
||||
// update request context to include our new span
|
||||
req = req.WithContext(ctx)
|
||||
|
||||
// next middleware or actual request handler
|
||||
next.ServeHTTP(w, req)
|
||||
})
|
||||
}
|
||||
}
|
||||
154
vendor/github.com/openzipkin/zipkin-go-opentracing/thrift/gen-go/scribe/scribe-remote/scribe-remote.go
generated
vendored
Normal file
154
vendor/github.com/openzipkin/zipkin-go-opentracing/thrift/gen-go/scribe/scribe-remote/scribe-remote.go
generated
vendored
Normal file
@@ -0,0 +1,154 @@
|
||||
// Autogenerated by Thrift Compiler (1.0.0-dev)
|
||||
// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"math"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"github.com/apache/thrift/lib/go/thrift"
|
||||
"github.com/openzipkin/zipkin-go-opentracing/thrift/gen-go/scribe"
|
||||
)
|
||||
|
||||
|
||||
func Usage() {
|
||||
fmt.Fprintln(os.Stderr, "Usage of ", os.Args[0], " [-h host:port] [-u url] [-f[ramed]] function [arg1 [arg2...]]:")
|
||||
flag.PrintDefaults()
|
||||
fmt.Fprintln(os.Stderr, "\nFunctions:")
|
||||
fmt.Fprintln(os.Stderr, " ResultCode Log( messages)")
|
||||
fmt.Fprintln(os.Stderr)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Usage = Usage
|
||||
var host string
|
||||
var port int
|
||||
var protocol string
|
||||
var urlString string
|
||||
var framed bool
|
||||
var useHttp bool
|
||||
var parsedUrl url.URL
|
||||
var trans thrift.TTransport
|
||||
_ = strconv.Atoi
|
||||
_ = math.Abs
|
||||
flag.Usage = Usage
|
||||
flag.StringVar(&host, "h", "localhost", "Specify host and port")
|
||||
flag.IntVar(&port, "p", 9090, "Specify port")
|
||||
flag.StringVar(&protocol, "P", "binary", "Specify the protocol (binary, compact, simplejson, json)")
|
||||
flag.StringVar(&urlString, "u", "", "Specify the url")
|
||||
flag.BoolVar(&framed, "framed", false, "Use framed transport")
|
||||
flag.BoolVar(&useHttp, "http", false, "Use http")
|
||||
flag.Parse()
|
||||
|
||||
if len(urlString) > 0 {
|
||||
parsedUrl, err := url.Parse(urlString)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Error parsing URL: ", err)
|
||||
flag.Usage()
|
||||
}
|
||||
host = parsedUrl.Host
|
||||
useHttp = len(parsedUrl.Scheme) <= 0 || parsedUrl.Scheme == "http"
|
||||
} else if useHttp {
|
||||
_, err := url.Parse(fmt.Sprint("http://", host, ":", port))
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Error parsing URL: ", err)
|
||||
flag.Usage()
|
||||
}
|
||||
}
|
||||
|
||||
cmd := flag.Arg(0)
|
||||
var err error
|
||||
if useHttp {
|
||||
trans, err = thrift.NewTHttpClient(parsedUrl.String())
|
||||
} else {
|
||||
portStr := fmt.Sprint(port)
|
||||
if strings.Contains(host, ":") {
|
||||
host, portStr, err = net.SplitHostPort(host)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "error with host:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
trans, err = thrift.NewTSocket(net.JoinHostPort(host, portStr))
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "error resolving address:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if framed {
|
||||
trans = thrift.NewTFramedTransport(trans)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Error creating transport", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer trans.Close()
|
||||
var protocolFactory thrift.TProtocolFactory
|
||||
switch protocol {
|
||||
case "compact":
|
||||
protocolFactory = thrift.NewTCompactProtocolFactory()
|
||||
break
|
||||
case "simplejson":
|
||||
protocolFactory = thrift.NewTSimpleJSONProtocolFactory()
|
||||
break
|
||||
case "json":
|
||||
protocolFactory = thrift.NewTJSONProtocolFactory()
|
||||
break
|
||||
case "binary", "":
|
||||
protocolFactory = thrift.NewTBinaryProtocolFactoryDefault()
|
||||
break
|
||||
default:
|
||||
fmt.Fprintln(os.Stderr, "Invalid protocol specified: ", protocol)
|
||||
Usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
iprot := protocolFactory.GetProtocol(trans)
|
||||
oprot := protocolFactory.GetProtocol(trans)
|
||||
client := scribe.NewScribeClient(thrift.NewTStandardClient(iprot, oprot))
|
||||
if err := trans.Open(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Error opening socket to ", host, ":", port, " ", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
switch cmd {
|
||||
case "Log":
|
||||
if flag.NArg() - 1 != 1 {
|
||||
fmt.Fprintln(os.Stderr, "Log requires 1 args")
|
||||
flag.Usage()
|
||||
}
|
||||
arg5 := flag.Arg(1)
|
||||
mbTrans6 := thrift.NewTMemoryBufferLen(len(arg5))
|
||||
defer mbTrans6.Close()
|
||||
_, err7 := mbTrans6.WriteString(arg5)
|
||||
if err7 != nil {
|
||||
Usage()
|
||||
return
|
||||
}
|
||||
factory8 := thrift.NewTSimpleJSONProtocolFactory()
|
||||
jsProt9 := factory8.GetProtocol(mbTrans6)
|
||||
containerStruct0 := scribe.NewScribeLogArgs()
|
||||
err10 := containerStruct0.ReadField1(jsProt9)
|
||||
if err10 != nil {
|
||||
Usage()
|
||||
return
|
||||
}
|
||||
argvalue0 := containerStruct0.Messages
|
||||
value0 := argvalue0
|
||||
fmt.Print(client.Log(context.Background(), value0))
|
||||
fmt.Print("\n")
|
||||
break
|
||||
case "":
|
||||
Usage()
|
||||
break
|
||||
default:
|
||||
fmt.Fprintln(os.Stderr, "Invalid function ", cmd)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user