phase 2: mattes/migrate -> migratex (#848)

* move mattes migrations to migratex

* changes format of migrations to migratex format
* updates test runner to use new interface (double checked this with printlns,
the tests go fully down and then up, and work on pg/mysql)

* remove mattes/migrate

* update tests from deps

* update readme

* fix other file extensions
This commit is contained in:
Reed Allman
2018-03-13 14:12:34 -07:00
committed by GitHub
parent 1f43545b63
commit 4084b727c0
697 changed files with 16924 additions and 35406 deletions

View File

@@ -20,12 +20,6 @@ import (
"google.golang.org/grpc/stats"
)
// NewClientStatsHandler enables OpenCensus stats and trace
// for gRPC clients. Deprecated, construct a ClientHandler directly.
func NewClientStatsHandler() stats.Handler {
return &ClientHandler{}
}
// ClientHandler implements a gRPC stats.Handler for recording OpenCensus stats and
// traces. Use with gRPC clients only.
type ClientHandler struct {
@@ -38,11 +32,6 @@ type ClientHandler struct {
NoStats bool
}
var (
clientTrace clientTraceHandler
clientStats clientStatsHandler
)
func (c *ClientHandler) HandleConn(ctx context.Context, cs stats.ConnStats) {
// no-op
}
@@ -54,19 +43,19 @@ func (c *ClientHandler) TagConn(ctx context.Context, cti *stats.ConnTagInfo) con
func (c *ClientHandler) HandleRPC(ctx context.Context, rs stats.RPCStats) {
if !c.NoTrace {
clientTrace.HandleRPC(ctx, rs)
c.traceHandleRPC(ctx, rs)
}
if !c.NoStats {
clientStats.HandleRPC(ctx, rs)
c.statsHandleRPC(ctx, rs)
}
}
func (c *ClientHandler) TagRPC(ctx context.Context, rti *stats.RPCTagInfo) context.Context {
if !c.NoTrace {
ctx = clientTrace.TagRPC(ctx, rti)
ctx = c.traceTagRPC(ctx, rti)
}
if !c.NoStats {
ctx = clientStats.TagRPC(ctx, rti)
ctx = c.statsTagRPC(ctx, rti)
}
return ctx
}

View File

@@ -38,47 +38,53 @@ var (
// package. These are declared as a convenience only; none are subscribed by
// default.
var (
ClientErrorCountView, _ = view.New(
"grpc.io/client/error_count",
"RPC Errors",
[]tag.Key{KeyStatus, KeyMethod},
ClientErrorCount,
view.MeanAggregation{})
ClientErrorCountView = &view.View{
Name: "grpc.io/client/error_count",
Description: "RPC Errors",
TagKeys: []tag.Key{KeyStatus, KeyMethod},
Measure: ClientErrorCount,
Aggregation: view.MeanAggregation{},
}
ClientRoundTripLatencyView, _ = view.New(
"grpc.io/client/roundtrip_latency",
"Latency in msecs",
[]tag.Key{KeyMethod},
ClientRoundTripLatency,
DefaultMillisecondsDistribution)
ClientRoundTripLatencyView = &view.View{
Name: "grpc.io/client/roundtrip_latency",
Description: "Latency in msecs",
TagKeys: []tag.Key{KeyMethod},
Measure: ClientRoundTripLatency,
Aggregation: DefaultMillisecondsDistribution,
}
ClientRequestBytesView, _ = view.New(
"grpc.io/client/request_bytes",
"Request bytes",
[]tag.Key{KeyMethod},
ClientRequestBytes,
DefaultBytesDistribution)
ClientRequestBytesView = &view.View{
Name: "grpc.io/client/request_bytes",
Description: "Request bytes",
TagKeys: []tag.Key{KeyMethod},
Measure: ClientRequestBytes,
Aggregation: DefaultBytesDistribution,
}
ClientResponseBytesView, _ = view.New(
"grpc.io/client/response_bytes",
"Response bytes",
[]tag.Key{KeyMethod},
ClientResponseBytes,
DefaultBytesDistribution)
ClientResponseBytesView = &view.View{
Name: "grpc.io/client/response_bytes",
Description: "Response bytes",
TagKeys: []tag.Key{KeyMethod},
Measure: ClientResponseBytes,
Aggregation: DefaultBytesDistribution,
}
ClientRequestCountView, _ = view.New(
"grpc.io/client/request_count",
"Count of request messages per client RPC",
[]tag.Key{KeyMethod},
ClientRequestCount,
DefaultMessageCountDistribution)
ClientRequestCountView = &view.View{
Name: "grpc.io/client/request_count",
Description: "Count of request messages per client RPC",
TagKeys: []tag.Key{KeyMethod},
Measure: ClientRequestCount,
Aggregation: DefaultMessageCountDistribution,
}
ClientResponseCountView, _ = view.New(
"grpc.io/client/response_count",
"Count of response messages per client RPC",
[]tag.Key{KeyMethod},
ClientResponseCount,
DefaultMessageCountDistribution)
ClientResponseCountView = &view.View{
Name: "grpc.io/client/response_count",
Description: "Count of response messages per client RPC",
TagKeys: []tag.Key{KeyMethod},
Measure: ClientResponseCount,
Aggregation: DefaultMessageCountDistribution,
}
)
// All the default client views provided by this package:

View File

@@ -31,7 +31,7 @@ func TestViewsAggregationsConform(t *testing.T) {
// Add any other defined views to be type checked during tests to ensure we don't regress.
assertTypeOf := func(v *view.View, wantSample view.Aggregation) {
aggregation := v.Aggregation()
aggregation := v.Aggregation
gotValue := reflect.ValueOf(aggregation)
wantValue := reflect.ValueOf(wantSample)
if gotValue.Type() != wantValue.Type() {
@@ -52,14 +52,14 @@ func TestStrictViewNames(t *testing.T) {
alreadySeen := make(map[string]int)
assertName := func(v *view.View, want string) {
_, _, line, _ := runtime.Caller(1)
if prevLine, ok := alreadySeen[v.Name()]; ok {
if prevLine, ok := alreadySeen[v.Name]; ok {
t.Errorf("Item's Name on line %d was already used on line %d", line, prevLine)
return
}
if got := v.Name(); got != want {
if got := v.Name; got != want {
t.Errorf("Item on line: %d got %q want %q", line, got, want)
}
alreadySeen[v.Name()] = line
alreadySeen[v.Name] = line
}
assertName(ClientErrorCountView, "grpc.io/client/error_count")

View File

@@ -27,14 +27,9 @@ import (
"google.golang.org/grpc/status"
)
// clientStatsHandler is a stats.Handler implementation
// that collects stats for a gRPC client. Predefined
// measures and views can be used to access the collected data.
type clientStatsHandler struct{}
// TagRPC gets the tag.Map populated by the application code, serializes
// its tags into the GRPC metadata in order to be sent to the server.
func (h *clientStatsHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo) context.Context {
func (h *ClientHandler) statsTagRPC(ctx context.Context, info *stats.RPCTagInfo) context.Context {
startTime := time.Now()
if info == nil {
if grpclog.V(2) {
@@ -60,7 +55,7 @@ func (h *clientStatsHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo)
}
// HandleRPC processes the RPC events.
func (h *clientStatsHandler) HandleRPC(ctx context.Context, s stats.RPCStats) {
func (h *ClientHandler) statsHandleRPC(ctx context.Context, s stats.RPCStats) {
switch st := s.(type) {
case *stats.Begin, *stats.OutHeader, *stats.InHeader, *stats.InTrailer, *stats.OutTrailer:
// do nothing for client
@@ -75,7 +70,7 @@ func (h *clientStatsHandler) HandleRPC(ctx context.Context, s stats.RPCStats) {
}
}
func (h *clientStatsHandler) handleRPCOutPayload(ctx context.Context, s *stats.OutPayload) {
func (h *ClientHandler) handleRPCOutPayload(ctx context.Context, s *stats.OutPayload) {
d, ok := ctx.Value(grpcClientRPCKey).(*rpcData)
if !ok {
if grpclog.V(2) {
@@ -88,7 +83,7 @@ func (h *clientStatsHandler) handleRPCOutPayload(ctx context.Context, s *stats.O
atomic.AddInt64(&d.reqCount, 1)
}
func (h *clientStatsHandler) handleRPCInPayload(ctx context.Context, s *stats.InPayload) {
func (h *ClientHandler) handleRPCInPayload(ctx context.Context, s *stats.InPayload) {
d, ok := ctx.Value(grpcClientRPCKey).(*rpcData)
if !ok {
if grpclog.V(2) {
@@ -101,7 +96,7 @@ func (h *clientStatsHandler) handleRPCInPayload(ctx context.Context, s *stats.In
atomic.AddInt64(&d.respCount, 1)
}
func (h *clientStatsHandler) handleRPCEnd(ctx context.Context, s *stats.End) {
func (h *ClientHandler) handleRPCEnd(ctx context.Context, s *stats.End) {
d, ok := ctx.Value(grpcClientRPCKey).(*rpcData)
if !ok {
if grpclog.V(2) {

View File

@@ -298,13 +298,12 @@ func TestClientDefaultCollections(t *testing.T) {
for _, tc := range tcs {
// Register views.
for _, v := range DefaultClientViews {
if err := v.Subscribe(); err != nil {
t.Error(err)
}
err := view.Subscribe(DefaultClientViews...)
if err != nil {
t.Error(err)
}
h := &clientStatsHandler{}
h := &ClientHandler{NoTrace: true}
for _, rpc := range tc.rpcs {
mods := []tag.Mutator{}
for _, t := range rpc.tags {
@@ -327,33 +326,29 @@ func TestClientDefaultCollections(t *testing.T) {
}
for _, wantData := range tc.wants {
gotRows, err := wantData.v().RetrieveData()
gotRows, err := view.RetrieveData(wantData.v().Name)
if err != nil {
t.Errorf("%q: RetrieveData(%q) = %v", tc.label, wantData.v().Name(), err)
t.Errorf("%q: RetrieveData(%q) = %v", tc.label, wantData.v().Name, err)
continue
}
for _, gotRow := range gotRows {
if !containsRow(wantData.rows, gotRow) {
t.Errorf("%q: unwanted row for view %q = %v", tc.label, wantData.v().Name(), gotRow)
t.Errorf("%q: unwanted row for view %q = %v", tc.label, wantData.v().Name, gotRow)
break
}
}
for _, wantRow := range wantData.rows {
if !containsRow(gotRows, wantRow) {
t.Errorf("%q: row missing for view %q; want %v", tc.label, wantData.v().Name(), wantRow)
t.Errorf("%q: row missing for view %q; want %v", tc.label, wantData.v().Name, wantRow)
break
}
}
}
// Unregister views to cleanup.
for _, v := range DefaultClientViews {
if err := v.Unsubscribe(); err != nil {
t.Error(err)
}
}
view.Unsubscribe(DefaultClientViews...)
}
}

View File

@@ -18,12 +18,14 @@ import (
"log"
"go.opencensus.io/plugin/ocgrpc"
"go.opencensus.io/stats/view"
"google.golang.org/grpc"
)
func ExampleClientHandler() {
// Subscribe to collect client request count.
if err := ocgrpc.ClientRequestCountView.Subscribe(); err != nil {
// Subscribe views to collect data.
err := view.Subscribe(ocgrpc.DefaultClientViews...)
if err != nil {
log.Fatal(err)
}
@@ -37,8 +39,9 @@ func ExampleClientHandler() {
}
func ExampleServerHandler() {
// Subscribe to collect server request count.
if err := ocgrpc.ServerRequestCountView.Subscribe(); err != nil {
// Subscribe to views to collect data.
err := view.Subscribe(ocgrpc.DefaultServerViews...)
if err != nil {
log.Fatal(err)
}

View File

@@ -18,6 +18,7 @@ import (
"testing"
"time"
"go.opencensus.io/stats/view"
"golang.org/x/net/context"
"go.opencensus.io/trace"
@@ -25,11 +26,8 @@ import (
"google.golang.org/grpc/stats"
)
func TestNewClientStatsHandler(t *testing.T) {
func TestClientHandler(t *testing.T) {
ctx := context.Background()
handler := NewClientStatsHandler()
te := &traceExporter{}
trace.RegisterExporter(te)
if err := ClientRequestCountView.Subscribe(); err != nil {
@@ -41,6 +39,7 @@ func TestNewClientStatsHandler(t *testing.T) {
})
ctx = trace.WithSpan(ctx, span)
var handler ClientHandler
ctx = handler.TagRPC(ctx, &stats.RPCTagInfo{
FullMethodName: "/service.foo/method",
})
@@ -53,7 +52,7 @@ func TestNewClientStatsHandler(t *testing.T) {
EndTime: time.Now(),
})
stats, err := ClientRequestCountView.RetrieveData()
stats, err := view.RetrieveData(ClientRequestCountView.Name)
if err != nil {
t.Fatal(err)
}
@@ -67,26 +66,24 @@ func TestNewClientStatsHandler(t *testing.T) {
}
// Cleanup.
if err := ClientRequestCountView.Unsubscribe(); err != nil {
t.Fatal(err)
}
view.Unsubscribe(ClientErrorCountView)
}
func TestNewServerStatsHandler(t *testing.T) {
func TestServerHandler(t *testing.T) {
ctx := context.Background()
handler := NewServerStatsHandler()
te := &traceExporter{}
trace.RegisterExporter(te)
if err := ServerRequestCountView.Subscribe(); err != nil {
t.Fatal(err)
}
// Ensure we start tracing.
span := trace.NewSpan("/foo", nil, trace.StartOptions{
Sampler: trace.AlwaysSample(),
})
ctx = trace.WithSpan(ctx, span)
handler := &ServerHandler{}
ctx = handler.TagRPC(ctx, &stats.RPCTagInfo{
FullMethodName: "/service.foo/method",
})
@@ -97,7 +94,7 @@ func TestNewServerStatsHandler(t *testing.T) {
EndTime: time.Now(),
})
stats, err := ServerRequestCountView.RetrieveData()
stats, err := view.RetrieveData(ServerRequestCountView.Name)
if err != nil {
t.Fatal(err)
}
@@ -111,10 +108,7 @@ func TestNewServerStatsHandler(t *testing.T) {
}
// Cleanup.
if err := ServerRequestCountView.Unsubscribe(); err != nil {
t.Fatal(err)
}
view.Unsubscribe(ServerRequestCountView)
}
type traceExporter struct {

View File

@@ -2,7 +2,7 @@
// source: test.proto
/*
Package testdata is a generated protocol buffer package.
Package testpb is a generated protocol buffer package.
It is generated from these files:
test.proto
@@ -11,7 +11,7 @@ It has these top-level messages:
FooRequest
FooResponse
*/
package testdata
package testpb
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
@@ -58,8 +58,8 @@ func (*FooResponse) ProtoMessage() {}
func (*FooResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
func init() {
proto.RegisterType((*FooRequest)(nil), "testdata.FooRequest")
proto.RegisterType((*FooResponse)(nil), "testdata.FooResponse")
proto.RegisterType((*FooRequest)(nil), "testpb.FooRequest")
proto.RegisterType((*FooResponse)(nil), "testpb.FooResponse")
}
// Reference imports to suppress errors if they are not otherwise used.
@@ -87,7 +87,7 @@ func NewFooClient(cc *grpc.ClientConn) FooClient {
func (c *fooClient) Single(ctx context.Context, in *FooRequest, opts ...grpc.CallOption) (*FooResponse, error) {
out := new(FooResponse)
err := grpc.Invoke(ctx, "/testdata.Foo/Single", in, out, c.cc, opts...)
err := grpc.Invoke(ctx, "/testpb.Foo/Single", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
@@ -95,7 +95,7 @@ func (c *fooClient) Single(ctx context.Context, in *FooRequest, opts ...grpc.Cal
}
func (c *fooClient) Multiple(ctx context.Context, opts ...grpc.CallOption) (Foo_MultipleClient, error) {
stream, err := grpc.NewClientStream(ctx, &_Foo_serviceDesc.Streams[0], c.cc, "/testdata.Foo/Multiple", opts...)
stream, err := grpc.NewClientStream(ctx, &_Foo_serviceDesc.Streams[0], c.cc, "/testpb.Foo/Multiple", opts...)
if err != nil {
return nil, err
}
@@ -146,7 +146,7 @@ func _Foo_Single_Handler(srv interface{}, ctx context.Context, dec func(interfac
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/testdata.Foo/Single",
FullMethod: "/testpb.Foo/Single",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(FooServer).Single(ctx, req.(*FooRequest))
@@ -181,7 +181,7 @@ func (x *fooMultipleServer) Recv() (*FooRequest, error) {
}
var _Foo_serviceDesc = grpc.ServiceDesc{
ServiceName: "testdata.Foo",
ServiceName: "testpb.Foo",
HandlerType: (*FooServer)(nil),
Methods: []grpc.MethodDesc{
{
@@ -203,16 +203,16 @@ var _Foo_serviceDesc = grpc.ServiceDesc{
func init() { proto.RegisterFile("test.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 139 bytes of a gzipped FileDescriptorProto
// 137 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x2a, 0x49, 0x2d, 0x2e,
0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x00, 0xb1, 0x53, 0x12, 0x4b, 0x12, 0x95, 0x14,
0xb8, 0xb8, 0xdc, 0xf2, 0xf3, 0x83, 0x52, 0x0b, 0x4b, 0x53, 0x8b, 0x4b, 0x84, 0x84, 0xb8, 0x58,
0xd2, 0x12, 0x33, 0x73, 0x24, 0x18, 0x15, 0x18, 0x35, 0x38, 0x82, 0xc0, 0x6c, 0x25, 0x5e, 0x2e,
0x6e, 0xb0, 0x8a, 0xe2, 0x82, 0xfc, 0xbc, 0xe2, 0x54, 0xa3, 0x4a, 0x2e, 0x66, 0xb7, 0xfc, 0x7c,
0x21, 0x53, 0x2e, 0xb6, 0xe0, 0xcc, 0xbc, 0xf4, 0x9c, 0x54, 0x21, 0x11, 0x3d, 0x98, 0x61, 0x7a,
0x08, 0x93, 0xa4, 0x44, 0xd1, 0x44, 0x21, 0xba, 0x85, 0xac, 0xb9, 0x38, 0x7c, 0x4b, 0x73, 0x4a,
0x32, 0x0b, 0x48, 0xd4, 0xa8, 0xc1, 0x68, 0xc0, 0x98, 0xc4, 0x06, 0x76, 0xbc, 0x31, 0x20, 0x00,
0x00, 0xff, 0xff, 0x10, 0x60, 0x13, 0xc6, 0xca, 0x00, 0x00, 0x00,
0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x03, 0xb1, 0x0b, 0x92, 0x94, 0x14, 0xb8, 0xb8,
0xdc, 0xf2, 0xf3, 0x83, 0x52, 0x0b, 0x4b, 0x53, 0x8b, 0x4b, 0x84, 0x84, 0xb8, 0x58, 0xd2, 0x12,
0x33, 0x73, 0x24, 0x18, 0x15, 0x18, 0x35, 0x38, 0x82, 0xc0, 0x6c, 0x25, 0x5e, 0x2e, 0x6e, 0xb0,
0x8a, 0xe2, 0x82, 0xfc, 0xbc, 0xe2, 0x54, 0xa3, 0x42, 0x2e, 0x66, 0xb7, 0xfc, 0x7c, 0x21, 0x43,
0x2e, 0xb6, 0xe0, 0xcc, 0xbc, 0xf4, 0x9c, 0x54, 0x21, 0x21, 0x3d, 0x88, 0x51, 0x7a, 0x08, 0x73,
0xa4, 0x84, 0x51, 0xc4, 0x20, 0x3a, 0x85, 0xcc, 0xb9, 0x38, 0x7c, 0x4b, 0x73, 0x4a, 0x32, 0x0b,
0x48, 0xd0, 0xa4, 0xc1, 0x68, 0xc0, 0x98, 0xc4, 0x06, 0x76, 0xb2, 0x31, 0x20, 0x00, 0x00, 0xff,
0xff, 0xda, 0xc5, 0x9f, 0x2f, 0xc0, 0x00, 0x00, 0x00,
}
//go:generate ./generate.sh

View File

@@ -1,6 +1,6 @@
syntax = "proto3";
package testdata;
package testpb;
message FooRequest {
bool fail = 1;

View File

@@ -20,12 +20,6 @@ import (
"google.golang.org/grpc/stats"
)
// NewServerStatsHandler enables OpenCensus stats and trace
// for gRPC servers. Deprecated, construct a ServerHandler directly.
func NewServerStatsHandler() stats.Handler {
return &ServerHandler{}
}
// ServerHandler implements gRPC stats.Handler recording OpenCensus stats and
// traces. Use with gRPC servers.
type ServerHandler struct {
@@ -38,11 +32,6 @@ type ServerHandler struct {
NoStats bool
}
var (
serverTrace serverTraceHandler
serverStats serverStatsHandler
)
func (s *ServerHandler) HandleConn(ctx context.Context, cs stats.ConnStats) {
// no-op
}
@@ -54,19 +43,19 @@ func (s *ServerHandler) TagConn(ctx context.Context, cti *stats.ConnTagInfo) con
func (s *ServerHandler) HandleRPC(ctx context.Context, rs stats.RPCStats) {
if !s.NoTrace {
serverTrace.HandleRPC(ctx, rs)
s.traceHandleRPC(ctx, rs)
}
if !s.NoStats {
serverStats.HandleRPC(ctx, rs)
s.statsHandleRPC(ctx, rs)
}
}
func (s *ServerHandler) TagRPC(ctx context.Context, rti *stats.RPCTagInfo) context.Context {
if !s.NoTrace {
ctx = serverTrace.TagRPC(ctx, rti)
ctx = s.traceTagRPC(ctx, rti)
}
if !s.NoStats {
ctx = serverStats.TagRPC(ctx, rti)
ctx = s.statsTagRPC(ctx, rti)
}
return ctx
}

View File

@@ -42,47 +42,53 @@ var (
// package. These are declared as a convenience only; none are subscribed by
// default.
var (
ServerErrorCountView, _ = view.New(
"grpc.io/server/error_count",
"RPC Errors",
[]tag.Key{KeyMethod, KeyStatus},
ServerErrorCount,
view.CountAggregation{})
ServerErrorCountView = &view.View{
Name: "grpc.io/server/error_count",
Description: "RPC Errors",
TagKeys: []tag.Key{KeyMethod, KeyStatus},
Measure: ServerErrorCount,
Aggregation: view.CountAggregation{},
}
ServerServerElapsedTimeView, _ = view.New(
"grpc.io/server/server_elapsed_time",
"Server elapsed time in msecs",
[]tag.Key{KeyMethod},
ServerServerElapsedTime,
DefaultMillisecondsDistribution)
ServerServerElapsedTimeView = &view.View{
Name: "grpc.io/server/server_elapsed_time",
Description: "Server elapsed time in msecs",
TagKeys: []tag.Key{KeyMethod},
Measure: ServerServerElapsedTime,
Aggregation: DefaultMillisecondsDistribution,
}
ServerRequestBytesView, _ = view.New(
"grpc.io/server/request_bytes",
"Request bytes",
[]tag.Key{KeyMethod},
ServerRequestBytes,
DefaultBytesDistribution)
ServerRequestBytesView = &view.View{
Name: "grpc.io/server/request_bytes",
Description: "Request bytes",
TagKeys: []tag.Key{KeyMethod},
Measure: ServerRequestBytes,
Aggregation: DefaultBytesDistribution,
}
ServerResponseBytesView, _ = view.New(
"grpc.io/server/response_bytes",
"Response bytes",
[]tag.Key{KeyMethod},
ServerResponseBytes,
DefaultBytesDistribution)
ServerResponseBytesView = &view.View{
Name: "grpc.io/server/response_bytes",
Description: "Response bytes",
TagKeys: []tag.Key{KeyMethod},
Measure: ServerResponseBytes,
Aggregation: DefaultBytesDistribution,
}
ServerRequestCountView, _ = view.New(
"grpc.io/server/request_count",
"Count of request messages per server RPC",
[]tag.Key{KeyMethod},
ServerRequestCount,
DefaultMessageCountDistribution)
ServerRequestCountView = &view.View{
Name: "grpc.io/server/request_count",
Description: "Count of request messages per server RPC",
TagKeys: []tag.Key{KeyMethod},
Measure: ServerRequestCount,
Aggregation: DefaultMessageCountDistribution,
}
ServerResponseCountView, _ = view.New(
"grpc.io/server/response_count",
"Count of response messages per server RPC",
[]tag.Key{KeyMethod},
ServerResponseCount,
DefaultMessageCountDistribution)
ServerResponseCountView = &view.View{
Name: "grpc.io/server/response_count",
Description: "Count of response messages per server RPC",
TagKeys: []tag.Key{KeyMethod},
Measure: ServerResponseCount,
Aggregation: DefaultMessageCountDistribution,
}
)
// All default server views provided by this package:

View File

@@ -29,14 +29,9 @@ import (
"google.golang.org/grpc/status"
)
// serverStatsHandler is a stats.Handler implementation
// that collects stats for a gRPC server. Predefined
// measures and views can be used to access the collected data.
type serverStatsHandler struct{}
// TagRPC gets the metadata from gRPC context, extracts the encoded tags from
// it and creates a new tag.Map and puts them into the returned context.
func (h *serverStatsHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo) context.Context {
func (h *ServerHandler) statsTagRPC(ctx context.Context, info *stats.RPCTagInfo) context.Context {
startTime := time.Now()
if info == nil {
if grpclog.V(2) {
@@ -51,7 +46,7 @@ func (h *serverStatsHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo)
}
// HandleRPC processes the RPC events.
func (h *serverStatsHandler) HandleRPC(ctx context.Context, s stats.RPCStats) {
func (h *ServerHandler) statsHandleRPC(ctx context.Context, s stats.RPCStats) {
switch st := s.(type) {
case *stats.Begin, *stats.InHeader, *stats.InTrailer, *stats.OutHeader, *stats.OutTrailer:
// Do nothing for server
@@ -67,7 +62,7 @@ func (h *serverStatsHandler) HandleRPC(ctx context.Context, s stats.RPCStats) {
}
}
func (h *serverStatsHandler) handleRPCInPayload(ctx context.Context, s *stats.InPayload) {
func (h *ServerHandler) handleRPCInPayload(ctx context.Context, s *stats.InPayload) {
d, ok := ctx.Value(grpcServerRPCKey).(*rpcData)
if !ok {
if grpclog.V(2) {
@@ -80,7 +75,7 @@ func (h *serverStatsHandler) handleRPCInPayload(ctx context.Context, s *stats.In
atomic.AddInt64(&d.reqCount, 1)
}
func (h *serverStatsHandler) handleRPCOutPayload(ctx context.Context, s *stats.OutPayload) {
func (h *ServerHandler) handleRPCOutPayload(ctx context.Context, s *stats.OutPayload) {
d, ok := ctx.Value(grpcServerRPCKey).(*rpcData)
if !ok {
if grpclog.V(2) {
@@ -93,7 +88,7 @@ func (h *serverStatsHandler) handleRPCOutPayload(ctx context.Context, s *stats.O
atomic.AddInt64(&d.respCount, 1)
}
func (h *serverStatsHandler) handleRPCEnd(ctx context.Context, s *stats.End) {
func (h *ServerHandler) handleRPCEnd(ctx context.Context, s *stats.End) {
d, ok := ctx.Value(grpcServerRPCKey).(*rpcData)
if !ok {
if grpclog.V(2) {
@@ -128,7 +123,7 @@ func (h *serverStatsHandler) handleRPCEnd(ctx context.Context, s *stats.End) {
// createTags creates a new tag map containing the tags extracted from the
// gRPC metadata.
func (h *serverStatsHandler) createTags(ctx context.Context, fullinfo string) (context.Context, error) {
func (h *ServerHandler) createTags(ctx context.Context, fullinfo string) (context.Context, error) {
mods := []tag.Mutator{
tag.Upsert(KeyMethod, methodName(fullinfo)),
}

View File

@@ -302,7 +302,7 @@ func TestServerDefaultCollections(t *testing.T) {
}
}
h := &serverStatsHandler{}
h := &ServerHandler{NoTrace: true}
for _, rpc := range tc.rpcs {
mods := []tag.Mutator{}
for _, t := range rpc.tags {
@@ -326,33 +326,29 @@ func TestServerDefaultCollections(t *testing.T) {
}
for _, wantData := range tc.wants {
gotRows, err := wantData.v().RetrieveData()
gotRows, err := view.RetrieveData(wantData.v().Name)
if err != nil {
t.Errorf("%q: RetrieveData (%q) = %v", tc.label, wantData.v().Name(), err)
t.Errorf("%q: RetrieveData (%q) = %v", tc.label, wantData.v().Name, err)
continue
}
for _, gotRow := range gotRows {
if !containsRow(wantData.rows, gotRow) {
t.Errorf("%q: unwanted row for view %q: %v", tc.label, wantData.v().Name(), gotRow)
t.Errorf("%q: unwanted row for view %q: %v", tc.label, wantData.v().Name, gotRow)
break
}
}
for _, wantRow := range wantData.rows {
if !containsRow(gotRows, wantRow) {
t.Errorf("%q: missing row for view %q: %v", tc.label, wantData.v().Name(), wantRow)
t.Errorf("%q: missing row for view %q: %v", tc.label, wantData.v().Name, wantRow)
break
}
}
}
// Unregister views to cleanup.
for _, v := range DefaultServerViews {
if err := v.Unsubscribe(); err != nil {
t.Error(err)
}
}
view.Unsubscribe(DefaultServerViews...)
}
}

View File

@@ -39,9 +39,9 @@ type rpcData struct {
// The following variables define the default hard-coded auxiliary data used by
// both the default GRPC client and GRPC server metrics.
var (
DefaultBytesDistribution = view.DistributionAggregation([]float64{0, 1024, 2048, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216, 67108864, 268435456, 1073741824, 4294967296})
DefaultMillisecondsDistribution = view.DistributionAggregation([]float64{0, 1, 2, 3, 4, 5, 6, 8, 10, 13, 16, 20, 25, 30, 40, 50, 65, 80, 100, 130, 160, 200, 250, 300, 400, 500, 650, 800, 1000, 2000, 5000, 10000, 20000, 50000, 100000})
DefaultMessageCountDistribution = view.DistributionAggregation([]float64{0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536})
DefaultBytesDistribution = view.DistributionAggregation{0, 1024, 2048, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216, 67108864, 268435456, 1073741824, 4294967296}
DefaultMillisecondsDistribution = view.DistributionAggregation{0, 1, 2, 3, 4, 5, 6, 8, 10, 13, 16, 20, 25, 30, 40, 50, 65, 80, 100, 130, 160, 200, 250, 300, 400, 500, 650, 800, 1000, 2000, 5000, 10000, 20000, 50000, 100000}
DefaultMessageCountDistribution = view.DistributionAggregation{0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536}
)
var (

View File

@@ -17,29 +17,23 @@ package ocgrpc
import (
"strings"
"google.golang.org/grpc/codes"
"go.opencensus.io/trace"
"go.opencensus.io/trace/propagation"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/stats"
"google.golang.org/grpc/status"
)
// clientTraceHandler is a an implementation of grpc.StatsHandler
// that can be passed to grpc.Dial
// using grpc.WithStatsHandler to enable trace context propagation and
// automatic span creation for outgoing gRPC requests.
type clientTraceHandler struct{}
type serverTraceHandler struct{}
const traceContextKey = "grpc-trace-bin"
// TagRPC creates a new trace span for the client side of the RPC.
//
// It returns ctx with the new trace span added and a serialization of the
// SpanContext added to the outgoing gRPC metadata.
func (c *clientTraceHandler) TagRPC(ctx context.Context, rti *stats.RPCTagInfo) context.Context {
func (c *ClientHandler) traceTagRPC(ctx context.Context, rti *stats.RPCTagInfo) context.Context {
name := "Sent" + strings.Replace(rti.FullMethodName, "/", ".", -1)
ctx, _ = trace.StartSpan(ctx, name)
traceContextBinary := propagation.Binary(trace.FromContext(ctx).SpanContext())
@@ -55,26 +49,27 @@ func (c *clientTraceHandler) TagRPC(ctx context.Context, rti *stats.RPCTagInfo)
// it finds one, uses that SpanContext as the parent context of the new span.
//
// It returns ctx, with the new trace span added.
func (s *serverTraceHandler) TagRPC(ctx context.Context, rti *stats.RPCTagInfo) context.Context {
func (s *ServerHandler) traceTagRPC(ctx context.Context, rti *stats.RPCTagInfo) context.Context {
md, _ := metadata.FromIncomingContext(ctx)
name := "Recv" + strings.Replace(rti.FullMethodName, "/", ".", -1)
if s := md[traceContextKey]; len(s) > 0 {
if parent, ok := propagation.FromBinary([]byte(s[0])); ok {
ctx, _ = trace.StartSpanWithRemoteParent(ctx, name, parent, trace.StartOptions{})
return ctx
span := trace.NewSpanWithRemoteParent(name, parent, trace.StartOptions{})
return trace.WithSpan(ctx, span)
}
}
// TODO(ramonza): should we ignore the in-process parent here?
ctx, _ = trace.StartSpan(ctx, name)
return ctx
}
// HandleRPC processes the RPC stats, adding information to the current trace span.
func (c *clientTraceHandler) HandleRPC(ctx context.Context, rs stats.RPCStats) {
func (c *ClientHandler) traceHandleRPC(ctx context.Context, rs stats.RPCStats) {
handleRPC(ctx, rs)
}
// HandleRPC processes the RPC stats, adding information to the current trace span.
func (s *serverTraceHandler) HandleRPC(ctx context.Context, rs stats.RPCStats) {
func (s *ServerHandler) traceHandleRPC(ctx context.Context, rs stats.RPCStats) {
handleRPC(ctx, rs)
}
@@ -84,16 +79,20 @@ func handleRPC(ctx context.Context, rs stats.RPCStats) {
switch rs := rs.(type) {
case *stats.Begin:
span.SetAttributes(
trace.BoolAttribute{Key: "Client", Value: rs.Client},
trace.BoolAttribute{Key: "FailFast", Value: rs.FailFast})
trace.BoolAttribute("Client", rs.Client),
trace.BoolAttribute("FailFast", rs.FailFast))
case *stats.InPayload:
span.AddMessageReceiveEvent(0 /* TODO: messageID */, int64(rs.Length), int64(rs.WireLength))
case *stats.OutPayload:
span.AddMessageSendEvent(0, int64(rs.Length), int64(rs.WireLength))
case *stats.End:
if rs.Error != nil {
code, desc := grpc.Code(rs.Error), grpc.ErrorDesc(rs.Error)
span.SetStatus(trace.Status{Code: int32(code), Message: desc})
s, ok := status.FromError(rs.Error)
if ok {
span.SetStatus(trace.Status{Code: int32(s.Code()), Message: s.Message()})
} else {
span.SetStatus(trace.Status{Code: int32(codes.Internal), Message: rs.Error.Error()})
}
}
span.End()
}

View File

@@ -21,7 +21,7 @@ import (
"testing"
"time"
testpb "go.opencensus.io/plugin/ocgrpc/testdata"
"go.opencensus.io/plugin/ocgrpc/internal/testpb"
"go.opencensus.io/trace"
"golang.org/x/net/context"
"google.golang.org/grpc"
@@ -124,7 +124,7 @@ func TestStreaming(t *testing.T) {
s1 := <-te.ch
s2 := <-te.ch
checkSpanData(t, s1, s2, ".testdata.Foo.Multiple", true)
checkSpanData(t, s1, s2, ".testpb.Foo.Multiple", true)
select {
case <-te.ch:
@@ -167,7 +167,7 @@ func TestStreamingFail(t *testing.T) {
s1 := <-te.ch
s2 := <-te.ch
checkSpanData(t, s1, s2, ".testdata.Foo.Multiple", false)
checkSpanData(t, s1, s2, ".testpb.Foo.Multiple", false)
cleanup()
select {
@@ -196,7 +196,7 @@ func TestSingle(t *testing.T) {
s1 := <-te.ch
s2 := <-te.ch
checkSpanData(t, s1, s2, ".testdata.Foo.Single", true)
checkSpanData(t, s1, s2, ".testpb.Foo.Single", true)
cleanup()
select {
@@ -225,7 +225,7 @@ func TestSingleFail(t *testing.T) {
s1 := <-te.ch
s2 := <-te.ch
checkSpanData(t, s1, s2, ".testdata.Foo.Single", false)
checkSpanData(t, s1, s2, ".testpb.Foo.Single", false)
cleanup()
select {

View File

@@ -42,10 +42,9 @@ type Transport struct {
// (currently B3 format) will be used.
Propagation propagation.HTTPFormat
// Sampler if provided, will be consulted for each span generated by this
// RoundTripper. Otherwise, the default sampling behavior takes place
// (see trace.StartOptions).
Sampler trace.Sampler
// StartOptions are applied to the span started by this Transport around each
// request.
StartOptions trace.StartOptions
// TODO: Implement tag propagation for HTTP.
}
@@ -60,15 +59,14 @@ func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
format = defaultFormat
}
rt = &traceTransport{
base: rt,
format: format,
sampler: t.Sampler,
base: rt,
format: format,
startOptions: t.StartOptions,
}
}
if !t.NoStats {
rt = statsTransport{
base: rt,
sampler: t.Sampler,
base: rt,
}
}
return rt.RoundTrip(req)

View File

@@ -24,13 +24,11 @@ import (
"go.opencensus.io/stats"
"go.opencensus.io/tag"
"go.opencensus.io/trace"
)
// statsTransport is an http.RoundTripper that collects stats for the outgoing requests.
type statsTransport struct {
base http.RoundTripper
sampler trace.Sampler
base http.RoundTripper
}
// RoundTrip implements http.RoundTripper, delegating to Base and recording stats for the request.
@@ -56,10 +54,10 @@ func (t statsTransport) RoundTrip(req *http.Request) (*http.Response, error) {
resp, err := t.base.RoundTrip(req)
if err != nil {
track.statusCode = "error"
track.statusCode = http.StatusInternalServerError
track.end()
} else {
track.statusCode = strconv.Itoa(resp.StatusCode)
track.statusCode = resp.StatusCode
if resp.Body == nil {
track.end()
} else {
@@ -67,7 +65,6 @@ func (t statsTransport) RoundTrip(req *http.Request) (*http.Response, error) {
resp.Body = track
}
}
return resp, err
}
@@ -82,15 +79,17 @@ func (t statsTransport) CancelRequest(req *http.Request) {
}
type tracker struct {
ctx context.Context
respSize int64
reqSize int64
ctx context.Context
start time.Time
body io.ReadCloser
statusCode string
statusCode int
endOnce sync.Once
}
var _ io.ReadCloser = (*tracker)(nil)
func (t *tracker) end() {
t.endOnce.Do(func() {
m := []stats.Measurement{
@@ -100,13 +99,11 @@ func (t *tracker) end() {
if t.reqSize >= 0 {
m = append(m, ClientRequestBytes.M(t.reqSize))
}
ctx, _ := tag.New(t.ctx, tag.Upsert(StatusCode, t.statusCode))
ctx, _ := tag.New(t.ctx, tag.Upsert(StatusCode, strconv.Itoa(t.statusCode)))
stats.Record(ctx, m...)
})
}
var _ io.ReadCloser = (*tracker)(nil)
func (t *tracker) Read(b []byte) (int, error) {
n, err := t.body.Read(b)
switch err {

View File

@@ -97,7 +97,7 @@ func TestClient(t *testing.T) {
t.Errorf("view not found %q", viewName)
continue
}
rows, err := v.RetrieveData()
rows, err := view.RetrieveData(v.Name)
if err != nil {
t.Error(err)
continue
@@ -114,7 +114,8 @@ func TestClient(t *testing.T) {
case *view.DistributionData:
count = data.Count
default:
t.Errorf("don't know how to handle data type: %v", data)
t.Errorf("Unkown data type: %v", data)
continue
}
if got := count; got != reqCount {
t.Fatalf("%s = %d; want %d", viewName, got, reqCount)
@@ -144,7 +145,7 @@ func benchmarkClientServer(b *testing.B, transport *ochttp.Transport) {
fmt.Fprintf(rw, "Hello world.\n")
}))
defer ts.Close()
transport.Sampler = trace.AlwaysSample()
transport.StartOptions.Sampler = trace.AlwaysSample()
var client http.Client
client.Transport = transport
b.ResetTimer()
@@ -194,7 +195,7 @@ func benchmarkClientServerParallel(b *testing.B, parallelism int, transport *och
MaxIdleConns: parallelism,
MaxIdleConnsPerHost: parallelism,
}
transport.Sampler = trace.AlwaysSample()
transport.StartOptions.Sampler = trace.AlwaysSample()
c.Transport = transport
b.ResetTimer()

View File

@@ -1,22 +0,0 @@
// Copyright 2018, OpenCensus Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ochttp
import (
"go.opencensus.io/plugin/ochttp/propagation/b3"
"go.opencensus.io/trace/propagation"
)
var defaultFormat propagation.HTTPFormat = &b3.HTTPFormat{}

View File

@@ -13,4 +13,7 @@
// limitations under the License.
// Package ochttp provides OpenCensus instrumentation for net/http package.
//
// For server instrumentation, see Handler. For client-side instrumentation,
// see Transport.
package ochttp // import "go.opencensus.io/plugin/ochttp"

View File

@@ -19,10 +19,29 @@ import (
"net/http"
"go.opencensus.io/plugin/ochttp"
"go.opencensus.io/plugin/ochttp/propagation/google"
"go.opencensus.io/plugin/ochttp/propagation/b3"
"go.opencensus.io/stats/view"
"go.opencensus.io/tag"
)
func ExampleTransport() {
err := view.Subscribe(
// Subscribe to a few default views, with renaming
ochttp.ClientRequestCountByMethod,
ochttp.ClientResponseCountByStatusCode,
ochttp.ClientLatencyView,
// Subscribe to a custom view
&view.View{
Name: "httpclient_latency_by_hostpath",
TagKeys: []tag.Key{ochttp.Host, ochttp.Path},
Measure: ochttp.ClientLatency,
Aggregation: ochttp.DefaultLatencyDistribution,
},
)
if err != nil {
log.Fatal(err)
}
client := &http.Client{
Transport: &ochttp.Transport{},
}
@@ -44,6 +63,6 @@ func ExampleHandler_mux() {
log.Fatal(http.ListenAndServe("localhost:8080", &ochttp.Handler{
Handler: mux,
Propagation: &google.HTTPFormat{}, // Uses Google's propagation format.
Propagation: &b3.HTTPFormat{},
}))
}

View File

@@ -12,8 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
// Package google contains a propagation.HTTPFormat implementation
// for Google Cloud Trace and Stackdriver.
// Package google is deprecated: Use go.opencensus.io/exporter/stackdriver/propagation.
package google // import "go.opencensus.io/plugin/ochttp/propagation/google"
import (
@@ -25,7 +24,6 @@ import (
"strings"
"go.opencensus.io/trace"
"go.opencensus.io/trace/propagation"
)
const (
@@ -33,12 +31,9 @@ const (
httpHeader = `X-Cloud-Trace-Context`
)
// HTTPFormat implements propagation.HTTPFormat to propagate
// traces in HTTP headers for Google Cloud Platform and Stackdriver Trace.
// Deprecated: Use go.opencensus.io/exporter/stackdriver/propagation.HTTPFormat
type HTTPFormat struct{}
var _ propagation.HTTPFormat = (*HTTPFormat)(nil)
// SpanContextFromRequest extracts a Stackdriver Trace span context from incoming requests.
func (f *HTTPFormat) SpanContextFromRequest(req *http.Request) (sc trace.SpanContext, ok bool) {
h := req.Header.Get(httpHeader)

View File

@@ -23,7 +23,7 @@ import (
"testing"
"go.opencensus.io/plugin/ochttp/propagation/b3"
"go.opencensus.io/plugin/ochttp/propagation/google"
"go.opencensus.io/plugin/ochttp/propagation/tracecontext"
"go.opencensus.io/trace"
"go.opencensus.io/trace/propagation"
)
@@ -32,7 +32,7 @@ func TestRoundTripAllFormats(t *testing.T) {
// TODO: test combinations of different formats for chains of calls
formats := []propagation.HTTPFormat{
&b3.HTTPFormat{},
&google.HTTPFormat{},
&tracecontext.HTTPFormat{},
}
ctx := context.Background()

164
vendor/go.opencensus.io/plugin/ochttp/server.go generated vendored Normal file
View File

@@ -0,0 +1,164 @@
// Copyright 2018, OpenCensus Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ochttp
import (
"context"
"net/http"
"strconv"
"sync"
"time"
"go.opencensus.io/stats"
"go.opencensus.io/tag"
"go.opencensus.io/trace"
"go.opencensus.io/trace/propagation"
)
// Handler is a http.Handler that is aware of the incoming request's span.
//
// The extracted span can be accessed from the incoming request's
// context.
//
// span := trace.FromContext(r.Context())
//
// The server span will be automatically ended at the end of ServeHTTP.
//
// Incoming propagation mechanism is determined by the given HTTP propagators.
type Handler struct {
// NoStats may be set to disable recording of stats.
NoStats bool
// NoTrace may be set to disable recording of traces.
NoTrace bool
// Propagation defines how traces are propagated. If unspecified,
// B3 propagation will be used.
Propagation propagation.HTTPFormat
// Handler is the handler used to handle the incoming request.
Handler http.Handler
// StartOptions are applied to the span started by this Handler around each
// request.
StartOptions trace.StartOptions
}
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if !h.NoTrace {
var end func()
r, end = h.startTrace(w, r)
defer end()
}
if !h.NoStats {
var end func()
w, end = h.startStats(w, r)
defer end()
}
handler := h.Handler
if handler == nil {
handler = http.DefaultServeMux
}
handler.ServeHTTP(w, r)
}
func (h *Handler) startTrace(w http.ResponseWriter, r *http.Request) (*http.Request, func()) {
name := spanNameFromURL("Recv", r.URL)
p := h.Propagation
if p == nil {
p = defaultFormat
}
ctx := r.Context()
var span *trace.Span
if sc, ok := p.SpanContextFromRequest(r); ok {
span = trace.NewSpanWithRemoteParent(name, sc, h.StartOptions)
} else {
span = trace.NewSpan(name, nil, h.StartOptions)
}
ctx = trace.WithSpan(ctx, span)
span.SetAttributes(requestAttrs(r)...)
return r.WithContext(trace.WithSpan(r.Context(), span)), span.End
}
func (h *Handler) startStats(w http.ResponseWriter, r *http.Request) (http.ResponseWriter, func()) {
ctx, _ := tag.New(r.Context(),
tag.Upsert(Host, r.URL.Host),
tag.Upsert(Path, r.URL.Path),
tag.Upsert(Method, r.Method))
track := &trackingResponseWriter{
start: time.Now(),
ctx: ctx,
writer: w,
}
if r.Body == nil {
// TODO: Handle cases where ContentLength is not set.
track.reqSize = -1
} else if r.ContentLength > 0 {
track.reqSize = r.ContentLength
}
stats.Record(ctx, ServerRequestCount.M(1))
return track, track.end
}
type trackingResponseWriter struct {
ctx context.Context
reqSize int64
respSize int64
start time.Time
statusCode int
endOnce sync.Once
writer http.ResponseWriter
}
var _ http.ResponseWriter = (*trackingResponseWriter)(nil)
func (t *trackingResponseWriter) end() {
t.endOnce.Do(func() {
if t.statusCode == 0 {
t.statusCode = 200
}
m := []stats.Measurement{
ServerLatency.M(float64(time.Since(t.start)) / float64(time.Millisecond)),
ServerResponseBytes.M(t.respSize),
}
if t.reqSize >= 0 {
m = append(m, ServerRequestBytes.M(t.reqSize))
}
ctx, _ := tag.New(t.ctx, tag.Upsert(StatusCode, strconv.Itoa(t.statusCode)))
stats.Record(ctx, m...)
})
}
func (t *trackingResponseWriter) Header() http.Header {
return t.writer.Header()
}
func (t *trackingResponseWriter) Write(data []byte) (int, error) {
n, err := t.writer.Write(data)
t.respSize += int64(n)
return n, err
}
func (t *trackingResponseWriter) WriteHeader(statusCode int) {
t.writer.WriteHeader(statusCode)
t.statusCode = statusCode
}
func (t *trackingResponseWriter) Flush() {
if flusher, ok := t.writer.(http.Flusher); ok {
flusher.Flush()
}
}

117
vendor/go.opencensus.io/plugin/ochttp/server_test.go generated vendored Normal file
View File

@@ -0,0 +1,117 @@
package ochttp
import (
"bytes"
"net/http"
"net/http/httptest"
"testing"
"go.opencensus.io/stats/view"
)
func httpHandler(statusCode, respSize int) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(statusCode)
body := make([]byte, respSize)
w.Write(body)
})
}
func updateMean(mean float64, sample, count int) float64 {
if count == 1 {
return float64(sample)
}
return mean + (float64(sample)-mean)/float64(count)
}
func TestHandlerStatsCollection(t *testing.T) {
for _, v := range DefaultViews {
v.Subscribe()
}
views := []string{
"opencensus.io/http/server/request_count",
"opencensus.io/http/server/latency",
"opencensus.io/http/server/request_bytes",
"opencensus.io/http/server/response_bytes",
}
// TODO: test latency measurements?
tests := []struct {
name, method, target string
count, statusCode, reqSize, respSize int
}{
{"get 200", "GET", "http://opencensus.io/request/one", 10, 200, 512, 512},
{"post 503", "POST", "http://opencensus.io/request/two", 5, 503, 1024, 16384},
{"no body 302", "GET", "http://opencensus.io/request/three", 2, 302, 0, 0},
}
totalCount, meanReqSize, meanRespSize := 0, 0.0, 0.0
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
body := bytes.NewBuffer(make([]byte, test.reqSize))
r := httptest.NewRequest(test.method, test.target, body)
w := httptest.NewRecorder()
h := &Handler{
NoTrace: true,
Handler: httpHandler(test.statusCode, test.respSize),
}
for i := 0; i < test.count; i++ {
h.ServeHTTP(w, r)
totalCount++
// Distributions do not track sum directly, we must
// mimic their behaviour to avoid rounding failures.
meanReqSize = updateMean(meanReqSize, test.reqSize, totalCount)
meanRespSize = updateMean(meanRespSize, test.respSize, totalCount)
}
})
}
for _, viewName := range views {
v := view.Find(viewName)
if v == nil {
t.Errorf("view not found %q", viewName)
continue
}
rows, err := view.RetrieveData(viewName)
if err != nil {
t.Error(err)
continue
}
if got, want := len(rows), 1; got != want {
t.Errorf("len(%q) = %d; want %d", viewName, got, want)
continue
}
data := rows[0].Data
var count int
var sum float64
switch data := data.(type) {
case *view.CountData:
count = int(*data)
case *view.DistributionData:
count = int(data.Count)
sum = data.Sum()
default:
t.Errorf("Unkown data type: %v", data)
continue
}
if got, want := count, totalCount; got != want {
t.Fatalf("%s = %d; want %d", viewName, got, want)
}
// We can only check sum for distribution views.
switch viewName {
case "opencensus.io/http/server/request_bytes":
if got, want := sum, meanReqSize*float64(totalCount); got != want {
t.Fatalf("%s = %g; want %g", viewName, got, want)
}
case "opencensus.io/http/server/response_bytes":
if got, want := sum, meanRespSize*float64(totalCount); got != want {
t.Fatalf("%s = %g; want %g", viewName, got, want)
}
}
}
}

View File

@@ -20,7 +20,7 @@ import (
"go.opencensus.io/tag"
)
// The following client HTTP measures are supported for use in custom views:
// The following client HTTP measures are supported for use in custom views.
var (
ClientRequestCount, _ = stats.Int64("opencensus.io/http/client/request_count", "Number of HTTP requests started", stats.UnitNone)
ClientRequestBytes, _ = stats.Int64("opencensus.io/http/client/request_bytes", "HTTP request body size if set as ContentLength (uncompressed)", stats.UnitBytes)
@@ -28,46 +28,128 @@ var (
ClientLatency, _ = stats.Float64("opencensus.io/http/client/latency", "End-to-end latency", stats.UnitMilliseconds)
)
// The following server HTTP measures are supported for use in custom views:
var (
ServerRequestCount, _ = stats.Int64("opencensus.io/http/server/request_count", "Number of HTTP requests started", stats.UnitNone)
ServerRequestBytes, _ = stats.Int64("opencensus.io/http/server/request_bytes", "HTTP request body size if set as ContentLength (uncompressed)", stats.UnitBytes)
ServerResponseBytes, _ = stats.Int64("opencensus.io/http/server/response_bytes", "HTTP response body size (uncompressed)", stats.UnitBytes)
ServerLatency, _ = stats.Float64("opencensus.io/http/server/latency", "End-to-end latency", stats.UnitMilliseconds)
)
// The following tags are applied to stats recorded by this package. Host, Path
// and Method are applied to all measures. StatusCode is not applied to
// ClientRequestCount, since it is recorded before the status is known.
// ClientRequestCount or ServerRequestCount, since it is recorded before the status is known.
var (
// Host is the value of the HTTP Host header.
Host, _ = tag.NewKey("http.host")
// StatusCode is the numeric HTTP response status code,
// or "error" if a transport error occurred and no status code was read.
StatusCode, _ = tag.NewKey("http.status")
// Path is the URL path (not including query string) in the request.
Path, _ = tag.NewKey("http.path")
// Method is the HTTP method of the request, capitalized (GET, POST, etc.).
Method, _ = tag.NewKey("http.method")
)
// Default distributions used by views in this package.
var (
DefaultSizeDistribution = view.DistributionAggregation([]float64{0, 1024, 2048, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216, 67108864, 268435456, 1073741824, 4294967296})
DefaultLatencyDistribution = view.DistributionAggregation([]float64{0, 1, 2, 3, 4, 5, 6, 8, 10, 13, 16, 20, 25, 30, 40, 50, 65, 80, 100, 130, 160, 200, 250, 300, 400, 500, 650, 800, 1000, 2000, 5000, 10000, 20000, 50000, 100000})
DefaultSizeDistribution = view.DistributionAggregation{0, 1024, 2048, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216, 67108864, 268435456, 1073741824, 4294967296}
DefaultLatencyDistribution = view.DistributionAggregation{0, 1, 2, 3, 4, 5, 6, 8, 10, 13, 16, 20, 25, 30, 40, 50, 65, 80, 100, 130, 160, 200, 250, 300, 400, 500, 650, 800, 1000, 2000, 5000, 10000, 20000, 50000, 100000}
)
// Package ochttp provides some convenience views.
// You need to subscribe to the views for data to actually be collected.
var (
ClientRequestCountView, _ = view.New("opencensus.io/http/client/request_count", "Count of HTTP requests started", nil, ClientRequestCount, view.CountAggregation{})
ClientRequestBytesView, _ = view.New("opencensus.io/http/client/request_bytes", "Size distribution of HTTP request body", nil, ClientRequestBytes, DefaultSizeDistribution)
ClientResponseBytesView, _ = view.New("opencensus.io/http/client/response_bytes", "Size distribution of HTTP response body", nil, ClientResponseBytes, DefaultSizeDistribution)
ClientLatencyView, _ = view.New("opencensus.io/http/client/latency", "Latency distribution of HTTP requests", nil, ClientLatency, DefaultLatencyDistribution)
ClientRequestCountView = &view.View{
Name: "opencensus.io/http/client/request_count",
Description: "Count of HTTP requests started",
Measure: ClientRequestCount,
Aggregation: view.CountAggregation{},
}
ClientRequestCountByMethod, _ = view.New(
"opencensus.io/http/client/request_count_by_method",
"Client request count by HTTP method",
[]tag.Key{Method},
ClientRequestCount,
view.CountAggregation{})
ClientResponseCountByStatusCode, _ = view.New(
"opencensus.io/http/client/response_count_by_status_code",
"Client response count by status code",
[]tag.Key{StatusCode},
ClientLatency,
view.CountAggregation{})
ClientRequestBytesView = &view.View{
Name: "opencensus.io/http/client/request_bytes",
Description: "Size distribution of HTTP request body",
Measure: ClientRequestBytes,
Aggregation: DefaultSizeDistribution,
}
ClientResponseBytesView = &view.View{
Name: "opencensus.io/http/client/response_bytes",
Description: "Size distribution of HTTP response body",
Measure: ClientResponseBytes,
Aggregation: DefaultSizeDistribution,
}
ClientLatencyView = &view.View{
Name: "opencensus.io/http/client/latency",
Description: "Latency distribution of HTTP requests",
Measure: ClientLatency,
Aggregation: DefaultLatencyDistribution,
}
ClientRequestCountByMethod = &view.View{
Name: "opencensus.io/http/client/request_count_by_method",
Description: "Client request count by HTTP method",
TagKeys: []tag.Key{Method},
Measure: ClientRequestCount,
Aggregation: view.CountAggregation{},
}
ClientResponseCountByStatusCode = &view.View{
Name: "opencensus.io/http/client/response_count_by_status_code",
Description: "Client response count by status code",
TagKeys: []tag.Key{StatusCode},
Measure: ClientLatency,
Aggregation: view.CountAggregation{},
}
ServerRequestCountView = &view.View{
Name: "opencensus.io/http/server/request_count",
Description: "Count of HTTP requests started",
Measure: ServerRequestCount,
Aggregation: view.CountAggregation{},
}
ServerRequestBytesView = &view.View{
Name: "opencensus.io/http/server/request_bytes",
Description: "Size distribution of HTTP request body",
Measure: ServerRequestBytes,
Aggregation: DefaultSizeDistribution,
}
ServerResponseBytesView = &view.View{
Name: "opencensus.io/http/server/response_bytes",
Description: "Size distribution of HTTP response body",
Measure: ServerResponseBytes,
Aggregation: DefaultSizeDistribution,
}
ServerLatencyView = &view.View{
Name: "opencensus.io/http/server/latency",
Description: "Latency distribution of HTTP requests",
Measure: ServerLatency,
Aggregation: DefaultLatencyDistribution,
}
ServerRequestCountByMethod = &view.View{
Name: "opencensus.io/http/server/request_count_by_method",
Description: "Server request count by HTTP method",
TagKeys: []tag.Key{Method},
Measure: ServerRequestCount,
Aggregation: view.CountAggregation{},
}
ServerResponseCountByStatusCode = &view.View{
Name: "opencensus.io/http/server/response_count_by_status_code",
Description: "Server response count by status code",
TagKeys: []tag.Key{StatusCode},
Measure: ServerLatency,
Aggregation: view.CountAggregation{},
}
DefaultViews = []*view.View{
ClientRequestCountView,
@@ -76,5 +158,11 @@ var (
ClientLatencyView,
ClientRequestCountByMethod,
ClientResponseCountByStatusCode,
ServerRequestCountView,
ServerRequestBytesView,
ServerResponseBytesView,
ServerLatencyView,
ServerRequestCountByMethod,
ServerResponseCountByStatusCode,
}
)

View File

@@ -1,27 +0,0 @@
package ochttp
import (
"testing"
"go.opencensus.io/stats"
"go.opencensus.io/tag"
)
func TestVarsInitialized(t *testing.T) {
// Test that global initialization was successful
for i, k := range []tag.Key{Host, StatusCode, Path, Method} {
if k.Name() == "" {
t.Errorf("key not initialized: %d", i)
}
}
for i, m := range []stats.Measure{ClientRequestCount, ClientResponseBytes, ClientRequestBytes, ClientLatency} {
if m == nil {
t.Errorf("measure not initialized: %d", i)
}
}
for i, v := range DefaultViews {
if v == nil {
t.Errorf("view not initialized: %d", i)
}
}
}

View File

@@ -20,12 +20,15 @@ import (
"net/url"
"sync"
"go.opencensus.io/plugin/ochttp/propagation/b3"
"go.opencensus.io/trace"
"go.opencensus.io/trace/propagation"
)
// TODO(jbd): Add godoc examples.
var defaultFormat propagation.HTTPFormat = &b3.HTTPFormat{}
// Attributes recorded on the span for the requests.
// Only trace exporters will need them.
const (
@@ -33,13 +36,13 @@ const (
MethodAttribute = "http.method"
PathAttribute = "http.path"
UserAgentAttribute = "http.user_agent"
StatusCodeAttribute = "http.status"
StatusCodeAttribute = "http.status_code"
)
type traceTransport struct {
base http.RoundTripper
sampler trace.Sampler
format propagation.HTTPFormat
base http.RoundTripper
startOptions trace.StartOptions
format propagation.HTTPFormat
}
// TODO(jbd): Add message events for request and response size.
@@ -52,7 +55,7 @@ func (t *traceTransport) RoundTrip(req *http.Request) (*http.Response, error) {
// TODO(jbd): Discuss whether we want to prefix
// outgoing requests with Sent.
parent := trace.FromContext(req.Context())
span := trace.NewSpan(name, parent, trace.StartOptions{Sampler: t.sampler})
span := trace.NewSpan(name, parent, t.startOptions)
req = req.WithContext(trace.WithSpan(req.Context(), span))
if t.format != nil {
@@ -132,55 +135,6 @@ func (t *traceTransport) CancelRequest(req *http.Request) {
}
}
// Handler is a http.Handler that is aware of the incoming request's span.
//
// The extracted span can be accessed from the incoming request's
// context.
//
// span := trace.FromContext(r.Context())
//
// The server span will be automatically ended at the end of ServeHTTP.
//
// Incoming propagation mechanism is determined by the given HTTP propagators.
type Handler struct {
// Propagation defines how traces are propagated. If unspecified,
// B3 propagation will be used.
Propagation propagation.HTTPFormat
// Handler is the handler used to handle the incoming request.
Handler http.Handler
}
// TODO(jbd): Add Handler.NoTrace and Handler.NoStats.
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
name := spanNameFromURL("Recv", r.URL)
p := h.Propagation
if p == nil {
p = defaultFormat
}
ctx := r.Context()
var span *trace.Span
if sc, ok := p.SpanContextFromRequest(r); ok {
ctx, span = trace.StartSpanWithRemoteParent(ctx, name, sc, trace.StartOptions{})
} else {
ctx, span = trace.StartSpan(ctx, name)
}
defer span.End()
span.SetAttributes(requestAttrs(r)...)
r = r.WithContext(ctx)
handler := h.Handler
if handler == nil {
handler = http.DefaultServeMux
}
handler.ServeHTTP(w, r)
}
func spanNameFromURL(prefix string, u *url.URL) string {
host := u.Hostname()
port := ":" + u.Port()
@@ -192,15 +146,15 @@ func spanNameFromURL(prefix string, u *url.URL) string {
func requestAttrs(r *http.Request) []trace.Attribute {
return []trace.Attribute{
trace.StringAttribute{Key: PathAttribute, Value: r.URL.Path},
trace.StringAttribute{Key: HostAttribute, Value: r.URL.Host},
trace.StringAttribute{Key: MethodAttribute, Value: r.Method},
trace.StringAttribute{Key: UserAgentAttribute, Value: r.UserAgent()},
trace.StringAttribute(PathAttribute, r.URL.Path),
trace.StringAttribute(HostAttribute, r.URL.Host),
trace.StringAttribute(MethodAttribute, r.Method),
trace.StringAttribute(UserAgentAttribute, r.UserAgent()),
}
}
func responseAttrs(resp *http.Response) []trace.Attribute {
return []trace.Attribute{
trace.Int64Attribute{Key: StatusCodeAttribute, Value: int64(resp.StatusCode)},
trace.Int64Attribute(StatusCodeAttribute, int64(resp.StatusCode)),
}
}

View File

@@ -121,9 +121,6 @@ func TestTransport_RoundTrip(t *testing.T) {
}
func TestHandler(t *testing.T) {
// TODO(#431): remove SetDefaultSampler
trace.SetDefaultSampler(trace.ProbabilitySampler(0.0))
traceID := [16]byte{16, 84, 69, 170, 120, 67, 188, 139, 242, 6, 177, 32, 0, 16, 0, 0}
tests := []struct {
header string
@@ -157,8 +154,9 @@ func TestHandler(t *testing.T) {
t.Errorf("TraceOptions = %v; want %v", got, want)
}
}),
StartOptions: trace.StartOptions{Sampler: trace.ProbabilitySampler(0.0)},
Propagation: propagator,
}
handler.Propagation = propagator
req, _ := http.NewRequest("GET", "http://foo.com", nil)
req.Header.Add("trace", tt.header)
handler.ServeHTTP(nil, req)
@@ -347,10 +345,10 @@ func TestRequestAttributes(t *testing.T) {
return req
},
wantAttrs: []trace.Attribute{
trace.StringAttribute{Key: PathAttribute, Value: "/hello"},
trace.StringAttribute{Key: HostAttribute, Value: "example.com"},
trace.StringAttribute{Key: MethodAttribute, Value: "GET"},
trace.StringAttribute{Key: UserAgentAttribute, Value: "ua"},
trace.StringAttribute("http.path", "/hello"),
trace.StringAttribute("http.host", "example.com"),
trace.StringAttribute("http.method", "GET"),
trace.StringAttribute("http.user_agent", "ua"),
},
},
}
@@ -376,14 +374,14 @@ func TestResponseAttributes(t *testing.T) {
name: "non-zero HTTP 200 response",
resp: &http.Response{StatusCode: 200},
wantAttrs: []trace.Attribute{
trace.Int64Attribute{Key: StatusCodeAttribute, Value: 200},
trace.Int64Attribute("http.status_code", 200),
},
},
{
name: "zero HTTP 500 response",
resp: &http.Response{StatusCode: 500},
wantAttrs: []trace.Attribute{
trace.Int64Attribute{Key: StatusCodeAttribute, Value: 500},
trace.Int64Attribute("http.status_code", 500),
},
},
}