diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index f3cc4e3..0000000 --- a/.gitmodules +++ /dev/null @@ -1,12 +0,0 @@ -[submodule "vendor/github.com/gogo/protobuf"] - path = vendor/github.com/gogo/protobuf - url = https://github.com/gogo/protobuf -[submodule "vendor/github.com/golang/protobuf"] - path = vendor/github.com/golang/protobuf - url = https://github.com/golang/protobuf -[submodule "vendor/github.com/stretchr/testify"] - path = vendor/github.com/stretchr/testify - url = https://github.com/stretchr/testify -[submodule "vendor/github.com/google/protobuf"] - path = vendor/github.com/google/protobuf - url = https://github.com/google/protobuf diff --git a/BUILD.bazel b/BUILD.bazel index fe8de9c..a91c26d 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -1,67 +1,48 @@ +load("@bazel_gazelle//:def.bzl", "gazelle") load("@io_bazel_rules_go//go:def.bzl", "go_library") load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") load("@io_bazel_rules_go//proto:compiler.bzl", "go_proto_compiler") load("@rules_proto//proto:defs.bzl", "proto_library") -filegroup( - name = "logfields", - srcs = ["logfields.proto"], - visibility = ["//visibility:public"], +gazelle( + name = "gazelle", ) -proto_library( - name = "logfields_proto", - srcs = [":logfields"], - import_prefix = "github.com/improbable-io/go-proto-logfields", - deps = [ - "@com_google_protobuf//:descriptor_proto", - ], - visibility = ["//visibility:public"], -) +# gazelle:build_file_name BUILD.bazel +# gazelle:prefix github.com/improbable-io/go-proto-logfields +# gazelle:proto_import_prefix github.com/improbable-io/go-proto-logfields go_proto_compiler( - name = "gogo_proto_logfields", + name = "go_proto_logfields", plugin = "//protoc-gen-gologfields", - deps = [ - "//:go_default_library", - "@com_github_gogo_protobuf//protoc-gen-gogo/descriptor:go_default_library", - ], - options = ["gogoimport=true"], suffix = ".logfields.pb.go", valid_archive = False, visibility = ["//visibility:public"], -) - -go_proto_compiler( - name = "go_proto_logfields", - plugin = "//protoc-gen-gologfields", deps = [ "//:go_default_library", - "@com_github_golang_protobuf//protoc-gen-go/descriptor:go_default_library", ], - options = ["gogoimport=false"], - suffix = ".logfields.pb.go", - valid_archive = False, +) + +proto_library( + name = "logfields_proto", + srcs = ["logfields.proto"], + import_prefix = "github.com/improbable-io/go-proto-logfields", visibility = ["//visibility:public"], + deps = ["@com_google_protobuf//:descriptor_proto"], ) go_proto_library( - name = "_logfields", + name = "logfields_go_proto", importpath = "github.com/improbable-io/go-proto-logfields", proto = ":logfields_proto", - compilers = [ - "@io_bazel_rules_go//proto:gogo_proto", - ], - deps = [ - "@com_github_gogo_protobuf//protoc-gen-gogo/descriptor:go_default_library", - ], - visibility = ["//:__pkg__"], + visibility = ["//visibility:public"], ) go_library( name = "go_default_library", - importpath = "github.com/improbable-io/go-proto-logfields", srcs = ["extract.go"], - embed = [":_logfields"], + embed = [":logfields_go_proto"], + importpath = "github.com/improbable-io/go-proto-logfields", visibility = ["//visibility:public"], + deps = ["@org_golang_google_protobuf//proto:go_default_library"], ) diff --git a/WORKSPACE b/WORKSPACE index 0570247..52cc7f0 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -2,7 +2,7 @@ workspace(name = "com_github_improbable_io_go_proto_logfields") load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") -GO_VERSION = "1.13.5" +GO_VERSION = "1.14.7" http_archive( name = "rules_proto", @@ -15,9 +15,9 @@ http_archive( http_archive( name = "com_google_protobuf", - sha256 = "678d91d8a939a1ef9cb268e1f20c14cd55e40361dc397bb5881e4e1e532679b1", - strip_prefix = "protobuf-3.10.1", - url = "https://github.com/protocolbuffers/protobuf/archive/v3.10.1.zip", + sha256 = "e5265d552e12c1f39c72842fa91d84941726026fa056d914ea6a25cd58d7bbf8", + strip_prefix = "protobuf-3.12.3", + url = "https://github.com/protocolbuffers/protobuf/archive/v3.12.3.zip", ) load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps") @@ -26,12 +26,13 @@ protobuf_deps() http_archive( name = "io_bazel_rules_go", - sha256 = "d5de13e9a994527b6dc41f39ad9ceee3214974dacb18f73a5fa2a4458ae6d3c9", - strip_prefix = "rules_go-0.20.3", - url = "https://github.com/bazelbuild/rules_go/archive/v0.20.3.tar.gz", + sha256 = "0310e837aed522875791750de44408ec91046c630374990edd51827cb169f616", + urls = [ + "https://github.com/bazelbuild/rules_go/releases/download/v0.23.7/rules_go-v0.23.7.tar.gz", + ], ) -load("@io_bazel_rules_go//go:deps.bzl", "go_rules_dependencies", "go_register_toolchains") +load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_dependencies") go_rules_dependencies() @@ -39,10 +40,10 @@ go_register_toolchains(go_version = GO_VERSION) http_archive( name = "bazel_gazelle", - sha256 = "d987004a72697334a095bbaa18d615804a28280201a50ed6c234c40ccc41e493", - strip_prefix = "bazel-gazelle-0.19.1", + sha256 = "2423201f91471ea87925b81962258e27a22cd8ebb4fe355bf033dcf2ad668541", + strip_prefix = "bazel-gazelle-0.21.1", urls = [ - "https://github.com/bazelbuild/bazel-gazelle/archive/v0.19.1.tar.gz", + "https://github.com/bazelbuild/bazel-gazelle/archive/v0.21.1.tar.gz", ], ) @@ -50,79 +51,8 @@ load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies", "go_repository") gazelle_dependencies() -go_repository( - name = "com_github_gogo_protobuf", - importpath = "github.com/gogo/protobuf", - sum = "h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=", - version = "v1.3.1", -) - -go_repository( - name = "com_github_stretchr_testify", - importpath = "github.com/stretchr/testify", - sum = "h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=", - version = "v1.4.0", -) +load("go_deps.bzl", "go_repositories") -go_repository( - name = "com_github_davecgh_go_spew", - importpath = "github.com/davecgh/go-spew", - sum = "h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=", - version = "v1.1.0", -) +go_repositories() -go_repository( - name = "com_github_golang_protobuf", - importpath = "github.com/golang/protobuf", - sum = "h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=", - version = "v1.3.2", -) - -go_repository( - name = "com_github_kisielk_errcheck", - importpath = "github.com/kisielk/errcheck", - sum = "h1:reN85Pxc5larApoH1keMBiu2GWtPqXQ1nc9gx+jOU+E=", - version = "v1.2.0", -) - -go_repository( - name = "com_github_kisielk_gotool", - importpath = "github.com/kisielk/gotool", - sum = "h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=", - version = "v1.0.0", -) - -go_repository( - name = "com_github_pmezard_go_difflib", - importpath = "github.com/pmezard/go-difflib", - sum = "h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=", - version = "v1.0.0", -) - -go_repository( - name = "com_github_stretchr_objx", - importpath = "github.com/stretchr/objx", - sum = "h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=", - version = "v0.1.0", -) - -go_repository( - name = "in_gopkg_check_v1", - importpath = "gopkg.in/check.v1", - sum = "h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=", - version = "v0.0.0-20161208181325-20d25e280405", -) - -go_repository( - name = "in_gopkg_yaml_v2", - importpath = "gopkg.in/yaml.v2", - sum = "h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=", - version = "v2.2.2", -) - -go_repository( - name = "org_golang_x_tools", - importpath = "golang.org/x/tools", - sum = "h1:NIou6eNFigscvKJmsbyez16S2cIS6idossORlFtSt2E=", - version = "v0.0.0-20181030221726-6c7e314b6563", -) +# gazelle:repository_macro go_deps.bzl%go_repositories diff --git a/examples/BUILD.bazel b/examples/BUILD.bazel new file mode 100644 index 0000000..15d38b2 --- /dev/null +++ b/examples/BUILD.bazel @@ -0,0 +1,31 @@ +load("@rules_proto//proto:defs.bzl", "proto_library") +load("@io_bazel_rules_go//go:def.bzl", "go_library") +load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") + +proto_library( + name = "example_proto", + srcs = ["service.proto"], + import_prefix = "github.com/mwitkow/go-proto-validators", + visibility = ["//visibility:public"], + deps = ["//:logfields_proto"], +) + +go_proto_library( + name = "example_go_proto", + importpath = "github.com/improbable-io/go-proto-logfields/examples", + proto = ":example_proto", + visibility = ["//visibility:public"], + deps = ["//:go_default_library"], +) + +go_library( + name = "go_default_library", + srcs = ["service.logfields.pb.go"], + embed = [":example_go_proto"], + importpath = "github.com/improbable-io/go-proto-logfields/examples", + visibility = ["//visibility:public"], + deps = [ + "//:go_default_library", + "@com_github_golang_protobuf//proto:go_default_library", + ], +) diff --git a/extract.go b/extract.go index 5c25b55..8ea671b 100644 --- a/extract.go +++ b/extract.go @@ -1,11 +1,11 @@ package logfields -import "github.com/gogo/protobuf/proto" +import "google.golang.org/protobuf/proto" type protoWithLogFields interface { proto.Message LogFields() map[string]string - ExtractRequestFields(dst map[string]interface{}) + ExtractRequestFields(prefixes []string, dst map[string]interface{}) } func ExtractLogFieldsFromMessage(message proto.Message) map[string]string { @@ -16,8 +16,8 @@ func ExtractLogFieldsFromMessage(message proto.Message) map[string]string { return map[string]string{} } -func ExtractRequestFieldsFromMessage(message proto.Message, dst map[string]interface{}) { +func ExtractRequestFieldsFromMessage(message proto.Message, prefixes []string, dst map[string]interface{}) { if m, ok := message.(protoWithLogFields); ok { - m.ExtractRequestFields(dst) + m.ExtractRequestFields(prefixes, dst) } } diff --git a/go.mod b/go.mod index fdc6ac3..3a63776 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/improbable-io/go-proto-logfields go 1.13 require ( - github.com/gogo/protobuf v1.3.1 - github.com/golang/protobuf v1.3.2 + github.com/golang/protobuf v1.4.2 github.com/stretchr/testify v1.4.0 + google.golang.org/protobuf v1.25.0 ) diff --git a/go.sum b/go.sum index 191b1f1..d3504fb 100644 --- a/go.sum +++ b/go.sum @@ -1,18 +1,79 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/go_deps.bzl b/go_deps.bzl new file mode 100644 index 0000000..5ce94ab --- /dev/null +++ b/go_deps.bzl @@ -0,0 +1,195 @@ +load("@bazel_gazelle//:deps.bzl", "go_repository") + +def go_repositories(): + go_repository( + name = "co_honnef_go_tools", + importpath = "honnef.co/go/tools", + sum = "h1:/hemPrYIhOhy8zYrNj+069zDB68us2sMGsfkFJO0iZs=", + version = "v0.0.0-20190523083050-ea95bdfd59fc", + ) + go_repository( + name = "com_github_burntsushi_toml", + importpath = "github.com/BurntSushi/toml", + sum = "h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=", + version = "v0.3.1", + ) + go_repository( + name = "com_github_census_instrumentation_opencensus_proto", + importpath = "github.com/census-instrumentation/opencensus-proto", + sum = "h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk=", + version = "v0.2.1", + ) + go_repository( + name = "com_github_client9_misspell", + importpath = "github.com/client9/misspell", + sum = "h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=", + version = "v0.3.4", + ) + go_repository( + name = "com_github_davecgh_go_spew", + importpath = "github.com/davecgh/go-spew", + sum = "h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=", + version = "v1.1.0", + ) + go_repository( + name = "com_github_envoyproxy_go_control_plane", + importpath = "github.com/envoyproxy/go-control-plane", + sum = "h1:4cmBvAEBNJaGARUEs3/suWRyfyBfhf7I60WBZq+bv2w=", + version = "v0.9.1-0.20191026205805-5f8ba28d4473", + ) + go_repository( + name = "com_github_envoyproxy_protoc_gen_validate", + importpath = "github.com/envoyproxy/protoc-gen-validate", + sum = "h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A=", + version = "v0.1.0", + ) + go_repository( + name = "com_github_golang_glog", + importpath = "github.com/golang/glog", + sum = "h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=", + version = "v0.0.0-20160126235308-23def4e6c14b", + ) + go_repository( + name = "com_github_golang_mock", + importpath = "github.com/golang/mock", + sum = "h1:G5FRp8JnTd7RQH5kemVNlMeyXQAztQ3mOWV95KxsXH8=", + version = "v1.1.1", + ) + go_repository( + name = "com_github_google_go_cmp", + importpath = "github.com/google/go-cmp", + sum = "h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=", + version = "v0.5.0", + ) + go_repository( + name = "com_github_pmezard_go_difflib", + importpath = "github.com/pmezard/go-difflib", + sum = "h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=", + version = "v1.0.0", + ) + go_repository( + name = "com_github_prometheus_client_model", + importpath = "github.com/prometheus/client_model", + sum = "h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM=", + version = "v0.0.0-20190812154241-14fe0d1b01d4", + ) + go_repository( + name = "com_github_stretchr_objx", + importpath = "github.com/stretchr/objx", + sum = "h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=", + version = "v0.1.0", + ) + go_repository( + name = "com_github_stretchr_testify", + importpath = "github.com/stretchr/testify", + sum = "h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=", + version = "v1.4.0", + ) + go_repository( + name = "com_google_cloud_go", + importpath = "cloud.google.com/go", + sum = "h1:e0WKqKTd5BnrG8aKH3J3h+QvEIQtSUcf2n5UZ5ZgLtQ=", + version = "v0.26.0", + ) + go_repository( + name = "in_gopkg_check_v1", + importpath = "gopkg.in/check.v1", + sum = "h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=", + version = "v0.0.0-20161208181325-20d25e280405", + ) + go_repository( + name = "in_gopkg_yaml_v2", + importpath = "gopkg.in/yaml.v2", + sum = "h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=", + version = "v2.2.2", + ) + go_repository( + name = "org_golang_google_appengine", + importpath = "google.golang.org/appengine", + sum = "h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=", + version = "v1.4.0", + ) + go_repository( + name = "org_golang_google_genproto", + importpath = "google.golang.org/genproto", + sum = "h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=", + version = "v0.0.0-20200526211855-cb27e3aa2013", + ) + go_repository( + name = "org_golang_google_grpc", + importpath = "google.golang.org/grpc", + sum = "h1:rRYRFMVgRv6E0D70Skyfsr28tDXIuuPZyWGMPdMcnXg=", + version = "v1.27.0", + ) + go_repository( + name = "org_golang_x_crypto", + importpath = "golang.org/x/crypto", + sum = "h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=", + version = "v0.0.0-20190308221718-c2843e01d9a2", + ) + go_repository( + name = "org_golang_x_exp", + importpath = "golang.org/x/exp", + sum = "h1:c2HOrn5iMezYjSlGPncknSEr/8x5LELb/ilJbXi9DEA=", + version = "v0.0.0-20190121172915-509febef88a4", + ) + go_repository( + name = "org_golang_x_lint", + importpath = "golang.org/x/lint", + sum = "h1:XQyxROzUlZH+WIQwySDgnISgOivlhjIEwaQaJEJrrN0=", + version = "v0.0.0-20190313153728-d0100b6bd8b3", + ) + go_repository( + name = "org_golang_x_net", + importpath = "golang.org/x/net", + sum = "h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=", + version = "v0.0.0-20190311183353-d8887717615a", + ) + go_repository( + name = "org_golang_x_oauth2", + importpath = "golang.org/x/oauth2", + sum = "h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=", + version = "v0.0.0-20180821212333-d2e6202438be", + ) + go_repository( + name = "org_golang_x_sync", + importpath = "golang.org/x/sync", + sum = "h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=", + version = "v0.0.0-20190423024810-112230192c58", + ) + go_repository( + name = "org_golang_x_sys", + importpath = "golang.org/x/sys", + sum = "h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=", + version = "v0.0.0-20190215142949-d0b11bdaac8a", + ) + go_repository( + name = "org_golang_x_text", + importpath = "golang.org/x/text", + sum = "h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=", + version = "v0.3.0", + ) + go_repository( + name = "org_golang_x_xerrors", + importpath = "golang.org/x/xerrors", + sum = "h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=", + version = "v0.0.0-20191204190536-9bdfabe68543", + ) + go_repository( + name = "com_github_golang_protobuf", + importpath = "github.com/golang/protobuf", + sum = "h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=", + version = "v1.4.2", + ) + go_repository( + name = "org_golang_google_protobuf", + importpath = "google.golang.org/protobuf", + sum = "h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=", + version = "v1.25.0", + ) + go_repository( + name = "org_golang_x_tools", + importpath = "golang.org/x/tools", + sum = "h1:5Beo0mZN8dRzgrMMkDp0jc8YXQKx9DiJ2k1dkvGsn5A=", + version = "v0.0.0-20190524140312-2c0ae7006135", + ) diff --git a/internal/BUILD.bazel b/internal/BUILD.bazel new file mode 100644 index 0000000..370c2ec --- /dev/null +++ b/internal/BUILD.bazel @@ -0,0 +1,32 @@ +load("@io_bazel_rules_go//proto:compiler.bzl", "go_proto_compiler") + +_WELL_KNOWN_TYPES = [ + "any", + "api", + "compiler_plugin", + "descriptor", + "duration", + "empty", + "field_mask", + "source_context", + "struct", + "timestamp", + "type", + "wrappers", +] + +go_proto_compiler( + name = "go_proto", + plugin = "@org_golang_google_protobuf//cmd/protoc-gen-go:protoc-gen-go", + suffix = ".pb.go", + valid_archive = True, + visibility = ["//:__subpackages__"], + deps = [ + "@com_github_golang_protobuf//proto:go_default_library", + "@com_github_golang_protobuf//protoc-gen-go/descriptor:go_default_library", + "@org_golang_google_protobuf//proto:go_default_library", + "@org_golang_google_protobuf//reflect/protoreflect:go_default_library", + "@org_golang_google_protobuf//runtime/protoiface:go_default_library", + "@org_golang_google_protobuf//runtime/protoimpl:go_default_library", + ] + ["@io_bazel_rules_go//proto/wkt:{}_go_proto".format(wkt) for wkt in _WELL_KNOWN_TYPES], +) diff --git a/internal/genlogfields/BUILD.bazel b/internal/genlogfields/BUILD.bazel new file mode 100644 index 0000000..bea1f63 --- /dev/null +++ b/internal/genlogfields/BUILD.bazel @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = ["genlogfields.go"], + importpath = "github.com/improbable-io/go-proto-logfields/internal/genlogfields", + visibility = ["//:__subpackages__"], + deps = [ + "//:go_default_library", + "@org_golang_google_protobuf//compiler/protogen:go_default_library", + "@org_golang_google_protobuf//proto:go_default_library", + "@org_golang_google_protobuf//reflect/protoreflect:go_default_library", + ], +) diff --git a/internal/genlogfields/genlogfields.go b/internal/genlogfields/genlogfields.go new file mode 100644 index 0000000..666af2e --- /dev/null +++ b/internal/genlogfields/genlogfields.go @@ -0,0 +1,423 @@ +package genlogfields + +import ( + "fmt" + "strings" + + "google.golang.org/protobuf/compiler/protogen" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" + + logfields "github.com/improbable-io/go-proto-logfields" +) + +func GenerateFile(gen *protogen.Plugin, file *protogen.File) *protogen.GeneratedFile { + if len(file.Messages) == 0 { + return nil // If there are no messages then we should not generate anything. + } + + filename := file.GeneratedFilenamePrefix + ".logfields.pb.go" + g := gen.NewGeneratedFile(filename, file.GoImportPath) + g.P("// Code generated by protoc-gen-gologfields. DO NOT EDIT.") + g.P() + g.P("package ", file.GoPackageName) + g.P() + + generateFileContent(gen, file, g) + return g +} + +var ( + fmtPkg = protogen.GoImportPath("fmt") + stringsPkg = protogen.GoImportPath("strings") + logfieldsPkg = protogen.GoImportPath("github.com/improbable-io/go-proto-logfields") +) + +func generateFileContent(gen *protogen.Plugin, file *protogen.File, g *protogen.GeneratedFile) { + if !validateFileLogNames(gen, file) { + return + } + + for _, msg := range file.Messages { + generateLogHandlers(g, msg) + generateExtractRequestFields(g, msg) + } +} + +func validateFileLogNames(gen *protogen.Plugin, file *protogen.File) bool { + isValid := true + for _, msg := range file.Messages { + isValid = isValid && validateMessageLogNames(gen, msg, nil, map[string]string{}) + } + return isValid +} + +func validateMessageLogNames(gen *protogen.Plugin, msg *protogen.Message, fieldStack []*protogen.Field, knownFieldNames map[string]string) bool { + isValid := true + + for _, oneOf := range msg.Oneofs { + oneOfFieldNames := map[string]string{} + for _, field := range oneOf.Fields { + fieldName, fieldLocation, ok := getFieldLogName(gen, field, fieldStack) + if fieldName == "" { + isValid = isValid && ok + continue + } + + if prevLocation, ok := knownFieldNames[fieldName]; ok { + gen.Error(fmt.Errorf("log field name %q used at %q is already defined at %q", fieldName, prevLocation, fieldLocation)) + isValid = false + } else { + oneOfFieldNames[fieldName] = fieldLocation + } + } + + for k, v := range oneOfFieldNames { + knownFieldNames[k] = v + } + } + + for _, field := range msg.Fields { + if field.Oneof != nil { + continue + } + + if field.Message == nil || field.Desc.Cardinality() == protoreflect.Repeated { + fieldName, fieldLocation, ok := getFieldLogName(gen, field, fieldStack) + if fieldName == "" { + isValid = isValid && ok + } else if prevLocation, ok := knownFieldNames[fieldName]; ok { + gen.Error(fmt.Errorf("log field name %q used at %q is already defined at %q", fieldName, prevLocation, fieldLocation)) + isValid = false + } else { + knownFieldNames[fieldName] = fieldLocation + } + continue + } + + log, ok := retrieveLogAnnotation(field.Desc.Options()) + if !ok { + gen.Error(extractionError(field.Desc.FullName())) + isValid = false + continue + } + + if log.GetName() != "" { + gen.Error(fmt.Errorf("cannot assign log name to message field %q, use a 'prefix' annotation if required", field.Desc.FullName())) + isValid = false + continue + } else if log != nil && log.GetPrefix() == "" { + gen.Error(fmt.Errorf("message field %q has an empty log prefix", field.Desc.FullName())) + isValid = false + continue + } + + isValid = isValid && validateMessageLogNames(gen, field.Message, append(fieldStack, field), knownFieldNames) + } + return isValid +} + +func getFieldLogName(gen *protogen.Plugin, field *protogen.Field, fieldStack []*protogen.Field) (string, string, bool) { + log, ok := retrieveLogAnnotation(field.Desc.Options()) + if !ok { + gen.Error(extractionError(field.Desc.FullName())) + return "", "", false + } else if log == nil { + return "", "", true + } + + if field.Desc.Cardinality() == protoreflect.Repeated { + gen.Error(fmt.Errorf("cannot log repeated field %q", field.Desc.FullName())) + return "", "", false + } + + if log.GetName() == "" { + gen.Error(fmt.Errorf("literal field %q has an empty log name", field.Desc.FullName())) + return "", "", false + } else if log.GetPrefix() != "" { + gen.Error(fmt.Errorf( + "literal field %q cannot have a log 'prefix' annotation, these are for nested message fields and one-ofs", field.Desc.FullName(), + )) + return "", "", false + } + + return fieldPrefix(fieldStack) + log.GetName(), fieldLocation(append(fieldStack, field)), true +} + +func generateLogHandlers(g *protogen.GeneratedFile, msg *protogen.Message) { + for _, childMsg := range msg.Messages { + if !childMsg.Desc.IsMapEntry() { + generateLogHandlers(g, childMsg) + } + } + + g.P("func (m *", msg.GoIdent, ") LogFields() map[string]string {") + defer func() { + g.P("}") + g.P() + }() + + var ( + complexMessageOrType bool = len(msg.Oneofs) > 0 + needsBody bool + ) + for _, field := range msg.Fields { + if field.Desc.Cardinality() == protoreflect.Repeated { + continue + } + + if field.Desc.Kind() == protoreflect.MessageKind { + complexMessageOrType = true + needsBody = true + } else if log, _ := retrieveLogAnnotation(field.Desc.Options()); log != nil { + needsBody = true + } + } + + if !needsBody { + g.P("return map[string]string{}") + return + } + + g.P("// Handle being called on nil message.") + g.P("if m == nil {") + g.P("return map[string]string{}") + g.P("}") + g.P() + + if !complexMessageOrType { + literalsMapVar := generateLogHandlerLiteralsMap(g, msg) + g.P("return ", literalsMapVar) + } else { + generateLogHandlerComplex(g, msg) + } +} + +func generateLogHandlerComplex(g *protogen.GeneratedFile, msg *protogen.Message) { + g.P("// Gather fields from oneofs and child messages.") + g.P("var hasInner bool") + g.P() + + type mergeMap struct { + mapVar string + prefix string + } + var mapsToMerge []mergeMap + + for _, oneOf := range msg.Oneofs { + oneOfMapVar := generateLogHandlerOneOfMap(g, oneOf) + g.P("hasInner = hasInner || len(", oneOfMapVar, ") > 0") + g.P() + + log, _ := retrieveLogAnnotation(oneOf.Desc.Options()) + mapsToMerge = append(mapsToMerge, mergeMap{ + mapVar: oneOfMapVar, + prefix: log.GetPrefix(), + }) + } + + for _, field := range msg.Fields { + // Only handle fields containing nested messages. + if field.Message == nil || field.Oneof != nil || field.Desc.Cardinality() == protoreflect.Repeated { + continue + } + + childMsgMapVar := "childMsgMap" + field.GoName + g.P(childMsgMapVar, " := ", logfieldsPkg.Ident("ExtractLogFieldsFromMessage"), "(m.", field.GoName, ")") + g.P("hasInner = hasInner || len(", childMsgMapVar, ") > 0") + g.P() + + log, _ := retrieveLogAnnotation(field.Desc.Options()) + mapsToMerge = append(mapsToMerge, mergeMap{ + mapVar: childMsgMapVar, + prefix: log.GetPrefix(), + }) + } + + literalsMapVar := generateLogHandlerLiteralsMap(g, msg) + + // If there are no complex field maps to merge than we can return early. + g.P("if !hasInner {") + g.P("return ", literalsMapVar) + g.P("}") + g.P("") + + for _, mapToMerge := range mapsToMerge { + g.P("for k, v := range ", mapToMerge.mapVar, " {") + if mapToMerge.prefix != "" { + g.P(literalsMapVar, `["`, mapToMerge.prefix, `."+k] = v`) + } else { + g.P(literalsMapVar, "[k] = v") + } + g.P("}") + g.P() + } + g.P("return ", literalsMapVar) +} + +func generateLogHandlerLiteralsMap(g *protogen.GeneratedFile, msg *protogen.Message) string { + const literalsMapVar = "literalsMap" + + g.P(literalsMapVar, " := map[string]string{") + for _, field := range msg.Fields { + if field.Message != nil || field.Oneof != nil { + continue + } + + log, _ := retrieveLogAnnotation(field.Desc.Options()) + if log == nil { + continue + } + // NB: See the comment on the 'fieldValue' on why this is so elaborate. + g.P(append(append([]interface{}{`"`, log.GetName(), `": `}, fieldValue("m", field)...), ",")...) + } + g.P("}") + g.P() + + return literalsMapVar +} + +func generateLogHandlerOneOfMap(g *protogen.GeneratedFile, oneOf *protogen.Oneof) string { + var oneOfMapVar = "oneOfMap" + oneOf.GoName + + g.P("var ", oneOfMapVar, " map[string]string") + g.P("switch m.", oneOf.GoName, ".(type) {") + for _, field := range oneOf.Fields { + log, _ := retrieveLogAnnotation(field.Desc.Options()) + if log == nil && field.Message == nil { + continue + } + + g.P("case *", field.GoIdent, ":") + if field.Message != nil { + fieldRef := fmt.Sprintf("m.%s.(*%s).%s", oneOf.GoName, field.GoIdent.GoName, field.GoName) + if log.GetPrefix() != "" { + g.P(oneOfMapVar, " = map[string]string{}") + g.P("for k, v := range ", logfieldsPkg.Ident("ExtractLogFieldsFromMessage"), "(", fieldRef, ") {") + g.P(oneOfMapVar, `["`, log.GetPrefix(), `."+k] = v`) + g.P("}") + } else { + g.P(oneOfMapVar, " = ", logfieldsPkg.Ident("ExtractLogFieldsFromMessage"), "(", fieldRef, ")") + } + } else { + // NB: See the comment on the 'fieldValue' on why this is so elaborate. + g.P(append(append([]interface{}{oneOfMapVar, ` = map[string]string{"`, log.GetName(), `": `}, fieldValue("m", field)...), "}")...) + } + } + g.P("default:") + g.P(oneOfMapVar, " = map[string]string{}") + g.P("}") + + return oneOfMapVar +} + +func generateExtractRequestFields(g *protogen.GeneratedFile, msg *protogen.Message) { + for _, childMsg := range msg.Messages { + if !childMsg.Desc.IsMapEntry() { + generateExtractRequestFields(g, childMsg) + } + } + + g.P("func (m *", msg.GoIdent, ") ExtractRequestFields(prefixes []string, dst map[string]interface{}) {") + defer func() { + g.P("}") + g.P() + }() + + g.P("// Handle being called on nil message.") + g.P("if m == nil {") + g.P("return") + g.P("}") + + for _, field := range msg.Fields { + if field.Desc.Cardinality() == protoreflect.Repeated { + continue + } + + log, _ := retrieveLogAnnotation(field.Desc.Options()) + if field.Message == nil && log == nil { + continue + } + + g.P() + if field.Oneof != nil { + g.P("if _, ok := m.", field.Oneof.GoName, ".(*", field.GoIdent, "); ok {") + } + + if field.Message != nil { + prefixesVar := "prefixes" + if log.GetPrefix() != "" { + prefixesVar = fmt.Sprintf(`append(prefixes, "%s")`, log.GetPrefix()) + } + g.P(logfieldsPkg.Ident("ExtractRequestFieldsFromMessage"), "(m.Get", field.GoName, "(), ", prefixesVar, ", dst)") + } else if log != nil { + g.P("dst[", stringsPkg.Ident("Join"), `(append(prefixes, "`, log.GetName(), `"), ".")] = m.Get`, field.GoName, "()") + } + + if field.Oneof != nil { + g.P("}") + } + } +} + +func fieldLocation(fieldStack []*protogen.Field) string { + var location string + for idx, field := range fieldStack { + if idx == 0 { + location += string(field.Desc.FullName()) + "." + } else { + location += string(field.Desc.FullName().Name()) + "." + } + } + return strings.TrimSuffix(location, ".") +} + +func fieldPrefix(fieldStack []*protogen.Field) string { + var prefix string + for _, field := range fieldStack { + if log, _ := retrieveLogAnnotation(field.Desc.Options()); log != nil { + prefix += log.GetPrefix() + "." + } + } + return prefix +} + +// fieldValue produces a slice that should be fed directly into the generated file's printer method +// `g.P()`. We can not return a string as that would lead the 'GoImportPath.Ident()' method not to +// be handled approriately, producing invalid code and missing import declarations. +func fieldValue(parentStructVar string, field *protogen.Field) []interface{} { + valueExpr := []interface{}{parentStructVar, ".Get", field.GoName, "()"} + switch field.Desc.Kind() { + case protoreflect.StringKind: + // No special casing required. + case protoreflect.BytesKind: + valueExpr = append([]interface{}{"string("}, valueExpr...) + valueExpr = append(valueExpr, ")") + default: + valueExpr = append([]interface{}{fmtPkg.Ident("Sprintf"), `("%v", `}, valueExpr...) + valueExpr = append(valueExpr, ")") + } + return valueExpr +} + +func extractionError(fieldName protoreflect.FullName) error { + return fmt.Errorf("failed to extract log field annotation for field %q", fieldName) +} + +func retrieveLogAnnotation(fieldOptions protoreflect.ProtoMessage) (*logfields.LogField, bool) { + // The supplied interface might be empty but will still not be comparible to a nil pointer. + // Hence we assert that the proto message is atleast valid before trying to extract our + // extension field. + if !fieldOptions.ProtoReflect().IsValid() { + return nil, true + } + + if proto.HasExtension(fieldOptions, logfields.E_Logfield) { + logExt, ok := proto.GetExtension(fieldOptions, logfields.E_Logfield).(*logfields.LogField) + if !ok { + return nil, false + } + return logExt, true + } + return nil, true +} diff --git a/logfields.pb.go b/logfields.pb.go index 4d7062b..0698b86 100644 --- a/logfields.pb.go +++ b/logfields.pb.go @@ -1,78 +1,213 @@ -// Code generated by protoc-gen-gogo. DO NOT EDIT. -// source: logfields.proto +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.22.0 +// protoc v3.12.3 +// source: github.com/improbable-io/go-proto-logfields/logfields.proto -/* -Package logfields is a generated protocol buffer package. - -It is generated from these files: - logfields.proto - -It has these top-level messages: - LogField -*/ package logfields -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" -import google_protobuf "github.com/gogo/protobuf/protoc-gen-gogo/descriptor" +import ( + proto "github.com/golang/protobuf/proto" + descriptor "github.com/golang/protobuf/protoc-gen-go/descriptor" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) -// 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.GoGoProtoPackageIsVersion2 // please upgrade the proto package +// This is a compile-time assertion that a sufficiently up-to-date version +// of the legacy proto package is being used. +const _ = proto.ProtoPackageIsVersion4 type LogField struct { - // name of the log context field where the value of this field should be - // recorded. Fields with empty names are ignored. - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Prefix string `protobuf:"bytes,2,opt,name=prefix,proto3" json:"prefix,omitempty"` +} + +func (x *LogField) Reset() { + *x = LogField{} + if protoimpl.UnsafeEnabled { + mi := &file_github_com_improbable_io_go_proto_logfields_logfields_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LogField) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LogField) ProtoMessage() {} + +func (x *LogField) ProtoReflect() protoreflect.Message { + mi := &file_github_com_improbable_io_go_proto_logfields_logfields_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LogField.ProtoReflect.Descriptor instead. +func (*LogField) Descriptor() ([]byte, []int) { + return file_github_com_improbable_io_go_proto_logfields_logfields_proto_rawDescGZIP(), []int{0} } -func (m *LogField) Reset() { *m = LogField{} } -func (m *LogField) String() string { return proto.CompactTextString(m) } -func (*LogField) ProtoMessage() {} -func (*LogField) Descriptor() ([]byte, []int) { return fileDescriptorLogfields, []int{0} } +func (x *LogField) GetName() string { + if x != nil { + return x.Name + } + return "" +} -func (m *LogField) GetName() string { - if m != nil { - return m.Name +func (x *LogField) GetPrefix() string { + if x != nil { + return x.Prefix } return "" } -var E_Logfield = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.FieldOptions)(nil), - ExtensionType: (*LogField)(nil), - Field: 62132, - Name: "improbable.logfield", - Tag: "bytes,62132,opt,name=logfield", - Filename: "logfields.proto", +var file_github_com_improbable_io_go_proto_logfields_logfields_proto_extTypes = []protoimpl.ExtensionInfo{ + { + ExtendedType: (*descriptor.FieldOptions)(nil), + ExtensionType: (*LogField)(nil), + Field: 62132, + Name: "improbable.logfield", + Tag: "bytes,62132,opt,name=logfield", + Filename: "github.com/improbable-io/go-proto-logfields/logfields.proto", + }, + { + ExtendedType: (*descriptor.OneofOptions)(nil), + ExtensionType: (*LogField)(nil), + Field: 62133, + Name: "improbable.prefix", + Tag: "bytes,62133,opt,name=prefix", + Filename: "github.com/improbable-io/go-proto-logfields/logfields.proto", + }, +} + +// Extension fields to descriptor.FieldOptions. +var ( + // optional improbable.LogField logfield = 62132; + E_Logfield = &file_github_com_improbable_io_go_proto_logfields_logfields_proto_extTypes[0] +) + +// Extension fields to descriptor.OneofOptions. +var ( + // optional improbable.LogField prefix = 62133; + E_Prefix = &file_github_com_improbable_io_go_proto_logfields_logfields_proto_extTypes[1] +) + +var File_github_com_improbable_io_go_proto_logfields_logfields_proto protoreflect.FileDescriptor + +var file_github_com_improbable_io_go_proto_logfields_logfields_proto_rawDesc = []byte{ + 0x0a, 0x3b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x69, 0x6d, 0x70, + 0x72, 0x6f, 0x62, 0x61, 0x62, 0x6c, 0x65, 0x2d, 0x69, 0x6f, 0x2f, 0x67, 0x6f, 0x2d, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2d, 0x6c, 0x6f, 0x67, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x2f, 0x6c, 0x6f, + 0x67, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x69, + 0x6d, 0x70, 0x72, 0x6f, 0x62, 0x61, 0x62, 0x6c, 0x65, 0x1a, 0x20, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x65, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x36, 0x0a, 0x08, 0x4c, + 0x6f, 0x67, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x70, + 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x72, 0x65, + 0x66, 0x69, 0x78, 0x3a, 0x51, 0x0a, 0x08, 0x6c, 0x6f, 0x67, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, + 0x1d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xb4, + 0xe5, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x69, 0x6d, 0x70, 0x72, 0x6f, 0x62, 0x61, + 0x62, 0x6c, 0x65, 0x2e, 0x4c, 0x6f, 0x67, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x08, 0x6c, 0x6f, + 0x67, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x3a, 0x4d, 0x0a, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, + 0x12, 0x1d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x4f, 0x6e, 0x65, 0x6f, 0x66, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, + 0xb5, 0xe5, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x69, 0x6d, 0x70, 0x72, 0x6f, 0x62, + 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x4c, 0x6f, 0x67, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x06, 0x70, + 0x72, 0x65, 0x66, 0x69, 0x78, 0x42, 0x37, 0x5a, 0x35, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x69, 0x6d, 0x70, 0x72, 0x6f, 0x62, 0x61, 0x62, 0x6c, 0x65, 0x2d, 0x69, + 0x6f, 0x2f, 0x67, 0x6f, 0x2d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2d, 0x6c, 0x6f, 0x67, 0x66, 0x69, + 0x65, 0x6c, 0x64, 0x73, 0x3b, 0x6c, 0x6f, 0x67, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_github_com_improbable_io_go_proto_logfields_logfields_proto_rawDescOnce sync.Once + file_github_com_improbable_io_go_proto_logfields_logfields_proto_rawDescData = file_github_com_improbable_io_go_proto_logfields_logfields_proto_rawDesc +) + +func file_github_com_improbable_io_go_proto_logfields_logfields_proto_rawDescGZIP() []byte { + file_github_com_improbable_io_go_proto_logfields_logfields_proto_rawDescOnce.Do(func() { + file_github_com_improbable_io_go_proto_logfields_logfields_proto_rawDescData = protoimpl.X.CompressGZIP(file_github_com_improbable_io_go_proto_logfields_logfields_proto_rawDescData) + }) + return file_github_com_improbable_io_go_proto_logfields_logfields_proto_rawDescData } -func init() { - proto.RegisterType((*LogField)(nil), "improbable.LogField") - proto.RegisterExtension(E_Logfield) +var file_github_com_improbable_io_go_proto_logfields_logfields_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_github_com_improbable_io_go_proto_logfields_logfields_proto_goTypes = []interface{}{ + (*LogField)(nil), // 0: improbable.LogField + (*descriptor.FieldOptions)(nil), // 1: google.protobuf.FieldOptions + (*descriptor.OneofOptions)(nil), // 2: google.protobuf.OneofOptions +} +var file_github_com_improbable_io_go_proto_logfields_logfields_proto_depIdxs = []int32{ + 1, // 0: improbable.logfield:extendee -> google.protobuf.FieldOptions + 2, // 1: improbable.prefix:extendee -> google.protobuf.OneofOptions + 0, // 2: improbable.logfield:type_name -> improbable.LogField + 0, // 3: improbable.prefix:type_name -> improbable.LogField + 4, // [4:4] is the sub-list for method output_type + 4, // [4:4] is the sub-list for method input_type + 2, // [2:4] is the sub-list for extension type_name + 0, // [0:2] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name } -func init() { proto.RegisterFile("logfields.proto", fileDescriptorLogfields) } - -var fileDescriptorLogfields = []byte{ - // 162 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0xcf, 0xc9, 0x4f, 0x4f, - 0xcb, 0x4c, 0xcd, 0x49, 0x29, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0xca, 0xcc, 0x2d, - 0x28, 0xca, 0x4f, 0x4a, 0x4c, 0xca, 0x49, 0x95, 0x52, 0x48, 0xcf, 0xcf, 0x4f, 0xcf, 0x49, 0xd5, - 0x07, 0xcb, 0x24, 0x95, 0xa6, 0xe9, 0xa7, 0xa4, 0x16, 0x27, 0x17, 0x65, 0x16, 0x94, 0xe4, 0x17, - 0x41, 0x54, 0x2b, 0xc9, 0x71, 0x71, 0xf8, 0xe4, 0xa7, 0xbb, 0x81, 0x0c, 0x10, 0x12, 0xe2, 0x62, - 0xc9, 0x4b, 0xcc, 0x4d, 0x95, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x0c, 0x02, 0xb3, 0xad, 0x02, 0xb9, - 0x38, 0x60, 0x16, 0x08, 0xc9, 0xea, 0x41, 0x8c, 0xd3, 0x83, 0x19, 0xa7, 0x07, 0xd6, 0xe7, 0x5f, - 0x50, 0x92, 0x99, 0x9f, 0x57, 0x2c, 0xb1, 0xe5, 0x29, 0xb3, 0x02, 0xa3, 0x06, 0xb7, 0x91, 0x88, - 0x1e, 0xc2, 0x05, 0x7a, 0x30, 0xc3, 0x83, 0xe0, 0xc6, 0x38, 0x71, 0x47, 0x71, 0xc2, 0xdd, 0x9c, - 0xc4, 0x06, 0x36, 0xcb, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0xe5, 0x19, 0xb4, 0x18, 0xc7, 0x00, - 0x00, 0x00, +func init() { file_github_com_improbable_io_go_proto_logfields_logfields_proto_init() } +func file_github_com_improbable_io_go_proto_logfields_logfields_proto_init() { + if File_github_com_improbable_io_go_proto_logfields_logfields_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_github_com_improbable_io_go_proto_logfields_logfields_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LogField); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_github_com_improbable_io_go_proto_logfields_logfields_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 2, + NumServices: 0, + }, + GoTypes: file_github_com_improbable_io_go_proto_logfields_logfields_proto_goTypes, + DependencyIndexes: file_github_com_improbable_io_go_proto_logfields_logfields_proto_depIdxs, + MessageInfos: file_github_com_improbable_io_go_proto_logfields_logfields_proto_msgTypes, + ExtensionInfos: file_github_com_improbable_io_go_proto_logfields_logfields_proto_extTypes, + }.Build() + File_github_com_improbable_io_go_proto_logfields_logfields_proto = out.File + file_github_com_improbable_io_go_proto_logfields_logfields_proto_rawDesc = nil + file_github_com_improbable_io_go_proto_logfields_logfields_proto_goTypes = nil + file_github_com_improbable_io_go_proto_logfields_logfields_proto_depIdxs = nil } diff --git a/logfields.proto b/logfields.proto index 31c8e65..99533af 100644 --- a/logfields.proto +++ b/logfields.proto @@ -17,7 +17,28 @@ extend google.protobuf.FieldOptions { } message LogField { - // name of the log context field where the value of this field should be + // Name of the log context field where the value of this field should be // recorded. Fields with empty names are ignored. string name = 1; + + // Prefix for nested fields in a nested message or group. The full log name + // that will be used for a field is consituted by prepending all prefixes from + // parent messages until reaching the top-level message. Parent messages + // without a specified prefix are skipped. + // + // Example: + // message Top { + // message Inner { + // string field = 1 [(improbable.logfields) = {name: "text"}]; + // } + // + // message Nested { + // Inner inner = 1 [(improbable.logfields) = {prefix: "inner"}]; + // } + // + // Nested nested = 1; + // } + // + // will see the value of the "field" string logged under key "inner.text". + string prefix = 2; } diff --git a/plugin/BUILD.bazel b/plugin/BUILD.bazel deleted file mode 100644 index 94a5333..0000000 --- a/plugin/BUILD.bazel +++ /dev/null @@ -1,27 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "go_default_library", - srcs = ["plugin.go"], - importpath = "github.com/improbable-io/go-proto-logfields/plugin", - visibility = ["//visibility:public"], - deps = [ - "//:go_default_library", - "@com_github_gogo_protobuf//gogoproto:go_default_library", - "@com_github_gogo_protobuf//proto:go_default_library", - "@com_github_gogo_protobuf//protoc-gen-gogo/descriptor:go_default_library", - "@com_github_gogo_protobuf//protoc-gen-gogo/generator:go_default_library", - "@com_github_gogo_protobuf//vanity:go_default_library", - ], -) - -go_test( - name = "go_default_test", - size = "small", - srcs = ["plugin_test.go"], - rundir = ".", - embed = [":go_default_library"], - deps = [ - "@com_github_stretchr_testify//assert:go_default_library", - ], -) diff --git a/plugin/plugin.go b/plugin/plugin.go deleted file mode 100644 index 11a4726..0000000 --- a/plugin/plugin.go +++ /dev/null @@ -1,415 +0,0 @@ -// Copyright (c) Improbable Worlds Ltd, All Rights Reserved - -// We generate a LogFields() method which returns a map. This method does the -// following for each field tagged with the logField annotation: -// -// * Primitive fields - adds the name and value of the filed to the map -// * Messages - Calls LogFields on the message and merges the returned map into the current map -// -// Oneofs containing logfields and embedded messages are supported. -// Duplicate names in the same message are reported as an error by the generator. -// Repeated fields, and therefore maps, are not supported, and are ignored by the generator. - -package plugin - -import ( - "fmt" - "strconv" - "strings" - - "github.com/gogo/protobuf/gogoproto" - "github.com/gogo/protobuf/proto" - "github.com/gogo/protobuf/protoc-gen-gogo/descriptor" - "github.com/gogo/protobuf/protoc-gen-gogo/generator" - "github.com/gogo/protobuf/vanity" - - "github.com/improbable-io/go-proto-logfields" -) - -type plugin struct { - *generator.Generator - generator.PluginImports - useGogo bool - selfImport generator.Single -} - -func NewPlugin(useGogo bool) generator.Plugin { - return &plugin{useGogo: useGogo} -} - -func (p *plugin) Name() string { - return "logfields" -} - -func (p *plugin) Init(g *generator.Generator) { - p.Generator = g - p.PluginImports = generator.NewPluginImports(p.Generator) -} - -func (p *plugin) Generate(file *generator.FileDescriptor) { - p.PluginImports = generator.NewPluginImports(p.Generator) - p.selfImport = p.NewImport("github.com/improbable-io/go-proto-logfields") - - if !p.useGogo { - vanity.TurnOffGogoImport(file.FileDescriptorProto) - } - - for _, msg := range file.Messages() { - logNames := map[string]struct{}{} - for _, field := range msg.GetField() { - logField := getLogFieldIfAny(field) - if logField == nil { - continue - } - if field.IsRepeated() { - p.Fail(fmt.Sprintf("Cannot log repeated field %v.%v", msg.GetName(), field.GetName())) - } - if _, duplicate := logNames[logField.Name]; duplicate { - p.Fail(fmt.Sprintf("Duplicate log field name %v in %v", logField.Name, msg.GetName())) - } - logNames[logField.Name] = struct{}{} - } - } - - for _, msg := range file.Messages() { - if msg.GetOptions().GetMapEntry() { - continue - } - - // Split the generated code into sections grouped by the outermost-level message being processed - p.P() - p.generateLogsExtractor(msg, gogoproto.IsProto3(file.FileDescriptorProto)) - p.P() - p.generateExtractRequestFields(msg, gogoproto.IsProto3(file.FileDescriptorProto)) - } -} - -func getLogFieldIfAny(field *descriptor.FieldDescriptorProto) *logfields.LogField { - opts := field.GetOptions() - if opts == nil { - return nil - } - e, err := proto.GetExtension(opts, logfields.E_Logfield) - if err != nil { - return nil - } - logField := e.(*logfields.LogField) - if logField != nil && logField.Name == "" { - logField = nil - } - return logField -} - -func hasLogField(field *descriptor.FieldDescriptorProto) bool { - return getLogFieldIfAny(field) != nil -} - -// Convert a UpperCamelCase to lowerCamelCase. -// Handles initialisms as the first word: HTMLThing is converted to htmlThing. -func lowerCamel(varName string) string { - firstNonUpper := 0 - for ; firstNonUpper < len(varName); firstNonUpper++ { - if !('A' <= varName[firstNonUpper] && varName[firstNonUpper] <= 'Z') { - break - } - } - lastInitUpper := firstNonUpper - 1 - if lastInitUpper < 0 { - return varName - } else if lastInitUpper == 0 { - return strings.ToLower(varName[:1]) + varName[1:] - } else if lastInitUpper == len(varName)-1 { - return strings.ToLower(varName) - } else { - return strings.ToLower(varName[:lastInitUpper]) + varName[lastInitUpper:] - } -} - -func (p *plugin) GetFieldVar(msg *generator.Descriptor, field *descriptor.FieldDescriptorProto) string { - return lowerCamel(p.GetFieldName(msg, field)) + `Fields` -} - -func (p *plugin) GetFieldMethod(msg *generator.Descriptor, field *descriptor.FieldDescriptorProto) string { - return lowerCamel(p.GetFieldName(msg, field)) + `LogFields` -} - -func (p *plugin) getFieldFmtExpr(msg *generator.Descriptor, field *descriptor.FieldDescriptorProto, proto3 bool) string { - expr := p.GetFieldName(msg, field) - if proto3 { - expr = `this.` + expr - } else { - expr = `this.Get` + expr + `()` - } - return p.getFmtExpr(expr, field) -} - -func (p *plugin) getFmtExpr(fieldName string, field *descriptor.FieldDescriptorProto) string { - fmtExpr := fieldName - if field.IsString() { - // no need to convert strings - } else if field.IsBytes() { - // pass through to string, let log handlers deal with non-printable bytes - fmtExpr = `string(` + fmtExpr + `)` - } else { - fmtExpr = `fmt.Sprintf("%v", ` + fmtExpr + `)` - } - return fmtExpr -} - -func (p *plugin) generateFieldsLiteralReturn(msg *generator.Descriptor, proto3 bool) { - p.P(`return map[string]string{`) - p.In() - for _, field := range msg.GetField() { - if field.IsMessage() { - continue - } - if field.OneofIndex != nil { - continue - } - logField := getLogFieldIfAny(field) - if logField == nil { - continue - } - - expr := p.getFieldFmtExpr(msg, field, proto3) - p.P(strconv.Quote(logField.Name), `: `, expr, `,`) - } - p.Out() - p.P(`}`) -} - -func findLoggedOneofField(msg *generator.Descriptor, oneofIndex int) *descriptor.FieldDescriptorProto { - var loggedOneOfField *descriptor.FieldDescriptorProto - for _, field := range msg.GetField() { - if field.OneofIndex == nil || int(field.GetOneofIndex()) != oneofIndex { - continue - } - if field.IsMessage() || hasLogField(field) { - loggedOneOfField = field - break - } - } - return loggedOneOfField -} - -func (p *plugin) generateOneOfSwitch(msg *generator.Descriptor, oneofProxy *descriptor.FieldDescriptorProto) string { - oneOfVar := p.GetFieldVar(msg, oneofProxy) - p.P(`var `, oneOfVar, ` map[string]string`) - p.P(`switch f := this.`, p.GetFieldName(msg, oneofProxy), `.(type) {`) - for _, field := range msg.GetField() { - if field.OneofIndex == nil || field.GetOneofIndex() != oneofProxy.GetOneofIndex() { - continue - } - // Oneof fields that can't generate log fields will use the default clause. - if !field.IsMessage() && !hasLogField(field) { - continue - } - p.P(`case *`, p.OneOfTypeName(msg, field), `:`) - p.In() - if field.IsMessage() { - p.P(oneOfVar, ` = `, p.selfImport.Use(), `.ExtractLogFieldsFromMessage(f.`, p.GetOneOfFieldName(msg, field), `)`) - } else { - logName := getLogFieldIfAny(field).Name - p.P(oneOfVar, ` = map[string]string{`, strconv.Quote(logName), `: `, p.getFmtExpr(`f.`+p.GetOneOfFieldName(msg, field), field), `}`) - } - p.Out() - } - p.P(`default:`) - p.In() - p.P(oneOfVar, ` = map[string]string{}`) - p.Out() - p.P(`}`) - return oneOfVar -} - -func (p *plugin) generateLogsExtractor(msg *generator.Descriptor, proto3 bool) { - p.P(`func (this *`, generator.CamelCaseSlice(msg.TypeName()), `) LogFields() map[string]string {`) - p.In() - - var needsBody bool - for _, field := range msg.GetField() { - if field.IsRepeated() { - continue - } - if field.IsMessage() || hasLogField(field) { - needsBody = true - break - } - } - if !needsBody { - // If the message has nothing that might generate log fields, we can immediately return an empty map and skip everything else - p.P(`return map[string]string{}`) - p.Out() - p.P(`}`) - return - } - - p.P(`// Handle being called on nil message.`) - p.P(`if this == nil {`) - p.In() - p.P(`return map[string]string{}`) - p.Out() - p.P(`}`) - - canUseLiteral := true - for _, field := range msg.GetField() { - if field.IsMessage() || field.OneofIndex != nil { - canUseLiteral = false - break - } - } - if canUseLiteral { - // If there were no message fields, return a fields literal directly - p.P(`// Generate fields for this message.`) - p.generateFieldsLiteralReturn(msg, proto3) - p.Out() - p.P(`}`) - return - } - - p.P(`// Gather fields from oneofs and child messages.`) - p.P(`var hasInner bool`) - loggedOneOfs := map[int]string{} - // Generate code to build a log field map for each oenof. - for oneOfIndex, _ := range msg.GetOneofDecl() { - // loggedOneOfField is used later as a proxy for the oneof - loggedOneOfField := findLoggedOneofField(msg, oneOfIndex) - if loggedOneOfField == nil { - // We can skip generating code for the oneof. - continue - } - - // Generate a type-switch. - oneOfVar := p.generateOneOfSwitch(msg, loggedOneOfField) - loggedOneOfs[oneOfIndex] = oneOfVar - // Keep track of whether any log fields were generated at runtime. - p.P(`hasInner = hasInner || len(`, oneOfVar, `) > 0`) - } - - // Generate code for embedded messages - for _, field := range msg.GetField() { - if field.OneofIndex != nil { - continue - } else if !field.IsMessage() { - continue - } else if field.IsRepeated() { - continue - } - p.P(p.GetFieldVar(msg, field), ` := `, p.selfImport.Use(), `.ExtractLogFieldsFromMessage(this.`+p.GetFieldName(msg, field)+`)`) - // Keep track of whether any log fields were generated at runtime. - p.P(`hasInner = hasInner || len(` + p.GetFieldVar(msg, field) + `) > 0`) - } - p.P(`if !hasInner {`) - p.In() - p.P(`// If no inner messages added any fields, avoid merging maps.`) - p.generateFieldsLiteralReturn(msg, proto3) - p.Out() - p.P(`}`) - - p.P(`// Merge all the field maps.`) - p.P(`res := map[string]string{}`) - // Generally try and keep the order of fields intact. - // Merge each oneof on encountering the first field that belongs to it. - visitedOneOfs := map[int]struct{}{} - for _, field := range msg.GetField() { - if field.IsRepeated() { - continue - } - if field.OneofIndex != nil { - oneOfVar, logged := loggedOneOfs[int(field.GetOneofIndex())] - if !logged { - continue - } - if _, visited := visitedOneOfs[int(field.GetOneofIndex())]; visited { - continue - } - visitedOneOfs[int(field.GetOneofIndex())] = struct{}{} - p.P(`for k, v := range ` + oneOfVar + ` {`) - p.In() - p.P(`res[k] = v`) - p.Out() - p.P(`}`) - } else if field.IsMessage() { - p.P(`for k, v := range ` + p.GetFieldVar(msg, field) + ` {`) - p.In() - p.P(`res[k] = v`) - p.Out() - p.P(`}`) - } else { - logField := getLogFieldIfAny(field) - if logField == nil { - continue - } - quoted := strconv.Quote(logField.Name) - p.P(`res[`, quoted, `] = `, p.getFieldFmtExpr(msg, field, proto3)) - } - } - - p.P(`return res`) - p.Out() - p.P(`}`) -} - -func (p *plugin) generateExtractRequestFields(msg *generator.Descriptor, proto3 bool) { - p.P(`func (this *`, generator.CamelCaseSlice(msg.TypeName()), `) ExtractRequestFields(dst map[string]interface{}) {`) - p.In() - - var needsBody bool - for _, field := range msg.GetField() { - if field.IsRepeated() { - continue - } - if field.IsMessage() || hasLogField(field) { - needsBody = true - break - } - } - if !needsBody { - // If the message has nothing that might generate log fields, we can generate an empty method - p.Out() - p.P(`}`) - return - } - - p.P(`// Handle being called on nil message.`) - p.P(`if this == nil {`) - p.In() - p.P(`return`) - p.Out() - p.P(`}`) - p.P() - - // Generate code for embedded messages - for _, field := range msg.GetField() { - if field.IsRepeated() { - continue - } - if !(field.IsMessage() || hasLogField(field)) { - continue - } - var fieldName string - if proto3 { - fieldName = `this.` + p.GetFieldName(msg, field) - } else { - fieldName = `this.Get` + p.GetFieldName(msg, field) + `()` - } - if field.OneofIndex != nil { - p.P(`if f, ok := `, fieldName, `.(*`, p.OneOfTypeName(msg, field), `); ok {`) - p.In() - fieldName = "f." + p.GetOneOfFieldName(msg, field) - } - if field.IsMessage() { - p.P(p.selfImport.Use(), `.ExtractRequestFieldsFromMessage(`, fieldName, `, dst)`) - } else { - logField := getLogFieldIfAny(field) - p.P(`dst[`, strconv.Quote(logField.Name), `] = `, fieldName) - } - if field.OneofIndex != nil { - p.Out() - p.P(`}`) - } - } - - p.Out() - p.P(`}`) -} diff --git a/plugin/plugin_test.go b/plugin/plugin_test.go deleted file mode 100644 index bedc06a..0000000 --- a/plugin/plugin_test.go +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) Improbable Worlds Ltd, All Rights Reserved - -package plugin - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestLowerCamel(t *testing.T) { - assert.Equal(t, "", lowerCamel("")) - assert.Equal(t, "a", lowerCamel("a")) - assert.Equal(t, "a", lowerCamel("A")) - assert.Equal(t, "aa", lowerCamel("AA")) - assert.Equal(t, "asdfTest", lowerCamel("asdfTest")) - assert.Equal(t, "asdfTest", lowerCamel("AsdfTest")) - assert.Equal(t, "asdf", lowerCamel("asdf")) -} diff --git a/protoc-gen-gologfields/BUILD.bazel b/protoc-gen-gologfields/BUILD.bazel index b112e6f..a253dea 100644 --- a/protoc-gen-gologfields/BUILD.bazel +++ b/protoc-gen-gologfields/BUILD.bazel @@ -2,14 +2,13 @@ load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") go_library( name = "go_default_library", - importpath = "github.com/improbable-io/go-proto-logfields/protoc-gen-gologfields", srcs = ["main.go"], + importpath = "github.com/improbable-io/go-proto-logfields/protoc-gen-gologfields", + visibility = ["//visibility:private"], deps = [ - "//plugin:go_default_library", - "@com_github_gogo_protobuf//proto:go_default_library", - "@com_github_gogo_protobuf//protoc-gen-gogo/generator:go_default_library", + "//internal/genlogfields:go_default_library", + "@org_golang_google_protobuf//compiler/protogen:go_default_library", ], - visibility = ["//visibility:private"], ) go_binary( diff --git a/protoc-gen-gologfields/main.go b/protoc-gen-gologfields/main.go index 81e55d7..d5028d0 100644 --- a/protoc-gen-gologfields/main.go +++ b/protoc-gen-gologfields/main.go @@ -3,62 +3,18 @@ package main import ( - "io/ioutil" - "os" - "strconv" - "strings" + "google.golang.org/protobuf/compiler/protogen" - "github.com/gogo/protobuf/proto" - "github.com/gogo/protobuf/protoc-gen-gogo/generator" - - "github.com/improbable-io/go-proto-logfields/plugin" + "github.com/improbable-io/go-proto-logfields/internal/genlogfields" ) func main() { - gen := generator.New() - - data, err := ioutil.ReadAll(os.Stdin) - if err != nil { - gen.Error(err, "reading input") - } - - if err := proto.Unmarshal(data, gen.Request); err != nil { - gen.Error(err, "parsing input proto") - } - - if len(gen.Request.FileToGenerate) == 0 { - gen.Fail("no files to generate") - } - - useGogo := false - for _, kv := range strings.Split(gen.Request.GetParameter(), ",") { - a := strings.SplitN(kv, "=", 2) - if len(a) == 2 && a[0] == "gogoimport" { - useGogo, err = strconv.ParseBool(a[1]) - if err != nil { - gen.Error(err, "parsing gogoimport option") + protogen.Options{}.Run(func(gen *protogen.Plugin) error { + for _, f := range gen.Files { + if f.Generate { + genlogfields.GenerateFile(gen, f) } } - } - - gen.CommandLineParameters(gen.Request.GetParameter()) - - gen.WrapTypes() - gen.SetPackageNames() - gen.BuildTypeNameMap() - gen.GeneratePlugin(plugin.NewPlugin(useGogo)) - - for i := 0; i < len(gen.Response.File); i++ { - gen.Response.File[i].Name = proto.String(strings.Replace(*gen.Response.File[i].Name, ".pb.go", ".logfields.pb.go", -1)) - } - - // Send back the results. - data, err = proto.Marshal(gen.Response) - if err != nil { - gen.Error(err, "failed to marshal output proto") - } - _, err = os.Stdout.Write(data) - if err != nil { - gen.Error(err, "failed to write output proto") - } + return nil + }) } diff --git a/test/BUILD.bazel b/test/BUILD.bazel index a696cfb..db13cb2 100644 --- a/test/BUILD.bazel +++ b/test/BUILD.bazel @@ -2,88 +2,48 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") load("@rules_proto//proto:defs.bzl", "proto_library") -proto_library( - name = "test_proto2", - srcs = ["test_proto2.proto"], - deps = [ - "//:logfields_proto", - ], - visibility = ["//visibility:private"], -) +# gazelle:proto_import_prefix +# gazelle:go_proto_compilers @io_bazel_rules_go//proto:go_proto,//:go_proto_logfields +# gazelle:go_grpc_compilers @io_bazel_rules_go//proto:go_grpc,//:go_proto_logfields +# gazelle:resolve proto go github.com/improbable-io/go-proto-logfields/logfields.proto //:go_default_library +# gazelle:resolve proto proto github.com/improbable-io/go-proto-logfields/logfields.proto //:logfields_proto +# gazelle:exclude *.pb.go proto_library( - name = "test_proto3", - srcs = ["test_proto3.proto"], - deps = [ - "//:logfields_proto", - ], - visibility = ["//visibility:private"], -) - -go_proto_library( - name = "proto2_gogo", - importpath = "dummy", - proto = ":test_proto2", - compilers = [ - "//:gogo_proto_logfields", - "@io_bazel_rules_go//proto:gogo_proto", + name = "logfieldstest_proto", + srcs = [ + "test_proto2.proto", + "test_proto3.proto", ], + visibility = ["//visibility:public"], + deps = ["//:logfields_proto"], ) go_proto_library( - name = "proto2_golang", - importpath = "dummy", - proto = ":test_proto2", + name = "logfieldstest_go_proto", compilers = [ - "//:go_proto_logfields", "@io_bazel_rules_go//proto:go_proto", - ], -) - -go_proto_library( - name = "proto3_gogo", - importpath = "dummy", - proto = ":test_proto3", - compilers = [ - "//:gogo_proto_logfields", - "@io_bazel_rules_go//proto:gogo_proto", - ], -) - -go_proto_library( - name = "proto3_golang", - importpath = "dummy", - proto = ":test_proto3", - compilers = [ "//:go_proto_logfields", - "@io_bazel_rules_go//proto:go_proto", ], + importpath = "github.com/improbable-io/go-proto-logfields/test", + proto = ":logfieldstest_proto", + visibility = ["//visibility:public"], + deps = ["//:go_default_library"], ) -go_test( - name = "gogo_test", - size = "small", - srcs = ["gogo/logfields_pb_test.go"], - embed = [ - ":proto2_gogo", - ":proto3_gogo", - ], - rundir = ".", - deps = [ - "@com_github_stretchr_testify//assert:go_default_library", - ], +go_library( + name = "go_default_library", + embed = [":logfieldstest_go_proto"], + importpath = "github.com/improbable-io/go-proto-logfields/test", + visibility = ["//visibility:public"], ) go_test( - name = "golang_test", - size = "small", - srcs = ["golang/logfields_pb_test.go"], - embed = [ - ":proto2_golang", - ":proto3_golang", - ], - rundir = ".", + name = "go_default_test", + srcs = ["logfields_pb_test.go"], + embed = [":go_default_library"], deps = [ "@com_github_stretchr_testify//assert:go_default_library", + "@org_golang_google_protobuf//proto:go_default_library", ], ) diff --git a/test/bad_protos/BUILD.bazel b/test/bad_protos/BUILD.bazel index 44d4dc2..03d7aa2 100644 --- a/test/bad_protos/BUILD.bazel +++ b/test/bad_protos/BUILD.bazel @@ -1,6 +1,6 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") -load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") -load("@rules_proto//proto:defs.bzl", "proto_library") +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +# gazelle:exclude *.proto go_test( name = "go_default_test", @@ -16,10 +16,10 @@ go_test( "-repeated_path=$(rootpath :repeated_logfield.proto)", ], data = [ - "//:logfields", - "//protoc-gen-gologfields", ":duplicate_logfield_names.proto", ":repeated_logfield.proto", + "//:logfields", + "//protoc-gen-gologfields", "@com_github_golang_protobuf//protoc-gen-go", "@com_google_protobuf//:protoc", "@com_google_protobuf//:well_known_protos", @@ -29,4 +29,3 @@ go_test( "@com_github_stretchr_testify//require:go_default_library", ], ) - diff --git a/test/bad_protos/duplicate_logfield_names.proto b/test/bad_protos/duplicate_logfield_names.proto index 0f4e06b..c323f33 100644 --- a/test/bad_protos/duplicate_logfield_names.proto +++ b/test/bad_protos/duplicate_logfield_names.proto @@ -1,10 +1,12 @@ syntax = "proto3"; + package logfields_test; -option go_package = "logfieldstest"; + +option go_package = "test/bad_protos;logfieldstest"; import "logfields.proto"; message DuplicateLogFieldsTest { - string string_1 = 1 [(improbable.logfield) = {name: "text"}]; - string string_2 = 2 [(improbable.logfield) = {name: "text"}]; + string string_1 = 1 [ (improbable.logfield) = {name : "text"} ]; + string string_2 = 2 [ (improbable.logfield) = {name : "text"} ]; } diff --git a/test/bad_protos/repeated_logfield.proto b/test/bad_protos/repeated_logfield.proto index 4befe76..eadd36c 100644 --- a/test/bad_protos/repeated_logfield.proto +++ b/test/bad_protos/repeated_logfield.proto @@ -1,9 +1,11 @@ syntax = "proto3"; + package logfields_test; -option go_package = "logfieldstest"; + +option go_package = "test/bad_protos;logfieldstest"; import "logfields.proto"; message RepeatedLogFieldTest { - repeated string some_strings = 1 [(improbable.logfield) = {name: "text"}]; + repeated string some_strings = 1 [ (improbable.logfield) = {name : "text"} ]; } diff --git a/test/golang/logfields_pb_test.go b/test/golang/logfields_pb_test.go deleted file mode 100644 index 3fae619..0000000 --- a/test/golang/logfields_pb_test.go +++ /dev/null @@ -1,273 +0,0 @@ -// Copyright (c) Improbable Worlds Ltd, All Rights Reserved - -package logfieldstest - -import ( - "testing" - - "github.com/golang/protobuf/proto" - "github.com/stretchr/testify/assert" -) - -type msg interface { - LogFields() map[string]string - ExtractRequestFields(map[string]interface{}) -} - -func assertExtracts(t *testing.T, expected map[string]interface{}, msg msg) { - m := map[string]interface{}{} - msg.ExtractRequestFields(m) - assert.Equal(t, expected, m) -} - -func TestNilProto3(t *testing.T) { - assert.Equal(t, map[string]string{}, (*UnloggedTest3)(nil).LogFields()) - assert.Equal(t, map[string]string{}, (*FieldsTest3)(nil).LogFields()) - assert.Equal(t, map[string]string{}, (*EmbeddedTest3)(nil).LogFields()) - assert.Equal(t, map[string]string{}, (*OneOfTest3)(nil).LogFields()) - assert.Equal(t, map[string]string{}, (*MapTest3)(nil).LogFields()) - - assertExtracts(t, map[string]interface{}{}, (*UnloggedTest3)(nil)) - assertExtracts(t, map[string]interface{}{}, (*FieldsTest3)(nil)) - assertExtracts(t, map[string]interface{}{}, (*EmbeddedTest3)(nil)) - assertExtracts(t, map[string]interface{}{}, (*OneOfTest3)(nil)) - assertExtracts(t, map[string]interface{}{}, (*MapTest3)(nil)) -} - -func TestEmptyUnloggedProto3(t *testing.T) { - assert.Equal(t, map[string]string{}, (&UnloggedTest3{}).LogFields()) - assertExtracts(t, map[string]interface{}{}, (&UnloggedTest3{})) -} - -func TestEmptyWithLoggedFields(t *testing.T) { - assert.Equal(t, map[string]string{ - "an_int": "0", - "a_string": "", - "some_bytes": "", - }, (&FieldsTest3{}).LogFields()) - assertExtracts(t, map[string]interface{}{ - "an_int": int32(0), - "a_string": "", - "some_bytes": []uint8(nil), - }, &FieldsTest3{}) -} - -func TestEmptyWithEmbeddedMessages(t *testing.T) { - assert.Equal(t, map[string]string{ - "a_string": "", - "log_text": "", - }, (&EmbeddedTest3{}).LogFields()) - assertExtracts(t, map[string]interface{}{ - "a_string": "", - "log_text": "", - }, &EmbeddedTest3{}) -} - -func TestEmptyWithOneOf(t *testing.T) { - assert.Equal(t, map[string]string{ - "a_string": "", - }, (&OneOfTest3{}).LogFields()) - assertExtracts(t, map[string]interface{}{ - "a_string": "", - }, &OneOfTest3{}) -} - -func TestEmptyWithMap(t *testing.T) { - assert.Equal(t, map[string]string{}, (&MapTest3{}).LogFields()) - assertExtracts(t, map[string]interface{}{}, &MapTest3{}) -} - -func TestEmbeddedMessagesEmpty(t *testing.T) { - msg := &EmbeddedTest3{ - SingleMessage: &EmbeddedTest3_Inner{}, - RepeatedMessages: []*EmbeddedTest3_Inner{ - &EmbeddedTest3_Inner{}, - }, - } - assert.Equal(t, map[string]string{ - "a_string": "", - "log_text": "", - }, msg.LogFields()) - assertExtracts(t, map[string]interface{}{ - "a_string": "", - "log_text": "", - }, msg) -} - -func TestOneOfEmbeddedMessageEmpty(t *testing.T) { - msg := &OneOfTest3{ - AOneof: &OneOfTest3_SingleOneofMessage{ - &OneOfTest3_Inner{}, - }, - } - assert.Equal(t, map[string]string{ - "a_string": "", - "log_text": "", - }, msg.LogFields()) - assertExtracts(t, map[string]interface{}{ - "a_string": "", - "log_text": "", - }, msg) -} - -func TestOneOfEmbeddedMessage(t *testing.T) { - msg := &OneOfTest3{ - AOneof: &OneOfTest3_SingleOneofMessage{ - &OneOfTest3_Inner{ - SingleInnerString: "a_text", - }, - }, - } - assert.Equal(t, map[string]string{ - "a_string": "", - "log_text": "a_text", - }, msg.LogFields()) - assertExtracts(t, map[string]interface{}{ - "a_string": "", - "log_text": "a_text", - }, msg) -} - -func TestOneOfUnloggedField(t *testing.T) { - msg := &OneOfTest3{ - AOneof: &OneOfTest3_UnloggedOneofString{}, - } - assert.Equal(t, map[string]string{ - "a_string": "", - }, msg.LogFields()) - assertExtracts(t, map[string]interface{}{ - "a_string": "", - }, msg) -} - -func TestOneOfStringField(t *testing.T) { - msg := &OneOfTest3{ - AOneof: &OneOfTest3_SingleOneofString{}, - } - assert.Equal(t, map[string]string{ - "a_string": "", - "log_text": "", - }, msg.LogFields()) - assertExtracts(t, map[string]interface{}{ - "a_string": "", - "log_text": "", - }, msg) -} - -func TestMapEntry(t *testing.T) { - msg := &MapTest3{ - AStringMap: map[string]string{ - "a_string_key": "a_string_value", - }, - AStringToInnerMap: map[string]*MapTest3_Inner{ - "a_inner_key": &MapTest3_Inner{ - SingleInnerString: "a_inner_string_value", - }, - }, - } - assert.Equal(t, map[string]string{}, msg.LogFields()) - assertExtracts(t, map[string]interface{}{}, msg) -} - -func TestProto3Formatting(t *testing.T) { - msg := &FieldsTest3{ - SingleInteger: 42, - SingleString: "23", - SingleBytes: []byte{1, 'a'}, - } - assert.Equal(t, map[string]string{ - "an_int": "42", - "a_string": "23", - "some_bytes": "\x01a", - }, msg.LogFields()) - assertExtracts(t, map[string]interface{}{ - "an_int": int32(42), - "a_string": "23", - "some_bytes": []byte{1, 'a'}, - }, msg) -} - -func TestNilProto2(t *testing.T) { - assert.Equal(t, map[string]string{}, (*TestMessage2)(nil).LogFields()) - assertExtracts(t, map[string]interface{}{}, (*TestMessage2)(nil)) -} - -func TestEmptyProto2(t *testing.T) { - msg := &TestMessage2{} - assert.Equal(t, map[string]string{ - "opt_int": "0", - "req_int": "0", - "opt_string": "", - "req_string": "", - "opt_bytes": "", - "req_bytes": "", - }, msg.LogFields()) - assertExtracts(t, map[string]interface{}{ - "opt_int": int32(0), - "req_int": int32(0), - "opt_string": "", - "req_string": "", - "opt_bytes": []byte(nil), - "req_bytes": []byte(nil), - }, msg) -} - -func TestEmptyProto2WithChild(t *testing.T) { - msg := &TestMessage2{ - RequiredMessage: &TestMessage2_Inner{}, - OptionalMessage: &TestMessage2_Inner{}, - } - assert.Equal(t, map[string]string{ - "log_text": "", - "opt_int": "0", - "req_int": "0", - "opt_string": "", - "req_string": "", - "opt_bytes": "", - "req_bytes": "", - }, msg.LogFields()) - assertExtracts(t, map[string]interface{}{ - "log_text": "", - "opt_int": int32(0), - "req_int": int32(0), - "opt_string": "", - "req_string": "", - "opt_bytes": []byte(nil), - "req_bytes": []byte(nil), - }, msg) -} - -func TestProto2Formatting(t *testing.T) { - msg := &TestMessage2{ - RequiredInteger: proto.Int(42), - OptionalInteger: proto.Int(42), - RequiredString: proto.String("23"), - OptionalString: proto.String("23"), - RequiredBytes: []byte{1, 'a'}, - OptionalBytes: []byte{1, 'a'}, - RequiredMessage: &TestMessage2_Inner{ - Text: proto.String("required message text"), - }, - OptionalMessage: &TestMessage2_Inner{ - Text: proto.String("optional message text"), - }, - } - assert.Equal(t, map[string]string{ - "log_text": "optional message text", - "opt_int": "42", - "req_int": "42", - "opt_string": "23", - "req_string": "23", - "opt_bytes": "\x01a", - "req_bytes": "\x01a", - }, msg.LogFields()) - assertExtracts(t, map[string]interface{}{ - "log_text": "optional message text", - "opt_int": int32(42), - "req_int": int32(42), - "opt_string": "23", - "req_string": "23", - "opt_bytes": []byte{1, 'a'}, - "req_bytes": []byte{1, 'a'}, - }, msg) -} diff --git a/test/gogo/logfields_pb_test.go b/test/logfields_pb_test.go similarity index 80% rename from test/gogo/logfields_pb_test.go rename to test/logfields_pb_test.go index 509913c..29c6041 100644 --- a/test/gogo/logfields_pb_test.go +++ b/test/logfields_pb_test.go @@ -5,18 +5,18 @@ package logfieldstest import ( "testing" - "github.com/gogo/protobuf/proto" "github.com/stretchr/testify/assert" + "google.golang.org/protobuf/proto" ) type msg interface { LogFields() map[string]string - ExtractRequestFields(map[string]interface{}) + ExtractRequestFields([]string, map[string]interface{}) } func assertExtracts(t *testing.T, expected map[string]interface{}, msg msg) { m := map[string]interface{}{} - msg.ExtractRequestFields(m) + msg.ExtractRequestFields(nil, m) assert.Equal(t, expected, m) } @@ -81,16 +81,18 @@ func TestEmbeddedMessagesEmpty(t *testing.T) { msg := &EmbeddedTest3{ SingleMessage: &EmbeddedTest3_Inner{}, RepeatedMessages: []*EmbeddedTest3_Inner{ - &EmbeddedTest3_Inner{}, + {}, }, } assert.Equal(t, map[string]string{ - "a_string": "", - "log_text": "", + "a_string": "", + "log_text": "", + "inner.log_text": "", }, msg.LogFields()) assertExtracts(t, map[string]interface{}{ - "a_string": "", - "log_text": "", + "a_string": "", + "log_text": "", + "inner.log_text": "", }, msg) } @@ -160,7 +162,7 @@ func TestMapEntry(t *testing.T) { "a_string_key": "a_string_value", }, AStringToInnerMap: map[string]*MapTest3_Inner{ - "a_inner_key": &MapTest3_Inner{ + "a_inner_key": { SingleInnerString: "a_inner_string_value", }, }, @@ -218,29 +220,31 @@ func TestEmptyProto2WithChild(t *testing.T) { OptionalMessage: &TestMessage2_Inner{}, } assert.Equal(t, map[string]string{ - "log_text": "", - "opt_int": "0", - "req_int": "0", - "opt_string": "", - "req_string": "", - "opt_bytes": "", - "req_bytes": "", + "optional.log_text": "", + "required.log_text": "", + "opt_int": "0", + "req_int": "0", + "opt_string": "", + "req_string": "", + "opt_bytes": "", + "req_bytes": "", }, msg.LogFields()) assertExtracts(t, map[string]interface{}{ - "log_text": "", - "opt_int": int32(0), - "req_int": int32(0), - "opt_string": "", - "req_string": "", - "opt_bytes": []byte(nil), - "req_bytes": []byte(nil), + "optional.log_text": "", + "required.log_text": "", + "opt_int": int32(0), + "req_int": int32(0), + "opt_string": "", + "req_string": "", + "opt_bytes": []byte(nil), + "req_bytes": []byte(nil), }, msg) } func TestProto2Formatting(t *testing.T) { msg := &TestMessage2{ - RequiredInteger: proto.Int(42), - OptionalInteger: proto.Int(42), + RequiredInteger: proto.Int32(42), + OptionalInteger: proto.Int32(42), RequiredString: proto.String("23"), OptionalString: proto.String("23"), RequiredBytes: []byte{1, 'a'}, @@ -253,21 +257,23 @@ func TestProto2Formatting(t *testing.T) { }, } assert.Equal(t, map[string]string{ - "log_text": "optional message text", - "opt_int": "42", - "req_int": "42", - "opt_string": "23", - "req_string": "23", - "opt_bytes": "\x01a", - "req_bytes": "\x01a", + "optional.log_text": "optional message text", + "required.log_text": "required message text", + "opt_int": "42", + "req_int": "42", + "opt_string": "23", + "req_string": "23", + "opt_bytes": "\x01a", + "req_bytes": "\x01a", }, msg.LogFields()) assertExtracts(t, map[string]interface{}{ - "log_text": "optional message text", - "opt_int": int32(42), - "req_int": int32(42), - "opt_string": "23", - "req_string": "23", - "opt_bytes": []byte{1, 'a'}, - "req_bytes": []byte{1, 'a'}, + "optional.log_text": "optional message text", + "required.log_text": "required message text", + "opt_int": int32(42), + "req_int": int32(42), + "opt_string": "23", + "req_string": "23", + "opt_bytes": []byte{1, 'a'}, + "req_bytes": []byte{1, 'a'}, }, msg) } diff --git a/test/test_proto2.proto b/test/test_proto2.proto index 87e7318..e2022f3 100644 --- a/test/test_proto2.proto +++ b/test/test_proto2.proto @@ -7,18 +7,26 @@ option go_package = "logfieldstest"; import "github.com/improbable-io/go-proto-logfields/logfields.proto"; message TestMessage2 { - message Inner { - optional string text = 1 [(improbable.logfield) = {name: "log_text"}]; - } + message Inner { + optional string text = 1 [ (improbable.logfield) = {name : "log_text"} ]; + } - required int32 required_integer = 1 [(improbable.logfield) = {name: "req_int"}]; - optional int32 optional_integer = 2 [(improbable.logfield) = {name: "opt_int"}]; - required string required_string = 4 [(improbable.logfield) = {name: "req_string"}]; - optional string optional_string = 5 [(improbable.logfield) = {name: "opt_string"}]; - required bytes required_bytes = 7 [(improbable.logfield) = {name: "req_bytes"}]; - optional bytes optional_bytes = 8 [(improbable.logfield) = {name: "opt_bytes"}]; + required int32 required_integer = 1 + [ (improbable.logfield) = {name : "req_int"} ]; + optional int32 optional_integer = 2 + [ (improbable.logfield) = {name : "opt_int"} ]; + required string required_string = 4 + [ (improbable.logfield) = {name : "req_string"} ]; + optional string optional_string = 5 + [ (improbable.logfield) = {name : "opt_string"} ]; + required bytes required_bytes = 7 + [ (improbable.logfield) = {name : "req_bytes"} ]; + optional bytes optional_bytes = 8 + [ (improbable.logfield) = {name : "opt_bytes"} ]; - required Inner required_message = 10; - optional Inner optional_message = 11; - repeated Inner repeated_messages = 12; + required Inner required_message = 10 + [ (improbable.logfield) = {prefix : "required"} ]; + optional Inner optional_message = 11 + [ (improbable.logfield) = {prefix : "optional"} ]; + repeated Inner repeated_messages = 12; } diff --git a/test/test_proto3.proto b/test/test_proto3.proto index 22ec356..968d6a9 100644 --- a/test/test_proto3.proto +++ b/test/test_proto3.proto @@ -7,48 +7,51 @@ option go_package = "logfieldstest"; import "github.com/improbable-io/go-proto-logfields/logfields.proto"; message UnloggedTest3 { - int32 single_integer = 1; - string single_string = 3; - bytes single_bytes = 5; + int32 single_integer = 1; + string single_string = 3; + bytes single_bytes = 5; } message FieldsTest3 { - int32 single_integer = 1 [(improbable.logfield) = {name: "an_int"}]; - string single_string = 3 [(improbable.logfield) = {name: "a_string"}]; - bytes single_bytes = 5 [(improbable.logfield) = {name: "some_bytes"}]; + int32 single_integer = 1 [ (improbable.logfield) = {name : "an_int"} ]; + string single_string = 3 [ (improbable.logfield) = {name : "a_string"} ]; + bytes single_bytes = 5 [ (improbable.logfield) = {name : "some_bytes"} ]; } message EmbeddedTest3 { - message Inner { - string text = 1 [(improbable.logfield) = {name: "log_text"}]; - } + message Inner { + string text = 1 [ (improbable.logfield) = {name : "log_text"} ]; + } - string single_string = 1 [(improbable.logfield) = {name: "a_string"}]; - string outer_text = 2 [(improbable.logfield) = {name: "log_text"}]; + string single_string = 1 [ (improbable.logfield) = {name : "a_string"} ]; + string outer_text = 2 [ (improbable.logfield) = {name : "log_text"} ]; - Inner single_message = 3; - repeated Inner repeated_messages = 4; + Inner single_message = 3 [ (improbable.logfield) = {prefix : "inner"} ]; + repeated Inner repeated_messages = 4; } message OneOfTest3 { - message Inner { - string single_inner_string = 1 [(improbable.logfield) = {name: "log_text"}]; - } - - string outer_text = 2 [(improbable.logfield) = {name: "a_string"}]; - - oneof a_oneof { - string unlogged_oneof_string = 3; - string single_oneof_string = 4 [(improbable.logfield) = {name: "log_text"}]; - Inner single_oneof_message = 5; - } + message Inner { + string single_inner_string = 1 + [ (improbable.logfield) = {name : "log_text"} ]; + } + + string outer_text = 2 [ (improbable.logfield) = {name : "a_string"} ]; + + oneof a_oneof { + string unlogged_oneof_string = 3; + string single_oneof_string = 4 + [ (improbable.logfield) = {name : "log_text"} ]; + Inner single_oneof_message = 5; + } } message MapTest3 { - message Inner { - string single_inner_string = 1 [(improbable.logfield) = {name: "log_text"}]; - } + message Inner { + string single_inner_string = 1 + [ (improbable.logfield) = {name : "log_text"} ]; + } - map a_string_map = 1; - map a_string_to_inner_map = 2; + map a_string_map = 1; + map a_string_to_inner_map = 2; } diff --git a/vendor/github.com/gogo/protobuf b/vendor/github.com/gogo/protobuf deleted file mode 160000 index 3043356..0000000 --- a/vendor/github.com/gogo/protobuf +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 30433562cfbf487fe1df7cd26c7bab168d2f14d0 diff --git a/vendor/github.com/golang/protobuf b/vendor/github.com/golang/protobuf deleted file mode 160000 index 18c9bb3..0000000 --- a/vendor/github.com/golang/protobuf +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 18c9bb3261723cd5401db4d0c9fbc5c3b6c70fe8 diff --git a/vendor/github.com/google/protobuf b/vendor/github.com/google/protobuf deleted file mode 160000 index 067b1ee..0000000 --- a/vendor/github.com/google/protobuf +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 067b1eec3bf852abaad0844999461baff8a5fdc8 diff --git a/vendor/github.com/stretchr/testify b/vendor/github.com/stretchr/testify deleted file mode 160000 index 4d4bfba..0000000 --- a/vendor/github.com/stretchr/testify +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 4d4bfba8f1d1027c4fdbe371823030df51419987