From c81960af51cd06648faf929e2e685132aafb3646 Mon Sep 17 00:00:00 2001 From: Vasiliy Tolstov Date: Tue, 23 Apr 2024 22:21:27 +0300 Subject: [PATCH] add metrics and tracing Signed-off-by: Vasiliy Tolstov --- go.mod | 18 +++++--- go.sum | 27 +++++++++++ handler.go | 93 +++++++++++++++++++++++++++++++++----- handler/graphql/graphql.go | 77 +++++++++++++++++++++++++++++++ message.go | 1 - options.go | 2 +- subscriber.go | 9 ++-- 7 files changed, 205 insertions(+), 22 deletions(-) create mode 100644 handler/graphql/graphql.go diff --git a/go.mod b/go.mod index 0ff1d69..326e833 100644 --- a/go.mod +++ b/go.mod @@ -3,19 +3,25 @@ module go.unistack.org/micro-server-http/v3 go 1.18 require ( + github.com/99designs/gqlgen v0.17.45 + go.unistack.org/micro-client-http/v3 v3.9.7 go.unistack.org/micro-codec-yaml/v3 v3.10.0 - go.unistack.org/micro-proto/v3 v3.3.1 - go.unistack.org/micro/v3 v3.10.52 - golang.org/x/net v0.22.0 + go.unistack.org/micro-proto/v3 v3.4.1 + go.unistack.org/micro/v3 v3.10.66 + golang.org/x/net v0.24.0 ) require ( github.com/golang/protobuf v1.5.4 // indirect github.com/google/gnostic v0.7.0 // indirect github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect - golang.org/x/sys v0.18.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240314234333-6e1732d8331c // indirect - google.golang.org/grpc v1.62.1 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/patrickmn/go-cache v2.1.0+incompatible // indirect + github.com/sosodev/duration v1.2.0 // indirect + github.com/vektah/gqlparser/v2 v2.5.11 // indirect + golang.org/x/sys v0.19.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be // indirect + google.golang.org/grpc v1.63.2 // indirect google.golang.org/protobuf v1.33.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect sigs.k8s.io/yaml v1.4.0 // indirect diff --git a/go.sum b/go.sum index 09247b9..e6badb5 100644 --- a/go.sum +++ b/go.sum @@ -594,6 +594,8 @@ cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcP dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc= +github.com/99designs/gqlgen v0.17.45 h1:bH0AH67vIJo8JKNKPJP+pOPpQhZeuVRQLf53dKIpDik= +github.com/99designs/gqlgen v0.17.45/go.mod h1:Bas0XQ+Jiu/Xm5E33jC8sES3G+iC2esHBMXcq0fUPs0= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= @@ -602,6 +604,7 @@ github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apache/arrow/go/v10 v10.0.1/go.mod h1:YvhnlEePVnBS4+0z3fhPfUy7W1Ikj0Ih0vcRo/gZ1M0= @@ -751,6 +754,8 @@ github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLe github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= @@ -804,6 +809,7 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/ github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= +github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= @@ -814,6 +820,7 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= +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/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -825,7 +832,10 @@ github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZV github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk= +github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= github.com/silas/dag v0.0.0-20211117232152-9d50aa809f35/go.mod h1:7RTUFBdIRC9nZ7/3RyRNH1bdqIShrDejd1YbLwgPS+I= +github.com/sosodev/duration v1.2.0 h1:pqK/FLSjsAADWY74SyWDCjOcd5l7H8GSnnOGEB9A1Us= +github.com/sosodev/duration v1.2.0/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= @@ -843,6 +853,9 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/vektah/gqlparser/v2 v2.5.11 h1:JJxLtXIoN7+3x6MBdtIP59TP1RANnY7pXOaDnADQSf8= +github.com/vektah/gqlparser/v2 v2.5.11/go.mod h1:1rCcfwB2ekJofmluGWXMSEnPMZgbxzwj6FaZ/4OT8Cc= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= @@ -865,13 +878,19 @@ go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= +go.unistack.org/micro-client-http/v3 v3.9.7 h1:1NnlkYA6MYJuUOQxMoSyHwrsw3rwHFQ3VA1/392KqR0= +go.unistack.org/micro-client-http/v3 v3.9.7/go.mod h1:AvPlXxZ3WeoN0ekc9qb2koAvysW0lc7Jq7z+517u4h8= go.unistack.org/micro-codec-yaml/v3 v3.10.0 h1:cnNiWQZ3F+jsCX9DhEHBdIw8CjBItxb+TrKGGdlQ8F8= go.unistack.org/micro-codec-yaml/v3 v3.10.0/go.mod h1:UzwC3QZZ6+PgKSudlGZNLkugXzOFkp3sxhs/CctEwBY= go.unistack.org/micro-proto/v3 v3.3.1 h1:nQ0MtWvP2G3QrpOgawVOPhpZZYkq6umTGDqs8FxJYIo= go.unistack.org/micro-proto/v3 v3.3.1/go.mod h1:cwRyv8uInM2I7EbU7O8Fx2Ls3N90Uw9UCCcq4olOdfE= +go.unistack.org/micro-proto/v3 v3.4.1 h1:UTjLSRz2YZuaHk9iSlVqqsA50JQNAEK2ZFboGqtEa9Q= +go.unistack.org/micro-proto/v3 v3.4.1/go.mod h1:okx/cnOhzuCX0ggl/vToatbCupi0O44diiiLLsZ93Zo= go.unistack.org/micro/v3 v3.10.14/go.mod h1:uMAc0U/x7dmtICCrblGf0ZLgYegu3VwQAquu+OFCw1Q= go.unistack.org/micro/v3 v3.10.52 h1:6LlAvLLlf+3JLCEQEVNQWi7DXCoI1ocuOqqoEPj5S+k= go.unistack.org/micro/v3 v3.10.52/go.mod h1:erMgt3Bl7vQQ0e9UpQyR5NlLiZ9pKeEJ9+1tfYFaqUg= +go.unistack.org/micro/v3 v3.10.66 h1:tiG8HnyTC71IZWSC2qT/DmLhJinZJL9qvw+4Fvpm3d4= +go.unistack.org/micro/v3 v3.10.66/go.mod h1:erMgt3Bl7vQQ0e9UpQyR5NlLiZ9pKeEJ9+1tfYFaqUg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -995,6 +1014,8 @@ golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1116,6 +1137,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= @@ -1424,6 +1447,8 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234015-3fc162c6f38a/go. google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= google.golang.org/genproto/googleapis/rpc v0.0.0-20240314234333-6e1732d8331c h1:lfpJ/2rWPa/kJgxyyXM8PrNnfCzcmxJ265mADgwmvLI= google.golang.org/genproto/googleapis/rpc v0.0.0-20240314234333-6e1732d8331c/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be h1:LG9vZxsWGOmUKieR8wPAUR3u3MpnYFQZROPIMaXh7/A= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1465,6 +1490,8 @@ google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwS google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk= google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= +google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= +google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= 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= diff --git a/handler.go b/handler.go index 9dc8f54..983e944 100644 --- a/handler.go +++ b/handler.go @@ -6,14 +6,19 @@ import ( "io" "net/http" "reflect" + "strconv" "strings" "sync" + "time" "go.unistack.org/micro/v3/errors" "go.unistack.org/micro/v3/logger" "go.unistack.org/micro/v3/metadata" + "go.unistack.org/micro/v3/options" "go.unistack.org/micro/v3/register" + "go.unistack.org/micro/v3/semconv" "go.unistack.org/micro/v3/server" + "go.unistack.org/micro/v3/tracer" rhttp "go.unistack.org/micro/v3/util/http" rflutil "go.unistack.org/micro/v3/util/reflect" ) @@ -272,9 +277,11 @@ func (h *Server) HTTPHandlerFunc(handler interface{}) (http.HandlerFunc, error) } // wrap the handler func - for i := len(handler.sopts.HdlrWrappers); i > 0; i-- { - fn = handler.sopts.HdlrWrappers[i-1](fn) - } + h.opts.Hooks.EachNext(func(hook options.Hook) { + if h, ok := hook.(server.HookHandler); ok { + fn = h(fn) + } + }) if ct == "application/x-www-form-urlencoded" { cf, err = h.newCodec(DefaultContentType) @@ -345,8 +352,11 @@ func (h *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { ct = htype } + ts := time.Now() + ctx := context.WithValue(r.Context(), rspCodeKey{}, &rspCodeVal{}) ctx = context.WithValue(ctx, rspHeaderKey{}, &rspHeaderVal{}) + md, ok := metadata.FromIncomingContext(ctx) if !ok { md = metadata.New(len(r.Header) + 8) @@ -421,21 +431,77 @@ func (h *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { } } + var sp tracer.Span if !match && h.hd != nil { if hdlr, ok := h.hd.Handler().(http.Handler); ok { - hdlr.ServeHTTP(w, r) + h.opts.Meter.Counter(semconv.ServerRequestInflight, "endpoint", h.hd.Name()).Inc() + ctx, sp = h.opts.Tracer.Start(ctx, h.hd.Name()+" rpc-server", + tracer.WithSpanKind(tracer.SpanKindServer), + tracer.WithSpanLabels( + "endpoint", h.hd.Name(), + ), + ) + hdlr.ServeHTTP(w, r.WithContext(ctx)) + n := GetRspCode(ctx) + if n > 399 { + h.opts.Meter.Counter(semconv.ServerRequestTotal, "endpoint", h.hd.Name(), "status", "success", "code", strconv.Itoa(n)).Inc() + if s, _ := sp.Status(); s != tracer.SpanStatusError { + sp.SetStatus(tracer.SpanStatusError, http.StatusText(n)) + } + } else { + h.opts.Meter.Counter(semconv.ServerRequestTotal, "endpoint", h.hd.Name(), "status", "failure", "code", strconv.Itoa(n)).Inc() + } + te := time.Since(ts) + h.opts.Meter.Summary(semconv.ServerRequestLatencyMicroseconds, "endpoint", h.hd.Name()).Update(te.Seconds()) + h.opts.Meter.Histogram(semconv.ServerRequestDurationSeconds, "endpoint", h.hd.Name()).Update(te.Seconds()) + h.opts.Meter.Counter(semconv.ServerRequestInflight, "endpoint", h.hd.Name()).Dec() + sp.Finish() return } } else if !match { // check for http.HandlerFunc handlers + ctx, sp = h.opts.Tracer.Start(ctx, r.URL.Path+" rpc-server", + tracer.WithSpanKind(tracer.SpanKindServer), + tracer.WithSpanLabels( + "endpoint", r.URL.Path, + ), + ) if ph, _, err := h.pathHandlers.Search(r.Method, r.URL.Path); err == nil { - ph.(http.HandlerFunc)(w, r) + ph.(http.HandlerFunc)(w, r.WithContext(ctx)) + if n := GetRspCode(ctx); n > 399 { + sp.SetStatus(tracer.SpanStatusError, http.StatusText(n)) + } + sp.Finish() return } h.errorHandler(ctx, nil, w, r, fmt.Errorf("not matching route found"), http.StatusNotFound) return } + ctx, sp = h.opts.Tracer.Start(ctx, handler.name+" rpc-server", + tracer.WithSpanKind(tracer.SpanKindServer), + tracer.WithSpanLabels( + "endpoint", handler.name, + ), + ) + defer func() { + te := time.Since(ts) + h.opts.Meter.Summary(semconv.ServerRequestLatencyMicroseconds, "endpoint", handler.name).Update(te.Seconds()) + h.opts.Meter.Histogram(semconv.ServerRequestDurationSeconds, "endpoint", handler.name).Update(te.Seconds()) + h.opts.Meter.Counter(semconv.ServerRequestInflight, "endpoint", handler.name).Dec() + + n := GetRspCode(ctx) + if n > 399 { + if s, _ := sp.Status(); s != tracer.SpanStatusError { + sp.SetStatus(tracer.SpanStatusError, http.StatusText(n)) + } + h.opts.Meter.Counter(semconv.ServerRequestTotal, "endpoint", handler.name, "status", "failure", "code", strconv.Itoa(n)).Inc() + } else { + h.opts.Meter.Counter(semconv.ServerRequestTotal, "endpoint", handler.name, "status", "success", "code", strconv.Itoa(n)).Inc() + } + sp.Finish() + }() + // get fields from url values if len(r.URL.RawQuery) > 0 { umd, cerr := rflutil.URLMap(r.URL.RawQuery) @@ -531,13 +597,18 @@ func (h *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { } metadata.SetOutgoingContext(ctx, md) + if err != nil && sp != nil { + sp.SetStatus(tracer.SpanStatusError, err.Error()) + } + return err } - // wrap the handler func - for i := len(handler.sopts.HdlrWrappers); i > 0; i-- { - fn = handler.sopts.HdlrWrappers[i-1](fn) - } + h.opts.Hooks.EachNext(func(hook options.Hook) { + if h, ok := hook.(server.HookHandler); ok { + fn = h(fn) + } + }) if ct == "application/x-www-form-urlencoded" { cf, err = h.newCodec(DefaultContentType) @@ -587,7 +658,7 @@ func (h *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { } if err != nil && handler.sopts.Logger.V(logger.ErrorLevel) { - handler.sopts.Logger.Errorf(handler.sopts.Context, "handler err: %v", err) + handler.sopts.Logger.Error(handler.sopts.Context, "handler error", err) return } @@ -597,6 +668,6 @@ func (h *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { w.WriteHeader(scode) if _, cerr := w.Write(buf); cerr != nil { - handler.sopts.Logger.Errorf(ctx, "write failed: %v", cerr) + handler.sopts.Logger.Error(ctx, "respoonse write error", cerr) } } diff --git a/handler/graphql/graphql.go b/handler/graphql/graphql.go new file mode 100644 index 0000000..287fb57 --- /dev/null +++ b/handler/graphql/graphql.go @@ -0,0 +1,77 @@ +//go:build ignore + +package graphql_handler + +import ( + "context" + "fmt" + + "github.com/99designs/gqlgen/graphql" + "go.unistack.org/micro/v3/logger" + "go.unistack.org/micro/v3/store" +) + +var _ graphql.Cache = (*cacheWrapper)(nil) + +type Handler struct { + opts Options +} + +type Option func(*Options) + +type Options struct { + cache *cacheWrapper + Path string +} + +type cacheWrapper struct { + s store.Store + l logger.Logger +} + +func (c *cacheWrapper) Get(ctx context.Context, key string) (interface{}, bool) { + var val interface{} + if err := c.s.Read(ctx, key, val); err != nil && err != store.ErrNotFound { + c.l.Error(ctx, fmt.Sprintf("cache.Get %s failed", key), err) + return nil, false + } + return val, true +} + +func (c *cacheWrapper) Add(ctx context.Context, key string, val interface{}) { + if err := c.s.Write(ctx, key, val); err != nil { + c.l.Error(ctx, fmt.Sprintf("cache.Add %s failed", key), err) + } +} + +func Store(s store.Store) Option { + return func(o *Options) { + if o.cache == nil { + o.cache = &cacheWrapper{} + } + o.cache.s = s + } +} + +func Logger(l logger.Logger) Option { + return func(o *Options) { + if o.cache == nil { + o.cache = &cacheWrapper{} + } + o.cache.l = l + } +} + +func Path(path string) Option { + return func(o *Options) { + o.Path = path + } +} + +func NewHandler(opts ...Option) *Handler { + options := Options{} + for _, o := range opts { + o(&options) + } + return &Handler{opts: options} +} diff --git a/message.go b/message.go index 2146e5c..226d6b1 100644 --- a/message.go +++ b/message.go @@ -11,7 +11,6 @@ type httpMessage struct { header metadata.Metadata topic string contentType string - body []byte } func (r *httpMessage) Topic() string { diff --git a/options.go b/options.go index b20c015..98908df 100644 --- a/options.go +++ b/options.go @@ -69,7 +69,7 @@ func getRspHeader(ctx context.Context) http.Header { // GetRspCode used internally by generated http server handler func GetRspCode(ctx context.Context) int { - var code int + code := int(200) if rsp, ok := ctx.Value(rspCodeKey{}).(*rspCodeVal); ok { code = rsp.code } diff --git a/subscriber.go b/subscriber.go index f7e1b90..2aa157d 100644 --- a/subscriber.go +++ b/subscriber.go @@ -10,6 +10,7 @@ import ( "go.unistack.org/micro/v3/broker" "go.unistack.org/micro/v3/codec" "go.unistack.org/micro/v3/metadata" + "go.unistack.org/micro/v3/options" "go.unistack.org/micro/v3/register" "go.unistack.org/micro/v3/server" ) @@ -159,9 +160,11 @@ func (s *Server) createSubHandler(sb *httpSubscriber, opts server.Options) broke return nil } - for i := len(opts.SubWrappers); i > 0; i-- { - fn = opts.SubWrappers[i-1](fn) - } + opts.Hooks.EachNext(func(hook options.Hook) { + if h, ok := hook.(server.HookSubHandler); ok { + fn = h(fn) + } + }) go func() { results <- fn(ctx, &httpMessage{