diff options
author | Jacob Vosmaer <jacob@gitlab.com> | 2018-04-12 16:42:12 +0300 |
---|---|---|
committer | Jacob Vosmaer <jacob@gitlab.com> | 2018-04-12 16:42:12 +0300 |
commit | 2195e6ca9544764cf0b64daafd11d591de1ae4ea (patch) | |
tree | bee50cc9d62936930584e81e74a2c20d007ae083 /vendor/github.com | |
parent | 1429da85cb13d6d2105b475dac600832d756affb (diff) |
Add more gRPC dependencies
Diffstat (limited to 'vendor/github.com')
64 files changed, 7308 insertions, 0 deletions
diff --git a/vendor/github.com/golang/protobuf/jsonpb/jsonpb.go b/vendor/github.com/golang/protobuf/jsonpb/jsonpb.go new file mode 100644 index 00000000..110ae138 --- /dev/null +++ b/vendor/github.com/golang/protobuf/jsonpb/jsonpb.go @@ -0,0 +1,1083 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2015 The Go Authors. All rights reserved. +// https://github.com/golang/protobuf +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +/* +Package jsonpb provides marshaling and unmarshaling between protocol buffers and JSON. +It follows the specification at https://developers.google.com/protocol-buffers/docs/proto3#json. + +This package produces a different output than the standard "encoding/json" package, +which does not operate correctly on protocol buffers. +*/ +package jsonpb + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "math" + "reflect" + "sort" + "strconv" + "strings" + "time" + + "github.com/golang/protobuf/proto" + + stpb "github.com/golang/protobuf/ptypes/struct" +) + +// Marshaler is a configurable object for converting between +// protocol buffer objects and a JSON representation for them. +type Marshaler struct { + // Whether to render enum values as integers, as opposed to string values. + EnumsAsInts bool + + // Whether to render fields with zero values. + EmitDefaults bool + + // A string to indent each level by. The presence of this field will + // also cause a space to appear between the field separator and + // value, and for newlines to be appear between fields and array + // elements. + Indent string + + // Whether to use the original (.proto) name for fields. + OrigName bool + + // A custom URL resolver to use when marshaling Any messages to JSON. + // If unset, the default resolution strategy is to extract the + // fully-qualified type name from the type URL and pass that to + // proto.MessageType(string). + AnyResolver AnyResolver +} + +// AnyResolver takes a type URL, present in an Any message, and resolves it into +// an instance of the associated message. +type AnyResolver interface { + Resolve(typeUrl string) (proto.Message, error) +} + +func defaultResolveAny(typeUrl string) (proto.Message, error) { + // Only the part of typeUrl after the last slash is relevant. + mname := typeUrl + if slash := strings.LastIndex(mname, "/"); slash >= 0 { + mname = mname[slash+1:] + } + mt := proto.MessageType(mname) + if mt == nil { + return nil, fmt.Errorf("unknown message type %q", mname) + } + return reflect.New(mt.Elem()).Interface().(proto.Message), nil +} + +// JSONPBMarshaler is implemented by protobuf messages that customize the +// way they are marshaled to JSON. Messages that implement this should +// also implement JSONPBUnmarshaler so that the custom format can be +// parsed. +type JSONPBMarshaler interface { + MarshalJSONPB(*Marshaler) ([]byte, error) +} + +// JSONPBUnmarshaler is implemented by protobuf messages that customize +// the way they are unmarshaled from JSON. Messages that implement this +// should also implement JSONPBMarshaler so that the custom format can be +// produced. +type JSONPBUnmarshaler interface { + UnmarshalJSONPB(*Unmarshaler, []byte) error +} + +// Marshal marshals a protocol buffer into JSON. +func (m *Marshaler) Marshal(out io.Writer, pb proto.Message) error { + writer := &errWriter{writer: out} + return m.marshalObject(writer, pb, "", "") +} + +// MarshalToString converts a protocol buffer object to JSON string. +func (m *Marshaler) MarshalToString(pb proto.Message) (string, error) { + var buf bytes.Buffer + if err := m.Marshal(&buf, pb); err != nil { + return "", err + } + return buf.String(), nil +} + +type int32Slice []int32 + +var nonFinite = map[string]float64{ + `"NaN"`: math.NaN(), + `"Infinity"`: math.Inf(1), + `"-Infinity"`: math.Inf(-1), +} + +// For sorting extensions ids to ensure stable output. +func (s int32Slice) Len() int { return len(s) } +func (s int32Slice) Less(i, j int) bool { return s[i] < s[j] } +func (s int32Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +type wkt interface { + XXX_WellKnownType() string +} + +// marshalObject writes a struct to the Writer. +func (m *Marshaler) marshalObject(out *errWriter, v proto.Message, indent, typeURL string) error { + if jsm, ok := v.(JSONPBMarshaler); ok { + b, err := jsm.MarshalJSONPB(m) + if err != nil { + return err + } + if typeURL != "" { + // we are marshaling this object to an Any type + var js map[string]*json.RawMessage + if err = json.Unmarshal(b, &js); err != nil { + return fmt.Errorf("type %T produced invalid JSON: %v", v, err) + } + turl, err := json.Marshal(typeURL) + if err != nil { + return fmt.Errorf("failed to marshal type URL %q to JSON: %v", typeURL, err) + } + js["@type"] = (*json.RawMessage)(&turl) + if b, err = json.Marshal(js); err != nil { + return err + } + } + + out.write(string(b)) + return out.err + } + + s := reflect.ValueOf(v).Elem() + + // Handle well-known types. + if wkt, ok := v.(wkt); ok { + switch wkt.XXX_WellKnownType() { + case "DoubleValue", "FloatValue", "Int64Value", "UInt64Value", + "Int32Value", "UInt32Value", "BoolValue", "StringValue", "BytesValue": + // "Wrappers use the same representation in JSON + // as the wrapped primitive type, ..." + sprop := proto.GetProperties(s.Type()) + return m.marshalValue(out, sprop.Prop[0], s.Field(0), indent) + case "Any": + // Any is a bit more involved. + return m.marshalAny(out, v, indent) + case "Duration": + // "Generated output always contains 3, 6, or 9 fractional digits, + // depending on required precision." + s, ns := s.Field(0).Int(), s.Field(1).Int() + d := time.Duration(s)*time.Second + time.Duration(ns)*time.Nanosecond + x := fmt.Sprintf("%.9f", d.Seconds()) + x = strings.TrimSuffix(x, "000") + x = strings.TrimSuffix(x, "000") + out.write(`"`) + out.write(x) + out.write(`s"`) + return out.err + case "Struct", "ListValue": + // Let marshalValue handle the `Struct.fields` map or the `ListValue.values` slice. + // TODO: pass the correct Properties if needed. + return m.marshalValue(out, &proto.Properties{}, s.Field(0), indent) + case "Timestamp": + // "RFC 3339, where generated output will always be Z-normalized + // and uses 3, 6 or 9 fractional digits." + s, ns := s.Field(0).Int(), s.Field(1).Int() + t := time.Unix(s, ns).UTC() + // time.RFC3339Nano isn't exactly right (we need to get 3/6/9 fractional digits). + x := t.Format("2006-01-02T15:04:05.000000000") + x = strings.TrimSuffix(x, "000") + x = strings.TrimSuffix(x, "000") + out.write(`"`) + out.write(x) + out.write(`Z"`) + return out.err + case "Value": + // Value has a single oneof. + kind := s.Field(0) + if kind.IsNil() { + // "absence of any variant indicates an error" + return errors.New("nil Value") + } + // oneof -> *T -> T -> T.F + x := kind.Elem().Elem().Field(0) + // TODO: pass the correct Properties if needed. + return m.marshalValue(out, &proto.Properties{}, x, indent) + } + } + + out.write("{") + if m.Indent != "" { + out.write("\n") + } + + firstField := true + + if typeURL != "" { + if err := m.marshalTypeURL(out, indent, typeURL); err != nil { + return err + } + firstField = false + } + + for i := 0; i < s.NumField(); i++ { + value := s.Field(i) + valueField := s.Type().Field(i) + if strings.HasPrefix(valueField.Name, "XXX_") { + continue + } + + // IsNil will panic on most value kinds. + switch value.Kind() { + case reflect.Chan, reflect.Func, reflect.Interface: + if value.IsNil() { + continue + } + } + + if !m.EmitDefaults { + switch value.Kind() { + case reflect.Bool: + if !value.Bool() { + continue + } + case reflect.Int32, reflect.Int64: + if value.Int() == 0 { + continue + } + case reflect.Uint32, reflect.Uint64: + if value.Uint() == 0 { + continue + } + case reflect.Float32, reflect.Float64: + if value.Float() == 0 { + continue + } + case reflect.String: + if value.Len() == 0 { + continue + } + case reflect.Map, reflect.Ptr, reflect.Slice: + if value.IsNil() { + continue + } + } + } + + // Oneof fields need special handling. + if valueField.Tag.Get("protobuf_oneof") != "" { + // value is an interface containing &T{real_value}. + sv := value.Elem().Elem() // interface -> *T -> T + value = sv.Field(0) + valueField = sv.Type().Field(0) + } + prop := jsonProperties(valueField, m.OrigName) + if !firstField { + m.writeSep(out) + } + if err := m.marshalField(out, prop, value, indent); err != nil { + return err + } + firstField = false + } + + // Handle proto2 extensions. + if ep, ok := v.(proto.Message); ok { + extensions := proto.RegisteredExtensions(v) + // Sort extensions for stable output. + ids := make([]int32, 0, len(extensions)) + for id, desc := range extensions { + if !proto.HasExtension(ep, desc) { + continue + } + ids = append(ids, id) + } + sort.Sort(int32Slice(ids)) + for _, id := range ids { + desc := extensions[id] + if desc == nil { + // unknown extension + continue + } + ext, extErr := proto.GetExtension(ep, desc) + if extErr != nil { + return extErr + } + value := reflect.ValueOf(ext) + var prop proto.Properties + prop.Parse(desc.Tag) + prop.JSONName = fmt.Sprintf("[%s]", desc.Name) + if !firstField { + m.writeSep(out) + } + if err := m.marshalField(out, &prop, value, indent); err != nil { + return err + } + firstField = false + } + + } + + if m.Indent != "" { + out.write("\n") + out.write(indent) + } + out.write("}") + return out.err +} + +func (m *Marshaler) writeSep(out *errWriter) { + if m.Indent != "" { + out.write(",\n") + } else { + out.write(",") + } +} + +func (m *Marshaler) marshalAny(out *errWriter, any proto.Message, indent string) error { + // "If the Any contains a value that has a special JSON mapping, + // it will be converted as follows: {"@type": xxx, "value": yyy}. + // Otherwise, the value will be converted into a JSON object, + // and the "@type" field will be inserted to indicate the actual data type." + v := reflect.ValueOf(any).Elem() + turl := v.Field(0).String() + val := v.Field(1).Bytes() + + var msg proto.Message + var err error + if m.AnyResolver != nil { + msg, err = m.AnyResolver.Resolve(turl) + } else { + msg, err = defaultResolveAny(turl) + } + if err != nil { + return err + } + + if err := proto.Unmarshal(val, msg); err != nil { + return err + } + + if _, ok := msg.(wkt); ok { + out.write("{") + if m.Indent != "" { + out.write("\n") + } + if err := m.marshalTypeURL(out, indent, turl); err != nil { + return err + } + m.writeSep(out) + if m.Indent != "" { + out.write(indent) + out.write(m.Indent) + out.write(`"value": `) + } else { + out.write(`"value":`) + } + if err := m.marshalObject(out, msg, indent+m.Indent, ""); err != nil { + return err + } + if m.Indent != "" { + out.write("\n") + out.write(indent) + } + out.write("}") + return out.err + } + + return m.marshalObject(out, msg, indent, turl) +} + +func (m *Marshaler) marshalTypeURL(out *errWriter, indent, typeURL string) error { + if m.Indent != "" { + out.write(indent) + out.write(m.Indent) + } + out.write(`"@type":`) + if m.Indent != "" { + out.write(" ") + } + b, err := json.Marshal(typeURL) + if err != nil { + return err + } + out.write(string(b)) + return out.err +} + +// marshalField writes field description and value to the Writer. +func (m *Marshaler) marshalField(out *errWriter, prop *proto.Properties, v reflect.Value, indent string) error { + if m.Indent != "" { + out.write(indent) + out.write(m.Indent) + } + out.write(`"`) + out.write(prop.JSONName) + out.write(`":`) + if m.Indent != "" { + out.write(" ") + } + if err := m.marshalValue(out, prop, v, indent); err != nil { + return err + } + return nil +} + +// marshalValue writes the value to the Writer. +func (m *Marshaler) marshalValue(out *errWriter, prop *proto.Properties, v reflect.Value, indent string) error { + var err error + v = reflect.Indirect(v) + + // Handle nil pointer + if v.Kind() == reflect.Invalid { + out.write("null") + return out.err + } + + // Handle repeated elements. + if v.Kind() == reflect.Slice && v.Type().Elem().Kind() != reflect.Uint8 { + out.write("[") + comma := "" + for i := 0; i < v.Len(); i++ { + sliceVal := v.Index(i) + out.write(comma) + if m.Indent != "" { + out.write("\n") + out.write(indent) + out.write(m.Indent) + out.write(m.Indent) + } + if err := m.marshalValue(out, prop, sliceVal, indent+m.Indent); err != nil { + return err + } + comma = "," + } + if m.Indent != "" { + out.write("\n") + out.write(indent) + out.write(m.Indent) + } + out.write("]") + return out.err + } + + // Handle well-known types. + // Most are handled up in marshalObject (because 99% are messages). + if wkt, ok := v.Interface().(wkt); ok { + switch wkt.XXX_WellKnownType() { + case "NullValue": + out.write("null") + return out.err + } + } + + // Handle enumerations. + if !m.EnumsAsInts && prop.Enum != "" { + // Unknown enum values will are stringified by the proto library as their + // value. Such values should _not_ be quoted or they will be interpreted + // as an enum string instead of their value. + enumStr := v.Interface().(fmt.Stringer).String() + var valStr string + if v.Kind() == reflect.Ptr { + valStr = strconv.Itoa(int(v.Elem().Int())) + } else { + valStr = strconv.Itoa(int(v.Int())) + } + isKnownEnum := enumStr != valStr + if isKnownEnum { + out.write(`"`) + } + out.write(enumStr) + if isKnownEnum { + out.write(`"`) + } + return out.err + } + + // Handle nested messages. + if v.Kind() == reflect.Struct { + return m.marshalObject(out, v.Addr().Interface().(proto.Message), indent+m.Indent, "") + } + + // Handle maps. + // Since Go randomizes map iteration, we sort keys for stable output. + if v.Kind() == reflect.Map { + out.write(`{`) + keys := v.MapKeys() + sort.Sort(mapKeys(keys)) + for i, k := range keys { + if i > 0 { + out.write(`,`) + } + if m.Indent != "" { + out.write("\n") + out.write(indent) + out.write(m.Indent) + out.write(m.Indent) + } + + b, err := json.Marshal(k.Interface()) + if err != nil { + return err + } + s := string(b) + + // If the JSON is not a string value, encode it again to make it one. + if !strings.HasPrefix(s, `"`) { + b, err := json.Marshal(s) + if err != nil { + return err + } + s = string(b) + } + + out.write(s) + out.write(`:`) + if m.Indent != "" { + out.write(` `) + } + + if err := m.marshalValue(out, prop, v.MapIndex(k), indent+m.Indent); err != nil { + return err + } + } + if m.Indent != "" { + out.write("\n") + out.write(indent) + out.write(m.Indent) + } + out.write(`}`) + return out.err + } + + // Handle non-finite floats, e.g. NaN, Infinity and -Infinity. + if v.Kind() == reflect.Float32 || v.Kind() == reflect.Float64 { + f := v.Float() + var sval string + switch { + case math.IsInf(f, 1): + sval = `"Infinity"` + case math.IsInf(f, -1): + sval = `"-Infinity"` + case math.IsNaN(f): + sval = `"NaN"` + } + if sval != "" { + out.write(sval) + return out.err + } + } + + // Default handling defers to the encoding/json library. + b, err := json.Marshal(v.Interface()) + if err != nil { + return err + } + needToQuote := string(b[0]) != `"` && (v.Kind() == reflect.Int64 || v.Kind() == reflect.Uint64) + if needToQuote { + out.write(`"`) + } + out.write(string(b)) + if needToQuote { + out.write(`"`) + } + return out.err +} + +// Unmarshaler is a configurable object for converting from a JSON +// representation to a protocol buffer object. +type Unmarshaler struct { + // Whether to allow messages to contain unknown fields, as opposed to + // failing to unmarshal. + AllowUnknownFields bool + + // A custom URL resolver to use when unmarshaling Any messages from JSON. + // If unset, the default resolution strategy is to extract the + // fully-qualified type name from the type URL and pass that to + // proto.MessageType(string). + AnyResolver AnyResolver +} + +// UnmarshalNext unmarshals the next protocol buffer from a JSON object stream. +// This function is lenient and will decode any options permutations of the +// related Marshaler. +func (u *Unmarshaler) UnmarshalNext(dec *json.Decoder, pb proto.Message) error { + inputValue := json.RawMessage{} + if err := dec.Decode(&inputValue); err != nil { + return err + } + return u.unmarshalValue(reflect.ValueOf(pb).Elem(), inputValue, nil) +} + +// Unmarshal unmarshals a JSON object stream into a protocol +// buffer. This function is lenient and will decode any options +// permutations of the related Marshaler. +func (u *Unmarshaler) Unmarshal(r io.Reader, pb proto.Message) error { + dec := json.NewDecoder(r) + return u.UnmarshalNext(dec, pb) +} + +// UnmarshalNext unmarshals the next protocol buffer from a JSON object stream. +// This function is lenient and will decode any options permutations of the +// related Marshaler. +func UnmarshalNext(dec *json.Decoder, pb proto.Message) error { + return new(Unmarshaler).UnmarshalNext(dec, pb) +} + +// Unmarshal unmarshals a JSON object stream into a protocol +// buffer. This function is lenient and will decode any options +// permutations of the related Marshaler. +func Unmarshal(r io.Reader, pb proto.Message) error { + return new(Unmarshaler).Unmarshal(r, pb) +} + +// UnmarshalString will populate the fields of a protocol buffer based +// on a JSON string. This function is lenient and will decode any options +// permutations of the related Marshaler. +func UnmarshalString(str string, pb proto.Message) error { + return new(Unmarshaler).Unmarshal(strings.NewReader(str), pb) +} + +// unmarshalValue converts/copies a value into the target. +// prop may be nil. +func (u *Unmarshaler) unmarshalValue(target reflect.Value, inputValue json.RawMessage, prop *proto.Properties) error { + targetType := target.Type() + + // Allocate memory for pointer fields. + if targetType.Kind() == reflect.Ptr { + // If input value is "null" and target is a pointer type, then the field should be treated as not set + // UNLESS the target is structpb.Value, in which case it should be set to structpb.NullValue. + _, isJSONPBUnmarshaler := target.Interface().(JSONPBUnmarshaler) + if string(inputValue) == "null" && targetType != reflect.TypeOf(&stpb.Value{}) && !isJSONPBUnmarshaler { + return nil + } + target.Set(reflect.New(targetType.Elem())) + + return u.unmarshalValue(target.Elem(), inputValue, prop) + } + + if jsu, ok := target.Addr().Interface().(JSONPBUnmarshaler); ok { + return jsu.UnmarshalJSONPB(u, []byte(inputValue)) + } + + // Handle well-known types that are not pointers. + if w, ok := target.Addr().Interface().(wkt); ok { + switch w.XXX_WellKnownType() { + case "DoubleValue", "FloatValue", "Int64Value", "UInt64Value", + "Int32Value", "UInt32Value", "BoolValue", "StringValue", "BytesValue": + return u.unmarshalValue(target.Field(0), inputValue, prop) + case "Any": + // Use json.RawMessage pointer type instead of value to support pre-1.8 version. + // 1.8 changed RawMessage.MarshalJSON from pointer type to value type, see + // https://github.com/golang/go/issues/14493 + var jsonFields map[string]*json.RawMessage + if err := json.Unmarshal(inputValue, &jsonFields); err != nil { + return err + } + + val, ok := jsonFields["@type"] + if !ok || val == nil { + return errors.New("Any JSON doesn't have '@type'") + } + + var turl string + if err := json.Unmarshal([]byte(*val), &turl); err != nil { + return fmt.Errorf("can't unmarshal Any's '@type': %q", *val) + } + target.Field(0).SetString(turl) + + var m proto.Message + var err error + if u.AnyResolver != nil { + m, err = u.AnyResolver.Resolve(turl) + } else { + m, err = defaultResolveAny(turl) + } + if err != nil { + return err + } + + if _, ok := m.(wkt); ok { + val, ok := jsonFields["value"] + if !ok { + return errors.New("Any JSON doesn't have 'value'") + } + + if err := u.unmarshalValue(reflect.ValueOf(m).Elem(), *val, nil); err != nil { + return fmt.Errorf("can't unmarshal Any nested proto %T: %v", m, err) + } + } else { + delete(jsonFields, "@type") + nestedProto, err := json.Marshal(jsonFields) + if err != nil { + return fmt.Errorf("can't generate JSON for Any's nested proto to be unmarshaled: %v", err) + } + + if err = u.unmarshalValue(reflect.ValueOf(m).Elem(), nestedProto, nil); err != nil { + return fmt.Errorf("can't unmarshal Any nested proto %T: %v", m, err) + } + } + + b, err := proto.Marshal(m) + if err != nil { + return fmt.Errorf("can't marshal proto %T into Any.Value: %v", m, err) + } + target.Field(1).SetBytes(b) + + return nil + case "Duration": + unq, err := strconv.Unquote(string(inputValue)) + if err != nil { + return err + } + + d, err := time.ParseDuration(unq) + if err != nil { + return fmt.Errorf("bad Duration: %v", err) + } + + ns := d.Nanoseconds() + s := ns / 1e9 + ns %= 1e9 + target.Field(0).SetInt(s) + target.Field(1).SetInt(ns) + return nil + case "Timestamp": + unq, err := strconv.Unquote(string(inputValue)) + if err != nil { + return err + } + + t, err := time.Parse(time.RFC3339Nano, unq) + if err != nil { + return fmt.Errorf("bad Timestamp: %v", err) + } + + target.Field(0).SetInt(t.Unix()) + target.Field(1).SetInt(int64(t.Nanosecond())) + return nil + case "Struct": + var m map[string]json.RawMessage + if err := json.Unmarshal(inputValue, &m); err != nil { + return fmt.Errorf("bad StructValue: %v", err) + } + + target.Field(0).Set(reflect.ValueOf(map[string]*stpb.Value{})) + for k, jv := range m { + pv := &stpb.Value{} + if err := u.unmarshalValue(reflect.ValueOf(pv).Elem(), jv, prop); err != nil { + return fmt.Errorf("bad value in StructValue for key %q: %v", k, err) + } + target.Field(0).SetMapIndex(reflect.ValueOf(k), reflect.ValueOf(pv)) + } + return nil + case "ListValue": + var s []json.RawMessage + if err := json.Unmarshal(inputValue, &s); err != nil { + return fmt.Errorf("bad ListValue: %v", err) + } + + target.Field(0).Set(reflect.ValueOf(make([]*stpb.Value, len(s), len(s)))) + for i, sv := range s { + if err := u.unmarshalValue(target.Field(0).Index(i), sv, prop); err != nil { + return err + } + } + return nil + case "Value": + ivStr := string(inputValue) + if ivStr == "null" { + target.Field(0).Set(reflect.ValueOf(&stpb.Value_NullValue{})) + } else if v, err := strconv.ParseFloat(ivStr, 0); err == nil { + target.Field(0).Set(reflect.ValueOf(&stpb.Value_NumberValue{v})) + } else if v, err := strconv.Unquote(ivStr); err == nil { + target.Field(0).Set(reflect.ValueOf(&stpb.Value_StringValue{v})) + } else if v, err := strconv.ParseBool(ivStr); err == nil { + target.Field(0).Set(reflect.ValueOf(&stpb.Value_BoolValue{v})) + } else if err := json.Unmarshal(inputValue, &[]json.RawMessage{}); err == nil { + lv := &stpb.ListValue{} + target.Field(0).Set(reflect.ValueOf(&stpb.Value_ListValue{lv})) + return u.unmarshalValue(reflect.ValueOf(lv).Elem(), inputValue, prop) + } else if err := json.Unmarshal(inputValue, &map[string]json.RawMessage{}); err == nil { + sv := &stpb.Struct{} + target.Field(0).Set(reflect.ValueOf(&stpb.Value_StructValue{sv})) + return u.unmarshalValue(reflect.ValueOf(sv).Elem(), inputValue, prop) + } else { + return fmt.Errorf("unrecognized type for Value %q", ivStr) + } + return nil + } + } + + // Handle enums, which have an underlying type of int32, + // and may appear as strings. + // The case of an enum appearing as a number is handled + // at the bottom of this function. + if inputValue[0] == '"' && prop != nil && prop.Enum != "" { + vmap := proto.EnumValueMap(prop.Enum) + // Don't need to do unquoting; valid enum names + // are from a limited character set. + s := inputValue[1 : len(inputValue)-1] + n, ok := vmap[string(s)] + if !ok { + return fmt.Errorf("unknown value %q for enum %s", s, prop.Enum) + } + if target.Kind() == reflect.Ptr { // proto2 + target.Set(reflect.New(targetType.Elem())) + target = target.Elem() + } + target.SetInt(int64(n)) + return nil + } + + // Handle nested messages. + if targetType.Kind() == reflect.Struct { + var jsonFields map[string]json.RawMessage + if err := json.Unmarshal(inputValue, &jsonFields); err != nil { + return err + } + + consumeField := func(prop *proto.Properties) (json.RawMessage, bool) { + // Be liberal in what names we accept; both orig_name and camelName are okay. + fieldNames := acceptedJSONFieldNames(prop) + + vOrig, okOrig := jsonFields[fieldNames.orig] + vCamel, okCamel := jsonFields[fieldNames.camel] + if !okOrig && !okCamel { + return nil, false + } + // If, for some reason, both are present in the data, favour the camelName. + var raw json.RawMessage + if okOrig { + raw = vOrig + delete(jsonFields, fieldNames.orig) + } + if okCamel { + raw = vCamel + delete(jsonFields, fieldNames.camel) + } + return raw, true + } + + sprops := proto.GetProperties(targetType) + for i := 0; i < target.NumField(); i++ { + ft := target.Type().Field(i) + if strings.HasPrefix(ft.Name, "XXX_") { + continue + } + + valueForField, ok := consumeField(sprops.Prop[i]) + if !ok { + continue + } + + if err := u.unmarshalValue(target.Field(i), valueForField, sprops.Prop[i]); err != nil { + return err + } + } + // Check for any oneof fields. + if len(jsonFields) > 0 { + for _, oop := range sprops.OneofTypes { + raw, ok := consumeField(oop.Prop) + if !ok { + continue + } + nv := reflect.New(oop.Type.Elem()) + target.Field(oop.Field).Set(nv) + if err := u.unmarshalValue(nv.Elem().Field(0), raw, oop.Prop); err != nil { + return err + } + } + } + // Handle proto2 extensions. + if len(jsonFields) > 0 { + if ep, ok := target.Addr().Interface().(proto.Message); ok { + for _, ext := range proto.RegisteredExtensions(ep) { + name := fmt.Sprintf("[%s]", ext.Name) + raw, ok := jsonFields[name] + if !ok { + continue + } + delete(jsonFields, name) + nv := reflect.New(reflect.TypeOf(ext.ExtensionType).Elem()) + if err := u.unmarshalValue(nv.Elem(), raw, nil); err != nil { + return err + } + if err := proto.SetExtension(ep, ext, nv.Interface()); err != nil { + return err + } + } + } + } + if !u.AllowUnknownFields && len(jsonFields) > 0 { + // Pick any field to be the scapegoat. + var f string + for fname := range jsonFields { + f = fname + break + } + return fmt.Errorf("unknown field %q in %v", f, targetType) + } + return nil + } + + // Handle arrays (which aren't encoded bytes) + if targetType.Kind() == reflect.Slice && targetType.Elem().Kind() != reflect.Uint8 { + var slc []json.RawMessage + if err := json.Unmarshal(inputValue, &slc); err != nil { + return err + } + if slc != nil { + l := len(slc) + target.Set(reflect.MakeSlice(targetType, l, l)) + for i := 0; i < l; i++ { + if err := u.unmarshalValue(target.Index(i), slc[i], prop); err != nil { + return err + } + } + } + return nil + } + + // Handle maps (whose keys are always strings) + if targetType.Kind() == reflect.Map { + var mp map[string]json.RawMessage + if err := json.Unmarshal(inputValue, &mp); err != nil { + return err + } + if mp != nil { + target.Set(reflect.MakeMap(targetType)) + var keyprop, valprop *proto.Properties + if prop != nil { + // These could still be nil if the protobuf metadata is broken somehow. + // TODO: This won't work because the fields are unexported. + // We should probably just reparse them. + //keyprop, valprop = prop.mkeyprop, prop.mvalprop + } + for ks, raw := range mp { + // Unmarshal map key. The core json library already decoded the key into a + // string, so we handle that specially. Other types were quoted post-serialization. + var k reflect.Value + if targetType.Key().Kind() == reflect.String { + k = reflect.ValueOf(ks) + } else { + k = reflect.New(targetType.Key()).Elem() + if err := u.unmarshalValue(k, json.RawMessage(ks), keyprop); err != nil { + return err + } + } + + // Unmarshal map value. + v := reflect.New(targetType.Elem()).Elem() + if err := u.unmarshalValue(v, raw, valprop); err != nil { + return err + } + target.SetMapIndex(k, v) + } + } + return nil + } + + // 64-bit integers can be encoded as strings. In this case we drop + // the quotes and proceed as normal. + isNum := targetType.Kind() == reflect.Int64 || targetType.Kind() == reflect.Uint64 + if isNum && strings.HasPrefix(string(inputValue), `"`) { + inputValue = inputValue[1 : len(inputValue)-1] + } + + // Non-finite numbers can be encoded as strings. + isFloat := targetType.Kind() == reflect.Float32 || targetType.Kind() == reflect.Float64 + if isFloat { + if num, ok := nonFinite[string(inputValue)]; ok { + target.SetFloat(num) + return nil + } + } + + // Use the encoding/json for parsing other value types. + return json.Unmarshal(inputValue, target.Addr().Interface()) +} + +// jsonProperties returns parsed proto.Properties for the field and corrects JSONName attribute. +func jsonProperties(f reflect.StructField, origName bool) *proto.Properties { + var prop proto.Properties + prop.Init(f.Type, f.Name, f.Tag.Get("protobuf"), &f) + if origName || prop.JSONName == "" { + prop.JSONName = prop.OrigName + } + return &prop +} + +type fieldNames struct { + orig, camel string +} + +func acceptedJSONFieldNames(prop *proto.Properties) fieldNames { + opts := fieldNames{orig: prop.OrigName, camel: prop.OrigName} + if prop.JSONName != "" { + opts.camel = prop.JSONName + } + return opts +} + +// Writer wrapper inspired by https://blog.golang.org/errors-are-values +type errWriter struct { + writer io.Writer + err error +} + +func (w *errWriter) write(str string) { + if w.err != nil { + return + } + _, w.err = w.writer.Write([]byte(str)) +} + +// Map fields may have key types of non-float scalars, strings and enums. +// The easiest way to sort them in some deterministic order is to use fmt. +// If this turns out to be inefficient we can always consider other options, +// such as doing a Schwartzian transform. +// +// Numeric keys are sorted in numeric order per +// https://developers.google.com/protocol-buffers/docs/proto#maps. +type mapKeys []reflect.Value + +func (s mapKeys) Len() int { return len(s) } +func (s mapKeys) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s mapKeys) Less(i, j int) bool { + if k := s[i].Kind(); k == s[j].Kind() { + switch k { + case reflect.Int32, reflect.Int64: + return s[i].Int() < s[j].Int() + case reflect.Uint32, reflect.Uint64: + return s[i].Uint() < s[j].Uint() + } + } + return fmt.Sprint(s[i].Interface()) < fmt.Sprint(s[j].Interface()) +} diff --git a/vendor/github.com/golang/protobuf/ptypes/struct/struct.pb.go b/vendor/github.com/golang/protobuf/ptypes/struct/struct.pb.go new file mode 100644 index 00000000..4cfe6081 --- /dev/null +++ b/vendor/github.com/golang/protobuf/ptypes/struct/struct.pb.go @@ -0,0 +1,380 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: google/protobuf/struct.proto + +/* +Package structpb is a generated protocol buffer package. + +It is generated from these files: + google/protobuf/struct.proto + +It has these top-level messages: + Struct + Value + ListValue +*/ +package structpb + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +// `NullValue` is a singleton enumeration to represent the null value for the +// `Value` type union. +// +// The JSON representation for `NullValue` is JSON `null`. +type NullValue int32 + +const ( + // Null value. + NullValue_NULL_VALUE NullValue = 0 +) + +var NullValue_name = map[int32]string{ + 0: "NULL_VALUE", +} +var NullValue_value = map[string]int32{ + "NULL_VALUE": 0, +} + +func (x NullValue) String() string { + return proto.EnumName(NullValue_name, int32(x)) +} +func (NullValue) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } +func (NullValue) XXX_WellKnownType() string { return "NullValue" } + +// `Struct` represents a structured data value, consisting of fields +// which map to dynamically typed values. In some languages, `Struct` +// might be supported by a native representation. For example, in +// scripting languages like JS a struct is represented as an +// object. The details of that representation are described together +// with the proto support for the language. +// +// The JSON representation for `Struct` is JSON object. +type Struct struct { + // Unordered map of dynamically typed values. + Fields map[string]*Value `protobuf:"bytes,1,rep,name=fields" json:"fields,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` +} + +func (m *Struct) Reset() { *m = Struct{} } +func (m *Struct) String() string { return proto.CompactTextString(m) } +func (*Struct) ProtoMessage() {} +func (*Struct) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } +func (*Struct) XXX_WellKnownType() string { return "Struct" } + +func (m *Struct) GetFields() map[string]*Value { + if m != nil { + return m.Fields + } + return nil +} + +// `Value` represents a dynamically typed value which can be either +// null, a number, a string, a boolean, a recursive struct value, or a +// list of values. A producer of value is expected to set one of that +// variants, absence of any variant indicates an error. +// +// The JSON representation for `Value` is JSON value. +type Value struct { + // The kind of value. + // + // Types that are valid to be assigned to Kind: + // *Value_NullValue + // *Value_NumberValue + // *Value_StringValue + // *Value_BoolValue + // *Value_StructValue + // *Value_ListValue + Kind isValue_Kind `protobuf_oneof:"kind"` +} + +func (m *Value) Reset() { *m = Value{} } +func (m *Value) String() string { return proto.CompactTextString(m) } +func (*Value) ProtoMessage() {} +func (*Value) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } +func (*Value) XXX_WellKnownType() string { return "Value" } + +type isValue_Kind interface { + isValue_Kind() +} + +type Value_NullValue struct { + NullValue NullValue `protobuf:"varint,1,opt,name=null_value,json=nullValue,enum=google.protobuf.NullValue,oneof"` +} +type Value_NumberValue struct { + NumberValue float64 `protobuf:"fixed64,2,opt,name=number_value,json=numberValue,oneof"` +} +type Value_StringValue struct { + StringValue string `protobuf:"bytes,3,opt,name=string_value,json=stringValue,oneof"` +} +type Value_BoolValue struct { + BoolValue bool `protobuf:"varint,4,opt,name=bool_value,json=boolValue,oneof"` +} +type Value_StructValue struct { + StructValue *Struct `protobuf:"bytes,5,opt,name=struct_value,json=structValue,oneof"` +} +type Value_ListValue struct { + ListValue *ListValue `protobuf:"bytes,6,opt,name=list_value,json=listValue,oneof"` +} + +func (*Value_NullValue) isValue_Kind() {} +func (*Value_NumberValue) isValue_Kind() {} +func (*Value_StringValue) isValue_Kind() {} +func (*Value_BoolValue) isValue_Kind() {} +func (*Value_StructValue) isValue_Kind() {} +func (*Value_ListValue) isValue_Kind() {} + +func (m *Value) GetKind() isValue_Kind { + if m != nil { + return m.Kind + } + return nil +} + +func (m *Value) GetNullValue() NullValue { + if x, ok := m.GetKind().(*Value_NullValue); ok { + return x.NullValue + } + return NullValue_NULL_VALUE +} + +func (m *Value) GetNumberValue() float64 { + if x, ok := m.GetKind().(*Value_NumberValue); ok { + return x.NumberValue + } + return 0 +} + +func (m *Value) GetStringValue() string { + if x, ok := m.GetKind().(*Value_StringValue); ok { + return x.StringValue + } + return "" +} + +func (m *Value) GetBoolValue() bool { + if x, ok := m.GetKind().(*Value_BoolValue); ok { + return x.BoolValue + } + return false +} + +func (m *Value) GetStructValue() *Struct { + if x, ok := m.GetKind().(*Value_StructValue); ok { + return x.StructValue + } + return nil +} + +func (m *Value) GetListValue() *ListValue { + if x, ok := m.GetKind().(*Value_ListValue); ok { + return x.ListValue + } + return nil +} + +// XXX_OneofFuncs is for the internal use of the proto package. +func (*Value) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) { + return _Value_OneofMarshaler, _Value_OneofUnmarshaler, _Value_OneofSizer, []interface{}{ + (*Value_NullValue)(nil), + (*Value_NumberValue)(nil), + (*Value_StringValue)(nil), + (*Value_BoolValue)(nil), + (*Value_StructValue)(nil), + (*Value_ListValue)(nil), + } +} + +func _Value_OneofMarshaler(msg proto.Message, b *proto.Buffer) error { + m := msg.(*Value) + // kind + switch x := m.Kind.(type) { + case *Value_NullValue: + b.EncodeVarint(1<<3 | proto.WireVarint) + b.EncodeVarint(uint64(x.NullValue)) + case *Value_NumberValue: + b.EncodeVarint(2<<3 | proto.WireFixed64) + b.EncodeFixed64(math.Float64bits(x.NumberValue)) + case *Value_StringValue: + b.EncodeVarint(3<<3 | proto.WireBytes) + b.EncodeStringBytes(x.StringValue) + case *Value_BoolValue: + t := uint64(0) + if x.BoolValue { + t = 1 + } + b.EncodeVarint(4<<3 | proto.WireVarint) + b.EncodeVarint(t) + case *Value_StructValue: + b.EncodeVarint(5<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.StructValue); err != nil { + return err + } + case *Value_ListValue: + b.EncodeVarint(6<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.ListValue); err != nil { + return err + } + case nil: + default: + return fmt.Errorf("Value.Kind has unexpected type %T", x) + } + return nil +} + +func _Value_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) { + m := msg.(*Value) + switch tag { + case 1: // kind.null_value + if wire != proto.WireVarint { + return true, proto.ErrInternalBadWireType + } + x, err := b.DecodeVarint() + m.Kind = &Value_NullValue{NullValue(x)} + return true, err + case 2: // kind.number_value + if wire != proto.WireFixed64 { + return true, proto.ErrInternalBadWireType + } + x, err := b.DecodeFixed64() + m.Kind = &Value_NumberValue{math.Float64frombits(x)} + return true, err + case 3: // kind.string_value + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + x, err := b.DecodeStringBytes() + m.Kind = &Value_StringValue{x} + return true, err + case 4: // kind.bool_value + if wire != proto.WireVarint { + return true, proto.ErrInternalBadWireType + } + x, err := b.DecodeVarint() + m.Kind = &Value_BoolValue{x != 0} + return true, err + case 5: // kind.struct_value + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(Struct) + err := b.DecodeMessage(msg) + m.Kind = &Value_StructValue{msg} + return true, err + case 6: // kind.list_value + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(ListValue) + err := b.DecodeMessage(msg) + m.Kind = &Value_ListValue{msg} + return true, err + default: + return false, nil + } +} + +func _Value_OneofSizer(msg proto.Message) (n int) { + m := msg.(*Value) + // kind + switch x := m.Kind.(type) { + case *Value_NullValue: + n += proto.SizeVarint(1<<3 | proto.WireVarint) + n += proto.SizeVarint(uint64(x.NullValue)) + case *Value_NumberValue: + n += proto.SizeVarint(2<<3 | proto.WireFixed64) + n += 8 + case *Value_StringValue: + n += proto.SizeVarint(3<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(len(x.StringValue))) + n += len(x.StringValue) + case *Value_BoolValue: + n += proto.SizeVarint(4<<3 | proto.WireVarint) + n += 1 + case *Value_StructValue: + s := proto.Size(x.StructValue) + n += proto.SizeVarint(5<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case *Value_ListValue: + s := proto.Size(x.ListValue) + n += proto.SizeVarint(6<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case nil: + default: + panic(fmt.Sprintf("proto: unexpected type %T in oneof", x)) + } + return n +} + +// `ListValue` is a wrapper around a repeated field of values. +// +// The JSON representation for `ListValue` is JSON array. +type ListValue struct { + // Repeated field of dynamically typed values. + Values []*Value `protobuf:"bytes,1,rep,name=values" json:"values,omitempty"` +} + +func (m *ListValue) Reset() { *m = ListValue{} } +func (m *ListValue) String() string { return proto.CompactTextString(m) } +func (*ListValue) ProtoMessage() {} +func (*ListValue) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } +func (*ListValue) XXX_WellKnownType() string { return "ListValue" } + +func (m *ListValue) GetValues() []*Value { + if m != nil { + return m.Values + } + return nil +} + +func init() { + proto.RegisterType((*Struct)(nil), "google.protobuf.Struct") + proto.RegisterType((*Value)(nil), "google.protobuf.Value") + proto.RegisterType((*ListValue)(nil), "google.protobuf.ListValue") + proto.RegisterEnum("google.protobuf.NullValue", NullValue_name, NullValue_value) +} + +func init() { proto.RegisterFile("google/protobuf/struct.proto", fileDescriptor0) } + +var fileDescriptor0 = []byte{ + // 417 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x92, 0x41, 0x8b, 0xd3, 0x40, + 0x14, 0xc7, 0x3b, 0xc9, 0x36, 0x98, 0x17, 0x59, 0x97, 0x11, 0xb4, 0xac, 0xa2, 0xa1, 0x7b, 0x09, + 0x22, 0x29, 0xd6, 0x8b, 0x18, 0x2f, 0x06, 0xd6, 0x5d, 0x30, 0x2c, 0x31, 0xba, 0x15, 0xbc, 0x94, + 0x26, 0x4d, 0x63, 0xe8, 0x74, 0x26, 0x24, 0x33, 0x4a, 0x8f, 0x7e, 0x0b, 0xcf, 0x1e, 0x3d, 0xfa, + 0xe9, 0x3c, 0xca, 0xcc, 0x24, 0xa9, 0xb4, 0xf4, 0x94, 0xbc, 0xf7, 0x7e, 0xef, 0x3f, 0xef, 0xff, + 0x66, 0xe0, 0x71, 0xc1, 0x58, 0x41, 0xf2, 0x49, 0x55, 0x33, 0xce, 0x52, 0xb1, 0x9a, 0x34, 0xbc, + 0x16, 0x19, 0xf7, 0x55, 0x8c, 0xef, 0xe9, 0xaa, 0xdf, 0x55, 0xc7, 0x3f, 0x11, 0x58, 0x1f, 0x15, + 0x81, 0x03, 0xb0, 0x56, 0x65, 0x4e, 0x96, 0xcd, 0x08, 0xb9, 0xa6, 0xe7, 0x4c, 0x2f, 0xfc, 0x3d, + 0xd8, 0xd7, 0xa0, 0xff, 0x4e, 0x51, 0x97, 0x94, 0xd7, 0xdb, 0xa4, 0x6d, 0x39, 0xff, 0x00, 0xce, + 0x7f, 0x69, 0x7c, 0x06, 0xe6, 0x3a, 0xdf, 0x8e, 0x90, 0x8b, 0x3c, 0x3b, 0x91, 0xbf, 0xf8, 0x39, + 0x0c, 0xbf, 0x2d, 0x88, 0xc8, 0x47, 0x86, 0x8b, 0x3c, 0x67, 0xfa, 0xe0, 0x40, 0x7c, 0x26, 0xab, + 0x89, 0x86, 0x5e, 0x1b, 0xaf, 0xd0, 0xf8, 0x8f, 0x01, 0x43, 0x95, 0xc4, 0x01, 0x00, 0x15, 0x84, + 0xcc, 0xb5, 0x80, 0x14, 0x3d, 0x9d, 0x9e, 0x1f, 0x08, 0xdc, 0x08, 0x42, 0x14, 0x7f, 0x3d, 0x48, + 0x6c, 0xda, 0x05, 0xf8, 0x02, 0xee, 0x52, 0xb1, 0x49, 0xf3, 0x7a, 0xbe, 0x3b, 0x1f, 0x5d, 0x0f, + 0x12, 0x47, 0x67, 0x7b, 0xa8, 0xe1, 0x75, 0x49, 0x8b, 0x16, 0x32, 0xe5, 0xe0, 0x12, 0xd2, 0x59, + 0x0d, 0x3d, 0x05, 0x48, 0x19, 0xeb, 0xc6, 0x38, 0x71, 0x91, 0x77, 0x47, 0x1e, 0x25, 0x73, 0x1a, + 0x78, 0xa3, 0x54, 0x44, 0xc6, 0x5b, 0x64, 0xa8, 0xac, 0x3e, 0x3c, 0xb2, 0xc7, 0x56, 0x5e, 0x64, + 0xbc, 0x77, 0x49, 0xca, 0xa6, 0xeb, 0xb5, 0x54, 0xef, 0xa1, 0xcb, 0xa8, 0x6c, 0x78, 0xef, 0x92, + 0x74, 0x41, 0x68, 0xc1, 0xc9, 0xba, 0xa4, 0xcb, 0x71, 0x00, 0x76, 0x4f, 0x60, 0x1f, 0x2c, 0x25, + 0xd6, 0xdd, 0xe8, 0xb1, 0xa5, 0xb7, 0xd4, 0xb3, 0x47, 0x60, 0xf7, 0x4b, 0xc4, 0xa7, 0x00, 0x37, + 0xb7, 0x51, 0x34, 0x9f, 0xbd, 0x8d, 0x6e, 0x2f, 0xcf, 0x06, 0xe1, 0x0f, 0x04, 0xf7, 0x33, 0xb6, + 0xd9, 0x97, 0x08, 0x1d, 0xed, 0x26, 0x96, 0x71, 0x8c, 0xbe, 0xbc, 0x28, 0x4a, 0xfe, 0x55, 0xa4, + 0x7e, 0xc6, 0x36, 0x93, 0x82, 0x91, 0x05, 0x2d, 0x76, 0x4f, 0xb1, 0xe2, 0xdb, 0x2a, 0x6f, 0xda, + 0x17, 0x19, 0xe8, 0x4f, 0x95, 0xfe, 0x45, 0xe8, 0x97, 0x61, 0x5e, 0xc5, 0xe1, 0x6f, 0xe3, 0xc9, + 0x95, 0x16, 0x8f, 0xbb, 0xf9, 0x3e, 0xe7, 0x84, 0xbc, 0xa7, 0xec, 0x3b, 0xfd, 0x24, 0x3b, 0x53, + 0x4b, 0x49, 0xbd, 0xfc, 0x17, 0x00, 0x00, 0xff, 0xff, 0xe8, 0x1b, 0x59, 0xf8, 0xe5, 0x02, 0x00, + 0x00, +} diff --git a/vendor/github.com/golang/protobuf/ptypes/struct/struct.proto b/vendor/github.com/golang/protobuf/ptypes/struct/struct.proto new file mode 100644 index 00000000..7d7808e7 --- /dev/null +++ b/vendor/github.com/golang/protobuf/ptypes/struct/struct.proto @@ -0,0 +1,96 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option cc_enable_arenas = true; +option go_package = "github.com/golang/protobuf/ptypes/struct;structpb"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "StructProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; + + +// `Struct` represents a structured data value, consisting of fields +// which map to dynamically typed values. In some languages, `Struct` +// might be supported by a native representation. For example, in +// scripting languages like JS a struct is represented as an +// object. The details of that representation are described together +// with the proto support for the language. +// +// The JSON representation for `Struct` is JSON object. +message Struct { + // Unordered map of dynamically typed values. + map<string, Value> fields = 1; +} + +// `Value` represents a dynamically typed value which can be either +// null, a number, a string, a boolean, a recursive struct value, or a +// list of values. A producer of value is expected to set one of that +// variants, absence of any variant indicates an error. +// +// The JSON representation for `Value` is JSON value. +message Value { + // The kind of value. + oneof kind { + // Represents a null value. + NullValue null_value = 1; + // Represents a double value. + double number_value = 2; + // Represents a string value. + string string_value = 3; + // Represents a boolean value. + bool bool_value = 4; + // Represents a structured value. + Struct struct_value = 5; + // Represents a repeated `Value`. + ListValue list_value = 6; + } +} + +// `NullValue` is a singleton enumeration to represent the null value for the +// `Value` type union. +// +// The JSON representation for `NullValue` is JSON `null`. +enum NullValue { + // Null value. + NULL_VALUE = 0; +} + +// `ListValue` is a wrapper around a repeated field of values. +// +// The JSON representation for `ListValue` is JSON array. +message ListValue { + // Repeated field of dynamically typed values. + repeated Value values = 1; +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/DOC.md b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/DOC.md new file mode 100644 index 00000000..511d953a --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/DOC.md @@ -0,0 +1,166 @@ +# grpc_middleware +`import "github.com/grpc-ecosystem/go-grpc-middleware"` + +* [Overview](#pkg-overview) +* [Imported Packages](#pkg-imports) +* [Index](#pkg-index) + +## <a name="pkg-overview">Overview</a> +`grpc_middleware` is a collection of gRPC middleware packages: interceptors, helpers and tools. + +### Middleware +gRPC is a fantastic RPC middleware, which sees a lot of adoption in the Golang world. However, the +upstream gRPC codebase is relatively bare bones. + +This package, and most of its child packages provides commonly needed middleware for gRPC: +client-side interceptors for retires, server-side interceptors for input validation and auth, +functions for chaining said interceptors, metadata convenience methods and more. + +### Chaining +By default, gRPC doesn't allow one to have more than one interceptor either on the client nor on +the server side. `grpc_middleware` provides convenient chaining methods + +Simple way of turning a multiple interceptors into a single interceptor. Here's an example for +server chaining: + + myServer := grpc.NewServer( + grpc.StreamInterceptor(grpc_middleware.ChainStreamServer(loggingStream, monitoringStream, authStream)), + grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(loggingUnary, monitoringUnary, authUnary), + ) + +These interceptors will be executed from left to right: logging, monitoring and auth. + +Here's an example for client side chaining: + + clientConn, err = grpc.Dial( + address, + grpc.WithUnaryInterceptor(grpc_middleware.ChainUnaryClient(monitoringClientUnary, retryUnary)), + grpc.WithStreamInterceptor(grpc_middleware.ChainStreamClient(monitoringClientStream, retryStream)), + ) + client = pb_testproto.NewTestServiceClient(clientConn) + resp, err := client.PingEmpty(s.ctx, &myservice.Request{Msg: "hello"}) + +These interceptors will be executed from left to right: monitoring and then retry logic. + +The retry interceptor will call every interceptor that follows it whenever when a retry happens. + +### Writing Your Own +Implementing your own interceptor is pretty trivial: there are interfaces for that. But the interesting +bit exposing common data to handlers (and other middleware), similarly to HTTP Middleware design. +For example, you may want to pass the identity of the caller from the auth interceptor all the way +to the handling function. + +For example, a client side interceptor example for auth looks like: + + func FakeAuthUnaryInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { + newCtx := context.WithValue(ctx, "user_id", "john@example.com") + return handler(newCtx, req) + } + +Unfortunately, it's not as easy for streaming RPCs. These have the `context.Context` embedded within +the `grpc.ServerStream` object. To pass values through context, a wrapper (`WrappedServerStream`) is +needed. For example: + + func FakeAuthStreamingInterceptor(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { + newStream := grpc_middleware.WrapServerStream(stream) + newStream.WrappedContext = context.WithValue(ctx, "user_id", "john@example.com") + return handler(srv, stream) + } + +## <a name="pkg-imports">Imported Packages</a> + +- [golang.org/x/net/context](https://godoc.org/golang.org/x/net/context) +- [google.golang.org/grpc](https://godoc.org/google.golang.org/grpc) + +## <a name="pkg-index">Index</a> +* [func ChainStreamClient(interceptors ...grpc.StreamClientInterceptor) grpc.StreamClientInterceptor](#ChainStreamClient) +* [func ChainStreamServer(interceptors ...grpc.StreamServerInterceptor) grpc.StreamServerInterceptor](#ChainStreamServer) +* [func ChainUnaryClient(interceptors ...grpc.UnaryClientInterceptor) grpc.UnaryClientInterceptor](#ChainUnaryClient) +* [func ChainUnaryServer(interceptors ...grpc.UnaryServerInterceptor) grpc.UnaryServerInterceptor](#ChainUnaryServer) +* [func WithStreamServerChain(interceptors ...grpc.StreamServerInterceptor) grpc.ServerOption](#WithStreamServerChain) +* [func WithUnaryServerChain(interceptors ...grpc.UnaryServerInterceptor) grpc.ServerOption](#WithUnaryServerChain) +* [type WrappedServerStream](#WrappedServerStream) + * [func WrapServerStream(stream grpc.ServerStream) \*WrappedServerStream](#WrapServerStream) + * [func (w \*WrappedServerStream) Context() context.Context](#WrappedServerStream.Context) + +#### <a name="pkg-files">Package files</a> +[chain.go](./chain.go) [doc.go](./doc.go) [wrappers.go](./wrappers.go) + +## <a name="ChainStreamClient">func</a> [ChainStreamClient](./chain.go#L136) +``` go +func ChainStreamClient(interceptors ...grpc.StreamClientInterceptor) grpc.StreamClientInterceptor +``` +ChainStreamClient creates a single interceptor out of a chain of many interceptors. + +Execution is done in left-to-right order, including passing of context. +For example ChainStreamClient(one, two, three) will execute one before two before three. + +## <a name="ChainStreamServer">func</a> [ChainStreamServer](./chain.go#L58) +``` go +func ChainStreamServer(interceptors ...grpc.StreamServerInterceptor) grpc.StreamServerInterceptor +``` +ChainStreamServer creates a single interceptor out of a chain of many interceptors. + +Execution is done in left-to-right order, including passing of context. +For example ChainUnaryServer(one, two, three) will execute one before two before three. +If you want to pass context between interceptors, use WrapServerStream. + +## <a name="ChainUnaryClient">func</a> [ChainUnaryClient](./chain.go#L97) +``` go +func ChainUnaryClient(interceptors ...grpc.UnaryClientInterceptor) grpc.UnaryClientInterceptor +``` +ChainUnaryClient creates a single interceptor out of a chain of many interceptors. + +Execution is done in left-to-right order, including passing of context. +For example ChainUnaryClient(one, two, three) will execute one before two before three. + +## <a name="ChainUnaryServer">func</a> [ChainUnaryServer](./chain.go#L18) +``` go +func ChainUnaryServer(interceptors ...grpc.UnaryServerInterceptor) grpc.UnaryServerInterceptor +``` +ChainUnaryServer creates a single interceptor out of a chain of many interceptors. + +Execution is done in left-to-right order, including passing of context. +For example ChainUnaryServer(one, two, three) will execute one before two before three, and three +will see context changes of one and two. + +## <a name="WithStreamServerChain">func</a> [WithStreamServerChain](./chain.go#L181) +``` go +func WithStreamServerChain(interceptors ...grpc.StreamServerInterceptor) grpc.ServerOption +``` +WithStreamServerChain is a grpc.Server config option that accepts multiple stream interceptors. +Basically syntactic sugar. + +## <a name="WithUnaryServerChain">func</a> [WithUnaryServerChain](./chain.go#L175) +``` go +func WithUnaryServerChain(interceptors ...grpc.UnaryServerInterceptor) grpc.ServerOption +``` +Chain creates a single interceptor out of a chain of many interceptors. + +WithUnaryServerChain is a grpc.Server config option that accepts multiple unary interceptors. +Basically syntactic sugar. + +## <a name="WrappedServerStream">type</a> [WrappedServerStream](./wrappers.go#L12-L16) +``` go +type WrappedServerStream struct { + grpc.ServerStream + // WrappedContext is the wrapper's own Context. You can assign it. + WrappedContext context.Context +} +``` +WrappedServerStream is a thin wrapper around grpc.ServerStream that allows modifying context. + +### <a name="WrapServerStream">func</a> [WrapServerStream](./wrappers.go#L24) +``` go +func WrapServerStream(stream grpc.ServerStream) *WrappedServerStream +``` +WrapServerStream returns a ServerStream that has the ability to overwrite context. + +### <a name="WrappedServerStream.Context">func</a> (\*WrappedServerStream) [Context](./wrappers.go#L19) +``` go +func (w *WrappedServerStream) Context() context.Context +``` +Context returns the wrapper's WrappedContext, overwriting the nested grpc.ServerStream.Context() + +- - - +Generated by [godoc2ghmd](https://github.com/GandalfUK/godoc2ghmd)
\ No newline at end of file diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/Gopkg.lock b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/Gopkg.lock new file mode 100644 index 00000000..ebdcb75a --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/Gopkg.lock @@ -0,0 +1,123 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + name = "cloud.google.com/go" + packages = ["compute/metadata"] + revision = "2d3a6656c17a60b0815b7e06ab0be04eacb6e613" + version = "v0.16.0" + +[[projects]] + name = "github.com/davecgh/go-spew" + packages = ["spew"] + revision = "346938d642f2ec3594ed81d874461961cd0faa76" + version = "v1.1.0" + +[[projects]] + name = "github.com/gogo/protobuf" + packages = ["gogoproto","proto","protoc-gen-gogo/descriptor"] + revision = "342cbe0a04158f6dcb03ca0079991a51a4248c02" + version = "v0.5" + +[[projects]] + branch = "master" + name = "github.com/golang/protobuf" + packages = ["jsonpb","proto","ptypes","ptypes/any","ptypes/duration","ptypes/struct","ptypes/timestamp"] + revision = "1e59b77b52bf8e4b449a57e6f79f21226d571845" + +[[projects]] + name = "github.com/opentracing/opentracing-go" + packages = [".","ext","log","mocktracer"] + revision = "1949ddbfd147afd4d964a9f00b24eb291e0e7c38" + version = "v1.0.2" + +[[projects]] + name = "github.com/pmezard/go-difflib" + packages = ["difflib"] + revision = "792786c7400a136282c1664665ae0a8db921c6c2" + version = "v1.0.0" + +[[projects]] + name = "github.com/sirupsen/logrus" + packages = ["."] + revision = "f006c2ac4710855cf0f916dd6b77acf6b048dc6e" + version = "v1.0.3" + +[[projects]] + name = "github.com/stretchr/testify" + packages = ["assert","require","suite"] + revision = "69483b4bd14f5845b5a1e55bca19e954e827f1d0" + version = "v1.1.4" + +[[projects]] + name = "go.uber.org/atomic" + packages = ["."] + revision = "8474b86a5a6f79c443ce4b2992817ff32cf208b8" + version = "v1.3.1" + +[[projects]] + name = "go.uber.org/multierr" + packages = ["."] + revision = "3c4937480c32f4c13a875a1829af76c98ca3d40a" + version = "v1.1.0" + +[[projects]] + name = "go.uber.org/zap" + packages = [".","buffer","internal/bufferpool","internal/color","internal/exit","zapcore"] + revision = "35aad584952c3e7020db7b839f6b102de6271f89" + version = "v1.7.1" + +[[projects]] + branch = "master" + name = "golang.org/x/crypto" + packages = ["ssh/terminal"] + revision = "94eea52f7b742c7cbe0b03b22f0c4c8631ece122" + +[[projects]] + branch = "master" + name = "golang.org/x/net" + packages = ["context","context/ctxhttp","http2","http2/hpack","idna","internal/timeseries","lex/httplex","trace"] + revision = "a8b9294777976932365dabb6640cf1468d95c70f" + +[[projects]] + branch = "master" + name = "golang.org/x/oauth2" + packages = [".","google","internal","jws","jwt"] + revision = "f95fa95eaa936d9d87489b15d1d18b97c1ba9c28" + +[[projects]] + branch = "master" + name = "golang.org/x/sys" + packages = ["unix","windows"] + revision = "13fcbd661c8ececa8807a29b48407d674b1d8ed8" + +[[projects]] + branch = "master" + name = "golang.org/x/text" + packages = ["collate","collate/build","internal/colltab","internal/gen","internal/tag","internal/triegen","internal/ucd","language","secure/bidirule","transform","unicode/bidi","unicode/cldr","unicode/norm","unicode/rangetable"] + revision = "75cc3cad82b5f47d3fb229ddda8c5167da14f294" + +[[projects]] + name = "google.golang.org/appengine" + packages = [".","internal","internal/app_identity","internal/base","internal/datastore","internal/log","internal/modules","internal/remote_api","internal/urlfetch","urlfetch"] + revision = "150dc57a1b433e64154302bdc40b6bb8aefa313a" + version = "v1.0.0" + +[[projects]] + branch = "master" + name = "google.golang.org/genproto" + packages = ["googleapis/rpc/status"] + revision = "7f0da29060c682909f650ad8ed4e515bd74fa12a" + +[[projects]] + name = "google.golang.org/grpc" + packages = [".","balancer","balancer/roundrobin","codes","connectivity","credentials","credentials/oauth","encoding","grpclb/grpc_lb_v1/messages","grpclog","internal","keepalive","metadata","naming","peer","resolver","resolver/dns","resolver/passthrough","stats","status","tap","transport"] + revision = "5a9f7b402fe85096d2e1d0383435ee1876e863d0" + version = "v1.8.0" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + inputs-digest = "b24c6670412eb0bc44ed1db77fecc52333f8725f3e3272bdc568f5683a63031f" + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/Gopkg.toml b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/Gopkg.toml new file mode 100644 index 00000000..0a7d4c1c --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/Gopkg.toml @@ -0,0 +1,35 @@ +[[constraint]] + name = "github.com/gogo/protobuf" + version = "0.5.0" + +[[constraint]] + branch = "master" + name = "github.com/golang/protobuf" + +[[constraint]] + name = "github.com/opentracing/opentracing-go" + version = "1.0.2" + +[[constraint]] + name = "github.com/sirupsen/logrus" + version = "1.0.3" + +[[constraint]] + name = "github.com/stretchr/testify" + version = "1.1.4" + +[[constraint]] + name = "go.uber.org/zap" + version = "1.7.1" + +[[constraint]] + branch = "master" + name = "golang.org/x/net" + +[[constraint]] + branch = "master" + name = "golang.org/x/oauth2" + +[[constraint]] + name = "google.golang.org/grpc" + version = "1.8.0" diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/LICENSE b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/LICENSE new file mode 100644 index 00000000..b2b06503 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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.
\ No newline at end of file diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/README.md b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/README.md new file mode 100644 index 00000000..4fe246f2 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/README.md @@ -0,0 +1,86 @@ +# Go gRPC Middleware + +[![Travis Build](https://travis-ci.org/grpc-ecosystem/go-grpc-middleware.svg?branch=master)](https://travis-ci.org/grpc-ecosystem/go-grpc-middleware) +[![Go Report Card](https://goreportcard.com/badge/github.com/grpc-ecosystem/go-grpc-middleware)](https://goreportcard.com/report/github.com/grpc-ecosystem/go-grpc-middleware) +[![GoDoc](http://img.shields.io/badge/GoDoc-Reference-blue.svg)](https://godoc.org/github.com/grpc-ecosystem/go-grpc-middleware) +[![SourceGraph](https://sourcegraph.com/github.com/grpc-ecosystem/go-grpc-middleware/-/badge.svg)](https://sourcegraph.com/github.com/grpc-ecosystem/go-grpc-middleware/?badge) +[![codecov](https://codecov.io/gh/grpc-ecosystem/go-grpc-middleware/branch/master/graph/badge.svg)](https://codecov.io/gh/grpc-ecosystem/go-grpc-middleware) +[![Apache 2.0 License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE) +[![quality: production](https://img.shields.io/badge/quality-production-orange.svg)](#status) + + +[gRPC Go](https://github.com/grpc/grpc-go) Middleware: interceptors, helpers, utilities. + +**Important** The repo recently moved to `github.com/grpc-ecosystem/go-grpc-middleware`, please update your import paths. + +## Middleware + +[gRPC Go](https://github.com/grpc/grpc-go) recently acquired support for +Interceptors, i.e. [middleware](https://medium.com/@matryer/writing-middleware-in-golang-and-how-go-makes-it-so-much-fun-4375c1246e81#.gv7tdlghs) +that is executed either on the gRPC Server before the request is passed onto the user's application logic, or on the gRPC client either around the user call. It is a perfect way to implement +common patterns: auth, logging, message, validation, retries or monitoring. + +These are generic building blocks that make it easy to build multiple microservices easily. +The purpose of this repository is to act as a go-to point for such reusable functionality. It contains +some of them itself, but also will link to useful external repos. + +`grpc_middleware` itself provides support for chaining interceptors. See [Documentation](DOC.md), but here's an example: + +```go +import "github.com/grpc-ecosystem/go-grpc-middleware" + +myServer := grpc.NewServer( + grpc.StreamInterceptor(grpc_middleware.ChainStreamServer( + grpc_ctxtags.StreamServerInterceptor(), + grpc_opentracing.StreamServerInterceptor(), + grpc_prometheus.StreamServerInterceptor, + grpc_zap.StreamServerInterceptor(zapLogger), + grpc_auth.StreamServerInterceptor(myAuthFunction), + grpc_recovery.StreamServerInterceptor(), + )), + grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer( + grpc_ctxtags.UnaryServerInterceptor(), + grpc_opentracing.UnaryServerInterceptor(), + grpc_prometheus.UnaryServerInterceptor, + grpc_zap.UnaryServerInterceptor(zapLogger), + grpc_auth.UnaryServerInterceptor(myAuthFunction), + grpc_recovery.UnaryServerInterceptor(), + )), +) +``` + +## Interceptors + +*Please send a PR to add new interceptors or middleware to this list* + +#### Auth + * [`grpc_auth`](auth) - a customizable (via `AuthFunc`) piece of auth middleware + +#### Logging + * [`grpc_ctxtags`](tags/) - a library that adds a `Tag` map to context, with data populated from request body + * [`grpc_zap`](logging/zap/) - integration of [zap](https://github.com/uber-go/zap) logging library into gRPC handlers. + * [`grpc_logrus`](logging/logrus/) - integration of [logrus](https://github.com/sirupsen/logrus) logging library into gRPC handlers. + + +#### Monitoring + * [`grpc_prometheus`âš¡](https://github.com/grpc-ecosystem/go-grpc-prometheus) - Prometheus client-side and server-side monitoring middleware + * [`otgrpc`âš¡](https://github.com/grpc-ecosystem/grpc-opentracing/tree/master/go/otgrpc) - [OpenTracing](http://opentracing.io/) client-side and server-side interceptors + * [`grpc_opentracing`](tracing/opentracing) - [OpenTracing](http://opentracing.io/) client-side and server-side interceptors with support for streaming and handler-returned tags + +#### Client + * [`grpc_retry`](retry/) - a generic gRPC response code retry mechanism, client-side middleware + +#### Server + * [`grpc_validator`](validator/) - codegen inbound message validation from `.proto` options + * [`grpc_recovery`](recovery/) - turn panics into gRPC errors + + +## Status + +This code has been running in *production* since May 2016 as the basis of the gRPC micro services stack at [Improbable](https://improbable.io). + +Additional tooling will be added, and contributions are welcome. + +## License + +`go-grpc-middleware` is released under the Apache 2.0 license. See the [LICENSE](LICENSE) file for details. diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/auth/DOC.md b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/auth/DOC.md new file mode 100644 index 00000000..5834112b --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/auth/DOC.md @@ -0,0 +1,148 @@ +# grpc_auth +`import "github.com/grpc-ecosystem/go-grpc-middleware/auth"` + +* [Overview](#pkg-overview) +* [Imported Packages](#pkg-imports) +* [Index](#pkg-index) +* [Examples](#pkg-examples) + +## <a name="pkg-overview">Overview</a> +`grpc_auth` a generic server-side auth middleware for gRPC. + +### Server Side Auth Middleware +It allows for easy assertion of `:authorization` headers in gRPC calls, be it HTTP Basic auth, or +OAuth2 Bearer tokens. + +The middleware takes a user-customizable `AuthFunc`, which can be customized to verify and extract +auth information from the request. The extracted information can be put in the `context.Context` of +handlers downstream for retrieval. + +It also allows for per-service implementation overrides of `AuthFunc`. See `ServiceAuthFuncOverride`. + +Please see examples for simple examples of use. + +#### Example: + +<details> +<summary>Click to expand code.</summary> + +```go +package grpc_auth_test + +import ( + "github.com/grpc-ecosystem/go-grpc-middleware/auth" + "github.com/grpc-ecosystem/go-grpc-middleware/tags" + "golang.org/x/net/context" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" +) + +var ( + cc *grpc.ClientConn +) + +func parseToken(token string) (struct{}, error) { + return struct{}{}, nil +} + +func userClaimFromToken(struct{}) string { + return "foobar" +} + +// Simple example of server initialization code. +func Example_serverConfig() { + exampleAuthFunc := func(ctx context.Context) (context.Context, error) { + token, err := grpc_auth.AuthFromMD(ctx, "bearer") + if err != nil { + return nil, err + } + tokenInfo, err := parseToken(token) + if err != nil { + return nil, grpc.Errorf(codes.Unauthenticated, "invalid auth token: %v", err) + } + grpc_ctxtags.Extract(ctx).Set("auth.sub", userClaimFromToken(tokenInfo)) + newCtx := context.WithValue(ctx, "tokenInfo", tokenInfo) + return newCtx, nil + } + + _ = grpc.NewServer( + grpc.StreamInterceptor(grpc_auth.StreamServerInterceptor(exampleAuthFunc)), + grpc.UnaryInterceptor(grpc_auth.UnaryServerInterceptor(exampleAuthFunc)), + ) +} +``` + +</details> + +## <a name="pkg-imports">Imported Packages</a> + +- [github.com/grpc-ecosystem/go-grpc-middleware](./..) +- [github.com/grpc-ecosystem/go-grpc-middleware/util/metautils](./../util/metautils) +- [golang.org/x/net/context](https://godoc.org/golang.org/x/net/context) +- [google.golang.org/grpc](https://godoc.org/google.golang.org/grpc) +- [google.golang.org/grpc/codes](https://godoc.org/google.golang.org/grpc/codes) + +## <a name="pkg-index">Index</a> +* [func AuthFromMD(ctx context.Context, expectedScheme string) (string, error)](#AuthFromMD) +* [func StreamServerInterceptor(authFunc AuthFunc) grpc.StreamServerInterceptor](#StreamServerInterceptor) +* [func UnaryServerInterceptor(authFunc AuthFunc) grpc.UnaryServerInterceptor](#UnaryServerInterceptor) +* [type AuthFunc](#AuthFunc) +* [type ServiceAuthFuncOverride](#ServiceAuthFuncOverride) + +#### <a name="pkg-examples">Examples</a> +* [Package (ServerConfig)](#example__serverConfig) + +#### <a name="pkg-files">Package files</a> +[auth.go](./auth.go) [doc.go](./doc.go) [metadata.go](./metadata.go) + +## <a name="AuthFromMD">func</a> [AuthFromMD](./metadata.go#L24) +``` go +func AuthFromMD(ctx context.Context, expectedScheme string) (string, error) +``` +AuthFromMD is a helper function for extracting the :authorization header from the gRPC metadata of the request. + +It expects the `:authorization` header to be of a certain scheme (e.g. `basic`, `bearer`), in a +case-insensitive format (see rfc2617, sec 1.2). If no such authorization is found, or the token +is of wrong scheme, an error with gRPC status `Unauthenticated` is returned. + +## <a name="StreamServerInterceptor">func</a> [StreamServerInterceptor](./auth.go#L51) +``` go +func StreamServerInterceptor(authFunc AuthFunc) grpc.StreamServerInterceptor +``` +StreamServerInterceptor returns a new unary server interceptors that performs per-request auth. + +## <a name="UnaryServerInterceptor">func</a> [UnaryServerInterceptor](./auth.go#L34) +``` go +func UnaryServerInterceptor(authFunc AuthFunc) grpc.UnaryServerInterceptor +``` +UnaryServerInterceptor returns a new unary server interceptors that performs per-request auth. + +## <a name="AuthFunc">type</a> [AuthFunc](./auth.go#L23) +``` go +type AuthFunc func(ctx context.Context) (context.Context, error) +``` +AuthFunc is the pluggable function that performs authentication. + +The passed in `Context` will contain the gRPC metadata.MD object (for header-based authentication) and +the peer.Peer information that can contain transport-based credentials (e.g. `credentials.AuthInfo`). + +The returned context will be propagated to handlers, allowing user changes to `Context`. However, +please make sure that the `Context` returned is a child `Context` of the one passed in. + +If error is returned, its `grpc.Code()` will be returned to the user as well as the verbatim message. +Please make sure you use `codes.Unauthenticated` (lacking auth) and `codes.PermissionDenied` +(authed, but lacking perms) appropriately. + +## <a name="ServiceAuthFuncOverride">type</a> [ServiceAuthFuncOverride](./auth.go#L29-L31) +``` go +type ServiceAuthFuncOverride interface { + AuthFuncOverride(ctx context.Context, fullMethodName string) (context.Context, error) +} +``` +ServiceAuthFuncOverride allows a given gRPC service implementation to override the global `AuthFunc`. + +If a service implements the AuthFuncOverride method, it takes precedence over the `AuthFunc` method, +and will be called instead of AuthFunc for all method invocations within that service. + +- - - +Generated by [godoc2ghmd](https://github.com/GandalfUK/godoc2ghmd)
\ No newline at end of file diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/auth/README.md b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/auth/README.md new file mode 100644 index 00000000..5834112b --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/auth/README.md @@ -0,0 +1,148 @@ +# grpc_auth +`import "github.com/grpc-ecosystem/go-grpc-middleware/auth"` + +* [Overview](#pkg-overview) +* [Imported Packages](#pkg-imports) +* [Index](#pkg-index) +* [Examples](#pkg-examples) + +## <a name="pkg-overview">Overview</a> +`grpc_auth` a generic server-side auth middleware for gRPC. + +### Server Side Auth Middleware +It allows for easy assertion of `:authorization` headers in gRPC calls, be it HTTP Basic auth, or +OAuth2 Bearer tokens. + +The middleware takes a user-customizable `AuthFunc`, which can be customized to verify and extract +auth information from the request. The extracted information can be put in the `context.Context` of +handlers downstream for retrieval. + +It also allows for per-service implementation overrides of `AuthFunc`. See `ServiceAuthFuncOverride`. + +Please see examples for simple examples of use. + +#### Example: + +<details> +<summary>Click to expand code.</summary> + +```go +package grpc_auth_test + +import ( + "github.com/grpc-ecosystem/go-grpc-middleware/auth" + "github.com/grpc-ecosystem/go-grpc-middleware/tags" + "golang.org/x/net/context" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" +) + +var ( + cc *grpc.ClientConn +) + +func parseToken(token string) (struct{}, error) { + return struct{}{}, nil +} + +func userClaimFromToken(struct{}) string { + return "foobar" +} + +// Simple example of server initialization code. +func Example_serverConfig() { + exampleAuthFunc := func(ctx context.Context) (context.Context, error) { + token, err := grpc_auth.AuthFromMD(ctx, "bearer") + if err != nil { + return nil, err + } + tokenInfo, err := parseToken(token) + if err != nil { + return nil, grpc.Errorf(codes.Unauthenticated, "invalid auth token: %v", err) + } + grpc_ctxtags.Extract(ctx).Set("auth.sub", userClaimFromToken(tokenInfo)) + newCtx := context.WithValue(ctx, "tokenInfo", tokenInfo) + return newCtx, nil + } + + _ = grpc.NewServer( + grpc.StreamInterceptor(grpc_auth.StreamServerInterceptor(exampleAuthFunc)), + grpc.UnaryInterceptor(grpc_auth.UnaryServerInterceptor(exampleAuthFunc)), + ) +} +``` + +</details> + +## <a name="pkg-imports">Imported Packages</a> + +- [github.com/grpc-ecosystem/go-grpc-middleware](./..) +- [github.com/grpc-ecosystem/go-grpc-middleware/util/metautils](./../util/metautils) +- [golang.org/x/net/context](https://godoc.org/golang.org/x/net/context) +- [google.golang.org/grpc](https://godoc.org/google.golang.org/grpc) +- [google.golang.org/grpc/codes](https://godoc.org/google.golang.org/grpc/codes) + +## <a name="pkg-index">Index</a> +* [func AuthFromMD(ctx context.Context, expectedScheme string) (string, error)](#AuthFromMD) +* [func StreamServerInterceptor(authFunc AuthFunc) grpc.StreamServerInterceptor](#StreamServerInterceptor) +* [func UnaryServerInterceptor(authFunc AuthFunc) grpc.UnaryServerInterceptor](#UnaryServerInterceptor) +* [type AuthFunc](#AuthFunc) +* [type ServiceAuthFuncOverride](#ServiceAuthFuncOverride) + +#### <a name="pkg-examples">Examples</a> +* [Package (ServerConfig)](#example__serverConfig) + +#### <a name="pkg-files">Package files</a> +[auth.go](./auth.go) [doc.go](./doc.go) [metadata.go](./metadata.go) + +## <a name="AuthFromMD">func</a> [AuthFromMD](./metadata.go#L24) +``` go +func AuthFromMD(ctx context.Context, expectedScheme string) (string, error) +``` +AuthFromMD is a helper function for extracting the :authorization header from the gRPC metadata of the request. + +It expects the `:authorization` header to be of a certain scheme (e.g. `basic`, `bearer`), in a +case-insensitive format (see rfc2617, sec 1.2). If no such authorization is found, or the token +is of wrong scheme, an error with gRPC status `Unauthenticated` is returned. + +## <a name="StreamServerInterceptor">func</a> [StreamServerInterceptor](./auth.go#L51) +``` go +func StreamServerInterceptor(authFunc AuthFunc) grpc.StreamServerInterceptor +``` +StreamServerInterceptor returns a new unary server interceptors that performs per-request auth. + +## <a name="UnaryServerInterceptor">func</a> [UnaryServerInterceptor](./auth.go#L34) +``` go +func UnaryServerInterceptor(authFunc AuthFunc) grpc.UnaryServerInterceptor +``` +UnaryServerInterceptor returns a new unary server interceptors that performs per-request auth. + +## <a name="AuthFunc">type</a> [AuthFunc](./auth.go#L23) +``` go +type AuthFunc func(ctx context.Context) (context.Context, error) +``` +AuthFunc is the pluggable function that performs authentication. + +The passed in `Context` will contain the gRPC metadata.MD object (for header-based authentication) and +the peer.Peer information that can contain transport-based credentials (e.g. `credentials.AuthInfo`). + +The returned context will be propagated to handlers, allowing user changes to `Context`. However, +please make sure that the `Context` returned is a child `Context` of the one passed in. + +If error is returned, its `grpc.Code()` will be returned to the user as well as the verbatim message. +Please make sure you use `codes.Unauthenticated` (lacking auth) and `codes.PermissionDenied` +(authed, but lacking perms) appropriately. + +## <a name="ServiceAuthFuncOverride">type</a> [ServiceAuthFuncOverride](./auth.go#L29-L31) +``` go +type ServiceAuthFuncOverride interface { + AuthFuncOverride(ctx context.Context, fullMethodName string) (context.Context, error) +} +``` +ServiceAuthFuncOverride allows a given gRPC service implementation to override the global `AuthFunc`. + +If a service implements the AuthFuncOverride method, it takes precedence over the `AuthFunc` method, +and will be called instead of AuthFunc for all method invocations within that service. + +- - - +Generated by [godoc2ghmd](https://github.com/GandalfUK/godoc2ghmd)
\ No newline at end of file diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/auth/auth.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/auth/auth.go new file mode 100644 index 00000000..a35c95fa --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/auth/auth.go @@ -0,0 +1,67 @@ +// Copyright 2016 Michal Witkowski. All Rights Reserved. +// See LICENSE for licensing terms. + +package grpc_auth + +import ( + "github.com/grpc-ecosystem/go-grpc-middleware" + "golang.org/x/net/context" + "google.golang.org/grpc" +) + +// AuthFunc is the pluggable function that performs authentication. +// +// The passed in `Context` will contain the gRPC metadata.MD object (for header-based authentication) and +// the peer.Peer information that can contain transport-based credentials (e.g. `credentials.AuthInfo`). +// +// The returned context will be propagated to handlers, allowing user changes to `Context`. However, +// please make sure that the `Context` returned is a child `Context` of the one passed in. +// +// If error is returned, its `grpc.Code()` will be returned to the user as well as the verbatim message. +// Please make sure you use `codes.Unauthenticated` (lacking auth) and `codes.PermissionDenied` +// (authed, but lacking perms) appropriately. +type AuthFunc func(ctx context.Context) (context.Context, error) + +// ServiceAuthFuncOverride allows a given gRPC service implementation to override the global `AuthFunc`. +// +// If a service implements the AuthFuncOverride method, it takes precedence over the `AuthFunc` method, +// and will be called instead of AuthFunc for all method invocations within that service. +type ServiceAuthFuncOverride interface { + AuthFuncOverride(ctx context.Context, fullMethodName string) (context.Context, error) +} + +// UnaryServerInterceptor returns a new unary server interceptors that performs per-request auth. +func UnaryServerInterceptor(authFunc AuthFunc) grpc.UnaryServerInterceptor { + return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { + var newCtx context.Context + var err error + if overrideSrv, ok := info.Server.(ServiceAuthFuncOverride); ok { + newCtx, err = overrideSrv.AuthFuncOverride(ctx, info.FullMethod) + } else { + newCtx, err = authFunc(ctx) + } + if err != nil { + return nil, err + } + return handler(newCtx, req) + } +} + +// StreamServerInterceptor returns a new unary server interceptors that performs per-request auth. +func StreamServerInterceptor(authFunc AuthFunc) grpc.StreamServerInterceptor { + return func(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { + var newCtx context.Context + var err error + if overrideSrv, ok := srv.(ServiceAuthFuncOverride); ok { + newCtx, err = overrideSrv.AuthFuncOverride(stream.Context(), info.FullMethod) + } else { + newCtx, err = authFunc(stream.Context()) + } + if err != nil { + return err + } + wrapped := grpc_middleware.WrapServerStream(stream) + wrapped.WrappedContext = newCtx + return handler(srv, wrapped) + } +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/auth/doc.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/auth/doc.go new file mode 100644 index 00000000..0550f023 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/auth/doc.go @@ -0,0 +1,20 @@ +// Copyright 2016 Michal Witkowski. All Rights Reserved. +// See LICENSE for licensing terms. + +/* +`grpc_auth` a generic server-side auth middleware for gRPC. + +Server Side Auth Middleware + +It allows for easy assertion of `:authorization` headers in gRPC calls, be it HTTP Basic auth, or +OAuth2 Bearer tokens. + +The middleware takes a user-customizable `AuthFunc`, which can be customized to verify and extract +auth information from the request. The extracted information can be put in the `context.Context` of +handlers downstream for retrieval. + +It also allows for per-service implementation overrides of `AuthFunc`. See `ServiceAuthFuncOverride`. + +Please see examples for simple examples of use. +*/ +package grpc_auth diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/auth/metadata.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/auth/metadata.go new file mode 100644 index 00000000..50020653 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/auth/metadata.go @@ -0,0 +1,38 @@ +// Copyright 2016 Michal Witkowski. All Rights Reserved. +// See LICENSE for licensing terms. + +package grpc_auth + +import ( + "strings" + + "github.com/grpc-ecosystem/go-grpc-middleware/util/metautils" + "golang.org/x/net/context" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" +) + +var ( + headerAuthorize = "authorization" +) + +// AuthFromMD is a helper function for extracting the :authorization header from the gRPC metadata of the request. +// +// It expects the `:authorization` header to be of a certain scheme (e.g. `basic`, `bearer`), in a +// case-insensitive format (see rfc2617, sec 1.2). If no such authorization is found, or the token +// is of wrong scheme, an error with gRPC status `Unauthenticated` is returned. +func AuthFromMD(ctx context.Context, expectedScheme string) (string, error) { + val := metautils.ExtractIncoming(ctx).Get(headerAuthorize) + if val == "" { + return "", grpc.Errorf(codes.Unauthenticated, "Request unauthenticated with "+expectedScheme) + + } + splits := strings.SplitN(val, " ", 2) + if len(splits) < 2 { + return "", grpc.Errorf(codes.Unauthenticated, "Bad authorization string") + } + if strings.ToLower(splits[0]) != strings.ToLower(expectedScheme) { + return "", grpc.Errorf(codes.Unauthenticated, "Request unauthenticated with "+expectedScheme) + } + return splits[1], nil +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/chain.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/chain.go new file mode 100644 index 00000000..45a2f5f4 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/chain.go @@ -0,0 +1,183 @@ +// Copyright 2016 Michal Witkowski. All Rights Reserved. +// See LICENSE for licensing terms. + +// gRPC Server Interceptor chaining middleware. + +package grpc_middleware + +import ( + "golang.org/x/net/context" + "google.golang.org/grpc" +) + +// ChainUnaryServer creates a single interceptor out of a chain of many interceptors. +// +// Execution is done in left-to-right order, including passing of context. +// For example ChainUnaryServer(one, two, three) will execute one before two before three, and three +// will see context changes of one and two. +func ChainUnaryServer(interceptors ...grpc.UnaryServerInterceptor) grpc.UnaryServerInterceptor { + n := len(interceptors) + + if n > 1 { + lastI := n - 1 + return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { + var ( + chainHandler grpc.UnaryHandler + curI int + ) + + chainHandler = func(currentCtx context.Context, currentReq interface{}) (interface{}, error) { + if curI == lastI { + return handler(currentCtx, currentReq) + } + curI++ + resp, err := interceptors[curI](currentCtx, currentReq, info, chainHandler) + curI-- + return resp, err + } + + return interceptors[0](ctx, req, info, chainHandler) + } + } + + if n == 1 { + return interceptors[0] + } + + // n == 0; Dummy interceptor maintained for backward compatibility to avoid returning nil. + return func(ctx context.Context, req interface{}, _ *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { + return handler(ctx, req) + } +} + +// ChainStreamServer creates a single interceptor out of a chain of many interceptors. +// +// Execution is done in left-to-right order, including passing of context. +// For example ChainUnaryServer(one, two, three) will execute one before two before three. +// If you want to pass context between interceptors, use WrapServerStream. +func ChainStreamServer(interceptors ...grpc.StreamServerInterceptor) grpc.StreamServerInterceptor { + n := len(interceptors) + + if n > 1 { + lastI := n - 1 + return func(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { + var ( + chainHandler grpc.StreamHandler + curI int + ) + + chainHandler = func(currentSrv interface{}, currentStream grpc.ServerStream) error { + if curI == lastI { + return handler(currentSrv, currentStream) + } + curI++ + err := interceptors[curI](currentSrv, currentStream, info, chainHandler) + curI-- + return err + } + + return interceptors[0](srv, stream, info, chainHandler) + } + } + + if n == 1 { + return interceptors[0] + } + + // n == 0; Dummy interceptor maintained for backward compatibility to avoid returning nil. + return func(srv interface{}, stream grpc.ServerStream, _ *grpc.StreamServerInfo, handler grpc.StreamHandler) error { + return handler(srv, stream) + } +} + +// ChainUnaryClient creates a single interceptor out of a chain of many interceptors. +// +// Execution is done in left-to-right order, including passing of context. +// For example ChainUnaryClient(one, two, three) will execute one before two before three. +func ChainUnaryClient(interceptors ...grpc.UnaryClientInterceptor) grpc.UnaryClientInterceptor { + n := len(interceptors) + + if n > 1 { + lastI := n - 1 + return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { + var ( + chainHandler grpc.UnaryInvoker + curI int + ) + + chainHandler = func(currentCtx context.Context, currentMethod string, currentReq, currentRepl interface{}, currentConn *grpc.ClientConn, currentOpts ...grpc.CallOption) error { + if curI == lastI { + return invoker(currentCtx, currentMethod, currentReq, currentRepl, currentConn, currentOpts...) + } + curI++ + err := interceptors[curI](currentCtx, currentMethod, currentReq, currentRepl, currentConn, chainHandler, currentOpts...) + curI-- + return err + } + + return interceptors[0](ctx, method, req, reply, cc, chainHandler, opts...) + } + } + + if n == 1 { + return interceptors[0] + } + + // n == 0; Dummy interceptor maintained for backward compatibility to avoid returning nil. + return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { + return invoker(ctx, method, req, reply, cc, opts...) + } +} + +// ChainStreamClient creates a single interceptor out of a chain of many interceptors. +// +// Execution is done in left-to-right order, including passing of context. +// For example ChainStreamClient(one, two, three) will execute one before two before three. +func ChainStreamClient(interceptors ...grpc.StreamClientInterceptor) grpc.StreamClientInterceptor { + n := len(interceptors) + + if n > 1 { + lastI := n - 1 + return func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) { + var ( + chainHandler grpc.Streamer + curI int + ) + + chainHandler = func(currentCtx context.Context, currentDesc *grpc.StreamDesc, currentConn *grpc.ClientConn, currentMethod string, currentOpts ...grpc.CallOption) (grpc.ClientStream, error) { + if curI == lastI { + return streamer(currentCtx, currentDesc, currentConn, currentMethod, currentOpts...) + } + curI++ + stream, err := interceptors[curI](currentCtx, currentDesc, currentConn, currentMethod, chainHandler, currentOpts...) + curI-- + return stream, err + } + + return interceptors[0](ctx, desc, cc, method, chainHandler, opts...) + } + } + + if n == 1 { + return interceptors[0] + } + + // n == 0; Dummy interceptor maintained for backward compatibility to avoid returning nil. + return func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) { + return streamer(ctx, desc, cc, method, opts...) + } +} + +// Chain creates a single interceptor out of a chain of many interceptors. +// +// WithUnaryServerChain is a grpc.Server config option that accepts multiple unary interceptors. +// Basically syntactic sugar. +func WithUnaryServerChain(interceptors ...grpc.UnaryServerInterceptor) grpc.ServerOption { + return grpc.UnaryInterceptor(ChainUnaryServer(interceptors...)) +} + +// WithStreamServerChain is a grpc.Server config option that accepts multiple stream interceptors. +// Basically syntactic sugar. +func WithStreamServerChain(interceptors ...grpc.StreamServerInterceptor) grpc.ServerOption { + return grpc.StreamInterceptor(ChainStreamServer(interceptors...)) +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/doc.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/doc.go new file mode 100644 index 00000000..71689503 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/doc.go @@ -0,0 +1,69 @@ +// Copyright 2016 Michal Witkowski. All Rights Reserved. +// See LICENSE for licensing terms. + +/* +`grpc_middleware` is a collection of gRPC middleware packages: interceptors, helpers and tools. + +Middleware + +gRPC is a fantastic RPC middleware, which sees a lot of adoption in the Golang world. However, the +upstream gRPC codebase is relatively bare bones. + +This package, and most of its child packages provides commonly needed middleware for gRPC: +client-side interceptors for retires, server-side interceptors for input validation and auth, +functions for chaining said interceptors, metadata convenience methods and more. + +Chaining + +By default, gRPC doesn't allow one to have more than one interceptor either on the client nor on +the server side. `grpc_middleware` provides convenient chaining methods + +Simple way of turning a multiple interceptors into a single interceptor. Here's an example for +server chaining: + + myServer := grpc.NewServer( + grpc.StreamInterceptor(grpc_middleware.ChainStreamServer(loggingStream, monitoringStream, authStream)), + grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(loggingUnary, monitoringUnary, authUnary), + ) + +These interceptors will be executed from left to right: logging, monitoring and auth. + +Here's an example for client side chaining: + + clientConn, err = grpc.Dial( + address, + grpc.WithUnaryInterceptor(grpc_middleware.ChainUnaryClient(monitoringClientUnary, retryUnary)), + grpc.WithStreamInterceptor(grpc_middleware.ChainStreamClient(monitoringClientStream, retryStream)), + ) + client = pb_testproto.NewTestServiceClient(clientConn) + resp, err := client.PingEmpty(s.ctx, &myservice.Request{Msg: "hello"}) + +These interceptors will be executed from left to right: monitoring and then retry logic. + +The retry interceptor will call every interceptor that follows it whenever when a retry happens. + +Writing Your Own + +Implementing your own interceptor is pretty trivial: there are interfaces for that. But the interesting +bit exposing common data to handlers (and other middleware), similarly to HTTP Middleware design. +For example, you may want to pass the identity of the caller from the auth interceptor all the way +to the handling function. + +For example, a client side interceptor example for auth looks like: + + func FakeAuthUnaryInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { + newCtx := context.WithValue(ctx, "user_id", "john@example.com") + return handler(newCtx, req) + } + +Unfortunately, it's not as easy for streaming RPCs. These have the `context.Context` embedded within +the `grpc.ServerStream` object. To pass values through context, a wrapper (`WrappedServerStream`) is +needed. For example: + + func FakeAuthStreamingInterceptor(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { + newStream := grpc_middleware.WrapServerStream(stream) + newStream.WrappedContext = context.WithValue(ctx, "user_id", "john@example.com") + return handler(srv, stream) + } +*/ +package grpc_middleware diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/DOC.md b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/DOC.md new file mode 100644 index 00000000..9deb4722 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/DOC.md @@ -0,0 +1,91 @@ +# grpc_logging +`import "github.com/grpc-ecosystem/go-grpc-middleware/logging"` + +* [Overview](#pkg-overview) +* [Imported Packages](#pkg-imports) +* [Index](#pkg-index) + +## <a name="pkg-overview">Overview</a> +grpc_logging is a "parent" package for gRPC logging middlewares. + +### General functionality of all middleware +The gRPC logging middleware populates request-scoped data to `grpc_ctxtags.Tags` that relate to the current gRPC call +(e.g. service and method names). + +Once the gRPC logging middleware has added the gRPC specific Tags to the ctx they will then be written with the logs +that are made using the `ctx_logrus` or `ctx_zap` loggers. + +All logging middleware will emit a final log statement. It is based on the error returned by the handler function, +the gRPC status code, an error (if any) and it will emit at a level controlled via `WithLevels`. + +### This parent package +This particular package is intended for use by other middleware, logging or otherwise. It contains interfaces that other +logging middlewares *could* share . This allows code to be shared between different implementations. + +### Field names +All field names of loggers follow the OpenTracing semantics definitions, with `grpc.` prefix if needed: +<a href="https://github.com/opentracing/specification/blob/master/semantic_conventions.md">https://github.com/opentracing/specification/blob/master/semantic_conventions.md</a> + +### Implementations +There are two implementations at the moment: logrus and zap + +See relevant packages below. + +## <a name="pkg-imports">Imported Packages</a> + +- [golang.org/x/net/context](https://godoc.org/golang.org/x/net/context) +- [google.golang.org/grpc](https://godoc.org/google.golang.org/grpc) +- [google.golang.org/grpc/codes](https://godoc.org/google.golang.org/grpc/codes) + +## <a name="pkg-index">Index</a> +* [func DefaultDeciderMethod(fullMethodName string, err error) bool](#DefaultDeciderMethod) +* [func DefaultErrorToCode(err error) codes.Code](#DefaultErrorToCode) +* [type ClientPayloadLoggingDecider](#ClientPayloadLoggingDecider) +* [type Decider](#Decider) +* [type ErrorToCode](#ErrorToCode) +* [type ServerPayloadLoggingDecider](#ServerPayloadLoggingDecider) + +#### <a name="pkg-files">Package files</a> +[common.go](./common.go) [doc.go](./doc.go) + +## <a name="DefaultDeciderMethod">func</a> [DefaultDeciderMethod](./common.go#L25) +``` go +func DefaultDeciderMethod(fullMethodName string, err error) bool +``` +DefaultDeciderMethod is the default implementation of decider to see if you should log the call +by default this if always true so all calls are logged + +## <a name="DefaultErrorToCode">func</a> [DefaultErrorToCode](./common.go#L16) +``` go +func DefaultErrorToCode(err error) codes.Code +``` + +## <a name="ClientPayloadLoggingDecider">type</a> [ClientPayloadLoggingDecider](./common.go#L35) +``` go +type ClientPayloadLoggingDecider func(ctx context.Context, fullMethodName string) bool +``` +ClientPayloadLoggingDecider is a user-provided function for deciding whether to log the client-side +request/response payloads + +## <a name="Decider">type</a> [Decider](./common.go#L21) +``` go +type Decider func(fullMethodName string, err error) bool +``` +Decider function defines rules for suppressing any interceptor logs + +## <a name="ErrorToCode">type</a> [ErrorToCode](./common.go#L14) +``` go +type ErrorToCode func(err error) codes.Code +``` +ErrorToCode function determines the error code of an error +This makes using custom errors with grpc middleware easier + +## <a name="ServerPayloadLoggingDecider">type</a> [ServerPayloadLoggingDecider](./common.go#L31) +``` go +type ServerPayloadLoggingDecider func(ctx context.Context, fullMethodName string, servingObject interface{}) bool +``` +ServerPayloadLoggingDecider is a user-provided function for deciding whether to log the server-side +request/response payloads + +- - - +Generated by [godoc2ghmd](https://github.com/GandalfUK/godoc2ghmd)
\ No newline at end of file diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/README.md b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/README.md new file mode 100644 index 00000000..9deb4722 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/README.md @@ -0,0 +1,91 @@ +# grpc_logging +`import "github.com/grpc-ecosystem/go-grpc-middleware/logging"` + +* [Overview](#pkg-overview) +* [Imported Packages](#pkg-imports) +* [Index](#pkg-index) + +## <a name="pkg-overview">Overview</a> +grpc_logging is a "parent" package for gRPC logging middlewares. + +### General functionality of all middleware +The gRPC logging middleware populates request-scoped data to `grpc_ctxtags.Tags` that relate to the current gRPC call +(e.g. service and method names). + +Once the gRPC logging middleware has added the gRPC specific Tags to the ctx they will then be written with the logs +that are made using the `ctx_logrus` or `ctx_zap` loggers. + +All logging middleware will emit a final log statement. It is based on the error returned by the handler function, +the gRPC status code, an error (if any) and it will emit at a level controlled via `WithLevels`. + +### This parent package +This particular package is intended for use by other middleware, logging or otherwise. It contains interfaces that other +logging middlewares *could* share . This allows code to be shared between different implementations. + +### Field names +All field names of loggers follow the OpenTracing semantics definitions, with `grpc.` prefix if needed: +<a href="https://github.com/opentracing/specification/blob/master/semantic_conventions.md">https://github.com/opentracing/specification/blob/master/semantic_conventions.md</a> + +### Implementations +There are two implementations at the moment: logrus and zap + +See relevant packages below. + +## <a name="pkg-imports">Imported Packages</a> + +- [golang.org/x/net/context](https://godoc.org/golang.org/x/net/context) +- [google.golang.org/grpc](https://godoc.org/google.golang.org/grpc) +- [google.golang.org/grpc/codes](https://godoc.org/google.golang.org/grpc/codes) + +## <a name="pkg-index">Index</a> +* [func DefaultDeciderMethod(fullMethodName string, err error) bool](#DefaultDeciderMethod) +* [func DefaultErrorToCode(err error) codes.Code](#DefaultErrorToCode) +* [type ClientPayloadLoggingDecider](#ClientPayloadLoggingDecider) +* [type Decider](#Decider) +* [type ErrorToCode](#ErrorToCode) +* [type ServerPayloadLoggingDecider](#ServerPayloadLoggingDecider) + +#### <a name="pkg-files">Package files</a> +[common.go](./common.go) [doc.go](./doc.go) + +## <a name="DefaultDeciderMethod">func</a> [DefaultDeciderMethod](./common.go#L25) +``` go +func DefaultDeciderMethod(fullMethodName string, err error) bool +``` +DefaultDeciderMethod is the default implementation of decider to see if you should log the call +by default this if always true so all calls are logged + +## <a name="DefaultErrorToCode">func</a> [DefaultErrorToCode](./common.go#L16) +``` go +func DefaultErrorToCode(err error) codes.Code +``` + +## <a name="ClientPayloadLoggingDecider">type</a> [ClientPayloadLoggingDecider](./common.go#L35) +``` go +type ClientPayloadLoggingDecider func(ctx context.Context, fullMethodName string) bool +``` +ClientPayloadLoggingDecider is a user-provided function for deciding whether to log the client-side +request/response payloads + +## <a name="Decider">type</a> [Decider](./common.go#L21) +``` go +type Decider func(fullMethodName string, err error) bool +``` +Decider function defines rules for suppressing any interceptor logs + +## <a name="ErrorToCode">type</a> [ErrorToCode](./common.go#L14) +``` go +type ErrorToCode func(err error) codes.Code +``` +ErrorToCode function determines the error code of an error +This makes using custom errors with grpc middleware easier + +## <a name="ServerPayloadLoggingDecider">type</a> [ServerPayloadLoggingDecider](./common.go#L31) +``` go +type ServerPayloadLoggingDecider func(ctx context.Context, fullMethodName string, servingObject interface{}) bool +``` +ServerPayloadLoggingDecider is a user-provided function for deciding whether to log the server-side +request/response payloads + +- - - +Generated by [godoc2ghmd](https://github.com/GandalfUK/godoc2ghmd)
\ No newline at end of file diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/common.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/common.go new file mode 100644 index 00000000..f725d038 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/common.go @@ -0,0 +1,35 @@ +// Copyright 2017 Michal Witkowski. All Rights Reserved. +// See LICENSE for licensing terms. + +package grpc_logging + +import ( + "golang.org/x/net/context" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" +) + +// ErrorToCode function determines the error code of an error +// This makes using custom errors with grpc middleware easier +type ErrorToCode func(err error) codes.Code + +func DefaultErrorToCode(err error) codes.Code { + return grpc.Code(err) +} + +// Decider function defines rules for suppressing any interceptor logs +type Decider func(fullMethodName string, err error) bool + +// DefaultDeciderMethod is the default implementation of decider to see if you should log the call +// by default this if always true so all calls are logged +func DefaultDeciderMethod(fullMethodName string, err error) bool { + return true +} + +// ServerPayloadLoggingDecider is a user-provided function for deciding whether to log the server-side +// request/response payloads +type ServerPayloadLoggingDecider func(ctx context.Context, fullMethodName string, servingObject interface{}) bool + +// ClientPayloadLoggingDecider is a user-provided function for deciding whether to log the client-side +// request/response payloads +type ClientPayloadLoggingDecider func(ctx context.Context, fullMethodName string) bool diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/doc.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/doc.go new file mode 100644 index 00000000..ce8bb298 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/doc.go @@ -0,0 +1,35 @@ +// Copyright 2017 Michal Witkowski. All Rights Reserved. +// See LICENSE for licensing terms. + +// +/* +grpc_logging is a "parent" package for gRPC logging middlewares. + +General functionality of all middleware + +The gRPC logging middleware populates request-scoped data to `grpc_ctxtags.Tags` that relate to the current gRPC call +(e.g. service and method names). + +Once the gRPC logging middleware has added the gRPC specific Tags to the ctx they will then be written with the logs +that are made using the `ctx_logrus` or `ctx_zap` loggers. + +All logging middleware will emit a final log statement. It is based on the error returned by the handler function, +the gRPC status code, an error (if any) and it will emit at a level controlled via `WithLevels`. + +This parent package + +This particular package is intended for use by other middleware, logging or otherwise. It contains interfaces that other +logging middlewares *could* share . This allows code to be shared between different implementations. + +Field names + +All field names of loggers follow the OpenTracing semantics definitions, with `grpc.` prefix if needed: +https://github.com/opentracing/specification/blob/master/semantic_conventions.md + +Implementations + +There are two implementations at the moment: logrus and zap + +See relevant packages below. +*/ +package grpc_logging diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/DOC.md b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/DOC.md new file mode 100644 index 00000000..77ebb64c --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/DOC.md @@ -0,0 +1,391 @@ +# grpc_logrus +`import "github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus"` + +* [Overview](#pkg-overview) +* [Imported Packages](#pkg-imports) +* [Index](#pkg-index) +* [Examples](#pkg-examples) + +## <a name="pkg-overview">Overview</a> +`grpc_logrus` is a gRPC logging middleware backed by Logrus loggers + +It accepts a user-configured `logrus.Entry` that will be used for logging completed gRPC calls. The same +`logrus.Entry` will be used for logging completed gRPC calls, and be populated into the `context.Context` passed into gRPC handler code. + +On calling `StreamServerInterceptor` or `UnaryServerInterceptor` this logging middleware will add gRPC call information +to the ctx so that it will be present on subsequent use of the `ctxlogrus` logger. + +This package also implements request and response *payload* logging, both for server-side and client-side. These will be +logged as structured `jsonpb` fields for every message received/sent (both unary and streaming). For that please use +`Payload*Interceptor` functions for that. Please note that the user-provided function that determines whetether to log +the full request/response payload needs to be written with care, this can significantly slow down gRPC. + +If a deadline is present on the gRPC request the grpc.request.deadline tag is populated when the request begins. grpc.request.deadline +is a string representing the time (RFC3339) when the current call will expire. + +Logrus can also be made as a backend for gRPC library internals. For that use `ReplaceGrpcLogger`. + +*Server Interceptor* +Below is a JSON formatted example of a log that would be logged by the server interceptor: + + { + "level": "info", // string logrus log levels + "msg": "finished unary call", // string log message + "grpc.code": "OK", // string grpc status code + "grpc.method": "Ping", // string method name + "grpc.service": "mwitkow.testproto.TestService", // string full name of the called service + "grpc.start_time": "2006-01-02T15:04:05Z07:00", // string RFC3339 representation of the start time + "grpc.request.deadline": "2006-01-02T15:04:05Z07:00", // string RFC3339 deadline of the current request if supplied + "grpc.request.value": "something", // string value on the request + "grpc.time_ms": 1.234, // float32 run time of the call in ms + "peer.address": { + "IP": "127.0.0.1", // string IP address of calling party + "Port": 60216, // int port call is coming in on + "Zone": "" // string peer zone for caller + }, + "span.kind": "server", // string client | server + "system": "grpc" // string + + "custom_field": "custom_value", // string user defined field + "custom_tags.int": 1337, // int user defined tag on the ctx + "custom_tags.string": "something", // string user defined tag on the ctx + } + +*Payload Interceptor* +Below is a JSON formatted example of a log that would be logged by the payload interceptor: + + { + "level": "info", // string logrus log levels + "msg": "client request payload logged as grpc.request.content", // string log message + + "grpc.request.content": { // object content of RPC request + "value": "something", // string defined by caller + "sleepTimeMs": 9999 // int defined by caller + }, + "grpc.method": "Ping", // string method being called + "grpc.service": "mwitkow.testproto.TestService", // string service being called + "span.kind": "client", // string client | server + "system": "grpc" // string + } + +Note - due to implementation ZAP differs from Logrus in the "grpc.request.content" object by having an inner "msg" object. + +Please see examples and tests for examples of use. + +#### Example: + +<details> +<summary>Click to expand code.</summary> + +```go +// Logrus entry is used, allowing pre-definition of certain fields by the user. +logrusEntry := logrus.NewEntry(logrusLogger) +// Shared options for the logger, with a custom gRPC code to log level function. +opts := []grpc_logrus.Option{ + grpc_logrus.WithLevels(customFunc), +} +// Make sure that log statements internal to gRPC library are logged using the logrus Logger as well. +grpc_logrus.ReplaceGrpcLogger(logrusEntry) +// Create a server, make sure we put the grpc_ctxtags context before everything else. +_ = grpc.NewServer( + grpc_middleware.WithUnaryServerChain( + grpc_ctxtags.UnaryServerInterceptor(grpc_ctxtags.WithFieldExtractor(grpc_ctxtags.CodeGenRequestFieldExtractor)), + grpc_logrus.UnaryServerInterceptor(logrusEntry, opts...), + ), + grpc_middleware.WithStreamServerChain( + grpc_ctxtags.StreamServerInterceptor(grpc_ctxtags.WithFieldExtractor(grpc_ctxtags.CodeGenRequestFieldExtractor)), + grpc_logrus.StreamServerInterceptor(logrusEntry, opts...), + ), +) +``` + +</details> + +#### Example: + +<details> +<summary>Click to expand code.</summary> + +```go +// Logrus entry is used, allowing pre-definition of certain fields by the user. +logrusEntry := logrus.NewEntry(logrusLogger) +// Shared options for the logger, with a custom duration to log field function. +opts := []grpc_logrus.Option{ + grpc_logrus.WithDurationField(func(duration time.Duration) (key string, value interface{}) { + return "grpc.time_ns", duration.Nanoseconds() + }), +} +_ = grpc.NewServer( + grpc_middleware.WithUnaryServerChain( + grpc_ctxtags.UnaryServerInterceptor(), + grpc_logrus.UnaryServerInterceptor(logrusEntry, opts...), + ), + grpc_middleware.WithStreamServerChain( + grpc_ctxtags.StreamServerInterceptor(), + grpc_logrus.StreamServerInterceptor(logrusEntry, opts...), + ), +) +``` + +</details> + +## <a name="pkg-imports">Imported Packages</a> + +- [github.com/golang/protobuf/jsonpb](https://godoc.org/github.com/golang/protobuf/jsonpb) +- [github.com/golang/protobuf/proto](https://godoc.org/github.com/golang/protobuf/proto) +- [github.com/grpc-ecosystem/go-grpc-middleware](./../..) +- [github.com/grpc-ecosystem/go-grpc-middleware/logging](./..) +- [github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus](./ctxlogrus) +- [github.com/grpc-ecosystem/go-grpc-middleware/tags/logrus](./../../tags/logrus) +- [github.com/sirupsen/logrus](https://godoc.org/github.com/sirupsen/logrus) +- [golang.org/x/net/context](https://godoc.org/golang.org/x/net/context) +- [google.golang.org/grpc](https://godoc.org/google.golang.org/grpc) +- [google.golang.org/grpc/codes](https://godoc.org/google.golang.org/grpc/codes) +- [google.golang.org/grpc/grpclog](https://godoc.org/google.golang.org/grpc/grpclog) + +## <a name="pkg-index">Index</a> +* [Variables](#pkg-variables) +* [func AddFields(ctx context.Context, fields logrus.Fields)](#AddFields) +* [func DefaultClientCodeToLevel(code codes.Code) logrus.Level](#DefaultClientCodeToLevel) +* [func DefaultCodeToLevel(code codes.Code) logrus.Level](#DefaultCodeToLevel) +* [func DurationToDurationField(duration time.Duration) (key string, value interface{})](#DurationToDurationField) +* [func DurationToTimeMillisField(duration time.Duration) (key string, value interface{})](#DurationToTimeMillisField) +* [func Extract(ctx context.Context) \*logrus.Entry](#Extract) +* [func PayloadStreamClientInterceptor(entry \*logrus.Entry, decider grpc\_logging.ClientPayloadLoggingDecider) grpc.StreamClientInterceptor](#PayloadStreamClientInterceptor) +* [func PayloadStreamServerInterceptor(entry \*logrus.Entry, decider grpc\_logging.ServerPayloadLoggingDecider) grpc.StreamServerInterceptor](#PayloadStreamServerInterceptor) +* [func PayloadUnaryClientInterceptor(entry \*logrus.Entry, decider grpc\_logging.ClientPayloadLoggingDecider) grpc.UnaryClientInterceptor](#PayloadUnaryClientInterceptor) +* [func PayloadUnaryServerInterceptor(entry \*logrus.Entry, decider grpc\_logging.ServerPayloadLoggingDecider) grpc.UnaryServerInterceptor](#PayloadUnaryServerInterceptor) +* [func ReplaceGrpcLogger(logger \*logrus.Entry)](#ReplaceGrpcLogger) +* [func StreamClientInterceptor(entry \*logrus.Entry, opts ...Option) grpc.StreamClientInterceptor](#StreamClientInterceptor) +* [func StreamServerInterceptor(entry \*logrus.Entry, opts ...Option) grpc.StreamServerInterceptor](#StreamServerInterceptor) +* [func UnaryClientInterceptor(entry \*logrus.Entry, opts ...Option) grpc.UnaryClientInterceptor](#UnaryClientInterceptor) +* [func UnaryServerInterceptor(entry \*logrus.Entry, opts ...Option) grpc.UnaryServerInterceptor](#UnaryServerInterceptor) +* [type CodeToLevel](#CodeToLevel) +* [type DurationToField](#DurationToField) +* [type Option](#Option) + * [func WithCodes(f grpc\_logging.ErrorToCode) Option](#WithCodes) + * [func WithDecider(f grpc\_logging.Decider) Option](#WithDecider) + * [func WithDurationField(f DurationToField) Option](#WithDurationField) + * [func WithLevels(f CodeToLevel) Option](#WithLevels) + +#### <a name="pkg-examples">Examples</a> +* [Extract (Unary)](#example_Extract_unary) +* [WithDecider](#example_WithDecider) +* [Package (Initialization)](#example__initialization) +* [Package (InitializationWithDurationFieldOverride)](#example__initializationWithDurationFieldOverride) + +#### <a name="pkg-files">Package files</a> +[client_interceptors.go](./client_interceptors.go) [context.go](./context.go) [doc.go](./doc.go) [grpclogger.go](./grpclogger.go) [options.go](./options.go) [payload_interceptors.go](./payload_interceptors.go) [server_interceptors.go](./server_interceptors.go) + +## <a name="pkg-variables">Variables</a> +``` go +var ( + // SystemField is used in every log statement made through grpc_logrus. Can be overwritten before any initialization code. + SystemField = "system" + + // KindField describes the log gield used to incicate whether this is a server or a client log statment. + KindField = "span.kind" +) +``` +``` go +var DefaultDurationToField = DurationToTimeMillisField +``` +DefaultDurationToField is the default implementation of converting request duration to a log field (key and value). + +``` go +var ( + // JsonPBMarshaller is the marshaller used for serializing protobuf messages. + JsonPbMarshaller = &jsonpb.Marshaler{} +) +``` + +## <a name="AddFields">func</a> [AddFields](./context.go#L11) +``` go +func AddFields(ctx context.Context, fields logrus.Fields) +``` +AddFields adds logrus fields to the logger. +Deprecated: should use the ctxlogrus.Extract instead + +## <a name="DefaultClientCodeToLevel">func</a> [DefaultClientCodeToLevel](./options.go#L129) +``` go +func DefaultClientCodeToLevel(code codes.Code) logrus.Level +``` +DefaultClientCodeToLevel is the default implementation of gRPC return codes to log levels for client side. + +## <a name="DefaultCodeToLevel">func</a> [DefaultCodeToLevel](./options.go#L87) +``` go +func DefaultCodeToLevel(code codes.Code) logrus.Level +``` +DefaultCodeToLevel is the default implementation of gRPC return codes to log levels for server side. + +## <a name="DurationToDurationField">func</a> [DurationToDurationField](./options.go#L179) +``` go +func DurationToDurationField(duration time.Duration) (key string, value interface{}) +``` +DurationToDurationField uses the duration value to log the request duration. + +## <a name="DurationToTimeMillisField">func</a> [DurationToTimeMillisField](./options.go#L174) +``` go +func DurationToTimeMillisField(duration time.Duration) (key string, value interface{}) +``` +DurationToTimeMillisField converts the duration to milliseconds and uses the key `grpc.time_ms`. + +## <a name="Extract">func</a> [Extract](./context.go#L17) +``` go +func Extract(ctx context.Context) *logrus.Entry +``` +Extract takes the call-scoped logrus.Entry from grpc_logrus middleware. +Deprecated: should use the ctxlogrus.Extract instead + +#### Example: + +<details> +<summary>Click to expand code.</summary> + +```go +_ = func(ctx context.Context, ping *pb_testproto.PingRequest) (*pb_testproto.PingResponse, error) { + // Add fields the ctxtags of the request which will be added to all extracted loggers. + grpc_ctxtags.Extract(ctx).Set("custom_tags.string", "something").Set("custom_tags.int", 1337) + // Extract a single request-scoped logrus.Logger and log messages. + l := ctx_logrus.Extract(ctx) + l.Info("some ping") + l.Info("another ping") + return &pb_testproto.PingResponse{Value: ping.Value}, nil +} +``` + +</details> + +## <a name="PayloadStreamClientInterceptor">func</a> [PayloadStreamClientInterceptor](./payload_interceptors.go#L74) +``` go +func PayloadStreamClientInterceptor(entry *logrus.Entry, decider grpc_logging.ClientPayloadLoggingDecider) grpc.StreamClientInterceptor +``` +PayloadStreamServerInterceptor returns a new streaming client interceptor that logs the paylods of requests and responses. + +## <a name="PayloadStreamServerInterceptor">func</a> [PayloadStreamServerInterceptor](./payload_interceptors.go#L45) +``` go +func PayloadStreamServerInterceptor(entry *logrus.Entry, decider grpc_logging.ServerPayloadLoggingDecider) grpc.StreamServerInterceptor +``` +PayloadUnaryServerInterceptor returns a new server server interceptors that logs the payloads of requests. + +This *only* works when placed *after* the `grpc_logrus.StreamServerInterceptor`. However, the logging can be done to a +separate instance of the logger. + +## <a name="PayloadUnaryClientInterceptor">func</a> [PayloadUnaryClientInterceptor](./payload_interceptors.go#L58) +``` go +func PayloadUnaryClientInterceptor(entry *logrus.Entry, decider grpc_logging.ClientPayloadLoggingDecider) grpc.UnaryClientInterceptor +``` +PayloadUnaryClientInterceptor returns a new unary client interceptor that logs the paylods of requests and responses. + +## <a name="PayloadUnaryServerInterceptor">func</a> [PayloadUnaryServerInterceptor](./payload_interceptors.go#L25) +``` go +func PayloadUnaryServerInterceptor(entry *logrus.Entry, decider grpc_logging.ServerPayloadLoggingDecider) grpc.UnaryServerInterceptor +``` +PayloadUnaryServerInterceptor returns a new unary server interceptors that logs the payloads of requests. + +This *only* works when placed *after* the `grpc_logrus.UnaryServerInterceptor`. However, the logging can be done to a +separate instance of the logger. + +## <a name="ReplaceGrpcLogger">func</a> [ReplaceGrpcLogger](./grpclogger.go#L13) +``` go +func ReplaceGrpcLogger(logger *logrus.Entry) +``` +ReplaceGrpcLogger sets the given logrus.Logger as a gRPC-level logger. +This should be called *before* any other initialization, preferably from init() functions. + +## <a name="StreamClientInterceptor">func</a> [StreamClientInterceptor](./client_interceptors.go#L28) +``` go +func StreamClientInterceptor(entry *logrus.Entry, opts ...Option) grpc.StreamClientInterceptor +``` +StreamServerInterceptor returns a new streaming client interceptor that optionally logs the execution of external gRPC calls. + +## <a name="StreamServerInterceptor">func</a> [StreamServerInterceptor](./server_interceptors.go#L58) +``` go +func StreamServerInterceptor(entry *logrus.Entry, opts ...Option) grpc.StreamServerInterceptor +``` +StreamServerInterceptor returns a new streaming server interceptor that adds logrus.Entry to the context. + +## <a name="UnaryClientInterceptor">func</a> [UnaryClientInterceptor](./client_interceptors.go#L16) +``` go +func UnaryClientInterceptor(entry *logrus.Entry, opts ...Option) grpc.UnaryClientInterceptor +``` +UnaryClientInterceptor returns a new unary client interceptor that optionally logs the execution of external gRPC calls. + +## <a name="UnaryServerInterceptor">func</a> [UnaryServerInterceptor](./server_interceptors.go#L26) +``` go +func UnaryServerInterceptor(entry *logrus.Entry, opts ...Option) grpc.UnaryServerInterceptor +``` +PayloadUnaryServerInterceptor returns a new unary server interceptors that adds logrus.Entry to the context. + +## <a name="CodeToLevel">type</a> [CodeToLevel](./options.go#L53) +``` go +type CodeToLevel func(code codes.Code) logrus.Level +``` +CodeToLevel function defines the mapping between gRPC return codes and interceptor log level. + +## <a name="DurationToField">type</a> [DurationToField](./options.go#L56) +``` go +type DurationToField func(duration time.Duration) (key string, value interface{}) +``` +DurationToField function defines how to produce duration fields for logging + +## <a name="Option">type</a> [Option](./options.go#L50) +``` go +type Option func(*options) +``` + +### <a name="WithCodes">func</a> [WithCodes](./options.go#L73) +``` go +func WithCodes(f grpc_logging.ErrorToCode) Option +``` +WithCodes customizes the function for mapping errors to error codes. + +### <a name="WithDecider">func</a> [WithDecider](./options.go#L59) +``` go +func WithDecider(f grpc_logging.Decider) Option +``` +WithDecider customizes the function for deciding if the gRPC interceptor logs should log. + +#### Example: + +<details> +<summary>Click to expand code.</summary> + +```go +opts := []grpc_logrus.Option{ + grpc_logrus.WithDecider(func(methodFullName string, err error) bool { + // will not log gRPC calls if it was a call to healthcheck and no error was raised + if err == nil && methodFullName == "blah.foo.healthcheck" { + return false + } + + // by default you will log all calls + return true + }), +} + +_ = []grpc.ServerOption{ + grpc_middleware.WithStreamServerChain( + grpc_ctxtags.StreamServerInterceptor(), + grpc_logrus.StreamServerInterceptor(logrus.NewEntry(logrus.New()), opts...)), + grpc_middleware.WithUnaryServerChain( + grpc_ctxtags.UnaryServerInterceptor(), + grpc_logrus.UnaryServerInterceptor(logrus.NewEntry(logrus.New()), opts...)), +} +``` + +</details> +### <a name="WithDurationField">func</a> [WithDurationField](./options.go#L80) +``` go +func WithDurationField(f DurationToField) Option +``` +WithDurationField customizes the function for mapping request durations to log fields. + +### <a name="WithLevels">func</a> [WithLevels](./options.go#L66) +``` go +func WithLevels(f CodeToLevel) Option +``` +WithLevels customizes the function for mapping gRPC return codes and interceptor log level statements. + +- - - +Generated by [godoc2ghmd](https://github.com/GandalfUK/godoc2ghmd)
\ No newline at end of file diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/README.md b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/README.md new file mode 100644 index 00000000..77ebb64c --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/README.md @@ -0,0 +1,391 @@ +# grpc_logrus +`import "github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus"` + +* [Overview](#pkg-overview) +* [Imported Packages](#pkg-imports) +* [Index](#pkg-index) +* [Examples](#pkg-examples) + +## <a name="pkg-overview">Overview</a> +`grpc_logrus` is a gRPC logging middleware backed by Logrus loggers + +It accepts a user-configured `logrus.Entry` that will be used for logging completed gRPC calls. The same +`logrus.Entry` will be used for logging completed gRPC calls, and be populated into the `context.Context` passed into gRPC handler code. + +On calling `StreamServerInterceptor` or `UnaryServerInterceptor` this logging middleware will add gRPC call information +to the ctx so that it will be present on subsequent use of the `ctxlogrus` logger. + +This package also implements request and response *payload* logging, both for server-side and client-side. These will be +logged as structured `jsonpb` fields for every message received/sent (both unary and streaming). For that please use +`Payload*Interceptor` functions for that. Please note that the user-provided function that determines whetether to log +the full request/response payload needs to be written with care, this can significantly slow down gRPC. + +If a deadline is present on the gRPC request the grpc.request.deadline tag is populated when the request begins. grpc.request.deadline +is a string representing the time (RFC3339) when the current call will expire. + +Logrus can also be made as a backend for gRPC library internals. For that use `ReplaceGrpcLogger`. + +*Server Interceptor* +Below is a JSON formatted example of a log that would be logged by the server interceptor: + + { + "level": "info", // string logrus log levels + "msg": "finished unary call", // string log message + "grpc.code": "OK", // string grpc status code + "grpc.method": "Ping", // string method name + "grpc.service": "mwitkow.testproto.TestService", // string full name of the called service + "grpc.start_time": "2006-01-02T15:04:05Z07:00", // string RFC3339 representation of the start time + "grpc.request.deadline": "2006-01-02T15:04:05Z07:00", // string RFC3339 deadline of the current request if supplied + "grpc.request.value": "something", // string value on the request + "grpc.time_ms": 1.234, // float32 run time of the call in ms + "peer.address": { + "IP": "127.0.0.1", // string IP address of calling party + "Port": 60216, // int port call is coming in on + "Zone": "" // string peer zone for caller + }, + "span.kind": "server", // string client | server + "system": "grpc" // string + + "custom_field": "custom_value", // string user defined field + "custom_tags.int": 1337, // int user defined tag on the ctx + "custom_tags.string": "something", // string user defined tag on the ctx + } + +*Payload Interceptor* +Below is a JSON formatted example of a log that would be logged by the payload interceptor: + + { + "level": "info", // string logrus log levels + "msg": "client request payload logged as grpc.request.content", // string log message + + "grpc.request.content": { // object content of RPC request + "value": "something", // string defined by caller + "sleepTimeMs": 9999 // int defined by caller + }, + "grpc.method": "Ping", // string method being called + "grpc.service": "mwitkow.testproto.TestService", // string service being called + "span.kind": "client", // string client | server + "system": "grpc" // string + } + +Note - due to implementation ZAP differs from Logrus in the "grpc.request.content" object by having an inner "msg" object. + +Please see examples and tests for examples of use. + +#### Example: + +<details> +<summary>Click to expand code.</summary> + +```go +// Logrus entry is used, allowing pre-definition of certain fields by the user. +logrusEntry := logrus.NewEntry(logrusLogger) +// Shared options for the logger, with a custom gRPC code to log level function. +opts := []grpc_logrus.Option{ + grpc_logrus.WithLevels(customFunc), +} +// Make sure that log statements internal to gRPC library are logged using the logrus Logger as well. +grpc_logrus.ReplaceGrpcLogger(logrusEntry) +// Create a server, make sure we put the grpc_ctxtags context before everything else. +_ = grpc.NewServer( + grpc_middleware.WithUnaryServerChain( + grpc_ctxtags.UnaryServerInterceptor(grpc_ctxtags.WithFieldExtractor(grpc_ctxtags.CodeGenRequestFieldExtractor)), + grpc_logrus.UnaryServerInterceptor(logrusEntry, opts...), + ), + grpc_middleware.WithStreamServerChain( + grpc_ctxtags.StreamServerInterceptor(grpc_ctxtags.WithFieldExtractor(grpc_ctxtags.CodeGenRequestFieldExtractor)), + grpc_logrus.StreamServerInterceptor(logrusEntry, opts...), + ), +) +``` + +</details> + +#### Example: + +<details> +<summary>Click to expand code.</summary> + +```go +// Logrus entry is used, allowing pre-definition of certain fields by the user. +logrusEntry := logrus.NewEntry(logrusLogger) +// Shared options for the logger, with a custom duration to log field function. +opts := []grpc_logrus.Option{ + grpc_logrus.WithDurationField(func(duration time.Duration) (key string, value interface{}) { + return "grpc.time_ns", duration.Nanoseconds() + }), +} +_ = grpc.NewServer( + grpc_middleware.WithUnaryServerChain( + grpc_ctxtags.UnaryServerInterceptor(), + grpc_logrus.UnaryServerInterceptor(logrusEntry, opts...), + ), + grpc_middleware.WithStreamServerChain( + grpc_ctxtags.StreamServerInterceptor(), + grpc_logrus.StreamServerInterceptor(logrusEntry, opts...), + ), +) +``` + +</details> + +## <a name="pkg-imports">Imported Packages</a> + +- [github.com/golang/protobuf/jsonpb](https://godoc.org/github.com/golang/protobuf/jsonpb) +- [github.com/golang/protobuf/proto](https://godoc.org/github.com/golang/protobuf/proto) +- [github.com/grpc-ecosystem/go-grpc-middleware](./../..) +- [github.com/grpc-ecosystem/go-grpc-middleware/logging](./..) +- [github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus](./ctxlogrus) +- [github.com/grpc-ecosystem/go-grpc-middleware/tags/logrus](./../../tags/logrus) +- [github.com/sirupsen/logrus](https://godoc.org/github.com/sirupsen/logrus) +- [golang.org/x/net/context](https://godoc.org/golang.org/x/net/context) +- [google.golang.org/grpc](https://godoc.org/google.golang.org/grpc) +- [google.golang.org/grpc/codes](https://godoc.org/google.golang.org/grpc/codes) +- [google.golang.org/grpc/grpclog](https://godoc.org/google.golang.org/grpc/grpclog) + +## <a name="pkg-index">Index</a> +* [Variables](#pkg-variables) +* [func AddFields(ctx context.Context, fields logrus.Fields)](#AddFields) +* [func DefaultClientCodeToLevel(code codes.Code) logrus.Level](#DefaultClientCodeToLevel) +* [func DefaultCodeToLevel(code codes.Code) logrus.Level](#DefaultCodeToLevel) +* [func DurationToDurationField(duration time.Duration) (key string, value interface{})](#DurationToDurationField) +* [func DurationToTimeMillisField(duration time.Duration) (key string, value interface{})](#DurationToTimeMillisField) +* [func Extract(ctx context.Context) \*logrus.Entry](#Extract) +* [func PayloadStreamClientInterceptor(entry \*logrus.Entry, decider grpc\_logging.ClientPayloadLoggingDecider) grpc.StreamClientInterceptor](#PayloadStreamClientInterceptor) +* [func PayloadStreamServerInterceptor(entry \*logrus.Entry, decider grpc\_logging.ServerPayloadLoggingDecider) grpc.StreamServerInterceptor](#PayloadStreamServerInterceptor) +* [func PayloadUnaryClientInterceptor(entry \*logrus.Entry, decider grpc\_logging.ClientPayloadLoggingDecider) grpc.UnaryClientInterceptor](#PayloadUnaryClientInterceptor) +* [func PayloadUnaryServerInterceptor(entry \*logrus.Entry, decider grpc\_logging.ServerPayloadLoggingDecider) grpc.UnaryServerInterceptor](#PayloadUnaryServerInterceptor) +* [func ReplaceGrpcLogger(logger \*logrus.Entry)](#ReplaceGrpcLogger) +* [func StreamClientInterceptor(entry \*logrus.Entry, opts ...Option) grpc.StreamClientInterceptor](#StreamClientInterceptor) +* [func StreamServerInterceptor(entry \*logrus.Entry, opts ...Option) grpc.StreamServerInterceptor](#StreamServerInterceptor) +* [func UnaryClientInterceptor(entry \*logrus.Entry, opts ...Option) grpc.UnaryClientInterceptor](#UnaryClientInterceptor) +* [func UnaryServerInterceptor(entry \*logrus.Entry, opts ...Option) grpc.UnaryServerInterceptor](#UnaryServerInterceptor) +* [type CodeToLevel](#CodeToLevel) +* [type DurationToField](#DurationToField) +* [type Option](#Option) + * [func WithCodes(f grpc\_logging.ErrorToCode) Option](#WithCodes) + * [func WithDecider(f grpc\_logging.Decider) Option](#WithDecider) + * [func WithDurationField(f DurationToField) Option](#WithDurationField) + * [func WithLevels(f CodeToLevel) Option](#WithLevels) + +#### <a name="pkg-examples">Examples</a> +* [Extract (Unary)](#example_Extract_unary) +* [WithDecider](#example_WithDecider) +* [Package (Initialization)](#example__initialization) +* [Package (InitializationWithDurationFieldOverride)](#example__initializationWithDurationFieldOverride) + +#### <a name="pkg-files">Package files</a> +[client_interceptors.go](./client_interceptors.go) [context.go](./context.go) [doc.go](./doc.go) [grpclogger.go](./grpclogger.go) [options.go](./options.go) [payload_interceptors.go](./payload_interceptors.go) [server_interceptors.go](./server_interceptors.go) + +## <a name="pkg-variables">Variables</a> +``` go +var ( + // SystemField is used in every log statement made through grpc_logrus. Can be overwritten before any initialization code. + SystemField = "system" + + // KindField describes the log gield used to incicate whether this is a server or a client log statment. + KindField = "span.kind" +) +``` +``` go +var DefaultDurationToField = DurationToTimeMillisField +``` +DefaultDurationToField is the default implementation of converting request duration to a log field (key and value). + +``` go +var ( + // JsonPBMarshaller is the marshaller used for serializing protobuf messages. + JsonPbMarshaller = &jsonpb.Marshaler{} +) +``` + +## <a name="AddFields">func</a> [AddFields](./context.go#L11) +``` go +func AddFields(ctx context.Context, fields logrus.Fields) +``` +AddFields adds logrus fields to the logger. +Deprecated: should use the ctxlogrus.Extract instead + +## <a name="DefaultClientCodeToLevel">func</a> [DefaultClientCodeToLevel](./options.go#L129) +``` go +func DefaultClientCodeToLevel(code codes.Code) logrus.Level +``` +DefaultClientCodeToLevel is the default implementation of gRPC return codes to log levels for client side. + +## <a name="DefaultCodeToLevel">func</a> [DefaultCodeToLevel](./options.go#L87) +``` go +func DefaultCodeToLevel(code codes.Code) logrus.Level +``` +DefaultCodeToLevel is the default implementation of gRPC return codes to log levels for server side. + +## <a name="DurationToDurationField">func</a> [DurationToDurationField](./options.go#L179) +``` go +func DurationToDurationField(duration time.Duration) (key string, value interface{}) +``` +DurationToDurationField uses the duration value to log the request duration. + +## <a name="DurationToTimeMillisField">func</a> [DurationToTimeMillisField](./options.go#L174) +``` go +func DurationToTimeMillisField(duration time.Duration) (key string, value interface{}) +``` +DurationToTimeMillisField converts the duration to milliseconds and uses the key `grpc.time_ms`. + +## <a name="Extract">func</a> [Extract](./context.go#L17) +``` go +func Extract(ctx context.Context) *logrus.Entry +``` +Extract takes the call-scoped logrus.Entry from grpc_logrus middleware. +Deprecated: should use the ctxlogrus.Extract instead + +#### Example: + +<details> +<summary>Click to expand code.</summary> + +```go +_ = func(ctx context.Context, ping *pb_testproto.PingRequest) (*pb_testproto.PingResponse, error) { + // Add fields the ctxtags of the request which will be added to all extracted loggers. + grpc_ctxtags.Extract(ctx).Set("custom_tags.string", "something").Set("custom_tags.int", 1337) + // Extract a single request-scoped logrus.Logger and log messages. + l := ctx_logrus.Extract(ctx) + l.Info("some ping") + l.Info("another ping") + return &pb_testproto.PingResponse{Value: ping.Value}, nil +} +``` + +</details> + +## <a name="PayloadStreamClientInterceptor">func</a> [PayloadStreamClientInterceptor](./payload_interceptors.go#L74) +``` go +func PayloadStreamClientInterceptor(entry *logrus.Entry, decider grpc_logging.ClientPayloadLoggingDecider) grpc.StreamClientInterceptor +``` +PayloadStreamServerInterceptor returns a new streaming client interceptor that logs the paylods of requests and responses. + +## <a name="PayloadStreamServerInterceptor">func</a> [PayloadStreamServerInterceptor](./payload_interceptors.go#L45) +``` go +func PayloadStreamServerInterceptor(entry *logrus.Entry, decider grpc_logging.ServerPayloadLoggingDecider) grpc.StreamServerInterceptor +``` +PayloadUnaryServerInterceptor returns a new server server interceptors that logs the payloads of requests. + +This *only* works when placed *after* the `grpc_logrus.StreamServerInterceptor`. However, the logging can be done to a +separate instance of the logger. + +## <a name="PayloadUnaryClientInterceptor">func</a> [PayloadUnaryClientInterceptor](./payload_interceptors.go#L58) +``` go +func PayloadUnaryClientInterceptor(entry *logrus.Entry, decider grpc_logging.ClientPayloadLoggingDecider) grpc.UnaryClientInterceptor +``` +PayloadUnaryClientInterceptor returns a new unary client interceptor that logs the paylods of requests and responses. + +## <a name="PayloadUnaryServerInterceptor">func</a> [PayloadUnaryServerInterceptor](./payload_interceptors.go#L25) +``` go +func PayloadUnaryServerInterceptor(entry *logrus.Entry, decider grpc_logging.ServerPayloadLoggingDecider) grpc.UnaryServerInterceptor +``` +PayloadUnaryServerInterceptor returns a new unary server interceptors that logs the payloads of requests. + +This *only* works when placed *after* the `grpc_logrus.UnaryServerInterceptor`. However, the logging can be done to a +separate instance of the logger. + +## <a name="ReplaceGrpcLogger">func</a> [ReplaceGrpcLogger](./grpclogger.go#L13) +``` go +func ReplaceGrpcLogger(logger *logrus.Entry) +``` +ReplaceGrpcLogger sets the given logrus.Logger as a gRPC-level logger. +This should be called *before* any other initialization, preferably from init() functions. + +## <a name="StreamClientInterceptor">func</a> [StreamClientInterceptor](./client_interceptors.go#L28) +``` go +func StreamClientInterceptor(entry *logrus.Entry, opts ...Option) grpc.StreamClientInterceptor +``` +StreamServerInterceptor returns a new streaming client interceptor that optionally logs the execution of external gRPC calls. + +## <a name="StreamServerInterceptor">func</a> [StreamServerInterceptor](./server_interceptors.go#L58) +``` go +func StreamServerInterceptor(entry *logrus.Entry, opts ...Option) grpc.StreamServerInterceptor +``` +StreamServerInterceptor returns a new streaming server interceptor that adds logrus.Entry to the context. + +## <a name="UnaryClientInterceptor">func</a> [UnaryClientInterceptor](./client_interceptors.go#L16) +``` go +func UnaryClientInterceptor(entry *logrus.Entry, opts ...Option) grpc.UnaryClientInterceptor +``` +UnaryClientInterceptor returns a new unary client interceptor that optionally logs the execution of external gRPC calls. + +## <a name="UnaryServerInterceptor">func</a> [UnaryServerInterceptor](./server_interceptors.go#L26) +``` go +func UnaryServerInterceptor(entry *logrus.Entry, opts ...Option) grpc.UnaryServerInterceptor +``` +PayloadUnaryServerInterceptor returns a new unary server interceptors that adds logrus.Entry to the context. + +## <a name="CodeToLevel">type</a> [CodeToLevel](./options.go#L53) +``` go +type CodeToLevel func(code codes.Code) logrus.Level +``` +CodeToLevel function defines the mapping between gRPC return codes and interceptor log level. + +## <a name="DurationToField">type</a> [DurationToField](./options.go#L56) +``` go +type DurationToField func(duration time.Duration) (key string, value interface{}) +``` +DurationToField function defines how to produce duration fields for logging + +## <a name="Option">type</a> [Option](./options.go#L50) +``` go +type Option func(*options) +``` + +### <a name="WithCodes">func</a> [WithCodes](./options.go#L73) +``` go +func WithCodes(f grpc_logging.ErrorToCode) Option +``` +WithCodes customizes the function for mapping errors to error codes. + +### <a name="WithDecider">func</a> [WithDecider](./options.go#L59) +``` go +func WithDecider(f grpc_logging.Decider) Option +``` +WithDecider customizes the function for deciding if the gRPC interceptor logs should log. + +#### Example: + +<details> +<summary>Click to expand code.</summary> + +```go +opts := []grpc_logrus.Option{ + grpc_logrus.WithDecider(func(methodFullName string, err error) bool { + // will not log gRPC calls if it was a call to healthcheck and no error was raised + if err == nil && methodFullName == "blah.foo.healthcheck" { + return false + } + + // by default you will log all calls + return true + }), +} + +_ = []grpc.ServerOption{ + grpc_middleware.WithStreamServerChain( + grpc_ctxtags.StreamServerInterceptor(), + grpc_logrus.StreamServerInterceptor(logrus.NewEntry(logrus.New()), opts...)), + grpc_middleware.WithUnaryServerChain( + grpc_ctxtags.UnaryServerInterceptor(), + grpc_logrus.UnaryServerInterceptor(logrus.NewEntry(logrus.New()), opts...)), +} +``` + +</details> +### <a name="WithDurationField">func</a> [WithDurationField](./options.go#L80) +``` go +func WithDurationField(f DurationToField) Option +``` +WithDurationField customizes the function for mapping request durations to log fields. + +### <a name="WithLevels">func</a> [WithLevels](./options.go#L66) +``` go +func WithLevels(f CodeToLevel) Option +``` +WithLevels customizes the function for mapping gRPC return codes and interceptor log level statements. + +- - - +Generated by [godoc2ghmd](https://github.com/GandalfUK/godoc2ghmd)
\ No newline at end of file diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/client_interceptors.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/client_interceptors.go new file mode 100644 index 00000000..f7655ff6 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/client_interceptors.go @@ -0,0 +1,65 @@ +// Copyright 2017 Michal Witkowski. All Rights Reserved. +// See LICENSE for licensing terms. + +package grpc_logrus + +import ( + "path" + "time" + + "github.com/sirupsen/logrus" + "golang.org/x/net/context" + "google.golang.org/grpc" +) + +// UnaryClientInterceptor returns a new unary client interceptor that optionally logs the execution of external gRPC calls. +func UnaryClientInterceptor(entry *logrus.Entry, opts ...Option) grpc.UnaryClientInterceptor { + o := evaluateClientOpt(opts) + return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { + fields := newClientLoggerFields(ctx, method) + startTime := time.Now() + err := invoker(ctx, method, req, reply, cc, opts...) + logFinalClientLine(o, entry.WithFields(fields), startTime, err, "finished client unary call") + return err + } +} + +// StreamServerInterceptor returns a new streaming client interceptor that optionally logs the execution of external gRPC calls. +func StreamClientInterceptor(entry *logrus.Entry, opts ...Option) grpc.StreamClientInterceptor { + o := evaluateClientOpt(opts) + return func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) { + fields := newClientLoggerFields(ctx, method) + startTime := time.Now() + clientStream, err := streamer(ctx, desc, cc, method, opts...) + logFinalClientLine(o, entry.WithFields(fields), startTime, err, "finished client streaming call") + return clientStream, err + } +} + +func logFinalClientLine(o *options, entry *logrus.Entry, startTime time.Time, err error, msg string) { + code := o.codeFunc(err) + level := o.levelFunc(code) + durField, durVal := o.durationFunc(time.Now().Sub(startTime)) + fields := logrus.Fields{ + "grpc.code": code.String(), + durField: durVal, + } + if err != nil { + fields[logrus.ErrorKey] = err + } + levelLogf( + entry.WithFields(fields), + level, + msg) +} + +func newClientLoggerFields(ctx context.Context, fullMethodString string) logrus.Fields { + service := path.Dir(fullMethodString)[1:] + method := path.Base(fullMethodString) + return logrus.Fields{ + SystemField: "grpc", + KindField: "client", + "grpc.service": service, + "grpc.method": method, + } +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/context.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/context.go new file mode 100644 index 00000000..a39bb772 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/context.go @@ -0,0 +1,19 @@ +package grpc_logrus + +import ( + "github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus" + "github.com/sirupsen/logrus" + "golang.org/x/net/context" +) + +// AddFields adds logrus fields to the logger. +// Deprecated: should use the ctxlogrus.Extract instead +func AddFields(ctx context.Context, fields logrus.Fields) { + ctxlogrus.AddFields(ctx, fields) +} + +// Extract takes the call-scoped logrus.Entry from grpc_logrus middleware. +// Deprecated: should use the ctxlogrus.Extract instead +func Extract(ctx context.Context) *logrus.Entry { + return ctxlogrus.Extract(ctx) +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus/DOC.md b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus/DOC.md new file mode 100644 index 00000000..90918029 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus/DOC.md @@ -0,0 +1,93 @@ +# ctxlogrus +`import "github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus"` + +* [Overview](#pkg-overview) +* [Imported Packages](#pkg-imports) +* [Index](#pkg-index) +* [Examples](#pkg-examples) + +## <a name="pkg-overview">Overview</a> +`ctxlogrus` is a ctxlogger that is backed by logrus + +It accepts a user-configured `logrus.Logger` that will be used for logging. The same `logrus.Logger` will +be populated into the `context.Context` passed into gRPC handler code. + +You can use `ctx_logrus.Extract` to log into a request-scoped `logrus.Logger` instance in your handler code. + +As `ctx_logrus.Extract` will iterate all tags on from `grpc_ctxtags` it is therefore expensive so it is advised that you +extract once at the start of the function from the context and reuse it for the remainder of the function (see examples). + +Please see examples and tests for examples of use. + +## <a name="pkg-imports">Imported Packages</a> + +- [github.com/grpc-ecosystem/go-grpc-middleware/tags](./../../../tags) +- [github.com/sirupsen/logrus](https://godoc.org/github.com/sirupsen/logrus) +- [golang.org/x/net/context](https://godoc.org/golang.org/x/net/context) + +## <a name="pkg-index">Index</a> +* [func AddFields(ctx context.Context, fields logrus.Fields)](#AddFields) +* [func Extract(ctx context.Context) \*logrus.Entry](#Extract) +* [func ToContext(ctx context.Context, entry \*logrus.Entry) context.Context](#ToContext) + +#### <a name="pkg-examples">Examples</a> +* [Extract (Unary)](#example_Extract_unary) + +#### <a name="pkg-files">Package files</a> +[context.go](./context.go) [doc.go](./doc.go) [noop.go](./noop.go) + +## <a name="AddFields">func</a> [AddFields](./context.go#L21) +``` go +func AddFields(ctx context.Context, fields logrus.Fields) +``` +AddFields adds logrus fields to the logger. + +## <a name="Extract">func</a> [Extract](./context.go#L35) +``` go +func Extract(ctx context.Context) *logrus.Entry +``` +Extract takes the call-scoped logrus.Entry from ctx_logrus middleware. + +If the ctx_logrus middleware wasn't used, a no-op `logrus.Entry` is returned. This makes it safe to +use regardless. + +#### Example: + +<details> +<summary>Click to expand code.</summary> + +```go +package ctxlogrus_test + +import ( + "github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus" + "github.com/grpc-ecosystem/go-grpc-middleware/tags" + "github.com/sirupsen/logrus" + "golang.org/x/net/context" +) + +var logrusLogger *logrus.Logger + +// Simple unary handler that adds custom fields to the requests's context. These will be used for all log statements. +func ExampleExtract_unary() { + ctx := context.Background() + // setting tags will be added to the logger as log fields + grpc_ctxtags.Extract(ctx).Set("custom_tags.string", "something").Set("custom_tags.int", 1337) + // Extract a single request-scoped logrus.Logger and log messages. + l := ctxlogrus.Extract(ctx) + l.Info("some ping") + l.Info("another ping") +} +``` + +</details> + +## <a name="ToContext">func</a> [ToContext](./context.go#L59) +``` go +func ToContext(ctx context.Context, entry *logrus.Entry) context.Context +``` +ToContext adds the logrus.Entry to the context for extraction later. +Returning the new context that has been created. + +- - - +Generated by [godoc2ghmd](https://github.com/GandalfUK/godoc2ghmd)
\ No newline at end of file diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus/README.md b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus/README.md new file mode 100644 index 00000000..fc1d0a92 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus/README.md @@ -0,0 +1,58 @@ +# ctx_logrus +`import "github.com/grpc-ecosystem/go-grpc-middleware/tags/logrus"` + +* [Overview](#pkg-overview) +* [Imported Packages](#pkg-imports) +* [Index](#pkg-index) + +## <a name="pkg-overview">Overview</a> +`ctx_logrus` is a ctxlogger that is backed by logrus + +It accepts a user-configured `logrus.Logger` that will be used for logging. The same `logrus.Logger` will +be populated into the `context.Context` passed into gRPC handler code. + +You can use `ctx_logrus.Extract` to log into a request-scoped `logrus.Logger` instance in your handler code. + +As `ctx_logrus.Extract` will iterate all tags on from `grpc_ctxtags` it is therefore expensive so it is advised that you +extract once at the start of the function from the context and reuse it for the remainder of the function (see examples). + +Please see examples and tests for examples of use. + +## <a name="pkg-imports">Imported Packages</a> + +- [github.com/grpc-ecosystem/go-grpc-middleware/tags](./..) +- [github.com/sirupsen/logrus](https://godoc.org/github.com/sirupsen/logrus) +- [golang.org/x/net/context](https://godoc.org/golang.org/x/net/context) + +## <a name="pkg-index">Index</a> +* [func AddFields(ctx context.Context, fields logrus.Fields)](#AddFields) +* [func Extract(ctx context.Context) \*logrus.Entry](#Extract) +* [func ToContext(ctx context.Context, entry \*logrus.Entry) context.Context](#ToContext) + +#### <a name="pkg-files">Package files</a> +[context.go](./context.go) [doc.go](./doc.go) [noop.go](./noop.go) + +## <a name="AddFields">func</a> [AddFields](./context.go#L21) +``` go +func AddFields(ctx context.Context, fields logrus.Fields) +``` +AddFields adds logrus fields to the logger. + +## <a name="Extract">func</a> [Extract](./context.go#L35) +``` go +func Extract(ctx context.Context) *logrus.Entry +``` +Extract takes the call-scoped logrus.Entry from ctx_logrus middleware. + +If the ctx_logrus middleware wasn't used, a no-op `logrus.Entry` is returned. This makes it safe to +use regardless. + +## <a name="ToContext">func</a> [ToContext](./context.go#L59) +``` go +func ToContext(ctx context.Context, entry *logrus.Entry) context.Context +``` +ToContext adds the logrus.Entry to the context for extraction later. +Returning the new context that has been created. + +- - - +Generated by [godoc2ghmd](https://github.com/GandalfUK/godoc2ghmd)
\ No newline at end of file diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus/context.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus/context.go new file mode 100644 index 00000000..ff3e3353 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus/context.go @@ -0,0 +1,65 @@ +package ctxlogrus + +import ( + "github.com/grpc-ecosystem/go-grpc-middleware/tags" + "github.com/sirupsen/logrus" + "golang.org/x/net/context" +) + +type ctxLoggerMarker struct{} + +type ctxLogger struct { + logger *logrus.Entry + fields logrus.Fields +} + +var ( + ctxLoggerKey = &ctxLoggerMarker{} +) + +// AddFields adds logrus fields to the logger. +func AddFields(ctx context.Context, fields logrus.Fields) { + l, ok := ctx.Value(ctxLoggerKey).(*ctxLogger) + if !ok || l == nil { + return + } + for k, v := range fields { + l.fields[k] = v + } +} + +// Extract takes the call-scoped logrus.Entry from ctx_logrus middleware. +// +// If the ctx_logrus middleware wasn't used, a no-op `logrus.Entry` is returned. This makes it safe to +// use regardless. +func Extract(ctx context.Context) *logrus.Entry { + l, ok := ctx.Value(ctxLoggerKey).(*ctxLogger) + if !ok || l == nil { + return logrus.NewEntry(nullLogger) + } + + fields := logrus.Fields{} + + // Add grpc_ctxtags tags metadata until now. + tags := grpc_ctxtags.Extract(ctx) + for k, v := range tags.Values() { + fields[k] = v + } + + // Add logrus fields added until now. + for k, v := range l.fields { + fields[k] = v + } + + return l.logger.WithFields(fields) +} + +// ToContext adds the logrus.Entry to the context for extraction later. +// Returning the new context that has been created. +func ToContext(ctx context.Context, entry *logrus.Entry) context.Context { + l := &ctxLogger{ + logger: entry, + fields: logrus.Fields{}, + } + return context.WithValue(ctx, ctxLoggerKey, l) +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus/doc.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus/doc.go new file mode 100644 index 00000000..95803fb6 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus/doc.go @@ -0,0 +1,14 @@ +/* +`ctxlogrus` is a ctxlogger that is backed by logrus + +It accepts a user-configured `logrus.Logger` that will be used for logging. The same `logrus.Logger` will +be populated into the `context.Context` passed into gRPC handler code. + +You can use `ctx_logrus.Extract` to log into a request-scoped `logrus.Logger` instance in your handler code. + +As `ctx_logrus.Extract` will iterate all tags on from `grpc_ctxtags` it is therefore expensive so it is advised that you +extract once at the start of the function from the context and reuse it for the remainder of the function (see examples). + +Please see examples and tests for examples of use. +*/ +package ctxlogrus diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus/noop.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus/noop.go new file mode 100644 index 00000000..7fcc0f64 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus/noop.go @@ -0,0 +1,16 @@ +package ctxlogrus + +import ( + "io/ioutil" + + "github.com/sirupsen/logrus" +) + +var ( + nullLogger = &logrus.Logger{ + Out: ioutil.Discard, + Formatter: new(logrus.TextFormatter), + Hooks: make(logrus.LevelHooks), + Level: logrus.PanicLevel, + } +) diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/doc.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/doc.go new file mode 100644 index 00000000..df8785f5 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/doc.go @@ -0,0 +1,67 @@ +/* +`grpc_logrus` is a gRPC logging middleware backed by Logrus loggers + +It accepts a user-configured `logrus.Entry` that will be used for logging completed gRPC calls. The same +`logrus.Entry` will be used for logging completed gRPC calls, and be populated into the `context.Context` passed into gRPC handler code. + +On calling `StreamServerInterceptor` or `UnaryServerInterceptor` this logging middleware will add gRPC call information +to the ctx so that it will be present on subsequent use of the `ctxlogrus` logger. + +This package also implements request and response *payload* logging, both for server-side and client-side. These will be +logged as structured `jsonpb` fields for every message received/sent (both unary and streaming). For that please use +`Payload*Interceptor` functions for that. Please note that the user-provided function that determines whetether to log +the full request/response payload needs to be written with care, this can significantly slow down gRPC. + +If a deadline is present on the gRPC request the grpc.request.deadline tag is populated when the request begins. grpc.request.deadline +is a string representing the time (RFC3339) when the current call will expire. + +Logrus can also be made as a backend for gRPC library internals. For that use `ReplaceGrpcLogger`. + +*Server Interceptor* +Below is a JSON formatted example of a log that would be logged by the server interceptor: + + { + "level": "info", // string logrus log levels + "msg": "finished unary call", // string log message + "grpc.code": "OK", // string grpc status code + "grpc.method": "Ping", // string method name + "grpc.service": "mwitkow.testproto.TestService", // string full name of the called service + "grpc.start_time": "2006-01-02T15:04:05Z07:00", // string RFC3339 representation of the start time + "grpc.request.deadline": "2006-01-02T15:04:05Z07:00", // string RFC3339 deadline of the current request if supplied + "grpc.request.value": "something", // string value on the request + "grpc.time_ms": 1.234, // float32 run time of the call in ms + "peer.address": { + "IP": "127.0.0.1", // string IP address of calling party + "Port": 60216, // int port call is coming in on + "Zone": "" // string peer zone for caller + }, + "span.kind": "server", // string client | server + "system": "grpc" // string + + "custom_field": "custom_value", // string user defined field + "custom_tags.int": 1337, // int user defined tag on the ctx + "custom_tags.string": "something", // string user defined tag on the ctx + } + +*Payload Interceptor* +Below is a JSON formatted example of a log that would be logged by the payload interceptor: + + { + "level": "info", // string logrus log levels + "msg": "client request payload logged as grpc.request.content", // string log message + + "grpc.request.content": { // object content of RPC request + "value": "something", // string defined by caller + "sleepTimeMs": 9999 // int defined by caller + }, + "grpc.method": "Ping", // string method being called + "grpc.service": "mwitkow.testproto.TestService", // string service being called + "span.kind": "client", // string client | server + "system": "grpc" // string + } + +Note - due to implementation ZAP differs from Logrus in the "grpc.request.content" object by having an inner "msg" object. + +Please see examples and tests for examples of use. +*/ +package grpc_logrus diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/grpclogger.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/grpclogger.go new file mode 100644 index 00000000..c0f6c5ab --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/grpclogger.go @@ -0,0 +1,15 @@ +// Copyright 2017 Michal Witkowski. All Rights Reserved. +// See LICENSE for licensing terms. + +package grpc_logrus + +import ( + "github.com/sirupsen/logrus" + "google.golang.org/grpc/grpclog" +) + +// ReplaceGrpcLogger sets the given logrus.Logger as a gRPC-level logger. +// This should be called *before* any other initialization, preferably from init() functions. +func ReplaceGrpcLogger(logger *logrus.Entry) { + grpclog.SetLogger(logger.WithField("system", SystemField)) +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/options.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/options.go new file mode 100644 index 00000000..b2b35189 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/options.go @@ -0,0 +1,185 @@ +// Copyright 2017 Michal Witkowski. All Rights Reserved. +// See LICENSE for licensing terms. + +package grpc_logrus + +import ( + "time" + + "github.com/grpc-ecosystem/go-grpc-middleware/logging" + "github.com/sirupsen/logrus" + "google.golang.org/grpc/codes" +) + +var ( + defaultOptions = &options{ + levelFunc: nil, + shouldLog: grpc_logging.DefaultDeciderMethod, + codeFunc: grpc_logging.DefaultErrorToCode, + durationFunc: DefaultDurationToField, + } +) + +type options struct { + levelFunc CodeToLevel + shouldLog grpc_logging.Decider + codeFunc grpc_logging.ErrorToCode + durationFunc DurationToField +} + +func evaluateServerOpt(opts []Option) *options { + optCopy := &options{} + *optCopy = *defaultOptions + optCopy.levelFunc = DefaultCodeToLevel + for _, o := range opts { + o(optCopy) + } + return optCopy +} + +func evaluateClientOpt(opts []Option) *options { + optCopy := &options{} + *optCopy = *defaultOptions + optCopy.levelFunc = DefaultClientCodeToLevel + for _, o := range opts { + o(optCopy) + } + return optCopy +} + +type Option func(*options) + +// CodeToLevel function defines the mapping between gRPC return codes and interceptor log level. +type CodeToLevel func(code codes.Code) logrus.Level + +// DurationToField function defines how to produce duration fields for logging +type DurationToField func(duration time.Duration) (key string, value interface{}) + +// WithDecider customizes the function for deciding if the gRPC interceptor logs should log. +func WithDecider(f grpc_logging.Decider) Option { + return func(o *options) { + o.shouldLog = f + } +} + +// WithLevels customizes the function for mapping gRPC return codes and interceptor log level statements. +func WithLevels(f CodeToLevel) Option { + return func(o *options) { + o.levelFunc = f + } +} + +// WithCodes customizes the function for mapping errors to error codes. +func WithCodes(f grpc_logging.ErrorToCode) Option { + return func(o *options) { + o.codeFunc = f + } +} + +// WithDurationField customizes the function for mapping request durations to log fields. +func WithDurationField(f DurationToField) Option { + return func(o *options) { + o.durationFunc = f + } +} + +// DefaultCodeToLevel is the default implementation of gRPC return codes to log levels for server side. +func DefaultCodeToLevel(code codes.Code) logrus.Level { + switch code { + case codes.OK: + return logrus.InfoLevel + case codes.Canceled: + return logrus.InfoLevel + case codes.Unknown: + return logrus.ErrorLevel + case codes.InvalidArgument: + return logrus.InfoLevel + case codes.DeadlineExceeded: + return logrus.WarnLevel + case codes.NotFound: + return logrus.InfoLevel + case codes.AlreadyExists: + return logrus.InfoLevel + case codes.PermissionDenied: + return logrus.WarnLevel + case codes.Unauthenticated: + return logrus.InfoLevel // unauthenticated requests can happen + case codes.ResourceExhausted: + return logrus.WarnLevel + case codes.FailedPrecondition: + return logrus.WarnLevel + case codes.Aborted: + return logrus.WarnLevel + case codes.OutOfRange: + return logrus.WarnLevel + case codes.Unimplemented: + return logrus.ErrorLevel + case codes.Internal: + return logrus.ErrorLevel + case codes.Unavailable: + return logrus.WarnLevel + case codes.DataLoss: + return logrus.ErrorLevel + default: + return logrus.ErrorLevel + } +} + +// DefaultClientCodeToLevel is the default implementation of gRPC return codes to log levels for client side. +func DefaultClientCodeToLevel(code codes.Code) logrus.Level { + switch code { + case codes.OK: + return logrus.DebugLevel + case codes.Canceled: + return logrus.DebugLevel + case codes.Unknown: + return logrus.InfoLevel + case codes.InvalidArgument: + return logrus.DebugLevel + case codes.DeadlineExceeded: + return logrus.InfoLevel + case codes.NotFound: + return logrus.DebugLevel + case codes.AlreadyExists: + return logrus.DebugLevel + case codes.PermissionDenied: + return logrus.InfoLevel + case codes.Unauthenticated: + return logrus.InfoLevel // unauthenticated requests can happen + case codes.ResourceExhausted: + return logrus.DebugLevel + case codes.FailedPrecondition: + return logrus.DebugLevel + case codes.Aborted: + return logrus.DebugLevel + case codes.OutOfRange: + return logrus.DebugLevel + case codes.Unimplemented: + return logrus.WarnLevel + case codes.Internal: + return logrus.WarnLevel + case codes.Unavailable: + return logrus.WarnLevel + case codes.DataLoss: + return logrus.WarnLevel + default: + return logrus.InfoLevel + } +} + +// DefaultDurationToField is the default implementation of converting request duration to a log field (key and value). +var DefaultDurationToField = DurationToTimeMillisField + +// DurationToTimeMillisField converts the duration to milliseconds and uses the key `grpc.time_ms`. +func DurationToTimeMillisField(duration time.Duration) (key string, value interface{}) { + return "grpc.time_ms", durationToMilliseconds(duration) +} + +// DurationToDurationField uses the duration value to log the request duration. +func DurationToDurationField(duration time.Duration) (key string, value interface{}) { + return "grpc.duration", duration +} + +func durationToMilliseconds(duration time.Duration) float32 { + return float32(duration.Nanoseconds()/1000) / 1000 +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/payload_interceptors.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/payload_interceptors.go new file mode 100644 index 00000000..6ac4d04a --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/payload_interceptors.go @@ -0,0 +1,144 @@ +package grpc_logrus + +import ( + "bytes" + "fmt" + + "github.com/golang/protobuf/jsonpb" + "github.com/golang/protobuf/proto" + "github.com/grpc-ecosystem/go-grpc-middleware/logging" + "github.com/grpc-ecosystem/go-grpc-middleware/tags/logrus" + "github.com/sirupsen/logrus" + "golang.org/x/net/context" + "google.golang.org/grpc" +) + +var ( + // JsonPBMarshaller is the marshaller used for serializing protobuf messages. + JsonPbMarshaller = &jsonpb.Marshaler{} +) + +// PayloadUnaryServerInterceptor returns a new unary server interceptors that logs the payloads of requests. +// +// This *only* works when placed *after* the `grpc_logrus.UnaryServerInterceptor`. However, the logging can be done to a +// separate instance of the logger. +func PayloadUnaryServerInterceptor(entry *logrus.Entry, decider grpc_logging.ServerPayloadLoggingDecider) grpc.UnaryServerInterceptor { + return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { + if !decider(ctx, info.FullMethod, info.Server) { + return handler(ctx, req) + } + // Use the provided logrus.Entry for logging but use the fields from context. + logEntry := entry.WithFields(ctx_logrus.Extract(ctx).Data) + logProtoMessageAsJson(logEntry, req, "grpc.request.content", "server request payload logged as grpc.request.content field") + resp, err := handler(ctx, req) + if err == nil { + logProtoMessageAsJson(logEntry, resp, "grpc.response.content", "server response payload logged as grpc.request.content field") + } + return resp, err + } +} + +// PayloadUnaryServerInterceptor returns a new server server interceptors that logs the payloads of requests. +// +// This *only* works when placed *after* the `grpc_logrus.StreamServerInterceptor`. However, the logging can be done to a +// separate instance of the logger. +func PayloadStreamServerInterceptor(entry *logrus.Entry, decider grpc_logging.ServerPayloadLoggingDecider) grpc.StreamServerInterceptor { + return func(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { + if !decider(stream.Context(), info.FullMethod, srv) { + return handler(srv, stream) + } + // Use the provided logrus.Entry for logging but use the fields from context. + logEntry := entry.WithFields(Extract(stream.Context()).Data) + newStream := &loggingServerStream{ServerStream: stream, entry: logEntry} + return handler(srv, newStream) + } +} + +// PayloadUnaryClientInterceptor returns a new unary client interceptor that logs the paylods of requests and responses. +func PayloadUnaryClientInterceptor(entry *logrus.Entry, decider grpc_logging.ClientPayloadLoggingDecider) grpc.UnaryClientInterceptor { + return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { + if !decider(ctx, method) { + return invoker(ctx, method, req, reply, cc, opts...) + } + logEntry := entry.WithFields(newClientLoggerFields(ctx, method)) + logProtoMessageAsJson(logEntry, req, "grpc.request.content", "client request payload logged as grpc.request.content") + err := invoker(ctx, method, req, reply, cc, opts...) + if err == nil { + logProtoMessageAsJson(logEntry, reply, "grpc.response.content", "client response payload logged as grpc.response.content") + } + return err + } +} + +// PayloadStreamServerInterceptor returns a new streaming client interceptor that logs the paylods of requests and responses. +func PayloadStreamClientInterceptor(entry *logrus.Entry, decider grpc_logging.ClientPayloadLoggingDecider) grpc.StreamClientInterceptor { + return func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) { + if !decider(ctx, method) { + return streamer(ctx, desc, cc, method, opts...) + } + logEntry := entry.WithFields(newClientLoggerFields(ctx, method)) + clientStream, err := streamer(ctx, desc, cc, method, opts...) + newStream := &loggingClientStream{ClientStream: clientStream, entry: logEntry} + return newStream, err + } +} + +type loggingClientStream struct { + grpc.ClientStream + entry *logrus.Entry +} + +func (l *loggingClientStream) SendMsg(m interface{}) error { + err := l.ClientStream.SendMsg(m) + if err == nil { + logProtoMessageAsJson(l.entry, m, "grpc.request.content", "server request payload logged as grpc.request.content field") + } + return err +} + +func (l *loggingClientStream) RecvMsg(m interface{}) error { + err := l.ClientStream.RecvMsg(m) + if err == nil { + logProtoMessageAsJson(l.entry, m, "grpc.response.content", "server response payload logged as grpc.response.content field") + } + return err +} + +type loggingServerStream struct { + grpc.ServerStream + entry *logrus.Entry +} + +func (l *loggingServerStream) SendMsg(m interface{}) error { + err := l.ServerStream.SendMsg(m) + if err == nil { + logProtoMessageAsJson(l.entry, m, "grpc.response.content", "server response payload logged as grpc.response.content field") + } + return err +} + +func (l *loggingServerStream) RecvMsg(m interface{}) error { + err := l.ServerStream.RecvMsg(m) + if err == nil { + logProtoMessageAsJson(l.entry, m, "grpc.request.content", "server request payload logged as grpc.request.content field") + } + return err +} + +func logProtoMessageAsJson(entry *logrus.Entry, pbMsg interface{}, key string, msg string) { + if p, ok := pbMsg.(proto.Message); ok { + entry.WithField(key, &jsonpbMarshalleble{p}).Info(msg) + } +} + +type jsonpbMarshalleble struct { + proto.Message +} + +func (j *jsonpbMarshalleble) MarshalJSON() ([]byte, error) { + b := &bytes.Buffer{} + if err := JsonPbMarshaller.Marshal(b, j.Message); err != nil { + return nil, fmt.Errorf("jsonpb serializer failed: %v", err) + } + return b.Bytes(), nil +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/server_interceptors.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/server_interceptors.go new file mode 100644 index 00000000..0711d881 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/server_interceptors.go @@ -0,0 +1,129 @@ +// Copyright (c) Improbable Worlds Ltd, All Rights Reserved + +package grpc_logrus + +import ( + "path" + "time" + + "github.com/grpc-ecosystem/go-grpc-middleware" + "github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus" + "github.com/grpc-ecosystem/go-grpc-middleware/tags/logrus" + "github.com/sirupsen/logrus" + "golang.org/x/net/context" + "google.golang.org/grpc" +) + +var ( + // SystemField is used in every log statement made through grpc_logrus. Can be overwritten before any initialization code. + SystemField = "system" + + // KindField describes the log gield used to incicate whether this is a server or a client log statment. + KindField = "span.kind" +) + +// PayloadUnaryServerInterceptor returns a new unary server interceptors that adds logrus.Entry to the context. +func UnaryServerInterceptor(entry *logrus.Entry, opts ...Option) grpc.UnaryServerInterceptor { + o := evaluateServerOpt(opts) + return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { + startTime := time.Now() + newCtx := newLoggerForCall(ctx, entry, info.FullMethod, startTime) + + resp, err := handler(newCtx, req) + + if !o.shouldLog(info.FullMethod, err) { + return resp, err + } + code := o.codeFunc(err) + level := o.levelFunc(code) + durField, durVal := o.durationFunc(time.Since(startTime)) + fields := logrus.Fields{ + "grpc.code": code.String(), + durField: durVal, + } + if err != nil { + fields[logrus.ErrorKey] = err + } + + levelLogf( + ctx_logrus.Extract(newCtx).WithFields(fields), // re-extract logger from newCtx, as it may have extra fields that changed in the holder. + level, + "finished unary call with code "+code.String()) + + return resp, err + } +} + +// StreamServerInterceptor returns a new streaming server interceptor that adds logrus.Entry to the context. +func StreamServerInterceptor(entry *logrus.Entry, opts ...Option) grpc.StreamServerInterceptor { + o := evaluateServerOpt(opts) + return func(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { + startTime := time.Now() + newCtx := newLoggerForCall(stream.Context(), entry, info.FullMethod, startTime) + wrapped := grpc_middleware.WrapServerStream(stream) + wrapped.WrappedContext = newCtx + + err := handler(srv, wrapped) + + if !o.shouldLog(info.FullMethod, err) { + return err + } + code := o.codeFunc(err) + level := o.levelFunc(code) + durField, durVal := o.durationFunc(time.Since(startTime)) + fields := logrus.Fields{ + "grpc.code": code.String(), + durField: durVal, + } + if err != nil { + fields[logrus.ErrorKey] = err + } + + levelLogf( + ctx_logrus.Extract(newCtx).WithFields(fields), // re-extract logger from newCtx, as it may have extra fields that changed in the holder. + level, + "finished streaming call with code "+code.String()) + + return err + } +} + +func levelLogf(entry *logrus.Entry, level logrus.Level, format string, args ...interface{}) { + switch level { + case logrus.DebugLevel: + entry.Debugf(format, args...) + case logrus.InfoLevel: + entry.Infof(format, args...) + case logrus.WarnLevel: + entry.Warningf(format, args...) + case logrus.ErrorLevel: + entry.Errorf(format, args...) + case logrus.FatalLevel: + entry.Fatalf(format, args...) + case logrus.PanicLevel: + entry.Panicf(format, args...) + } +} + +func newLoggerForCall(ctx context.Context, entry *logrus.Entry, fullMethodString string, start time.Time) context.Context { + service := path.Dir(fullMethodString)[1:] + method := path.Base(fullMethodString) + callLog := entry.WithFields( + logrus.Fields{ + SystemField: "grpc", + KindField: "server", + "grpc.service": service, + "grpc.method": method, + "grpc.start_time": start.Format(time.RFC3339), + }) + + if d, ok := ctx.Deadline(); ok { + callLog = callLog.WithFields( + logrus.Fields{ + "grpc.request.deadline": d.Format(time.RFC3339), + }) + } + + callLog = callLog.WithFields(ctx_logrus.Extract(ctx).Data) + return ctxlogrus.ToContext(ctx, callLog) +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/makefile b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/makefile new file mode 100644 index 00000000..4ad54c37 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/makefile @@ -0,0 +1,19 @@ +SHELL="/bin/bash" + +GOFILES_NOVENDOR = $(shell go list ./... | grep -v /vendor/) + +all: vet fmt docs test + +docs: + ./scripts/fixup.sh + +fmt: + go fmt $(GOFILES_NOVENDOR) + +vet: + go vet $(GOFILES_NOVENDOR) + +test: vet + ./scripts/test_all.sh + +.PHONY: all docs validate test diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/recovery/DOC.md b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/recovery/DOC.md new file mode 100644 index 00000000..d4ef78af --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/recovery/DOC.md @@ -0,0 +1,107 @@ +# grpc_recovery +`import "github.com/grpc-ecosystem/go-grpc-middleware/recovery"` + +* [Overview](#pkg-overview) +* [Imported Packages](#pkg-imports) +* [Index](#pkg-index) +* [Examples](#pkg-examples) + +## <a name="pkg-overview">Overview</a> +`grpc_recovery` are intereceptors that recover from gRPC handler panics. + +### Server Side Recovery Middleware +By default a panic will be converted into a gRPC error with `code.Internal`. + +Handling can be customised by providing an alternate recovery function. + +Please see examples for simple examples of use. + +#### Example: + +<details> +<summary>Click to expand code.</summary> + +```go +package grpc_recovery_test + +import ( + "github.com/grpc-ecosystem/go-grpc-middleware" + "github.com/grpc-ecosystem/go-grpc-middleware/recovery" + "google.golang.org/grpc" +) + +var ( + customFunc grpc_recovery.RecoveryHandlerFunc +) + +// Initialization shows an initialization sequence with a custom recovery handler func. +func Example_initialization() { + // Shared options for the logger, with a custom gRPC code to log level function. + opts := []grpc_recovery.Option{ + grpc_recovery.WithRecoveryHandler(customFunc), + } + // Create a server. Recovery handlers should typically be last in the chain so that other middleware + // (e.g. logging) can operate on the recovered state instead of being directly affected by any panic + _ = grpc.NewServer( + grpc_middleware.WithUnaryServerChain( + grpc_recovery.UnaryServerInterceptor(opts...), + ), + grpc_middleware.WithStreamServerChain( + grpc_recovery.StreamServerInterceptor(opts...), + ), + ) +} +``` + +</details> + +## <a name="pkg-imports">Imported Packages</a> + +- [golang.org/x/net/context](https://godoc.org/golang.org/x/net/context) +- [google.golang.org/grpc](https://godoc.org/google.golang.org/grpc) +- [google.golang.org/grpc/codes](https://godoc.org/google.golang.org/grpc/codes) + +## <a name="pkg-index">Index</a> +* [func StreamServerInterceptor(opts ...Option) grpc.StreamServerInterceptor](#StreamServerInterceptor) +* [func UnaryServerInterceptor(opts ...Option) grpc.UnaryServerInterceptor](#UnaryServerInterceptor) +* [type Option](#Option) + * [func WithRecoveryHandler(f RecoveryHandlerFunc) Option](#WithRecoveryHandler) +* [type RecoveryHandlerFunc](#RecoveryHandlerFunc) + +#### <a name="pkg-examples">Examples</a> +* [Package (Initialization)](#example__initialization) + +#### <a name="pkg-files">Package files</a> +[doc.go](./doc.go) [interceptors.go](./interceptors.go) [options.go](./options.go) + +## <a name="StreamServerInterceptor">func</a> [StreamServerInterceptor](./interceptors.go#L30) +``` go +func StreamServerInterceptor(opts ...Option) grpc.StreamServerInterceptor +``` +StreamServerInterceptor returns a new streaming server interceptor for panic recovery. + +## <a name="UnaryServerInterceptor">func</a> [UnaryServerInterceptor](./interceptors.go#L16) +``` go +func UnaryServerInterceptor(opts ...Option) grpc.UnaryServerInterceptor +``` +UnaryServerInterceptor returns a new unary server interceptor for panic recovery. + +## <a name="Option">type</a> [Option](./options.go#L25) +``` go +type Option func(*options) +``` + +### <a name="WithRecoveryHandler">func</a> [WithRecoveryHandler](./options.go#L28) +``` go +func WithRecoveryHandler(f RecoveryHandlerFunc) Option +``` +WithRecoveryHandler customizes the function for recovering from a panic. + +## <a name="RecoveryHandlerFunc">type</a> [RecoveryHandlerFunc](./interceptors.go#L13) +``` go +type RecoveryHandlerFunc func(p interface{}) (err error) +``` +RecoveryHandlerFunc is a function that recovers from the panic `p` by returning an `error`. + +- - - +Generated by [godoc2ghmd](https://github.com/GandalfUK/godoc2ghmd)
\ No newline at end of file diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/recovery/README.md b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/recovery/README.md new file mode 100644 index 00000000..d4ef78af --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/recovery/README.md @@ -0,0 +1,107 @@ +# grpc_recovery +`import "github.com/grpc-ecosystem/go-grpc-middleware/recovery"` + +* [Overview](#pkg-overview) +* [Imported Packages](#pkg-imports) +* [Index](#pkg-index) +* [Examples](#pkg-examples) + +## <a name="pkg-overview">Overview</a> +`grpc_recovery` are intereceptors that recover from gRPC handler panics. + +### Server Side Recovery Middleware +By default a panic will be converted into a gRPC error with `code.Internal`. + +Handling can be customised by providing an alternate recovery function. + +Please see examples for simple examples of use. + +#### Example: + +<details> +<summary>Click to expand code.</summary> + +```go +package grpc_recovery_test + +import ( + "github.com/grpc-ecosystem/go-grpc-middleware" + "github.com/grpc-ecosystem/go-grpc-middleware/recovery" + "google.golang.org/grpc" +) + +var ( + customFunc grpc_recovery.RecoveryHandlerFunc +) + +// Initialization shows an initialization sequence with a custom recovery handler func. +func Example_initialization() { + // Shared options for the logger, with a custom gRPC code to log level function. + opts := []grpc_recovery.Option{ + grpc_recovery.WithRecoveryHandler(customFunc), + } + // Create a server. Recovery handlers should typically be last in the chain so that other middleware + // (e.g. logging) can operate on the recovered state instead of being directly affected by any panic + _ = grpc.NewServer( + grpc_middleware.WithUnaryServerChain( + grpc_recovery.UnaryServerInterceptor(opts...), + ), + grpc_middleware.WithStreamServerChain( + grpc_recovery.StreamServerInterceptor(opts...), + ), + ) +} +``` + +</details> + +## <a name="pkg-imports">Imported Packages</a> + +- [golang.org/x/net/context](https://godoc.org/golang.org/x/net/context) +- [google.golang.org/grpc](https://godoc.org/google.golang.org/grpc) +- [google.golang.org/grpc/codes](https://godoc.org/google.golang.org/grpc/codes) + +## <a name="pkg-index">Index</a> +* [func StreamServerInterceptor(opts ...Option) grpc.StreamServerInterceptor](#StreamServerInterceptor) +* [func UnaryServerInterceptor(opts ...Option) grpc.UnaryServerInterceptor](#UnaryServerInterceptor) +* [type Option](#Option) + * [func WithRecoveryHandler(f RecoveryHandlerFunc) Option](#WithRecoveryHandler) +* [type RecoveryHandlerFunc](#RecoveryHandlerFunc) + +#### <a name="pkg-examples">Examples</a> +* [Package (Initialization)](#example__initialization) + +#### <a name="pkg-files">Package files</a> +[doc.go](./doc.go) [interceptors.go](./interceptors.go) [options.go](./options.go) + +## <a name="StreamServerInterceptor">func</a> [StreamServerInterceptor](./interceptors.go#L30) +``` go +func StreamServerInterceptor(opts ...Option) grpc.StreamServerInterceptor +``` +StreamServerInterceptor returns a new streaming server interceptor for panic recovery. + +## <a name="UnaryServerInterceptor">func</a> [UnaryServerInterceptor](./interceptors.go#L16) +``` go +func UnaryServerInterceptor(opts ...Option) grpc.UnaryServerInterceptor +``` +UnaryServerInterceptor returns a new unary server interceptor for panic recovery. + +## <a name="Option">type</a> [Option](./options.go#L25) +``` go +type Option func(*options) +``` + +### <a name="WithRecoveryHandler">func</a> [WithRecoveryHandler](./options.go#L28) +``` go +func WithRecoveryHandler(f RecoveryHandlerFunc) Option +``` +WithRecoveryHandler customizes the function for recovering from a panic. + +## <a name="RecoveryHandlerFunc">type</a> [RecoveryHandlerFunc](./interceptors.go#L13) +``` go +type RecoveryHandlerFunc func(p interface{}) (err error) +``` +RecoveryHandlerFunc is a function that recovers from the panic `p` by returning an `error`. + +- - - +Generated by [godoc2ghmd](https://github.com/GandalfUK/godoc2ghmd)
\ No newline at end of file diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/recovery/doc.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/recovery/doc.go new file mode 100644 index 00000000..da40190c --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/recovery/doc.go @@ -0,0 +1,15 @@ +// Copyright 2017 David Ackroyd. All Rights Reserved. +// See LICENSE for licensing terms. + +/* +`grpc_recovery` are intereceptors that recover from gRPC handler panics. + +Server Side Recovery Middleware + +By default a panic will be converted into a gRPC error with `code.Internal`. + +Handling can be customised by providing an alternate recovery function. + +Please see examples for simple examples of use. +*/ +package grpc_recovery diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/recovery/interceptors.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/recovery/interceptors.go new file mode 100644 index 00000000..c0fb5ac8 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/recovery/interceptors.go @@ -0,0 +1,48 @@ +// Copyright 2017 David Ackroyd. All Rights Reserved. +// See LICENSE for licensing terms. + +package grpc_recovery + +import ( + "golang.org/x/net/context" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" +) + +// RecoveryHandlerFunc is a function that recovers from the panic `p` by returning an `error`. +type RecoveryHandlerFunc func(p interface{}) (err error) + +// UnaryServerInterceptor returns a new unary server interceptor for panic recovery. +func UnaryServerInterceptor(opts ...Option) grpc.UnaryServerInterceptor { + o := evaluateOptions(opts) + return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (_ interface{}, err error) { + defer func() { + if r := recover(); r != nil { + err = recoverFrom(r, o.recoveryHandlerFunc) + } + }() + + return handler(ctx, req) + } +} + +// StreamServerInterceptor returns a new streaming server interceptor for panic recovery. +func StreamServerInterceptor(opts ...Option) grpc.StreamServerInterceptor { + o := evaluateOptions(opts) + return func(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) (err error) { + defer func() { + if r := recover(); r != nil { + err = recoverFrom(r, o.recoveryHandlerFunc) + } + }() + + return handler(srv, stream) + } +} + +func recoverFrom(p interface{}, r RecoveryHandlerFunc) error { + if r == nil { + return grpc.Errorf(codes.Internal, "%s", p) + } + return r(p) +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/recovery/options.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/recovery/options.go new file mode 100644 index 00000000..e482d7a5 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/recovery/options.go @@ -0,0 +1,32 @@ +// Copyright 2017 David Ackroyd. All Rights Reserved. +// See LICENSE for licensing terms. + +package grpc_recovery + +var ( + defaultOptions = &options{ + recoveryHandlerFunc: nil, + } +) + +type options struct { + recoveryHandlerFunc RecoveryHandlerFunc +} + +func evaluateOptions(opts []Option) *options { + optCopy := &options{} + *optCopy = *defaultOptions + for _, o := range opts { + o(optCopy) + } + return optCopy +} + +type Option func(*options) + +// WithRecoveryHandler customizes the function for recovering from a panic. +func WithRecoveryHandler(f RecoveryHandlerFunc) Option { + return func(o *options) { + o.recoveryHandlerFunc = f + } +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/tags/DOC.md b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/tags/DOC.md new file mode 100644 index 00000000..b954b1f9 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/tags/DOC.md @@ -0,0 +1,188 @@ +# grpc_ctxtags +`import "github.com/grpc-ecosystem/go-grpc-middleware/tags"` + +* [Overview](#pkg-overview) +* [Imported Packages](#pkg-imports) +* [Index](#pkg-index) +* [Examples](#pkg-examples) + +## <a name="pkg-overview">Overview</a> +`grpc_ctxtags` adds a Tag object to the context that can be used by other middleware to add context about a request. + +### Request Context Tags +Tags describe information about the request, and can be set and used by other middleware, or handlers. Tags are used +for logging and tracing of requests. Tags are populated both upwards, *and* downwards in the interceptor-handler stack. + +You can automatically extract tags (in `grpc.request.<field_name>`) from request payloads. + +For unary and server-streaming methods, pass in the `WithFieldExtractor` option. For client-streams and bidirectional-streams, you can +use `WithFieldExtractorForInitialReq` which will extract the tags from the first message passed from client to server. +Note the tags will not be modified for subsequent requests, so this option only makes sense when the initial message +establishes the meta-data for the stream. + +If a user doesn't use the interceptors that initialize the `Tags` object, all operations following from an `Extract(ctx)` +will be no-ops. This is to ensure that code doesn't panic if the interceptors weren't used. + +Tags fields are typed, and shallow and should follow the OpenTracing semantics convention: +<a href="https://github.com/opentracing/specification/blob/master/semantic_conventions.md">https://github.com/opentracing/specification/blob/master/semantic_conventions.md</a> + +#### Example: + +<details> +<summary>Click to expand code.</summary> + +```go +opts := []grpc_ctxtags.Option{ + grpc_ctxtags.WithFieldExtractorForInitialReq(grpc_ctxtags.TagBasedRequestFieldExtractor("log_fields")), +} +_ = grpc.NewServer( + grpc.StreamInterceptor(grpc_ctxtags.StreamServerInterceptor(opts...)), + grpc.UnaryInterceptor(grpc_ctxtags.UnaryServerInterceptor(opts...)), +) +``` + +</details> + +#### Example: + +<details> +<summary>Click to expand code.</summary> + +```go +opts := []grpc_ctxtags.Option{ + grpc_ctxtags.WithFieldExtractor(grpc_ctxtags.TagBasedRequestFieldExtractor("log_fields")), +} +_ = grpc.NewServer( + grpc.StreamInterceptor(grpc_ctxtags.StreamServerInterceptor(opts...)), + grpc.UnaryInterceptor(grpc_ctxtags.UnaryServerInterceptor(opts...)), +) +``` + +</details> + +## <a name="pkg-imports">Imported Packages</a> + +- [github.com/grpc-ecosystem/go-grpc-middleware](./..) +- [golang.org/x/net/context](https://godoc.org/golang.org/x/net/context) +- [google.golang.org/grpc](https://godoc.org/google.golang.org/grpc) +- [google.golang.org/grpc/peer](https://godoc.org/google.golang.org/grpc/peer) + +## <a name="pkg-index">Index</a> +* [func CodeGenRequestFieldExtractor(fullMethod string, req interface{}) map[string]interface{}](#CodeGenRequestFieldExtractor) +* [func StreamServerInterceptor(opts ...Option) grpc.StreamServerInterceptor](#StreamServerInterceptor) +* [func UnaryServerInterceptor(opts ...Option) grpc.UnaryServerInterceptor](#UnaryServerInterceptor) +* [type Option](#Option) + * [func WithFieldExtractor(f RequestFieldExtractorFunc) Option](#WithFieldExtractor) + * [func WithFieldExtractorForInitialReq(f RequestFieldExtractorFunc) Option](#WithFieldExtractorForInitialReq) +* [type RequestFieldExtractorFunc](#RequestFieldExtractorFunc) + * [func TagBasedRequestFieldExtractor(tagName string) RequestFieldExtractorFunc](#TagBasedRequestFieldExtractor) +* [type Tags](#Tags) + * [func Extract(ctx context.Context) \*Tags](#Extract) + * [func (t \*Tags) Has(key string) bool](#Tags.Has) + * [func (t \*Tags) Set(key string, value interface{}) \*Tags](#Tags.Set) + * [func (t \*Tags) Values() map[string]interface{}](#Tags.Values) + +#### <a name="pkg-examples">Examples</a> +* [Package (InitialisationWithOptions)](#example__initialisationWithOptions) +* [Package (Initialization)](#example__initialization) + +#### <a name="pkg-files">Package files</a> +[context.go](./context.go) [doc.go](./doc.go) [fieldextractor.go](./fieldextractor.go) [interceptors.go](./interceptors.go) [options.go](./options.go) + +## <a name="CodeGenRequestFieldExtractor">func</a> [CodeGenRequestFieldExtractor](./fieldextractor.go#L23) +``` go +func CodeGenRequestFieldExtractor(fullMethod string, req interface{}) map[string]interface{} +``` +CodeGenRequestFieldExtractor is a function that relies on code-generated functions that export log fields from requests. +These are usually coming from a protoc-plugin that generates additional information based on custom field options. + +## <a name="StreamServerInterceptor">func</a> [StreamServerInterceptor](./interceptors.go#L26) +``` go +func StreamServerInterceptor(opts ...Option) grpc.StreamServerInterceptor +``` +StreamServerInterceptor returns a new streaming server interceptor that sets the values for request tags. + +## <a name="UnaryServerInterceptor">func</a> [UnaryServerInterceptor](./interceptors.go#L14) +``` go +func UnaryServerInterceptor(opts ...Option) grpc.UnaryServerInterceptor +``` +UnaryServerInterceptor returns a new unary server interceptors that sets the values for request tags. + +## <a name="Option">type</a> [Option](./options.go#L26) +``` go +type Option func(*options) +``` + +### <a name="WithFieldExtractor">func</a> [WithFieldExtractor](./options.go#L30) +``` go +func WithFieldExtractor(f RequestFieldExtractorFunc) Option +``` +WithFieldExtractor customizes the function for extracting log fields from protobuf messages, for +unary and server-streamed methods only. + +### <a name="WithFieldExtractorForInitialReq">func</a> [WithFieldExtractorForInitialReq](./options.go#L39) +``` go +func WithFieldExtractorForInitialReq(f RequestFieldExtractorFunc) Option +``` +WithFieldExtractorForInitialReq customizes the function for extracting log fields from protobuf messages, +for all unary and streaming methods. For client-streams and bidirectional-streams, the tags will be +extracted from the first message from the client. + +## <a name="RequestFieldExtractorFunc">type</a> [RequestFieldExtractorFunc](./fieldextractor.go#L13) +``` go +type RequestFieldExtractorFunc func(fullMethod string, req interface{}) map[string]interface{} +``` +RequestFieldExtractorFunc is a user-provided function that extracts field information from a gRPC request. +It is called from tags middleware on arrival of unary request or a server-stream request. +Keys and values will be added to the context tags of the request. If there are no fields, you should return a nil. + +### <a name="TagBasedRequestFieldExtractor">func</a> [TagBasedRequestFieldExtractor](./fieldextractor.go#L43) +``` go +func TagBasedRequestFieldExtractor(tagName string) RequestFieldExtractorFunc +``` +TagBasedRequestFieldExtractor is a function that relies on Go struct tags to export log fields from requests. +These are usualy coming from a protoc-plugin, such as Gogo protobuf. + + message Metadata { + repeated string tags = 1 [ (gogoproto.moretags) = "log_field:\"meta_tags\"" ]; + } + +The tagName is configurable using the tagName variable. Here it would be "log_field". + +## <a name="Tags">type</a> [Tags](./context.go#L17-L19) +``` go +type Tags struct { + // contains filtered or unexported fields +} +``` +Tags is the struct used for storing request tags between Context calls. +This object is *not* thread safe, and should be handled only in the context of the request. + +### <a name="Extract">func</a> [Extract](./context.go#L41) +``` go +func Extract(ctx context.Context) *Tags +``` +Extracts returns a pre-existing Tags object in the Context. +If the context wasn't set in a tag interceptor, a no-op Tag storage is returned that will *not* be propagated in context. + +### <a name="Tags.Has">func</a> (\*Tags) [Has](./context.go#L28) +``` go +func (t *Tags) Has(key string) bool +``` +Has checks if the given key exists. + +### <a name="Tags.Set">func</a> (\*Tags) [Set](./context.go#L22) +``` go +func (t *Tags) Set(key string, value interface{}) *Tags +``` +Set sets the given key in the metadata tags. + +### <a name="Tags.Values">func</a> (\*Tags) [Values](./context.go#L35) +``` go +func (t *Tags) Values() map[string]interface{} +``` +Values returns a map of key to values. +Do not modify the underlying map, please use Set instead. + +- - - +Generated by [godoc2ghmd](https://github.com/GandalfUK/godoc2ghmd)
\ No newline at end of file diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/tags/README.md b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/tags/README.md new file mode 100644 index 00000000..b954b1f9 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/tags/README.md @@ -0,0 +1,188 @@ +# grpc_ctxtags +`import "github.com/grpc-ecosystem/go-grpc-middleware/tags"` + +* [Overview](#pkg-overview) +* [Imported Packages](#pkg-imports) +* [Index](#pkg-index) +* [Examples](#pkg-examples) + +## <a name="pkg-overview">Overview</a> +`grpc_ctxtags` adds a Tag object to the context that can be used by other middleware to add context about a request. + +### Request Context Tags +Tags describe information about the request, and can be set and used by other middleware, or handlers. Tags are used +for logging and tracing of requests. Tags are populated both upwards, *and* downwards in the interceptor-handler stack. + +You can automatically extract tags (in `grpc.request.<field_name>`) from request payloads. + +For unary and server-streaming methods, pass in the `WithFieldExtractor` option. For client-streams and bidirectional-streams, you can +use `WithFieldExtractorForInitialReq` which will extract the tags from the first message passed from client to server. +Note the tags will not be modified for subsequent requests, so this option only makes sense when the initial message +establishes the meta-data for the stream. + +If a user doesn't use the interceptors that initialize the `Tags` object, all operations following from an `Extract(ctx)` +will be no-ops. This is to ensure that code doesn't panic if the interceptors weren't used. + +Tags fields are typed, and shallow and should follow the OpenTracing semantics convention: +<a href="https://github.com/opentracing/specification/blob/master/semantic_conventions.md">https://github.com/opentracing/specification/blob/master/semantic_conventions.md</a> + +#### Example: + +<details> +<summary>Click to expand code.</summary> + +```go +opts := []grpc_ctxtags.Option{ + grpc_ctxtags.WithFieldExtractorForInitialReq(grpc_ctxtags.TagBasedRequestFieldExtractor("log_fields")), +} +_ = grpc.NewServer( + grpc.StreamInterceptor(grpc_ctxtags.StreamServerInterceptor(opts...)), + grpc.UnaryInterceptor(grpc_ctxtags.UnaryServerInterceptor(opts...)), +) +``` + +</details> + +#### Example: + +<details> +<summary>Click to expand code.</summary> + +```go +opts := []grpc_ctxtags.Option{ + grpc_ctxtags.WithFieldExtractor(grpc_ctxtags.TagBasedRequestFieldExtractor("log_fields")), +} +_ = grpc.NewServer( + grpc.StreamInterceptor(grpc_ctxtags.StreamServerInterceptor(opts...)), + grpc.UnaryInterceptor(grpc_ctxtags.UnaryServerInterceptor(opts...)), +) +``` + +</details> + +## <a name="pkg-imports">Imported Packages</a> + +- [github.com/grpc-ecosystem/go-grpc-middleware](./..) +- [golang.org/x/net/context](https://godoc.org/golang.org/x/net/context) +- [google.golang.org/grpc](https://godoc.org/google.golang.org/grpc) +- [google.golang.org/grpc/peer](https://godoc.org/google.golang.org/grpc/peer) + +## <a name="pkg-index">Index</a> +* [func CodeGenRequestFieldExtractor(fullMethod string, req interface{}) map[string]interface{}](#CodeGenRequestFieldExtractor) +* [func StreamServerInterceptor(opts ...Option) grpc.StreamServerInterceptor](#StreamServerInterceptor) +* [func UnaryServerInterceptor(opts ...Option) grpc.UnaryServerInterceptor](#UnaryServerInterceptor) +* [type Option](#Option) + * [func WithFieldExtractor(f RequestFieldExtractorFunc) Option](#WithFieldExtractor) + * [func WithFieldExtractorForInitialReq(f RequestFieldExtractorFunc) Option](#WithFieldExtractorForInitialReq) +* [type RequestFieldExtractorFunc](#RequestFieldExtractorFunc) + * [func TagBasedRequestFieldExtractor(tagName string) RequestFieldExtractorFunc](#TagBasedRequestFieldExtractor) +* [type Tags](#Tags) + * [func Extract(ctx context.Context) \*Tags](#Extract) + * [func (t \*Tags) Has(key string) bool](#Tags.Has) + * [func (t \*Tags) Set(key string, value interface{}) \*Tags](#Tags.Set) + * [func (t \*Tags) Values() map[string]interface{}](#Tags.Values) + +#### <a name="pkg-examples">Examples</a> +* [Package (InitialisationWithOptions)](#example__initialisationWithOptions) +* [Package (Initialization)](#example__initialization) + +#### <a name="pkg-files">Package files</a> +[context.go](./context.go) [doc.go](./doc.go) [fieldextractor.go](./fieldextractor.go) [interceptors.go](./interceptors.go) [options.go](./options.go) + +## <a name="CodeGenRequestFieldExtractor">func</a> [CodeGenRequestFieldExtractor](./fieldextractor.go#L23) +``` go +func CodeGenRequestFieldExtractor(fullMethod string, req interface{}) map[string]interface{} +``` +CodeGenRequestFieldExtractor is a function that relies on code-generated functions that export log fields from requests. +These are usually coming from a protoc-plugin that generates additional information based on custom field options. + +## <a name="StreamServerInterceptor">func</a> [StreamServerInterceptor](./interceptors.go#L26) +``` go +func StreamServerInterceptor(opts ...Option) grpc.StreamServerInterceptor +``` +StreamServerInterceptor returns a new streaming server interceptor that sets the values for request tags. + +## <a name="UnaryServerInterceptor">func</a> [UnaryServerInterceptor](./interceptors.go#L14) +``` go +func UnaryServerInterceptor(opts ...Option) grpc.UnaryServerInterceptor +``` +UnaryServerInterceptor returns a new unary server interceptors that sets the values for request tags. + +## <a name="Option">type</a> [Option](./options.go#L26) +``` go +type Option func(*options) +``` + +### <a name="WithFieldExtractor">func</a> [WithFieldExtractor](./options.go#L30) +``` go +func WithFieldExtractor(f RequestFieldExtractorFunc) Option +``` +WithFieldExtractor customizes the function for extracting log fields from protobuf messages, for +unary and server-streamed methods only. + +### <a name="WithFieldExtractorForInitialReq">func</a> [WithFieldExtractorForInitialReq](./options.go#L39) +``` go +func WithFieldExtractorForInitialReq(f RequestFieldExtractorFunc) Option +``` +WithFieldExtractorForInitialReq customizes the function for extracting log fields from protobuf messages, +for all unary and streaming methods. For client-streams and bidirectional-streams, the tags will be +extracted from the first message from the client. + +## <a name="RequestFieldExtractorFunc">type</a> [RequestFieldExtractorFunc](./fieldextractor.go#L13) +``` go +type RequestFieldExtractorFunc func(fullMethod string, req interface{}) map[string]interface{} +``` +RequestFieldExtractorFunc is a user-provided function that extracts field information from a gRPC request. +It is called from tags middleware on arrival of unary request or a server-stream request. +Keys and values will be added to the context tags of the request. If there are no fields, you should return a nil. + +### <a name="TagBasedRequestFieldExtractor">func</a> [TagBasedRequestFieldExtractor](./fieldextractor.go#L43) +``` go +func TagBasedRequestFieldExtractor(tagName string) RequestFieldExtractorFunc +``` +TagBasedRequestFieldExtractor is a function that relies on Go struct tags to export log fields from requests. +These are usualy coming from a protoc-plugin, such as Gogo protobuf. + + message Metadata { + repeated string tags = 1 [ (gogoproto.moretags) = "log_field:\"meta_tags\"" ]; + } + +The tagName is configurable using the tagName variable. Here it would be "log_field". + +## <a name="Tags">type</a> [Tags](./context.go#L17-L19) +``` go +type Tags struct { + // contains filtered or unexported fields +} +``` +Tags is the struct used for storing request tags between Context calls. +This object is *not* thread safe, and should be handled only in the context of the request. + +### <a name="Extract">func</a> [Extract](./context.go#L41) +``` go +func Extract(ctx context.Context) *Tags +``` +Extracts returns a pre-existing Tags object in the Context. +If the context wasn't set in a tag interceptor, a no-op Tag storage is returned that will *not* be propagated in context. + +### <a name="Tags.Has">func</a> (\*Tags) [Has](./context.go#L28) +``` go +func (t *Tags) Has(key string) bool +``` +Has checks if the given key exists. + +### <a name="Tags.Set">func</a> (\*Tags) [Set](./context.go#L22) +``` go +func (t *Tags) Set(key string, value interface{}) *Tags +``` +Set sets the given key in the metadata tags. + +### <a name="Tags.Values">func</a> (\*Tags) [Values](./context.go#L35) +``` go +func (t *Tags) Values() map[string]interface{} +``` +Values returns a map of key to values. +Do not modify the underlying map, please use Set instead. + +- - - +Generated by [godoc2ghmd](https://github.com/GandalfUK/godoc2ghmd)
\ No newline at end of file diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/tags/context.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/tags/context.go new file mode 100644 index 00000000..583025ce --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/tags/context.go @@ -0,0 +1,78 @@ +package grpc_ctxtags + +import ( + "context" +) + +type ctxMarker struct{} + +var ( + // ctxMarkerKey is the Context value marker used by *all* logging middleware. + // The logging middleware object must interf + ctxMarkerKey = &ctxMarker{} + // NoopTags is a trivial, minimum overhead implementation of Tags for which all operations are no-ops. + NoopTags = &noopTags{} +) + +// Tags is the interface used for storing request tags between Context calls. +// The default implementation is *not* thread safe, and should be handled only in the context of the request. +type Tags interface { + // Set sets the given key in the metadata tags. + Set(key string, value interface{}) Tags + // Has checks if the given key exists. + Has(key string) bool + // Values returns a map of key to values. + // Do not modify the underlying map, please use Set instead. + Values() map[string]interface{} +} + +type mapTags struct { + values map[string]interface{} +} + +func (t *mapTags) Set(key string, value interface{}) Tags { + t.values[key] = value + return t +} + +func (t *mapTags) Has(key string) bool { + _, ok := t.values[key] + return ok +} + +func (t *mapTags) Values() map[string]interface{} { + return t.values +} + +type noopTags struct{} + +func (t *noopTags) Set(key string, value interface{}) Tags { + return t +} + +func (t *noopTags) Has(key string) bool { + return false +} + +func (t *noopTags) Values() map[string]interface{} { + return nil +} + +// Extracts returns a pre-existing Tags object in the Context. +// If the context wasn't set in a tag interceptor, a no-op Tag storage is returned that will *not* be propagated in context. +func Extract(ctx context.Context) Tags { + t, ok := ctx.Value(ctxMarkerKey).(Tags) + if !ok { + return NoopTags + } + + return t +} + +func setInContext(ctx context.Context, tags Tags) context.Context { + return context.WithValue(ctx, ctxMarkerKey, tags) +} + +func newTags() Tags { + return &mapTags{values: make(map[string]interface{})} +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/tags/doc.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/tags/doc.go new file mode 100644 index 00000000..960638d0 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/tags/doc.go @@ -0,0 +1,22 @@ +/* +`grpc_ctxtags` adds a Tag object to the context that can be used by other middleware to add context about a request. + +Request Context Tags + +Tags describe information about the request, and can be set and used by other middleware, or handlers. Tags are used +for logging and tracing of requests. Tags are populated both upwards, *and* downwards in the interceptor-handler stack. + +You can automatically extract tags (in `grpc.request.<field_name>`) from request payloads. + +For unary and server-streaming methods, pass in the `WithFieldExtractor` option. For client-streams and bidirectional-streams, you can +use `WithFieldExtractorForInitialReq` which will extract the tags from the first message passed from client to server. +Note the tags will not be modified for subsequent requests, so this option only makes sense when the initial message +establishes the meta-data for the stream. + +If a user doesn't use the interceptors that initialize the `Tags` object, all operations following from an `Extract(ctx)` +will be no-ops. This is to ensure that code doesn't panic if the interceptors weren't used. + +Tags fields are typed, and shallow and should follow the OpenTracing semantics convention: +https://github.com/opentracing/specification/blob/master/semantic_conventions.md +*/ +package grpc_ctxtags diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/tags/fieldextractor.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/tags/fieldextractor.go new file mode 100644 index 00000000..d87ee438 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/tags/fieldextractor.go @@ -0,0 +1,85 @@ +// Copyright 2017 Michal Witkowski. All Rights Reserved. +// See LICENSE for licensing terms. + +package grpc_ctxtags + +import ( + "reflect" +) + +// RequestFieldExtractorFunc is a user-provided function that extracts field information from a gRPC request. +// It is called from tags middleware on arrival of unary request or a server-stream request. +// Keys and values will be added to the context tags of the request. If there are no fields, you should return a nil. +type RequestFieldExtractorFunc func(fullMethod string, req interface{}) map[string]interface{} + +type requestFieldsExtractor interface { + // ExtractRequestFields is a method declared on a Protobuf message that extracts fields from the interface. + // The values from the extracted fields should be set in the appendToMap, in order to avoid allocations. + ExtractRequestFields(appendToMap map[string]interface{}) +} + +// CodeGenRequestFieldExtractor is a function that relies on code-generated functions that export log fields from requests. +// These are usually coming from a protoc-plugin that generates additional information based on custom field options. +func CodeGenRequestFieldExtractor(fullMethod string, req interface{}) map[string]interface{} { + if ext, ok := req.(requestFieldsExtractor); ok { + retMap := make(map[string]interface{}) + ext.ExtractRequestFields(retMap) + if len(retMap) == 0 { + return nil + } + return retMap + } + return nil +} + +// TagBasedRequestFieldExtractor is a function that relies on Go struct tags to export log fields from requests. +// These are usualy coming from a protoc-plugin, such as Gogo protobuf. +// +// message Metadata { +// repeated string tags = 1 [ (gogoproto.moretags) = "log_field:\"meta_tags\"" ]; +// } +// +// The tagName is configurable using the tagName variable. Here it would be "log_field". +func TagBasedRequestFieldExtractor(tagName string) RequestFieldExtractorFunc { + return func(fullMethod string, req interface{}) map[string]interface{} { + retMap := make(map[string]interface{}) + reflectMessageTags(req, retMap, tagName) + if len(retMap) == 0 { + return nil + } + return retMap + } +} + +func reflectMessageTags(msg interface{}, existingMap map[string]interface{}, tagName string) { + v := reflect.ValueOf(msg) + // Only deal with pointers to structs. + if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct { + return + } + // Deref the pointer get to the struct. + v = v.Elem() + t := v.Type() + for i := 0; i < v.NumField(); i++ { + field := v.Field(i) + kind := field.Kind() + // Only recurse down direct pointers, which should only be to nested structs. + if kind == reflect.Ptr { + reflectMessageTags(field.Interface(), existingMap, tagName) + } + // In case of arrays/splices (repeated fields) go down to the concrete type. + if kind == reflect.Array || kind == reflect.Slice { + if field.Len() == 0 { + continue + } + kind = field.Index(0).Kind() + } + // Only be interested in + if (kind >= reflect.Bool && kind <= reflect.Float64) || kind == reflect.String { + if tag := t.Field(i).Tag.Get(tagName); tag != "" { + existingMap[tag] = field.Interface() + } + } + } + return +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/tags/interceptors.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/tags/interceptors.go new file mode 100644 index 00000000..038afd26 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/tags/interceptors.go @@ -0,0 +1,83 @@ +// Copyright 2017 Michal Witkowski. All Rights Reserved. +// See LICENSE for licensing terms. + +package grpc_ctxtags + +import ( + "github.com/grpc-ecosystem/go-grpc-middleware" + "golang.org/x/net/context" + "google.golang.org/grpc" + "google.golang.org/grpc/peer" +) + +// UnaryServerInterceptor returns a new unary server interceptors that sets the values for request tags. +func UnaryServerInterceptor(opts ...Option) grpc.UnaryServerInterceptor { + o := evaluateOptions(opts) + return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { + newCtx := newTagsForCtx(ctx) + if o.requestFieldsFunc != nil { + setRequestFieldTags(newCtx, o.requestFieldsFunc, info.FullMethod, req) + } + return handler(newCtx, req) + } +} + +// StreamServerInterceptor returns a new streaming server interceptor that sets the values for request tags. +func StreamServerInterceptor(opts ...Option) grpc.StreamServerInterceptor { + o := evaluateOptions(opts) + return func(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { + newCtx := newTagsForCtx(stream.Context()) + if o.requestFieldsFunc == nil { + // Short-circuit, don't do the expensive bit of allocating a wrappedStream. + wrappedStream := grpc_middleware.WrapServerStream(stream) + wrappedStream.WrappedContext = newCtx + return handler(srv, wrappedStream) + } + wrapped := &wrappedStream{stream, info, o, newCtx, true} + err := handler(srv, wrapped) + return err + } +} + +// wrappedStream is a thin wrapper around grpc.ServerStream that allows modifying context and extracts log fields from the initial message. +type wrappedStream struct { + grpc.ServerStream + info *grpc.StreamServerInfo + opts *options + // WrappedContext is the wrapper's own Context. You can assign it. + WrappedContext context.Context + initial bool +} + +// Context returns the wrapper's WrappedContext, overwriting the nested grpc.ServerStream.Context() +func (w *wrappedStream) Context() context.Context { + return w.WrappedContext +} + +func (w *wrappedStream) RecvMsg(m interface{}) error { + err := w.ServerStream.RecvMsg(m) + // We only do log fields extraction on the single-request of a server-side stream. + if !w.info.IsClientStream || w.opts.requestFieldsFromInitial && w.initial { + w.initial = false + + setRequestFieldTags(w.Context(), w.opts.requestFieldsFunc, w.info.FullMethod, m) + } + return err +} + +func newTagsForCtx(ctx context.Context) context.Context { + t := newTags() + if peer, ok := peer.FromContext(ctx); ok { + t.Set("peer.address", peer.Addr.String()) + } + return setInContext(ctx, t) +} + +func setRequestFieldTags(ctx context.Context, f RequestFieldExtractorFunc, fullMethodName string, req interface{}) { + if valMap := f(fullMethodName, req); valMap != nil { + t := Extract(ctx) + for k, v := range valMap { + t.Set("grpc.request."+k, v) + } + } +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/tags/logrus/context.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/tags/logrus/context.go new file mode 100644 index 00000000..2596be89 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/tags/logrus/context.go @@ -0,0 +1,25 @@ +package ctx_logrus + +import ( + "github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus" + "github.com/sirupsen/logrus" + "golang.org/x/net/context" +) + +// AddFields adds logrus fields to the logger. +// Deprecated: should use the ctxlogrus.Extract instead +func AddFields(ctx context.Context, fields logrus.Fields) { + ctxlogrus.AddFields(ctx, fields) +} + +// Extract takes the call-scoped logrus.Entry from grpc_logrus middleware. +// Deprecated: should use the ctxlogrus.Extract instead +func Extract(ctx context.Context) *logrus.Entry { + return ctxlogrus.Extract(ctx) +} + +// ToContext adds the logrus.Entry to the context for extraction later. +// Depricated: should use ctxlogrus.ToContext instead +func ToContext(ctx context.Context, entry *logrus.Entry) context.Context { + return ctxlogrus.ToContext(ctx, entry) +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/tags/options.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/tags/options.go new file mode 100644 index 00000000..952775f8 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/tags/options.go @@ -0,0 +1,44 @@ +// Copyright 2017 Michal Witkowski. All Rights Reserved. +// See LICENSE for licensing terms. + +package grpc_ctxtags + +var ( + defaultOptions = &options{ + requestFieldsFunc: nil, + } +) + +type options struct { + requestFieldsFunc RequestFieldExtractorFunc + requestFieldsFromInitial bool +} + +func evaluateOptions(opts []Option) *options { + optCopy := &options{} + *optCopy = *defaultOptions + for _, o := range opts { + o(optCopy) + } + return optCopy +} + +type Option func(*options) + +// WithFieldExtractor customizes the function for extracting log fields from protobuf messages, for +// unary and server-streamed methods only. +func WithFieldExtractor(f RequestFieldExtractorFunc) Option { + return func(o *options) { + o.requestFieldsFunc = f + } +} + +// WithFieldExtractorForInitialReq customizes the function for extracting log fields from protobuf messages, +// for all unary and streaming methods. For client-streams and bidirectional-streams, the tags will be +// extracted from the first message from the client. +func WithFieldExtractorForInitialReq(f RequestFieldExtractorFunc) Option { + return func(o *options) { + o.requestFieldsFunc = f + o.requestFieldsFromInitial = true + } +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/util/metautils/DOC.md b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/util/metautils/DOC.md new file mode 100644 index 00000000..a02cde31 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/util/metautils/DOC.md @@ -0,0 +1,114 @@ +# metautils +`import "github.com/grpc-ecosystem/go-grpc-middleware/util/metautils"` + +* [Overview](#pkg-overview) +* [Imported Packages](#pkg-imports) +* [Index](#pkg-index) + +## <a name="pkg-overview">Overview</a> + +## <a name="pkg-imports">Imported Packages</a> + +- [golang.org/x/net/context](https://godoc.org/golang.org/x/net/context) +- [google.golang.org/grpc/metadata](https://godoc.org/google.golang.org/grpc/metadata) + +## <a name="pkg-index">Index</a> +* [type NiceMD](#NiceMD) + * [func ExtractIncoming(ctx context.Context) NiceMD](#ExtractIncoming) + * [func ExtractOutgoing(ctx context.Context) NiceMD](#ExtractOutgoing) + * [func (m NiceMD) Add(key string, value string) NiceMD](#NiceMD.Add) + * [func (m NiceMD) Clone(copiedKeys ...string) NiceMD](#NiceMD.Clone) + * [func (m NiceMD) Del(key string) NiceMD](#NiceMD.Del) + * [func (m NiceMD) Get(key string) string](#NiceMD.Get) + * [func (m NiceMD) Set(key string, value string) NiceMD](#NiceMD.Set) + * [func (m NiceMD) ToIncoming(ctx context.Context) context.Context](#NiceMD.ToIncoming) + * [func (m NiceMD) ToOutgoing(ctx context.Context) context.Context](#NiceMD.ToOutgoing) + +#### <a name="pkg-files">Package files</a> +[doc.go](./doc.go) [nicemd.go](./nicemd.go) [single_key.go](./single_key.go) + +## <a name="NiceMD">type</a> [NiceMD](./nicemd.go#L14) +``` go +type NiceMD metadata.MD +``` +NiceMD is a convenience wrapper definiting extra functions on the metadata. + +### <a name="ExtractIncoming">func</a> [ExtractIncoming](./nicemd.go#L20) +``` go +func ExtractIncoming(ctx context.Context) NiceMD +``` +ExtractIncoming extracts an inbound metadata from the server-side context. + +This function always returns a NiceMD wrapper of the metadata.MD, in case the context doesn't have metadata it returns +a new empty NiceMD. + +### <a name="ExtractOutgoing">func</a> [ExtractOutgoing](./nicemd.go#L32) +``` go +func ExtractOutgoing(ctx context.Context) NiceMD +``` +ExtractOutgoing extracts an outbound metadata from the client-side context. + +This function always returns a NiceMD wrapper of the metadata.MD, in case the context doesn't have metadata it returns +a new empty NiceMD. + +### <a name="NiceMD.Add">func</a> (NiceMD) [Add](./nicemd.go#L122) +``` go +func (m NiceMD) Add(key string, value string) NiceMD +``` +Add retrieves a single value from the metadata. + +It works analogously to http.Header.Add, as it appends to any existing values associated with key. + +The function is binary-key safe. + +### <a name="NiceMD.Clone">func</a> (NiceMD) [Clone](./nicemd.go#L44) +``` go +func (m NiceMD) Clone(copiedKeys ...string) NiceMD +``` +Clone performs a *deep* copy of the metadata.MD. + +You can specify the lower-case copiedKeys to only copy certain whitelisted keys. If no keys are explicitly whitelisted +all keys get copied. + +### <a name="NiceMD.Del">func</a> (NiceMD) [Del](./nicemd.go#L100) +``` go +func (m NiceMD) Del(key string) NiceMD +``` + +### <a name="NiceMD.Get">func</a> (NiceMD) [Get](./nicemd.go#L85) +``` go +func (m NiceMD) Get(key string) string +``` +Get retrieves a single value from the metadata. + +It works analogously to http.Header.Get, returning the first value if there are many set. If the value is not set, +an empty string is returned. + +The function is binary-key safe. + +### <a name="NiceMD.Set">func</a> (NiceMD) [Set](./nicemd.go#L111) +``` go +func (m NiceMD) Set(key string, value string) NiceMD +``` +Set sets the given value in a metadata. + +It works analogously to http.Header.Set, overwriting all previous metadata values. + +The function is binary-key safe. + +### <a name="NiceMD.ToIncoming">func</a> (NiceMD) [ToIncoming](./nicemd.go#L75) +``` go +func (m NiceMD) ToIncoming(ctx context.Context) context.Context +``` +ToIncoming sets the given NiceMD as a server-side context for dispatching. + +This is mostly useful in ServerInterceptors.. + +### <a name="NiceMD.ToOutgoing">func</a> (NiceMD) [ToOutgoing](./nicemd.go#L68) +``` go +func (m NiceMD) ToOutgoing(ctx context.Context) context.Context +``` +ToOutgoing sets the given NiceMD as a client-side context for dispatching. + +- - - +Generated by [godoc2ghmd](https://github.com/GandalfUK/godoc2ghmd)
\ No newline at end of file diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/util/metautils/README.md b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/util/metautils/README.md new file mode 100644 index 00000000..a02cde31 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/util/metautils/README.md @@ -0,0 +1,114 @@ +# metautils +`import "github.com/grpc-ecosystem/go-grpc-middleware/util/metautils"` + +* [Overview](#pkg-overview) +* [Imported Packages](#pkg-imports) +* [Index](#pkg-index) + +## <a name="pkg-overview">Overview</a> + +## <a name="pkg-imports">Imported Packages</a> + +- [golang.org/x/net/context](https://godoc.org/golang.org/x/net/context) +- [google.golang.org/grpc/metadata](https://godoc.org/google.golang.org/grpc/metadata) + +## <a name="pkg-index">Index</a> +* [type NiceMD](#NiceMD) + * [func ExtractIncoming(ctx context.Context) NiceMD](#ExtractIncoming) + * [func ExtractOutgoing(ctx context.Context) NiceMD](#ExtractOutgoing) + * [func (m NiceMD) Add(key string, value string) NiceMD](#NiceMD.Add) + * [func (m NiceMD) Clone(copiedKeys ...string) NiceMD](#NiceMD.Clone) + * [func (m NiceMD) Del(key string) NiceMD](#NiceMD.Del) + * [func (m NiceMD) Get(key string) string](#NiceMD.Get) + * [func (m NiceMD) Set(key string, value string) NiceMD](#NiceMD.Set) + * [func (m NiceMD) ToIncoming(ctx context.Context) context.Context](#NiceMD.ToIncoming) + * [func (m NiceMD) ToOutgoing(ctx context.Context) context.Context](#NiceMD.ToOutgoing) + +#### <a name="pkg-files">Package files</a> +[doc.go](./doc.go) [nicemd.go](./nicemd.go) [single_key.go](./single_key.go) + +## <a name="NiceMD">type</a> [NiceMD](./nicemd.go#L14) +``` go +type NiceMD metadata.MD +``` +NiceMD is a convenience wrapper definiting extra functions on the metadata. + +### <a name="ExtractIncoming">func</a> [ExtractIncoming](./nicemd.go#L20) +``` go +func ExtractIncoming(ctx context.Context) NiceMD +``` +ExtractIncoming extracts an inbound metadata from the server-side context. + +This function always returns a NiceMD wrapper of the metadata.MD, in case the context doesn't have metadata it returns +a new empty NiceMD. + +### <a name="ExtractOutgoing">func</a> [ExtractOutgoing](./nicemd.go#L32) +``` go +func ExtractOutgoing(ctx context.Context) NiceMD +``` +ExtractOutgoing extracts an outbound metadata from the client-side context. + +This function always returns a NiceMD wrapper of the metadata.MD, in case the context doesn't have metadata it returns +a new empty NiceMD. + +### <a name="NiceMD.Add">func</a> (NiceMD) [Add](./nicemd.go#L122) +``` go +func (m NiceMD) Add(key string, value string) NiceMD +``` +Add retrieves a single value from the metadata. + +It works analogously to http.Header.Add, as it appends to any existing values associated with key. + +The function is binary-key safe. + +### <a name="NiceMD.Clone">func</a> (NiceMD) [Clone](./nicemd.go#L44) +``` go +func (m NiceMD) Clone(copiedKeys ...string) NiceMD +``` +Clone performs a *deep* copy of the metadata.MD. + +You can specify the lower-case copiedKeys to only copy certain whitelisted keys. If no keys are explicitly whitelisted +all keys get copied. + +### <a name="NiceMD.Del">func</a> (NiceMD) [Del](./nicemd.go#L100) +``` go +func (m NiceMD) Del(key string) NiceMD +``` + +### <a name="NiceMD.Get">func</a> (NiceMD) [Get](./nicemd.go#L85) +``` go +func (m NiceMD) Get(key string) string +``` +Get retrieves a single value from the metadata. + +It works analogously to http.Header.Get, returning the first value if there are many set. If the value is not set, +an empty string is returned. + +The function is binary-key safe. + +### <a name="NiceMD.Set">func</a> (NiceMD) [Set](./nicemd.go#L111) +``` go +func (m NiceMD) Set(key string, value string) NiceMD +``` +Set sets the given value in a metadata. + +It works analogously to http.Header.Set, overwriting all previous metadata values. + +The function is binary-key safe. + +### <a name="NiceMD.ToIncoming">func</a> (NiceMD) [ToIncoming](./nicemd.go#L75) +``` go +func (m NiceMD) ToIncoming(ctx context.Context) context.Context +``` +ToIncoming sets the given NiceMD as a server-side context for dispatching. + +This is mostly useful in ServerInterceptors.. + +### <a name="NiceMD.ToOutgoing">func</a> (NiceMD) [ToOutgoing](./nicemd.go#L68) +``` go +func (m NiceMD) ToOutgoing(ctx context.Context) context.Context +``` +ToOutgoing sets the given NiceMD as a client-side context for dispatching. + +- - - +Generated by [godoc2ghmd](https://github.com/GandalfUK/godoc2ghmd)
\ No newline at end of file diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/util/metautils/doc.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/util/metautils/doc.go new file mode 100644 index 00000000..1ed9bb49 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/util/metautils/doc.go @@ -0,0 +1,19 @@ +// Copyright 2016 Michal Witkowski. All Rights Reserved. +// See LICENSE for licensing terms. + +/* +Package `metautils` provides convenience functions for dealing with gRPC metadata.MD objects inside +Context handlers. + +While the upstream grpc-go package contains decent functionality (see https://github.com/grpc/grpc-go/blob/master/Documentation/grpc-metadata.md) +they are hard to use. + +The majority of functions center around the NiceMD, which is a convenience wrapper around metadata.MD. For example +the following code allows you to easily extract incoming metadata (server handler) and put it into a new client context +metadata. + + nmd := metautils.ExtractIncoming(serverCtx).Clone(":authorization", ":custom") + clientCtx := nmd.Set("x-client-header", "2").Set("x-another", "3").ToOutgoing(ctx) +*/ + +package metautils diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/util/metautils/nicemd.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/util/metautils/nicemd.go new file mode 100644 index 00000000..a277bee3 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/util/metautils/nicemd.go @@ -0,0 +1,126 @@ +// Copyright 2016 Michal Witkowski. All Rights Reserved. +// See LICENSE for licensing terms. + +package metautils + +import ( + "strings" + + "golang.org/x/net/context" + "google.golang.org/grpc/metadata" +) + +// NiceMD is a convenience wrapper definiting extra functions on the metadata. +type NiceMD metadata.MD + +// ExtractIncoming extracts an inbound metadata from the server-side context. +// +// This function always returns a NiceMD wrapper of the metadata.MD, in case the context doesn't have metadata it returns +// a new empty NiceMD. +func ExtractIncoming(ctx context.Context) NiceMD { + md, ok := metadata.FromIncomingContext(ctx) + if !ok { + return NiceMD(metadata.Pairs()) + } + return NiceMD(md) +} + +// ExtractOutgoing extracts an outbound metadata from the client-side context. +// +// This function always returns a NiceMD wrapper of the metadata.MD, in case the context doesn't have metadata it returns +// a new empty NiceMD. +func ExtractOutgoing(ctx context.Context) NiceMD { + md, ok := metadata.FromOutgoingContext(ctx) + if !ok { + return NiceMD(metadata.Pairs()) + } + return NiceMD(md) +} + +// Clone performs a *deep* copy of the metadata.MD. +// +// You can specify the lower-case copiedKeys to only copy certain whitelisted keys. If no keys are explicitly whitelisted +// all keys get copied. +func (m NiceMD) Clone(copiedKeys ...string) NiceMD { + newMd := NiceMD(metadata.Pairs()) + for k, vv := range m { + found := false + if len(copiedKeys) == 0 { + found = true + } else { + for _, allowedKey := range copiedKeys { + if strings.ToLower(allowedKey) == strings.ToLower(k) { + found = true + break + } + } + } + if !found { + continue + } + newMd[k] = make([]string, len(vv)) + copy(newMd[k], vv) + } + return NiceMD(newMd) +} + +// ToOutgoing sets the given NiceMD as a client-side context for dispatching. +func (m NiceMD) ToOutgoing(ctx context.Context) context.Context { + return metadata.NewOutgoingContext(ctx, metadata.MD(m)) +} + +// ToIncoming sets the given NiceMD as a server-side context for dispatching. +// +// This is mostly useful in ServerInterceptors.. +func (m NiceMD) ToIncoming(ctx context.Context) context.Context { + return metadata.NewIncomingContext(ctx, metadata.MD(m)) +} + +// Get retrieves a single value from the metadata. +// +// It works analogously to http.Header.Get, returning the first value if there are many set. If the value is not set, +// an empty string is returned. +// +// The function is binary-key safe. +func (m NiceMD) Get(key string) string { + k, _ := encodeKeyValue(key, "") + vv, ok := m[k] + if !ok { + return "" + } + return vv[0] +} + +// Del retrieves a single value from the metadata. +// +// It works analogously to http.Header.Del, deleting all values if they exist. +// +// The function is binary-key safe. + +func (m NiceMD) Del(key string) NiceMD { + k, _ := encodeKeyValue(key, "") + delete(m, k) + return m +} + +// Set sets the given value in a metadata. +// +// It works analogously to http.Header.Set, overwriting all previous metadata values. +// +// The function is binary-key safe. +func (m NiceMD) Set(key string, value string) NiceMD { + k, v := encodeKeyValue(key, value) + m[k] = []string{v} + return m +} + +// Add retrieves a single value from the metadata. +// +// It works analogously to http.Header.Add, as it appends to any existing values associated with key. +// +// The function is binary-key safe. +func (m NiceMD) Add(key string, value string) NiceMD { + k, v := encodeKeyValue(key, value) + m[k] = append(m[k], v) + return m +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/util/metautils/single_key.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/util/metautils/single_key.go new file mode 100644 index 00000000..8a538716 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/util/metautils/single_key.go @@ -0,0 +1,22 @@ +// Copyright 2016 Michal Witkowski. All Rights Reserved. +// See LICENSE for licensing terms. + +package metautils + +import ( + "encoding/base64" + "strings" +) + +const ( + binHdrSuffix = "-bin" +) + +func encodeKeyValue(k, v string) (string, string) { + k = strings.ToLower(k) + if strings.HasSuffix(k, binHdrSuffix) { + val := base64.StdEncoding.EncodeToString([]byte(v)) + v = string(val) + } + return k, v +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/wrappers.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/wrappers.go new file mode 100644 index 00000000..597b8624 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/wrappers.go @@ -0,0 +1,29 @@ +// Copyright 2016 Michal Witkowski. All Rights Reserved. +// See LICENSE for licensing terms. + +package grpc_middleware + +import ( + "golang.org/x/net/context" + "google.golang.org/grpc" +) + +// WrappedServerStream is a thin wrapper around grpc.ServerStream that allows modifying context. +type WrappedServerStream struct { + grpc.ServerStream + // WrappedContext is the wrapper's own Context. You can assign it. + WrappedContext context.Context +} + +// Context returns the wrapper's WrappedContext, overwriting the nested grpc.ServerStream.Context() +func (w *WrappedServerStream) Context() context.Context { + return w.WrappedContext +} + +// WrapServerStream returns a ServerStream that has the ability to overwrite context. +func WrapServerStream(stream grpc.ServerStream) *WrappedServerStream { + if existing, ok := stream.(*WrappedServerStream); ok { + return existing + } + return &WrappedServerStream{ServerStream: stream, WrappedContext: stream.Context()} +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/LICENSE b/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/LICENSE new file mode 100644 index 00000000..b2b06503 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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.
\ No newline at end of file diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/README.md b/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/README.md new file mode 100644 index 00000000..0eaa0846 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/README.md @@ -0,0 +1,247 @@ +# Go gRPC Interceptors for Prometheus monitoring + +[![Travis Build](https://travis-ci.org/grpc-ecosystem/go-grpc-prometheus.svg)](https://travis-ci.org/grpc-ecosystem/go-grpc-prometheus) +[![Go Report Card](https://goreportcard.com/badge/github.com/grpc-ecosystem/go-grpc-prometheus)](http://goreportcard.com/report/grpc-ecosystem/go-grpc-prometheus) +[![GoDoc](http://img.shields.io/badge/GoDoc-Reference-blue.svg)](https://godoc.org/github.com/grpc-ecosystem/go-grpc-prometheus) +[![SourceGraph](https://sourcegraph.com/github.com/grpc-ecosystem/go-grpc-prometheus/-/badge.svg)](https://sourcegraph.com/github.com/grpc-ecosystem/go-grpc-prometheus/?badge) +[![codecov](https://codecov.io/gh/grpc-ecosystem/go-grpc-prometheus/branch/master/graph/badge.svg)](https://codecov.io/gh/grpc-ecosystem/go-grpc-prometheus) +[![Apache 2.0 License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE) + +[Prometheus](https://prometheus.io/) monitoring for your [gRPC Go](https://github.com/grpc/grpc-go) servers and clients. + +A sister implementation for [gRPC Java](https://github.com/grpc/grpc-java) (same metrics, same semantics) is in [grpc-ecosystem/java-grpc-prometheus](https://github.com/grpc-ecosystem/java-grpc-prometheus). + +## Interceptors + +[gRPC Go](https://github.com/grpc/grpc-go) recently acquired support for Interceptors, i.e. middleware that is executed +by a gRPC Server before the request is passed onto the user's application logic. It is a perfect way to implement +common patterns: auth, logging and... monitoring. + +To use Interceptors in chains, please see [`go-grpc-middleware`](https://github.com/mwitkow/go-grpc-middleware). + +## Usage + +There are two types of interceptors: client-side and server-side. This package provides monitoring Interceptors for both. + +### Server-side + +```go +import "github.com/grpc-ecosystem/go-grpc-prometheus" +... + // Initialize your gRPC server's interceptor. + myServer := grpc.NewServer( + grpc.StreamInterceptor(grpc_prometheus.StreamServerInterceptor), + grpc.UnaryInterceptor(grpc_prometheus.UnaryServerInterceptor), + ) + // Register your gRPC service implementations. + myservice.RegisterMyServiceServer(s.server, &myServiceImpl{}) + // After all your registrations, make sure all of the Prometheus metrics are initialized. + grpc_prometheus.Register(myServer) + // Register Prometheus metrics handler. + http.Handle("/metrics", promhttp.Handler()) +... +``` + +### Client-side + +```go +import "github.com/grpc-ecosystem/go-grpc-prometheus" +... + clientConn, err = grpc.Dial( + address, + grpc.WithUnaryInterceptor(grpc_prometheus.UnaryClientInterceptor), + grpc.WithStreamInterceptor(grpc_prometheus.StreamClientInterceptor) + ) + client = pb_testproto.NewTestServiceClient(clientConn) + resp, err := client.PingEmpty(s.ctx, &myservice.Request{Msg: "hello"}) +... +``` + +# Metrics + +## Labels + +All server-side metrics start with `grpc_server` as Prometheus subsystem name. All client-side metrics start with `grpc_client`. Both of them have mirror-concepts. Similarly all methods +contain the same rich labels: + + * `grpc_service` - the [gRPC service](http://www.grpc.io/docs/#defining-a-service) name, which is the combination of protobuf `package` and + the `grpc_service` section name. E.g. for `package = mwitkow.testproto` and + `service TestService` the label will be `grpc_service="mwitkow.testproto.TestService"` + * `grpc_method` - the name of the method called on the gRPC service. E.g. + `grpc_method="Ping"` + * `grpc_type` - the gRPC [type of request](http://www.grpc.io/docs/guides/concepts.html#rpc-life-cycle). + Differentiating between the two is important especially for latency measurements. + + - `unary` is single request, single response RPC + - `client_stream` is a multi-request, single response RPC + - `server_stream` is a single request, multi-response RPC + - `bidi_stream` is a multi-request, multi-response RPC + + +Additionally for completed RPCs, the following labels are used: + + * `grpc_code` - the human-readable [gRPC status code](https://github.com/grpc/grpc-go/blob/master/codes/codes.go). + The list of all statuses is to long, but here are some common ones: + + - `OK` - means the RPC was successful + - `IllegalArgument` - RPC contained bad values + - `Internal` - server-side error not disclosed to the clients + +## Counters + +The counters and their up to date documentation is in [server_reporter.go](server_reporter.go) and [client_reporter.go](client_reporter.go) +the respective Prometheus handler (usually `/metrics`). + +For the purpose of this documentation we will only discuss `grpc_server` metrics. The `grpc_client` ones contain mirror concepts. + +For simplicity, let's assume we're tracking a single server-side RPC call of [`mwitkow.testproto.TestService`](examples/testproto/test.proto), +calling the method `PingList`. The call succeeds and returns 20 messages in the stream. + +First, immediately after the server receives the call it will increment the +`grpc_server_started_total` and start the handling time clock (if histograms are enabled). + +```jsoniq +grpc_server_started_total{grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream"} 1 +``` + +Then the user logic gets invoked. It receives one message from the client containing the request +(it's a `server_stream`): + +```jsoniq +grpc_server_msg_received_total{grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream"} 1 +``` + +The user logic may return an error, or send multiple messages back to the client. In this case, on +each of the 20 messages sent back, a counter will be incremented: + +```jsoniq +grpc_server_msg_sent_total{grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream"} 20 +``` + +After the call completes, it's status (`OK` or other [gRPC status code](https://github.com/grpc/grpc-go/blob/master/codes/codes.go)) +and the relevant call labels increment the `grpc_server_handled_total` counter. + +```jsoniq +grpc_server_handled_total{grpc_code="OK",grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream"} 1 +``` + +## Histograms + +[Prometheus histograms](https://prometheus.io/docs/concepts/metric_types/#histogram) are a great way +to measure latency distributions of your RPCs. However, since it is bad practice to have metrics +of [high cardinality](https://prometheus.io/docs/practices/instrumentation/#do-not-overuse-labels) +the latency monitoring metrics are disabled by default. To enable them please call the following +in your server initialization code: + +```jsoniq +grpc_prometheus.EnableHandlingTimeHistogram() +``` + +After the call completes, its handling time will be recorded in a [Prometheus histogram](https://prometheus.io/docs/concepts/metric_types/#histogram) +variable `grpc_server_handling_seconds`. The histogram variable contains three sub-metrics: + + * `grpc_server_handling_seconds_count` - the count of all completed RPCs by status and method + * `grpc_server_handling_seconds_sum` - cumulative time of RPCs by status and method, useful for + calculating average handling times + * `grpc_server_handling_seconds_bucket` - contains the counts of RPCs by status and method in respective + handling-time buckets. These buckets can be used by Prometheus to estimate SLAs (see [here](https://prometheus.io/docs/practices/histograms/)) + +The counter values will look as follows: + +```jsoniq +grpc_server_handling_seconds_bucket{grpc_code="OK",grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream",le="0.005"} 1 +grpc_server_handling_seconds_bucket{grpc_code="OK",grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream",le="0.01"} 1 +grpc_server_handling_seconds_bucket{grpc_code="OK",grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream",le="0.025"} 1 +grpc_server_handling_seconds_bucket{grpc_code="OK",grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream",le="0.05"} 1 +grpc_server_handling_seconds_bucket{grpc_code="OK",grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream",le="0.1"} 1 +grpc_server_handling_seconds_bucket{grpc_code="OK",grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream",le="0.25"} 1 +grpc_server_handling_seconds_bucket{grpc_code="OK",grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream",le="0.5"} 1 +grpc_server_handling_seconds_bucket{grpc_code="OK",grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream",le="1"} 1 +grpc_server_handling_seconds_bucket{grpc_code="OK",grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream",le="2.5"} 1 +grpc_server_handling_seconds_bucket{grpc_code="OK",grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream",le="5"} 1 +grpc_server_handling_seconds_bucket{grpc_code="OK",grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream",le="10"} 1 +grpc_server_handling_seconds_bucket{grpc_code="OK",grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream",le="+Inf"} 1 +grpc_server_handling_seconds_sum{grpc_code="OK",grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream"} 0.0003866430000000001 +grpc_server_handling_seconds_count{grpc_code="OK",grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream"} 1 +``` + + +## Useful query examples + +Prometheus philosophy is to provide raw metrics to the monitoring system, and +let the aggregations be handled there. The verbosity of above metrics make it possible to have that +flexibility. Here's a couple of useful monitoring queries: + + +### request inbound rate +```jsoniq +sum(rate(grpc_server_started_total{job="foo"}[1m])) by (grpc_service) +``` +For `job="foo"` (common label to differentiate between Prometheus monitoring targets), calculate the +rate of requests per second (1 minute window) for each gRPC `grpc_service` that the job has. Please note +how the `grpc_method` is being omitted here: all methods of a given gRPC service will be summed together. + +### unary request error rate +```jsoniq +sum(rate(grpc_server_handled_total{job="foo",grpc_type="unary",grpc_code!="OK"}[1m])) by (grpc_service) +``` +For `job="foo"`, calculate the per-`grpc_service` rate of `unary` (1:1) RPCs that failed, i.e. the +ones that didn't finish with `OK` code. + +### unary request error percentage +```jsoniq +sum(rate(grpc_server_handled_total{job="foo",grpc_type="unary",grpc_code!="OK"}[1m])) by (grpc_service) + / +sum(rate(grpc_server_started_total{job="foo",grpc_type="unary"}[1m])) by (grpc_service) + * 100.0 +``` +For `job="foo"`, calculate the percentage of failed requests by service. It's easy to notice that +this is a combination of the two above examples. This is an example of a query you would like to +[alert on](https://prometheus.io/docs/alerting/rules/) in your system for SLA violations, e.g. +"no more than 1% requests should fail". + +### average response stream size +```jsoniq +sum(rate(grpc_server_msg_sent_total{job="foo",grpc_type="server_stream"}[10m])) by (grpc_service) + / +sum(rate(grpc_server_started_total{job="foo",grpc_type="server_stream"}[10m])) by (grpc_service) +``` +For `job="foo"` what is the `grpc_service`-wide `10m` average of messages returned for all ` +server_stream` RPCs. This allows you to track the stream sizes returned by your system, e.g. allows +you to track when clients started to send "wide" queries that ret +Note the divisor is the number of started RPCs, in order to account for in-flight requests. + +### 99%-tile latency of unary requests +```jsoniq +histogram_quantile(0.99, + sum(rate(grpc_server_handling_seconds_bucket{job="foo",grpc_type="unary"}[5m])) by (grpc_service,le) +) +``` +For `job="foo"`, returns an 99%-tile [quantile estimation](https://prometheus.io/docs/practices/histograms/#quantiles) +of the handling time of RPCs per service. Please note the `5m` rate, this means that the quantile +estimation will take samples in a rolling `5m` window. When combined with other quantiles +(e.g. 50%, 90%), this query gives you tremendous insight into the responsiveness of your system +(e.g. impact of caching). + +### percentage of slow unary queries (>250ms) +```jsoniq +100.0 - ( +sum(rate(grpc_server_handling_seconds_bucket{job="foo",grpc_type="unary",le="0.25"}[5m])) by (grpc_service) + / +sum(rate(grpc_server_handling_seconds_count{job="foo",grpc_type="unary"}[5m])) by (grpc_service) +) * 100.0 +``` +For `job="foo"` calculate the by-`grpc_service` fraction of slow requests that took longer than `0.25` +seconds. This query is relatively complex, since the Prometheus aggregations use `le` (less or equal) +buckets, meaning that counting "fast" requests fractions is easier. However, simple maths helps. +This is an example of a query you would like to alert on in your system for SLA violations, +e.g. "less than 1% of requests are slower than 250ms". + + +## Status + +This code has been used since August 2015 as the basis for monitoring of *production* gRPC micro services at [Improbable](https://improbable.io). + +## License + +`go-grpc-prometheus` is released under the Apache 2.0 license. See the [LICENSE](LICENSE) file for details. diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/client.go b/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/client.go new file mode 100644 index 00000000..751a4c72 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/client.go @@ -0,0 +1,39 @@ +// Copyright 2016 Michal Witkowski. All Rights Reserved. +// See LICENSE for licensing terms. + +// gRPC Prometheus monitoring interceptors for client-side gRPC. + +package grpc_prometheus + +import ( + prom "github.com/prometheus/client_golang/prometheus" +) + +var ( + // DefaultClientMetrics is the default instance of ClientMetrics. It is + // intended to be used in conjunction the default Prometheus metrics + // registry. + DefaultClientMetrics = NewClientMetrics() + + // UnaryClientInterceptor is a gRPC client-side interceptor that provides Prometheus monitoring for Unary RPCs. + UnaryClientInterceptor = DefaultClientMetrics.UnaryClientInterceptor() + + // StreamClientInterceptor is a gRPC client-side interceptor that provides Prometheus monitoring for Streaming RPCs. + StreamClientInterceptor = DefaultClientMetrics.StreamClientInterceptor() +) + +func init() { + prom.MustRegister(DefaultClientMetrics.clientStartedCounter) + prom.MustRegister(DefaultClientMetrics.clientHandledCounter) + prom.MustRegister(DefaultClientMetrics.clientStreamMsgReceived) + prom.MustRegister(DefaultClientMetrics.clientStreamMsgSent) +} + +// EnableClientHandlingTimeHistogram turns on recording of handling time of +// RPCs. Histogram metrics can be very expensive for Prometheus to retain and +// query. This function acts on the DefaultClientMetrics variable and the +// default Prometheus metrics registry. +func EnableClientHandlingTimeHistogram(opts ...HistogramOption) { + DefaultClientMetrics.EnableClientHandlingTimeHistogram(opts...) + prom.Register(DefaultClientMetrics.clientHandledHistogram) +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/client_metrics.go b/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/client_metrics.go new file mode 100644 index 00000000..ba1a703f --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/client_metrics.go @@ -0,0 +1,166 @@ +package grpc_prometheus + +import ( + "io" + + prom "github.com/prometheus/client_golang/prometheus" + "golang.org/x/net/context" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" +) + +// ClientMetrics represents a collection of metrics to be registered on a +// Prometheus metrics registry for a gRPC client. +type ClientMetrics struct { + clientStartedCounter *prom.CounterVec + clientHandledCounter *prom.CounterVec + clientStreamMsgReceived *prom.CounterVec + clientStreamMsgSent *prom.CounterVec + clientHandledHistogramEnabled bool + clientHandledHistogramOpts prom.HistogramOpts + clientHandledHistogram *prom.HistogramVec +} + +// NewClientMetrics returns a ClientMetrics object. Use a new instance of +// ClientMetrics when not using the default Prometheus metrics registry, for +// example when wanting to control which metrics are added to a registry as +// opposed to automatically adding metrics via init functions. +func NewClientMetrics(counterOpts ...CounterOption) *ClientMetrics { + opts := counterOptions(counterOpts) + return &ClientMetrics{ + clientStartedCounter: prom.NewCounterVec( + opts.apply(prom.CounterOpts{ + Name: "grpc_client_started_total", + Help: "Total number of RPCs started on the client.", + }), []string{"grpc_type", "grpc_service", "grpc_method"}), + + clientHandledCounter: prom.NewCounterVec( + opts.apply(prom.CounterOpts{ + Name: "grpc_client_handled_total", + Help: "Total number of RPCs completed by the client, regardless of success or failure.", + }), []string{"grpc_type", "grpc_service", "grpc_method", "grpc_code"}), + + clientStreamMsgReceived: prom.NewCounterVec( + opts.apply(prom.CounterOpts{ + Name: "grpc_client_msg_received_total", + Help: "Total number of RPC stream messages received by the client.", + }), []string{"grpc_type", "grpc_service", "grpc_method"}), + + clientStreamMsgSent: prom.NewCounterVec( + opts.apply(prom.CounterOpts{ + Name: "grpc_client_msg_sent_total", + Help: "Total number of gRPC stream messages sent by the client.", + }), []string{"grpc_type", "grpc_service", "grpc_method"}), + + clientHandledHistogramEnabled: false, + clientHandledHistogramOpts: prom.HistogramOpts{ + Name: "grpc_client_handling_seconds", + Help: "Histogram of response latency (seconds) of the gRPC until it is finished by the application.", + Buckets: prom.DefBuckets, + }, + clientHandledHistogram: nil, + } +} + +// Describe sends the super-set of all possible descriptors of metrics +// collected by this Collector to the provided channel and returns once +// the last descriptor has been sent. +func (m *ClientMetrics) Describe(ch chan<- *prom.Desc) { + m.clientStartedCounter.Describe(ch) + m.clientHandledCounter.Describe(ch) + m.clientStreamMsgReceived.Describe(ch) + m.clientStreamMsgSent.Describe(ch) + if m.clientHandledHistogramEnabled { + m.clientHandledHistogram.Describe(ch) + } +} + +// Collect is called by the Prometheus registry when collecting +// metrics. The implementation sends each collected metric via the +// provided channel and returns once the last metric has been sent. +func (m *ClientMetrics) Collect(ch chan<- prom.Metric) { + m.clientStartedCounter.Collect(ch) + m.clientHandledCounter.Collect(ch) + m.clientStreamMsgReceived.Collect(ch) + m.clientStreamMsgSent.Collect(ch) + if m.clientHandledHistogramEnabled { + m.clientHandledHistogram.Collect(ch) + } +} + +// EnableClientHandlingTimeHistogram turns on recording of handling time of RPCs. +// Histogram metrics can be very expensive for Prometheus to retain and query. +func (m *ClientMetrics) EnableClientHandlingTimeHistogram(opts ...HistogramOption) { + for _, o := range opts { + o(&m.clientHandledHistogramOpts) + } + if !m.clientHandledHistogramEnabled { + m.clientHandledHistogram = prom.NewHistogramVec( + m.clientHandledHistogramOpts, + []string{"grpc_type", "grpc_service", "grpc_method"}, + ) + } + m.clientHandledHistogramEnabled = true +} + +// UnaryClientInterceptor is a gRPC client-side interceptor that provides Prometheus monitoring for Unary RPCs. +func (m *ClientMetrics) UnaryClientInterceptor() func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { + return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { + monitor := newClientReporter(m, Unary, method) + monitor.SentMessage() + err := invoker(ctx, method, req, reply, cc, opts...) + if err != nil { + monitor.ReceivedMessage() + } + monitor.Handled(grpc.Code(err)) + return err + } +} + +// StreamServerInterceptor is a gRPC client-side interceptor that provides Prometheus monitoring for Streaming RPCs. +func (m *ClientMetrics) StreamClientInterceptor() func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) { + return func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) { + monitor := newClientReporter(m, clientStreamType(desc), method) + clientStream, err := streamer(ctx, desc, cc, method, opts...) + if err != nil { + monitor.Handled(grpc.Code(err)) + return nil, err + } + return &monitoredClientStream{clientStream, monitor}, nil + } +} + +func clientStreamType(desc *grpc.StreamDesc) grpcType { + if desc.ClientStreams && !desc.ServerStreams { + return ClientStream + } else if !desc.ClientStreams && desc.ServerStreams { + return ServerStream + } + return BidiStream +} + +// monitoredClientStream wraps grpc.ClientStream allowing each Sent/Recv of message to increment counters. +type monitoredClientStream struct { + grpc.ClientStream + monitor *clientReporter +} + +func (s *monitoredClientStream) SendMsg(m interface{}) error { + err := s.ClientStream.SendMsg(m) + if err == nil { + s.monitor.SentMessage() + } + return err +} + +func (s *monitoredClientStream) RecvMsg(m interface{}) error { + err := s.ClientStream.RecvMsg(m) + if err == nil { + s.monitor.ReceivedMessage() + } else if err == io.EOF { + s.monitor.Handled(codes.OK) + } else { + s.monitor.Handled(grpc.Code(err)) + } + return err +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/client_reporter.go b/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/client_reporter.go new file mode 100644 index 00000000..cbf15322 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/client_reporter.go @@ -0,0 +1,46 @@ +// Copyright 2016 Michal Witkowski. All Rights Reserved. +// See LICENSE for licensing terms. + +package grpc_prometheus + +import ( + "time" + + "google.golang.org/grpc/codes" +) + +type clientReporter struct { + metrics *ClientMetrics + rpcType grpcType + serviceName string + methodName string + startTime time.Time +} + +func newClientReporter(m *ClientMetrics, rpcType grpcType, fullMethod string) *clientReporter { + r := &clientReporter{ + metrics: m, + rpcType: rpcType, + } + if r.metrics.clientHandledHistogramEnabled { + r.startTime = time.Now() + } + r.serviceName, r.methodName = splitMethodName(fullMethod) + r.metrics.clientStartedCounter.WithLabelValues(string(r.rpcType), r.serviceName, r.methodName).Inc() + return r +} + +func (r *clientReporter) ReceivedMessage() { + r.metrics.clientStreamMsgReceived.WithLabelValues(string(r.rpcType), r.serviceName, r.methodName).Inc() +} + +func (r *clientReporter) SentMessage() { + r.metrics.clientStreamMsgSent.WithLabelValues(string(r.rpcType), r.serviceName, r.methodName).Inc() +} + +func (r *clientReporter) Handled(code codes.Code) { + r.metrics.clientHandledCounter.WithLabelValues(string(r.rpcType), r.serviceName, r.methodName, code.String()).Inc() + if r.metrics.clientHandledHistogramEnabled { + r.metrics.clientHandledHistogram.WithLabelValues(string(r.rpcType), r.serviceName, r.methodName).Observe(time.Since(r.startTime).Seconds()) + } +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/makefile b/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/makefile new file mode 100644 index 00000000..74c08422 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/makefile @@ -0,0 +1,16 @@ +SHELL="/bin/bash" + +GOFILES_NOVENDOR = $(shell go list ./... | grep -v /vendor/) + +all: vet fmt test + +fmt: + go fmt $(GOFILES_NOVENDOR) + +vet: + go vet $(GOFILES_NOVENDOR) + +test: vet + ./scripts/test_all.sh + +.PHONY: all vet test diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/metric_options.go b/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/metric_options.go new file mode 100644 index 00000000..ad8a51c5 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/metric_options.go @@ -0,0 +1,35 @@ +package grpc_prometheus + +import ( + prom "github.com/prometheus/client_golang/prometheus" +) + +type CounterOption func(opts *prom.CounterOpts) + +type counterOptions []CounterOption + +func (co counterOptions) apply(o prom.CounterOpts) prom.CounterOpts { + for _, f := range co { + f(&o) + } + return o +} + +func WithConstLabels(labels prom.Labels) CounterOption { + return func(o *prom.CounterOpts) { + o.ConstLabels = labels + } +} + +type HistogramOption func(*prom.HistogramOpts) + +// WithHistogramBuckets allows you to specify custom bucket ranges for histograms if EnableHandlingTimeHistogram is on. +func WithHistogramBuckets(buckets []float64) HistogramOption { + return func(o *prom.HistogramOpts) { o.Buckets = buckets } +} + +func WithHistogramConstLabels(labels prom.Labels) HistogramOption { + return func(o *prom.HistogramOpts) { + o.ConstLabels = labels + } +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/server.go b/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/server.go new file mode 100644 index 00000000..322f9904 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/server.go @@ -0,0 +1,48 @@ +// Copyright 2016 Michal Witkowski. All Rights Reserved. +// See LICENSE for licensing terms. + +// gRPC Prometheus monitoring interceptors for server-side gRPC. + +package grpc_prometheus + +import ( + prom "github.com/prometheus/client_golang/prometheus" + "google.golang.org/grpc" +) + +var ( + // DefaultServerMetrics is the default instance of ServerMetrics. It is + // intended to be used in conjunction the default Prometheus metrics + // registry. + DefaultServerMetrics = NewServerMetrics() + + // UnaryServerInterceptor is a gRPC server-side interceptor that provides Prometheus monitoring for Unary RPCs. + UnaryServerInterceptor = DefaultServerMetrics.UnaryServerInterceptor() + + // StreamServerInterceptor is a gRPC server-side interceptor that provides Prometheus monitoring for Streaming RPCs. + StreamServerInterceptor = DefaultServerMetrics.StreamServerInterceptor() +) + +func init() { + prom.MustRegister(DefaultServerMetrics.serverStartedCounter) + prom.MustRegister(DefaultServerMetrics.serverHandledCounter) + prom.MustRegister(DefaultServerMetrics.serverStreamMsgReceived) + prom.MustRegister(DefaultServerMetrics.serverStreamMsgSent) +} + +// Register takes a gRPC server and pre-initializes all counters to 0. This +// allows for easier monitoring in Prometheus (no missing metrics), and should +// be called *after* all services have been registered with the server. This +// function acts on the DefaultServerMetrics variable. +func Register(server *grpc.Server) { + DefaultServerMetrics.InitializeMetrics(server) +} + +// EnableHandlingTimeHistogram turns on recording of handling time +// of RPCs. Histogram metrics can be very expensive for Prometheus +// to retain and query. This function acts on the DefaultServerMetrics +// variable and the default Prometheus metrics registry. +func EnableHandlingTimeHistogram(opts ...HistogramOption) { + DefaultServerMetrics.EnableHandlingTimeHistogram(opts...) + prom.Register(DefaultServerMetrics.serverHandledHistogram) +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/server_metrics.go b/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/server_metrics.go new file mode 100644 index 00000000..afa01428 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/server_metrics.go @@ -0,0 +1,202 @@ +package grpc_prometheus + +import ( + prom "github.com/prometheus/client_golang/prometheus" + "golang.org/x/net/context" + "google.golang.org/grpc" +) + +// ServerMetrics represents a collection of metrics to be registered on a +// Prometheus metrics registry for a gRPC server. +type ServerMetrics struct { + serverStartedCounter *prom.CounterVec + serverHandledCounter *prom.CounterVec + serverStreamMsgReceived *prom.CounterVec + serverStreamMsgSent *prom.CounterVec + serverHandledHistogramEnabled bool + serverHandledHistogramOpts prom.HistogramOpts + serverHandledHistogram *prom.HistogramVec +} + +// NewServerMetrics returns a ServerMetrics object. Use a new instance of +// ServerMetrics when not using the default Prometheus metrics registry, for +// example when wanting to control which metrics are added to a registry as +// opposed to automatically adding metrics via init functions. +func NewServerMetrics(counterOpts ...CounterOption) *ServerMetrics { + opts := counterOptions(counterOpts) + return &ServerMetrics{ + serverStartedCounter: prom.NewCounterVec( + opts.apply(prom.CounterOpts{ + Name: "grpc_server_started_total", + Help: "Total number of RPCs started on the server.", + }), []string{"grpc_type", "grpc_service", "grpc_method"}), + serverHandledCounter: prom.NewCounterVec( + opts.apply(prom.CounterOpts{ + Name: "grpc_server_handled_total", + Help: "Total number of RPCs completed on the server, regardless of success or failure.", + }), []string{"grpc_type", "grpc_service", "grpc_method", "grpc_code"}), + serverStreamMsgReceived: prom.NewCounterVec( + opts.apply(prom.CounterOpts{ + Name: "grpc_server_msg_received_total", + Help: "Total number of RPC stream messages received on the server.", + }), []string{"grpc_type", "grpc_service", "grpc_method"}), + serverStreamMsgSent: prom.NewCounterVec( + opts.apply(prom.CounterOpts{ + Name: "grpc_server_msg_sent_total", + Help: "Total number of gRPC stream messages sent by the server.", + }), []string{"grpc_type", "grpc_service", "grpc_method"}), + serverHandledHistogramEnabled: false, + serverHandledHistogramOpts: prom.HistogramOpts{ + Name: "grpc_server_handling_seconds", + Help: "Histogram of response latency (seconds) of gRPC that had been application-level handled by the server.", + Buckets: prom.DefBuckets, + }, + serverHandledHistogram: nil, + } +} + +// EnableHandlingTimeHistogram enables histograms being registered when +// registering the ServerMetrics on a Prometheus registry. Histograms can be +// expensive on Prometheus servers. It takes options to configure histogram +// options such as the defined buckets. +func (m *ServerMetrics) EnableHandlingTimeHistogram(opts ...HistogramOption) { + for _, o := range opts { + o(&m.serverHandledHistogramOpts) + } + if !m.serverHandledHistogramEnabled { + m.serverHandledHistogram = prom.NewHistogramVec( + m.serverHandledHistogramOpts, + []string{"grpc_type", "grpc_service", "grpc_method"}, + ) + } + m.serverHandledHistogramEnabled = true +} + +// Describe sends the super-set of all possible descriptors of metrics +// collected by this Collector to the provided channel and returns once +// the last descriptor has been sent. +func (m *ServerMetrics) Describe(ch chan<- *prom.Desc) { + m.serverStartedCounter.Describe(ch) + m.serverHandledCounter.Describe(ch) + m.serverStreamMsgReceived.Describe(ch) + m.serverStreamMsgSent.Describe(ch) + if m.serverHandledHistogramEnabled { + m.serverHandledHistogram.Describe(ch) + } +} + +// Collect is called by the Prometheus registry when collecting +// metrics. The implementation sends each collected metric via the +// provided channel and returns once the last metric has been sent. +func (m *ServerMetrics) Collect(ch chan<- prom.Metric) { + m.serverStartedCounter.Collect(ch) + m.serverHandledCounter.Collect(ch) + m.serverStreamMsgReceived.Collect(ch) + m.serverStreamMsgSent.Collect(ch) + if m.serverHandledHistogramEnabled { + m.serverHandledHistogram.Collect(ch) + } +} + +// UnaryServerInterceptor is a gRPC server-side interceptor that provides Prometheus monitoring for Unary RPCs. +func (m *ServerMetrics) UnaryServerInterceptor() func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { + return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { + monitor := newServerReporter(m, Unary, info.FullMethod) + monitor.ReceivedMessage() + resp, err := handler(ctx, req) + monitor.Handled(grpc.Code(err)) + if err == nil { + monitor.SentMessage() + } + return resp, err + } +} + +// StreamServerInterceptor is a gRPC server-side interceptor that provides Prometheus monitoring for Streaming RPCs. +func (m *ServerMetrics) StreamServerInterceptor() func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { + return func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { + monitor := newServerReporter(m, streamRpcType(info), info.FullMethod) + err := handler(srv, &monitoredServerStream{ss, monitor}) + monitor.Handled(grpc.Code(err)) + return err + } +} + +// InitializeMetrics initializes all metrics, with their appropriate null +// value, for all gRPC methods registered on a gRPC server. This is useful, to +// ensure that all metrics exist when collecting and querying. +func (m *ServerMetrics) InitializeMetrics(server *grpc.Server) { + serviceInfo := server.GetServiceInfo() + for serviceName, info := range serviceInfo { + for _, mInfo := range info.Methods { + preRegisterMethod(m, serviceName, &mInfo) + } + } +} + +// Register registers all server metrics in a given metrics registry. Depending +// on histogram options and whether they are enabled, histogram metrics are +// also registered. +// +// Deprecated: ServerMetrics implements Prometheus Collector interface. You can +// register an instance of ServerMetrics directly by using +// prometheus.Register(m). +func (m *ServerMetrics) Register(r prom.Registerer) error { + return r.Register(m) +} + +// MustRegister tries to register all server metrics and panics on an error. +// +// Deprecated: ServerMetrics implements Prometheus Collector interface. You can +// register an instance of ServerMetrics directly by using +// prometheus.MustRegister(m). +func (m *ServerMetrics) MustRegister(r prom.Registerer) { + r.MustRegister(m) +} + +func streamRpcType(info *grpc.StreamServerInfo) grpcType { + if info.IsClientStream && !info.IsServerStream { + return ClientStream + } else if !info.IsClientStream && info.IsServerStream { + return ServerStream + } + return BidiStream +} + +// monitoredStream wraps grpc.ServerStream allowing each Sent/Recv of message to increment counters. +type monitoredServerStream struct { + grpc.ServerStream + monitor *serverReporter +} + +func (s *monitoredServerStream) SendMsg(m interface{}) error { + err := s.ServerStream.SendMsg(m) + if err == nil { + s.monitor.SentMessage() + } + return err +} + +func (s *monitoredServerStream) RecvMsg(m interface{}) error { + err := s.ServerStream.RecvMsg(m) + if err == nil { + s.monitor.ReceivedMessage() + } + return err +} + +// preRegisterMethod is invoked on Register of a Server, allowing all gRPC services labels to be pre-populated. +func preRegisterMethod(metrics *ServerMetrics, serviceName string, mInfo *grpc.MethodInfo) { + methodName := mInfo.Name + methodType := string(typeFromMethodInfo(mInfo)) + // These are just references (no increments), as just referencing will create the labels but not set values. + metrics.serverStartedCounter.GetMetricWithLabelValues(methodType, serviceName, methodName) + metrics.serverStreamMsgReceived.GetMetricWithLabelValues(methodType, serviceName, methodName) + metrics.serverStreamMsgSent.GetMetricWithLabelValues(methodType, serviceName, methodName) + if metrics.serverHandledHistogramEnabled { + metrics.serverHandledHistogram.GetMetricWithLabelValues(methodType, serviceName, methodName) + } + for _, code := range allCodes { + metrics.serverHandledCounter.GetMetricWithLabelValues(methodType, serviceName, methodName, code.String()) + } +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/server_reporter.go b/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/server_reporter.go new file mode 100644 index 00000000..aa9db540 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/server_reporter.go @@ -0,0 +1,46 @@ +// Copyright 2016 Michal Witkowski. All Rights Reserved. +// See LICENSE for licensing terms. + +package grpc_prometheus + +import ( + "time" + + "google.golang.org/grpc/codes" +) + +type serverReporter struct { + metrics *ServerMetrics + rpcType grpcType + serviceName string + methodName string + startTime time.Time +} + +func newServerReporter(m *ServerMetrics, rpcType grpcType, fullMethod string) *serverReporter { + r := &serverReporter{ + metrics: m, + rpcType: rpcType, + } + if r.metrics.serverHandledHistogramEnabled { + r.startTime = time.Now() + } + r.serviceName, r.methodName = splitMethodName(fullMethod) + r.metrics.serverStartedCounter.WithLabelValues(string(r.rpcType), r.serviceName, r.methodName).Inc() + return r +} + +func (r *serverReporter) ReceivedMessage() { + r.metrics.serverStreamMsgReceived.WithLabelValues(string(r.rpcType), r.serviceName, r.methodName).Inc() +} + +func (r *serverReporter) SentMessage() { + r.metrics.serverStreamMsgSent.WithLabelValues(string(r.rpcType), r.serviceName, r.methodName).Inc() +} + +func (r *serverReporter) Handled(code codes.Code) { + r.metrics.serverHandledCounter.WithLabelValues(string(r.rpcType), r.serviceName, r.methodName, code.String()).Inc() + if r.metrics.serverHandledHistogramEnabled { + r.metrics.serverHandledHistogram.WithLabelValues(string(r.rpcType), r.serviceName, r.methodName).Observe(time.Since(r.startTime).Seconds()) + } +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/util.go b/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/util.go new file mode 100644 index 00000000..d581c792 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-prometheus/util.go @@ -0,0 +1,50 @@ +// Copyright 2016 Michal Witkowski. All Rights Reserved. +// See LICENSE for licensing terms. + +package grpc_prometheus + +import ( + "strings" + + "google.golang.org/grpc" + "google.golang.org/grpc/codes" +) + +type grpcType string + +const ( + Unary grpcType = "unary" + ClientStream grpcType = "client_stream" + ServerStream grpcType = "server_stream" + BidiStream grpcType = "bidi_stream" +) + +var ( + allCodes = []codes.Code{ + codes.OK, codes.Canceled, codes.Unknown, codes.InvalidArgument, codes.DeadlineExceeded, codes.NotFound, + codes.AlreadyExists, codes.PermissionDenied, codes.Unauthenticated, codes.ResourceExhausted, + codes.FailedPrecondition, codes.Aborted, codes.OutOfRange, codes.Unimplemented, codes.Internal, + codes.Unavailable, codes.DataLoss, + } +) + +func splitMethodName(fullMethodName string) (string, string) { + fullMethodName = strings.TrimPrefix(fullMethodName, "/") // remove leading slash + if i := strings.Index(fullMethodName, "/"); i >= 0 { + return fullMethodName[:i], fullMethodName[i+1:] + } + return "unknown", "unknown" +} + +func typeFromMethodInfo(mInfo *grpc.MethodInfo) grpcType { + if mInfo.IsClientStream == false && mInfo.IsServerStream == false { + return Unary + } + if mInfo.IsClientStream == true && mInfo.IsServerStream == false { + return ClientStream + } + if mInfo.IsClientStream == false && mInfo.IsServerStream == true { + return ServerStream + } + return BidiStream +} |