diff --git a/README.md b/README.md index 88e68718..d433a64c 100644 --- a/README.md +++ b/README.md @@ -96,6 +96,7 @@ Also there are several [**documents**](./docs) that you may find useful for eith | logrus | https://github.com/sirupsen/logrus | v1.5.0 | v1.9.3 | | mongodb | https://github.com/mongodb/mongo-go-driver | v1.11.1 | v1.15.2 | | mux | https://github.com/gorilla/mux | v1.3.0 | v1.8.1 | +| nacos | github.com/nacos-group/nacos-sdk-go/v2 | v2.0.0 | v2.2.7 | | net/http | https://pkg.go.dev/net/http | - | - | | redigo | https://github.com/gomodule/redigo | v1.9.0 | v1.9.2 | | slog | https://pkg.go.dev/log/slog | - | - | diff --git a/docs/experimental-feature.md b/docs/experimental-feature.md index cdc4b1cd..a7f39c3b 100644 --- a/docs/experimental-feature.md +++ b/docs/experimental-feature.md @@ -2,6 +2,7 @@ ## Settings for the kratos instrumentation -| Environment Variable | Type | Default | Description | -|------------------------------------------------------------|---------|---------|-----------------------------------------------------| -| `OTEL_INSTRUMENTATION_KRATOS_EXPERIMENTAL_SPAN_ATTRIBUTES` | Boolean | `false` | Enable the capture of experimental span attributes. | +| Environment Variable | Type | Default | Description | +|------------------------------------------------------------|---------|---------|------------------------------------------------------------| +| `OTEL_INSTRUMENTATION_KRATOS_EXPERIMENTAL_SPAN_ENABLE` | Boolean | `false` | Enable the capture of experimental kratos span attributes. | +| `OTEL_INSTRUMENTATION_NACOS_EXPERIMENTAL_METRICS_ENABLE` | Boolean | `false` | Enable the capture of experimental NACOSmetrics attributes.| diff --git a/docs/how-to-debug.md b/docs/how-to-debug.md index 3e4b2f5c..948d71a4 100644 --- a/docs/how-to-debug.md +++ b/docs/how-to-debug.md @@ -5,7 +5,7 @@ ## 1. Perform instrumentation with -debug options ```bash -$ ./otel -debug go build +$ ./otel set -debug ``` When using the `-debug` compilation option, the tool will compile an unoptimized binary diff --git a/docs/supported-libraries.md b/docs/supported-libraries.md index e26ea643..a2a3d612 100644 --- a/docs/supported-libraries.md +++ b/docs/supported-libraries.md @@ -20,6 +20,7 @@ | logrus | https://github.com/sirupsen/logrus | v1.5.0 | v1.9.3 | | mongodb | https://github.com/mongodb/mongo-go-driver | v1.11.1 | v1.15.2 | | mux | https://github.com/gorilla/mux | v1.3.0 | v1.8.1 | +| nacos | github.com/nacos-group/nacos-sdk-go/v2 | v2.0.0 | v2.2.7 | | net/http | https://pkg.go.dev/net/http | - | - | | redigo | https://github.com/gomodule/redigo | v1.9.0 | v1.9.2 | | slog | https://pkg.go.dev/log/slog | - | - | diff --git a/go.mod b/go.mod index c220a108..f0c35aff 100755 --- a/go.mod +++ b/go.mod @@ -23,6 +23,7 @@ require ( github.com/gomodule/redigo v1.9.0 github.com/gorilla/mux v1.8.1 github.com/labstack/echo/v4 v4.12.0 + github.com/nacos-group/nacos-sdk-go/v2 v2.0.0 github.com/prometheus/client_golang v1.16.0 github.com/redis/go-redis/v9 v9.6.1 github.com/rs/zerolog v1.10.0 @@ -50,15 +51,20 @@ require ( gorm.io/gorm v1.25.12 ) -require github.com/bytedance/gopkg v0.1.0 +require ( + github.com/bytedance/gopkg v0.1.0 + github.com/stretchr/testify v1.9.0 +) require ( dario.cat/mergo v1.0.0 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/aliyun/alibaba-cloud-sdk-go v1.61.18 // indirect github.com/andybalholm/brotli v1.1.0 // indirect github.com/apache/thrift v0.13.0 // indirect github.com/beorn7/perks v1.0.1 // indirect + github.com/buger/jsonparser v1.1.1 // indirect github.com/bytedance/go-tagexpr/v2 v2.9.2 // indirect github.com/bytedance/sonic v1.12.0 // indirect github.com/bytedance/sonic/loader v0.2.0 // indirect @@ -85,6 +91,7 @@ require ( github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-errors/errors v1.0.1 // indirect github.com/go-kratos/aegis v0.2.0 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect @@ -95,6 +102,7 @@ require ( github.com/go-playground/validator/v10 v10.20.0 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/mock v1.6.0 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/pprof v0.0.0-20220608213341-c488b8fa1db3 // indirect @@ -105,6 +113,7 @@ require ( github.com/jhump/protoreflect v1.8.2 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect + github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.17.9 // indirect github.com/klauspost/cpuid/v2 v2.2.7 // indirect @@ -139,12 +148,12 @@ require ( github.com/rivo/uniseg v0.2.0 // indirect github.com/shirou/gopsutil/v3 v3.23.12 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect - github.com/stretchr/testify v1.9.0 // indirect github.com/tidwall/gjson v1.14.4 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.0 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect + github.com/toolkits/concurrent v0.0.0-20150624120057-a4371d70e3e3 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect @@ -169,5 +178,7 @@ require ( google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28 // indirect google.golang.org/protobuf v1.35.1 // indirect + gopkg.in/ini.v1 v1.42.0 // indirect + gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 6859a6a2..b1996304 100644 --- a/go.sum +++ b/go.sum @@ -10,6 +10,7 @@ github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9 github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= 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/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= @@ -18,6 +19,8 @@ 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/aliyun/alibaba-cloud-sdk-go v1.61.18 h1:zOVTBdCKFd9JbCKz9/nt+FovbjPFmb7mUnp8nH9fQBA= +github.com/aliyun/alibaba-cloud-sdk-go v1.61.18/go.mod h1:v8ESoHo4SyHmuB4b1tJqDHxfTGEciD+yhvOU/5s1Rfk= github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= github.com/apache/thrift v0.13.0 h1:5hryIiq9gtn+MiLVn0wP37kb/uTeRZgN08WoCsAhIhI= @@ -30,6 +33,8 @@ github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= +github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= +github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/bytedance/go-tagexpr/v2 v2.9.2 h1:QySJaAIQgOEDQBLS3x9BxOWrnhqu5sQ+f6HaZIxD39I= github.com/bytedance/go-tagexpr/v2 v2.9.2/go.mod h1:5qsx05dYOiUXOUgnQ7w3Oz8BYs2qtM/bJokdLb79wRM= github.com/bytedance/gopkg v0.0.0-20220413063733-65bf48ffb3a7/go.mod h1:2ZlV9BaUH4+NXIBF0aMdKKAnHTzqH+iMU4KUjAbL23Q= @@ -136,6 +141,8 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= +github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= +github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks= github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= @@ -179,9 +186,11 @@ github.com/gofiber/fiber/v2 v2.52.5 h1:tWoP1MJQjGEe4GB5TUGOi7P2E0ZMMRx5ZTG4rT+yG github.com/gofiber/fiber/v2 v2.52.5/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= 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/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -239,6 +248,10 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= @@ -294,6 +307,7 @@ github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= @@ -302,6 +316,8 @@ github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8 github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/nacos-group/nacos-sdk-go/v2 v2.0.0 h1:QAT2461oFMkbxsSfzMwWcA9vH2pmvqx+Y4u6sGbfJRY= +github.com/nacos-group/nacos-sdk-go/v2 v2.0.0/go.mod h1:iTQOfCHajgZt8OVD9+2oCWSuO5QXgoYoniTs4yhkOW4= github.com/nishanths/predeclared v0.0.0-20200524104333-86fad755b4d3/go.mod h1:nt3d53pc1VYcphSCIaYAJtnPYnr3Zyn8fMq2wvPGPso= github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= @@ -368,6 +384,7 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -401,6 +418,8 @@ github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFA github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/toolkits/concurrent v0.0.0-20150624120057-a4371d70e3e3 h1:kF/7m/ZU+0D4Jj5eZ41Zm3IH/J8OElK1Qtd7tVKAwLk= +github.com/toolkits/concurrent v0.0.0-20150624120057-a4371d70e3e3/go.mod h1:QDlpd3qS71vYtakd2hmdpqhJ9nwv6mD6A30bQ1BPBFE= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= @@ -459,10 +478,14 @@ go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQD go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8= go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/arch v0.0.0-20201008161808-52c3e6f60cff/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4= @@ -498,6 +521,7 @@ golang.org/x/image v0.0.0-20220302094943-723b81ca9867/go.mod h1:023OzeP/+EPmXeap 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/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= @@ -605,8 +629,12 @@ golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGm 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-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -675,6 +703,10 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/ini.v1 v1.42.0 h1:7N3gPTt50s8GuLortA00n8AqRTk75qOP98+mTPpgzRk= +gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -694,6 +726,7 @@ gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= 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= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= diff --git a/pkg/data/default.json b/pkg/data/default.json index f4aa0b29..3e3d4545 100644 --- a/pkg/data/default.json +++ b/pkg/data/default.json @@ -489,6 +489,7 @@ "Path": "github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/rules/zap" }, { + "Version": "[1.44.0,1.63.0)", "ImportPath": "google.golang.org/grpc", "Function": "DialContext", "OnEnter": "grpcClientOnEnter", @@ -496,6 +497,7 @@ "Path": "github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/rules/grpc" }, { + "Version": "[1.63.0,)", "ImportPath": "google.golang.org/grpc", "Function": "NewClient", "OnEnter": "grpcNewClientOnEnter", @@ -503,6 +505,7 @@ "Path": "github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/rules/grpc" }, { + "Version": "[1.44.0,)", "ImportPath": "google.golang.org/grpc", "Function": "NewServer", "OnEnter": "grpcServerOnEnter", @@ -632,5 +635,126 @@ "Function": "NewServer", "OnEnter": "beforeNewKitexServerInstrument", "Path": "github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/rules/kitex" + }, + { + "Version": "[2.0.0,2.2.8)", + "ImportPath": "github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client", + "Function": "CloseClient", + "ReceiverType": "*NamingClient", + "OnEnter": "beforeNamingClientClose", + "Path": "github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/rules/nacos/service" + }, + { + "Version": "[2.0.0,2.1.0)", + "ImportPath": "github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client/naming_cache", + "Function": "NewServiceInfoHolder", + "OnEnter": "beforeNewServiceInfoHolder", + "OnExit": "afterNewServiceInfoHolder", + "Path": "github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/rules/nacos/service_holder" + }, + { + "Version": "[2.1.0,)", + "ImportPath": "github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client/naming_cache", + "Function": "NewServiceInfoHolder", + "OnEnter": "beforeNewServiceInfoHolder", + "OnExit": "afterNewServiceInfoHolder", + "Path": "github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/rules/nacos2_1_0/service_holder" + }, + { + "Version": "[2.0.0,2.2.8)", + "ImportPath": "github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client/naming_cache", + "StructType": "ServiceInfoHolder", + "FieldName": "OtelReg", + "FieldType": "interface{}" + }, + { + "Version": "[2.0.0,2.2.8)", + "ImportPath": "github.com/nacos-group/nacos-sdk-go/v2/clients/config_client", + "Function": "CloseClient", + "ReceiverType": "*ConfigClient", + "OnEnter": "beforeConfigClientClose", + "Path": "github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/rules/nacos/config" + }, + { + "Version": "[2.0.0,2.2.8)", + "ImportPath": "github.com/nacos-group/nacos-sdk-go/v2/clients/config_client", + "Function": "NewConfigClient", + "OnEnter": "beforeNewConfigClient", + "OnExit": "afterNewConfigClient", + "Path": "github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/rules/nacos/config" + }, + { + "Version": "[2.0.0,2.2.8)", + "ImportPath": "github.com/nacos-group/nacos-sdk-go/v2/clients/config_client", + "StructType": "ConfigClient", + "FieldName": "OtelReg", + "FieldType": "interface{}" + }, + { + "Version": "[2.0.0,2.2.8)", + "ImportPath": "github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client/naming_http", + "Function": "CloseClient", + "ReceiverType": "*NamingHttpProxy", + "OnEnter": "beforeNamingHttpProxyCloseClient", + "Path": "github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/rules/nacos/service" + }, + { + "Version": "[2.0.0,2.1.1)", + "ImportPath": "github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client/naming_http", + "Function": "NewBeatReactor", + "OnEnter": "beforeNewBeatReactor", + "OnExit": "afterNewBeatReactor", + "Path": "github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/rules/nacos/dom" + }, + { + "Version": "[2.1.1,)", + "ImportPath": "github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client/naming_http", + "Function": "NewBeatReactor", + "OnEnter": "beforeNewBeatReactor", + "OnExit": "afterNewBeatReactor", + "Path": "github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/rules/nacos2_1_1/dom" + }, + { + "Version": "[2.0.0,2.2.8)", + "ImportPath": "github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client/naming_http", + "StructType": "BeatReactor", + "FieldName": "OtelReg", + "FieldType": "interface{}" + }, + { + "Version": "[2.0.0,2.2.8)", + "ImportPath": "github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client/naming_grpc", + "Function": "requestToServer", + "ReceiverType": "*NamingGrpcProxy", + "OnEnter": "beforeRequestToServer", + "onExit": "afterRequestToServer", + "Path": "github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/rules/nacos/service" + }, + { + "Version": "[2.0.0,2.2.8)", + "ImportPath": "github.com/nacos-group/nacos-sdk-go/v2/clients/config_client", + "Function": "requestProxy", + "ReceiverType": "*ConfigProxy", + "OnEnter": "beforeRequestProxy", + "onExit": "afterRequestProxy", + "Path": "github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/rules/nacos/config" + }, + { + "Version": "[2.0.0,2.2.8)", + "ImportPath": "github.com/nacos-group/nacos-sdk-go/v2/common/nacos_server", + "Function": "callServer", + "ReceiverType": "*NacosServer", + "OnEnter": "beforeCallServer", + "onExit": "afterCallServer", + "Path": "github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/rules/nacos/service" + }, + { + "Version": "[2.0.0,2.2.8)", + "ImportPath": "github.com/nacos-group/nacos-sdk-go/v2/common/nacos_server", + "Function": "callConfigServer", + "ReceiverType": "*NacosServer", + "OnEnter": "beforeCallConfigServer", + "onExit": "afterCallConfigServer", + "Path": "github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/rules/nacos/config" } ] \ No newline at end of file diff --git a/pkg/inst-api-semconv/instrumenter/experimental/nacos_client_metrics.go b/pkg/inst-api-semconv/instrumenter/experimental/nacos_client_metrics.go new file mode 100644 index 00000000..b6db6142 --- /dev/null +++ b/pkg/inst-api-semconv/instrumenter/experimental/nacos_client_metrics.go @@ -0,0 +1,66 @@ +// Copyright (c) 2024 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package experimental + +import ( + "go.opentelemetry.io/otel/metric" + "log" + "os" +) + +var ( + ClientServiceInfoMapSize metric.Int64ObservableGauge + ClientConfigCacheMapSize metric.Int64ObservableGauge + ClientDomBeatMapSize metric.Int64ObservableGauge + ClientConfigRequestDuration metric.Float64Histogram + ClientNamingRequestDuration metric.Float64Histogram + GlobalMeter metric.Meter +) + +type nacosEnabler struct{} + +func (n nacosEnabler) Enable() bool { + return os.Getenv("OTEL_INSTRUMENTATION_NACOS_EXPERIMENTAL_METRICS_ENABLE") == "true" +} + +var NacosEnabler nacosEnabler + +func InitNacosExperimentalMetrics(m metric.Meter) { + GlobalMeter = m + if GlobalMeter == nil { + return + } + var err error + ClientServiceInfoMapSize, err = GlobalMeter.Int64ObservableGauge("nacos.client.serviceinfo.size", metric.WithDescription("Size of service info map")) + if err != nil { + log.Printf("failed to init ClientServiceInfoMapSize metrics") + } + ClientConfigCacheMapSize, err = GlobalMeter.Int64ObservableGauge("nacos.client.configinfo.size", metric.WithDescription("Size of config cache map")) + if err != nil { + log.Printf("failed to init ClientConfigCacheMapSize metrics") + } + ClientDomBeatMapSize, err = GlobalMeter.Int64ObservableGauge("nacos.client.dombeat.size", metric.WithDescription("Size of dom beat map")) + if err != nil { + log.Printf("failed to init ClientDomBeatMapSize metrics") + } + ClientConfigRequestDuration, err = GlobalMeter.Float64Histogram("nacos.client.config.request.duration", metric.WithDescription("Duration of config request")) + if err != nil { + log.Printf("failed to init ClientConfigRequestDuration metrics") + } + ClientNamingRequestDuration, err = GlobalMeter.Float64Histogram("nacos.client.naming.request.duration", metric.WithDescription("Duration of naming request")) + if err != nil { + log.Printf("failed to init ClientNamingRequestDuration metrics") + } +} diff --git a/pkg/inst-api-semconv/instrumenter/experimental/nacos_client_metrics_test.go b/pkg/inst-api-semconv/instrumenter/experimental/nacos_client_metrics_test.go new file mode 100644 index 00000000..3598adbf --- /dev/null +++ b/pkg/inst-api-semconv/instrumenter/experimental/nacos_client_metrics_test.go @@ -0,0 +1,57 @@ +// Copyright (c) 2024 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package experimental + +import ( + "go.opentelemetry.io/otel/sdk/metric" + "os" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestInitNacosExperimentalMetrics_GlobalMeterNil_NoMetricsInitialized(t *testing.T) { + InitNacosExperimentalMetrics(nil) + assert.Nil(t, ClientServiceInfoMapSize) + assert.Nil(t, ClientConfigCacheMapSize) + assert.Nil(t, ClientDomBeatMapSize) + assert.Nil(t, ClientConfigRequestDuration) + assert.Nil(t, ClientNamingRequestDuration) +} + +func TestInitNacosExperimentalMetrics_GlobalMeterNotNull_AllMetricsInitialized(t *testing.T) { + mp := metric.NewMeterProvider() + InitNacosExperimentalMetrics(mp.Meter("a")) + assert.NotNil(t, ClientServiceInfoMapSize) + assert.NotNil(t, ClientConfigCacheMapSize) + assert.NotNil(t, ClientDomBeatMapSize) + assert.NotNil(t, ClientConfigRequestDuration) + assert.NotNil(t, ClientNamingRequestDuration) +} + +func TestNacosEnablerDisable(t *testing.T) { + ne := nacosEnabler{} + if ne.Enable() { + panic("should not enable without OTEL_INSTRUMENTATION_NACOS_EXPERIMENTAL_METRICS_ENABLE") + } +} + +func TestNacosEnablerEnable(t *testing.T) { + os.Setenv("OTEL_INSTRUMENTATION_NACOS_EXPERIMENTAL_METRICS_ENABLE", "true") + ne := nacosEnabler{} + if !ne.Enable() { + panic("should enable with OTEL_INSTRUMENTATION_NACOS_EXPERIMENTAL_METRICS_ENABLE") + } +} diff --git a/pkg/inst-api-semconv/instrumenter/http/http_metrics.go b/pkg/inst-api-semconv/instrumenter/http/http_metrics.go index 52db6b0e..d1aab6e8 100644 --- a/pkg/inst-api-semconv/instrumenter/http/http_metrics.go +++ b/pkg/inst-api-semconv/instrumenter/http/http_metrics.go @@ -18,7 +18,7 @@ import ( "context" "errors" "fmt" - "github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/core/meter" + "github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/inst-api-semconv/instrumenter/utils" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" semconv "go.opentelemetry.io/otel/semconv/v1.26.0" @@ -42,9 +42,6 @@ type HttpClientMetric struct { } var mu sync.Mutex -var httpServerOperationListener *HttpServerMetric -var httpClientOperationListener *HttpClientMetric -var shadower = httpMetricAttributesShadower{} var httpMetricsConv = map[attribute.Key]bool{ semconv.HTTPRequestMethodKey: true, @@ -58,41 +55,30 @@ var httpMetricsConv = map[attribute.Key]bool{ semconv.ServerPortKey: true, } +var globalMeter metric.Meter + // InitHttpMetrics TODO: The init function may be executed after the HttpServerOperationListener() method // so we need to make sure the otel_setup is executed before all the init() function // related to issue https://github.com/alibaba/opentelemetry-go-auto-instrumentation/issues/48 -func InitHttpMetrics(meter metric.Meter) { - var err error - httpServerOperationListener, err = newHttpServerMetric("net.http.server", meter) - if err != nil { - panic(err) - } - httpClientOperationListener, err = newHttpClientMetric("net.http.client", meter) - if err != nil { - panic(err) - } +func InitHttpMetrics(m metric.Meter) { + mu.Lock() + defer mu.Unlock() + globalMeter = m } -func HttpServerMetrics() *HttpServerMetric { +func HttpServerMetrics(key string) *HttpServerMetric { mu.Lock() defer mu.Unlock() - if httpServerOperationListener != nil { - return httpServerOperationListener - } else { - return &HttpServerMetric{} - } + return &HttpServerMetric{key: attribute.Key(key)} } -func HttpClientMetrics() *HttpClientMetric { +func HttpClientMetrics(key string) *HttpClientMetric { mu.Lock() defer mu.Unlock() - if httpClientOperationListener != nil { - return httpClientOperationListener - } else { - return &HttpClientMetric{} - } + return &HttpClientMetric{key: attribute.Key(key)} } +// for test only func newHttpServerMetric(key string, meter metric.Meter) (*HttpServerMetric, error) { m := &HttpServerMetric{ key: attribute.Key(key), @@ -121,6 +107,7 @@ func newHttpServerRequestDurationMeasures(meter metric.Meter) (metric.Float64His } } +// for test only func newHttpClientMetric(key string, meter metric.Meter) (*HttpClientMetric, error) { m := &HttpClientMetric{ key: attribute.Key(key), @@ -175,13 +162,13 @@ func (h *HttpServerMetric) OnAfterEnd(context context.Context, endAttributes []a // end attributes should be shadowed by AttrsShadower if h.serverRequestDuration == nil { var err error - h.serverRequestDuration, err = newHttpServerRequestDurationMeasures(meter.GetMeter()) + h.serverRequestDuration, err = newHttpServerRequestDurationMeasures(globalMeter) if err != nil { log.Printf("failed to create serverRequestDuration, err is %v\n", err) } } endAttributes = append(endAttributes, startAttributes...) - n, metricsAttrs := shadower.Shadow(endAttributes) + n, metricsAttrs := utils.Shadow(endAttributes, httpMetricsConv) if h.serverRequestDuration != nil { h.serverRequestDuration.Record(context, float64(endTime.Sub(startTime)), metric.WithAttributeSet(attribute.NewSet(metricsAttrs[0:n]...))) } @@ -209,34 +196,14 @@ func (h HttpClientMetric) OnAfterEnd(context context.Context, endAttributes []at if h.clientRequestDuration == nil { var err error // second change to init the metric - h.clientRequestDuration, err = newHttpClientRequestDurationMeasures(meter.GetMeter()) + h.clientRequestDuration, err = newHttpClientRequestDurationMeasures(globalMeter) if err != nil { log.Printf("failed to create clientRequestDuration, err is %v\n", err) } } endAttributes = append(endAttributes, startAttributes...) - n, metricsAttrs := shadower.Shadow(endAttributes) + n, metricsAttrs := utils.Shadow(endAttributes, httpMetricsConv) if h.clientRequestDuration != nil { h.clientRequestDuration.Record(context, float64(endTime.Sub(startTime)), metric.WithAttributeSet(attribute.NewSet(metricsAttrs[0:n]...))) } } - -type httpMetricAttributesShadower struct{} - -func (h httpMetricAttributesShadower) Shadow(attrs []attribute.KeyValue) (int, []attribute.KeyValue) { - swap := func(attrs []attribute.KeyValue, i, j int) { - tmp := attrs[i] - attrs[i] = attrs[j] - attrs[j] = tmp - } - index := 0 - for i, attr := range attrs { - if _, ok := httpMetricsConv[attr.Key]; ok { - if index != i { - swap(attrs, i, index) - } - index++ - } - } - return index, attrs -} diff --git a/pkg/inst-api-semconv/instrumenter/http/http_metrics_test.go b/pkg/inst-api-semconv/instrumenter/http/http_metrics_test.go index f041f175..aa9ab163 100644 --- a/pkg/inst-api-semconv/instrumenter/http/http_metrics_test.go +++ b/pkg/inst-api-semconv/instrumenter/http/http_metrics_test.go @@ -16,7 +16,7 @@ package http import ( "context" - "github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/core/meter" + "github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/inst-api-semconv/instrumenter/utils" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/sdk/metric" "go.opentelemetry.io/otel/sdk/metric/metricdata" @@ -79,7 +79,6 @@ func TestHttpClientMetrics(t *testing.T) { } func TestHttpMetricAttributesShadower(t *testing.T) { - s := httpMetricAttributesShadower{} attrs := make([]attribute.KeyValue, 0) attrs = append(attrs, attribute.KeyValue{ Key: semconv.HTTPRequestMethodKey, @@ -94,7 +93,7 @@ func TestHttpMetricAttributesShadower(t *testing.T) { Key: semconv.ServerPortKey, Value: attribute.IntValue(8080), }) - n, attrs := s.Shadow(attrs) + n, attrs := utils.Shadow(attrs, httpMetricsConv) if n != 3 { panic("wrong shadow array") } @@ -112,8 +111,8 @@ func TestLazyHttpServerMetrics(t *testing.T) { ) mp := metric.NewMeterProvider(metric.WithResource(res), metric.WithReader(reader)) m := mp.Meter("test-meter") - meter.SetMeter(m) - server := HttpServerMetrics() + InitHttpMetrics(m) + server := HttpServerMetrics("net.http.server") ctx := context.Background() start := time.Now() ctx = server.OnBeforeStart(ctx, start) @@ -136,8 +135,8 @@ func TestLazyHttpClientMetrics(t *testing.T) { ) mp := metric.NewMeterProvider(metric.WithResource(res), metric.WithReader(reader)) m := mp.Meter("test-meter") - meter.SetMeter(m) - client := HttpClientMetrics() + InitHttpMetrics(m) + client := HttpClientMetrics("net.http.client") ctx := context.Background() start := time.Now() ctx = client.OnBeforeStart(ctx, start) @@ -161,7 +160,7 @@ func TestGlobalHttpServerMetrics(t *testing.T) { mp := metric.NewMeterProvider(metric.WithResource(res), metric.WithReader(reader)) m := mp.Meter("test-meter") InitHttpMetrics(m) - server := HttpServerMetrics() + server := HttpServerMetrics("net.http.server") ctx := context.Background() start := time.Now() ctx = server.OnBeforeStart(ctx, start) @@ -185,7 +184,7 @@ func TestGlobalHttpClientMetrics(t *testing.T) { mp := metric.NewMeterProvider(metric.WithResource(res), metric.WithReader(reader)) m := mp.Meter("test-meter") InitHttpMetrics(m) - client := HttpClientMetrics() + client := HttpClientMetrics("net.http.client") ctx := context.Background() start := time.Now() ctx = client.OnBeforeStart(ctx, start) diff --git a/pkg/inst-api-semconv/instrumenter/rpc/rpc_attrs_extractor.go b/pkg/inst-api-semconv/instrumenter/rpc/rpc_attrs_extractor.go index 9581bb2b..3a6dd956 100644 --- a/pkg/inst-api-semconv/instrumenter/rpc/rpc_attrs_extractor.go +++ b/pkg/inst-api-semconv/instrumenter/rpc/rpc_attrs_extractor.go @@ -35,6 +35,9 @@ func (r *RpcAttrsExtractor[REQUEST, RESPONSE, GETTER]) OnStart(attributes []attr }, attribute.KeyValue{ Key: semconv.RPCMethodKey, Value: attribute.StringValue(r.Getter.GetMethod(request)), + }, attribute.KeyValue{ + Key: semconv.ServerAddressKey, + Value: attribute.StringValue(r.Getter.GetServerAddress(request)), }) return attributes, parentContext } diff --git a/pkg/inst-api-semconv/instrumenter/rpc/rpc_attrs_extractor_test.go b/pkg/inst-api-semconv/instrumenter/rpc/rpc_attrs_extractor_test.go index 0352c597..1ba69648 100644 --- a/pkg/inst-api-semconv/instrumenter/rpc/rpc_attrs_extractor_test.go +++ b/pkg/inst-api-semconv/instrumenter/rpc/rpc_attrs_extractor_test.go @@ -41,6 +41,10 @@ func (h rpcAttrsGetter) GetMethod(request testRequest) string { return "method" } +func (h rpcAttrsGetter) GetServerAddress(request testRequest) string { + return "serverAddress" +} + func TestClientGetSpanKey(t *testing.T) { rpcExtractor := &ClientRpcAttrsExtractor[testRequest, any, rpcAttrsGetter]{} if rpcExtractor.GetSpanKey() != utils.RPC_CLIENT_KEY { diff --git a/pkg/inst-api-semconv/instrumenter/rpc/rpc_attrs_getter.go b/pkg/inst-api-semconv/instrumenter/rpc/rpc_attrs_getter.go index 432885de..a1c9fea1 100644 --- a/pkg/inst-api-semconv/instrumenter/rpc/rpc_attrs_getter.go +++ b/pkg/inst-api-semconv/instrumenter/rpc/rpc_attrs_getter.go @@ -18,4 +18,5 @@ type RpcAttrsGetter[REQUEST any] interface { GetSystem(request REQUEST) string GetService(request REQUEST) string GetMethod(request REQUEST) string + GetServerAddress(request REQUEST) string } diff --git a/pkg/inst-api-semconv/instrumenter/rpc/rpc_metrics.go b/pkg/inst-api-semconv/instrumenter/rpc/rpc_metrics.go new file mode 100644 index 00000000..5db10383 --- /dev/null +++ b/pkg/inst-api-semconv/instrumenter/rpc/rpc_metrics.go @@ -0,0 +1,206 @@ +// Copyright (c) 2024 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// rpc://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package rpc + +import ( + "context" + "errors" + "fmt" + "github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/inst-api-semconv/instrumenter/utils" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric" + semconv "go.opentelemetry.io/otel/semconv/v1.26.0" + "log" + "sync" + "time" +) + +const rpc_server_request_duration = "rpc.server.duration" + +const rpc_client_request_duration = "rpc.client.duration" + +type RpcServerMetric struct { + key attribute.Key + serverRequestDuration metric.Float64Histogram +} + +type RpcClientMetric struct { + key attribute.Key + clientRequestDuration metric.Float64Histogram +} + +var mu sync.Mutex + +var rpcMetricsConv = map[attribute.Key]bool{ + semconv.RPCSystemKey: true, + semconv.RPCMethodKey: true, + semconv.RPCServiceKey: true, + semconv.ServerAddressKey: true, +} + +var globalMeter metric.Meter + +// so we need to make sure the otel_setup is executed before all the init() function +// related to issue rpcs://github.com/alibaba/opentelemetry-go-auto-instrumentation/issues/48 +func InitRpcMetrics(m metric.Meter) { + mu.Lock() + defer mu.Unlock() + globalMeter = m +} + +func RpcServerMetrics(key string) *RpcServerMetric { + mu.Lock() + defer mu.Unlock() + return &RpcServerMetric{key: attribute.Key(key)} +} + +func RpcClientMetrics(key string) *RpcClientMetric { + mu.Lock() + defer mu.Unlock() + return &RpcClientMetric{key: attribute.Key(key)} +} + +func newRpcServerRequestDurationMeasures(meter metric.Meter) (metric.Float64Histogram, error) { + mu.Lock() + defer mu.Unlock() + if meter == nil { + return nil, errors.New("nil meter") + } + d, err := meter.Float64Histogram(rpc_server_request_duration, + metric.WithUnit("ms"), + metric.WithDescription("Duration of rpc server requests.")) + if err == nil { + return d, nil + } else { + return d, errors.New(fmt.Sprintf("failed to create rpc.server.request.duratio histogram, %v", err)) + } +} + +func newRpcClientRequestDurationMeasures(meter metric.Meter) (metric.Float64Histogram, error) { + mu.Lock() + defer mu.Unlock() + if meter == nil { + return nil, errors.New("nil meter") + } + d, err := meter.Float64Histogram(rpc_client_request_duration, + metric.WithUnit("ms"), + metric.WithDescription("Duration of rpc client requests.")) + if err == nil { + return d, nil + } else { + return d, errors.New(fmt.Sprintf("failed to create rpc.client.request.duratio histogram, %v", err)) + } +} + +type rpcMetricContext struct { + startTime time.Time + startAttributes []attribute.KeyValue +} + +func (h *RpcServerMetric) OnBeforeStart(parentContext context.Context, startTime time.Time) context.Context { + return parentContext +} + +func (h *RpcServerMetric) OnBeforeEnd(ctx context.Context, startAttributes []attribute.KeyValue, startTime time.Time) context.Context { + return context.WithValue(ctx, h.key, rpcMetricContext{ + startTime: startTime, + startAttributes: startAttributes, + }) +} + +func (h *RpcServerMetric) OnAfterStart(context context.Context, endTime time.Time) { + return +} + +func (h *RpcServerMetric) OnAfterEnd(context context.Context, endAttributes []attribute.KeyValue, endTime time.Time) { + mc := context.Value(h.key).(rpcMetricContext) + startTime, startAttributes := mc.startTime, mc.startAttributes + // end attributes should be shadowed by AttrsShadower + if h.serverRequestDuration == nil { + var err error + h.serverRequestDuration, err = newRpcServerRequestDurationMeasures(globalMeter) + if err != nil { + log.Printf("failed to create serverRequestDuration, err is %v\n", err) + } + } + endAttributes = append(endAttributes, startAttributes...) + n, metricsAttrs := utils.Shadow(endAttributes, rpcMetricsConv) + if h.serverRequestDuration != nil { + h.serverRequestDuration.Record(context, float64(endTime.Sub(startTime)), metric.WithAttributeSet(attribute.NewSet(metricsAttrs[0:n]...))) + } +} + +func (h *RpcClientMetric) OnBeforeStart(parentContext context.Context, startTime time.Time) context.Context { + return parentContext +} + +func (h *RpcClientMetric) OnBeforeEnd(ctx context.Context, startAttributes []attribute.KeyValue, startTime time.Time) context.Context { + return context.WithValue(ctx, h.key, rpcMetricContext{ + startTime: startTime, + startAttributes: startAttributes, + }) +} + +func (h *RpcClientMetric) OnAfterStart(context context.Context, endTime time.Time) { + return +} + +func (h *RpcClientMetric) OnAfterEnd(context context.Context, endAttributes []attribute.KeyValue, endTime time.Time) { + if context.Value(h.key) == nil { + return + } + mc := context.Value(h.key).(rpcMetricContext) + startTime, startAttributes := mc.startTime, mc.startAttributes + // end attributes should be shadowed by AttrsShadower + if h.clientRequestDuration == nil { + var err error + // second change to init the metric + h.clientRequestDuration, err = newRpcClientRequestDurationMeasures(globalMeter) + if err != nil { + log.Printf("failed to create clientRequestDuration, err is %v\n", err) + } + } + endAttributes = append(endAttributes, startAttributes...) + n, metricsAttrs := utils.Shadow(endAttributes, rpcMetricsConv) + if h.clientRequestDuration != nil { + h.clientRequestDuration.Record(context, float64(endTime.Sub(startTime)), metric.WithAttributeSet(attribute.NewSet(metricsAttrs[0:n]...))) + } +} + +// for test only +func newRpcServerMetric(key string, meter metric.Meter) (*RpcServerMetric, error) { + m := &RpcServerMetric{ + key: attribute.Key(key), + } + d, err := newRpcServerRequestDurationMeasures(meter) + if err != nil { + return nil, err + } + m.serverRequestDuration = d + return m, nil +} + +// for test only +func newRpcClientMetric(key string, meter metric.Meter) (*RpcClientMetric, error) { + m := &RpcClientMetric{ + key: attribute.Key(key), + } + d, err := newRpcClientRequestDurationMeasures(meter) + if err != nil { + return nil, err + } + m.clientRequestDuration = d + return m, nil +} diff --git a/pkg/inst-api-semconv/instrumenter/rpc/rpc_metrics_test.go b/pkg/inst-api-semconv/instrumenter/rpc/rpc_metrics_test.go new file mode 100644 index 00000000..2addda47 --- /dev/null +++ b/pkg/inst-api-semconv/instrumenter/rpc/rpc_metrics_test.go @@ -0,0 +1,199 @@ +// Copyright (c) 2024 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// rpc://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package rpc + +import ( + "context" + "github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/inst-api-semconv/instrumenter/utils" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/sdk/metric" + "go.opentelemetry.io/otel/sdk/metric/metricdata" + "go.opentelemetry.io/otel/sdk/resource" + semconv "go.opentelemetry.io/otel/semconv/v1.26.0" + "testing" + "time" +) + +func TestRpcServerMetrics(t *testing.T) { + reader := metric.NewManualReader() + res := resource.NewWithAttributes( + semconv.SchemaURL, + semconv.ServiceName("my-service"), + semconv.ServiceVersion("v0.1.0"), + ) + mp := metric.NewMeterProvider(metric.WithResource(res), metric.WithReader(reader)) + meter := mp.Meter("test-meter") + server, err := newRpcServerMetric("test", meter) + if err != nil { + panic(err) + } + ctx := context.Background() + start := time.Now() + ctx = server.OnBeforeStart(ctx, start) + ctx = server.OnBeforeEnd(ctx, []attribute.KeyValue{}, start) + server.OnAfterStart(ctx, start) + server.OnAfterEnd(ctx, []attribute.KeyValue{}, time.Now()) + rm := &metricdata.ResourceMetrics{} + reader.Collect(ctx, rm) + if rm.ScopeMetrics[0].Metrics[0].Name != "rpc.server.duration" { + panic("wrong metrics name, " + rm.ScopeMetrics[0].Metrics[0].Name) + } +} + +func TestRpcClientMetrics(t *testing.T) { + reader := metric.NewManualReader() + res := resource.NewWithAttributes( + semconv.SchemaURL, + semconv.ServiceName("my-service"), + semconv.ServiceVersion("v0.1.0"), + ) + mp := metric.NewMeterProvider(metric.WithResource(res), metric.WithReader(reader)) + meter := mp.Meter("test-meter") + client, err := newRpcClientMetric("test", meter) + if err != nil { + panic(err) + } + ctx := context.Background() + start := time.Now() + ctx = client.OnBeforeStart(ctx, start) + ctx = client.OnBeforeEnd(ctx, []attribute.KeyValue{}, start) + client.OnAfterStart(ctx, start) + client.OnAfterEnd(ctx, []attribute.KeyValue{}, time.Now()) + rm := &metricdata.ResourceMetrics{} + reader.Collect(ctx, rm) + if rm.ScopeMetrics[0].Metrics[0].Name != "rpc.client.duration" { + panic("wrong metrics name, " + rm.ScopeMetrics[0].Metrics[0].Name) + } +} + +func TestRpcMetricAttributesShadower(t *testing.T) { + attrs := make([]attribute.KeyValue, 0) + attrs = append(attrs, attribute.KeyValue{ + Key: semconv.RPCMethodKey, + Value: attribute.StringValue("method"), + }, attribute.KeyValue{ + Key: "unknown", + Value: attribute.Value{}, + }, attribute.KeyValue{ + Key: semconv.RPCServiceKey, + Value: attribute.StringValue("rpc"), + }, attribute.KeyValue{ + Key: semconv.RPCSystemKey, + Value: attribute.StringValue("abc"), + }) + n, attrs := utils.Shadow(attrs, rpcMetricsConv) + if n != 3 { + panic("wrong shadow array") + } + if attrs[3].Key != "unknown" { + panic("unknown should be the last attribute") + } +} + +func TestLazyRpcServerMetrics(t *testing.T) { + reader := metric.NewManualReader() + res := resource.NewWithAttributes( + semconv.SchemaURL, + semconv.ServiceName("my-service"), + semconv.ServiceVersion("v0.1.0"), + ) + mp := metric.NewMeterProvider(metric.WithResource(res), metric.WithReader(reader)) + m := mp.Meter("test-meter") + InitRpcMetrics(m) + server := RpcServerMetrics("net.rpc.server") + ctx := context.Background() + start := time.Now() + ctx = server.OnBeforeStart(ctx, start) + ctx = server.OnBeforeEnd(ctx, []attribute.KeyValue{}, start) + server.OnAfterStart(ctx, start) + server.OnAfterEnd(ctx, []attribute.KeyValue{}, time.Now()) + rm := &metricdata.ResourceMetrics{} + reader.Collect(ctx, rm) + if rm.ScopeMetrics[0].Metrics[0].Name != "rpc.server.duration" { + panic("wrong metrics name, " + rm.ScopeMetrics[0].Metrics[0].Name) + } +} + +func TestLazyRpcClientMetrics(t *testing.T) { + reader := metric.NewManualReader() + res := resource.NewWithAttributes( + semconv.SchemaURL, + semconv.ServiceName("my-service"), + semconv.ServiceVersion("v0.1.0"), + ) + mp := metric.NewMeterProvider(metric.WithResource(res), metric.WithReader(reader)) + m := mp.Meter("test-meter") + InitRpcMetrics(m) + client := RpcClientMetrics("net.rpc.client") + ctx := context.Background() + start := time.Now() + ctx = client.OnBeforeStart(ctx, start) + ctx = client.OnBeforeEnd(ctx, []attribute.KeyValue{}, start) + client.OnAfterStart(ctx, start) + client.OnAfterEnd(ctx, []attribute.KeyValue{}, time.Now()) + rm := &metricdata.ResourceMetrics{} + reader.Collect(ctx, rm) + if rm.ScopeMetrics[0].Metrics[0].Name != "rpc.client.duration" { + panic("wrong metrics name, " + rm.ScopeMetrics[0].Metrics[0].Name) + } +} + +func TestGlobalRpcServerMetrics(t *testing.T) { + reader := metric.NewManualReader() + res := resource.NewWithAttributes( + semconv.SchemaURL, + semconv.ServiceName("my-service"), + semconv.ServiceVersion("v0.1.0"), + ) + mp := metric.NewMeterProvider(metric.WithResource(res), metric.WithReader(reader)) + m := mp.Meter("test-meter") + InitRpcMetrics(m) + server := RpcServerMetrics("net.rpc.server") + ctx := context.Background() + start := time.Now() + ctx = server.OnBeforeStart(ctx, start) + ctx = server.OnBeforeEnd(ctx, []attribute.KeyValue{}, start) + server.OnAfterStart(ctx, start) + server.OnAfterEnd(ctx, []attribute.KeyValue{}, time.Now()) + rm := &metricdata.ResourceMetrics{} + reader.Collect(ctx, rm) + if rm.ScopeMetrics[0].Metrics[0].Name != "rpc.server.duration" { + panic("wrong metrics name, " + rm.ScopeMetrics[0].Metrics[0].Name) + } +} + +func TestGlobalRpcClientMetrics(t *testing.T) { + reader := metric.NewManualReader() + res := resource.NewWithAttributes( + semconv.SchemaURL, + semconv.ServiceName("my-service"), + semconv.ServiceVersion("v0.1.0"), + ) + mp := metric.NewMeterProvider(metric.WithResource(res), metric.WithReader(reader)) + m := mp.Meter("test-meter") + InitRpcMetrics(m) + client := RpcClientMetrics("net.rpc.client") + ctx := context.Background() + start := time.Now() + ctx = client.OnBeforeStart(ctx, start) + ctx = client.OnBeforeEnd(ctx, []attribute.KeyValue{}, start) + client.OnAfterStart(ctx, start) + client.OnAfterEnd(ctx, []attribute.KeyValue{}, time.Now()) + rm := &metricdata.ResourceMetrics{} + reader.Collect(ctx, rm) + if rm.ScopeMetrics[0].Metrics[0].Name != "rpc.client.duration" { + panic("wrong metrics name, " + rm.ScopeMetrics[0].Metrics[0].Name) + } +} diff --git a/pkg/inst-api-semconv/instrumenter/rpc/rpc_span_name_extractor_test.go b/pkg/inst-api-semconv/instrumenter/rpc/rpc_span_name_extractor_test.go index c60ae0ea..ed5a8bb3 100644 --- a/pkg/inst-api-semconv/instrumenter/rpc/rpc_span_name_extractor_test.go +++ b/pkg/inst-api-semconv/instrumenter/rpc/rpc_span_name_extractor_test.go @@ -46,6 +46,10 @@ func (t testGetter) GetMethod(request testRequest) string { return "" } +func (t testGetter) GetServerAddress(request testRequest) string { + return "test" +} + func TestExtractSpanName(t *testing.T) { r := RpcSpanNameExtractor[testRequest]{Getter: testGetter{}} spanName := r.Extract(testRequest{Method: "method", Service: "service"}) diff --git a/pkg/inst-api-semconv/instrumenter/utils/metrics_shadower.go b/pkg/inst-api-semconv/instrumenter/utils/metrics_shadower.go new file mode 100644 index 00000000..a5c4558a --- /dev/null +++ b/pkg/inst-api-semconv/instrumenter/utils/metrics_shadower.go @@ -0,0 +1,37 @@ +// Copyright (c) 2024 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// rpc://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package utils + +import ( + "go.opentelemetry.io/otel/attribute" +) + +func Shadow(attrs []attribute.KeyValue, metricsSemConv map[attribute.Key]bool) (int, []attribute.KeyValue) { + swap := func(attrs []attribute.KeyValue, i, j int) { + tmp := attrs[i] + attrs[i] = attrs[j] + attrs[j] = tmp + } + index := 0 + for i, attr := range attrs { + if _, ok := metricsSemConv[attr.Key]; ok { + if index != i { + swap(attrs, i, index) + } + index++ + } + } + return index, attrs +} diff --git a/pkg/otel_setup.go b/pkg/otel_setup.go index c6df5fe8..f96a4799 100644 --- a/pkg/otel_setup.go +++ b/pkg/otel_setup.go @@ -18,6 +18,8 @@ import ( "context" "errors" "fmt" + "github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/inst-api-semconv/instrumenter/experimental" + "github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/inst-api-semconv/instrumenter/rpc" "github.com/prometheus/client_golang/prometheus/promhttp" "go.opentelemetry.io/otel/exporters/prometheus" "log" @@ -157,6 +159,10 @@ func initMetrics() error { meter.SetMeter(m) // init http metrics http.InitHttpMetrics(m) + // init rpc metrics + rpc.InitRpcMetrics(m) + // nacos experimental metrics + experimental.InitNacosExperimentalMetrics(m) // DefaultMinimumReadMemStatsInterval is 15 second return runtime.Start(runtime.WithMeterProvider(mp)) } diff --git a/pkg/rules/grpc/grpc_client_setup.go b/pkg/rules/grpc/grpc_client_setup.go index b5aefd74..829645df 100644 --- a/pkg/rules/grpc/grpc_client_setup.go +++ b/pkg/rules/grpc/grpc_client_setup.go @@ -41,6 +41,7 @@ func grpcClientOnExit(call api.CallContext, cc *grpc.ClientConn, err error) { } type clientHandler struct { + serverAddr string *grpcOtelConfig } @@ -55,7 +56,8 @@ func NewClientHandler(opts ...Option) stats.Handler { // TagRPC can attach some information to the given context. func (h *clientHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo) context.Context { nCtx := grpcClientInstrument.Start(ctx, grpcRequest{ - methodName: info.FullMethodName, + methodName: info.FullMethodName, + serverAddress: h.serverAddr, }) gctx := gRPCContext{ methodName: info.FullMethodName, @@ -66,12 +68,12 @@ func (h *clientHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo) cont // HandleRPC processes the RPC stats. func (h *clientHandler) HandleRPC(ctx context.Context, rs stats.RPCStats) { - isServer := false - h.handleRPC(ctx, rs, isServer) + h.handleRPC(ctx, rs, false) } // TagConn can attach some information to the given context. func (h *clientHandler) TagConn(ctx context.Context, info *stats.ConnTagInfo) context.Context { + h.serverAddr = info.RemoteAddr.String() return ctx } diff --git a/pkg/rules/grpc/grpc_config.go b/pkg/rules/grpc/grpc_config.go index 355e75f3..8974e345 100644 --- a/pkg/rules/grpc/grpc_config.go +++ b/pkg/rules/grpc/grpc_config.go @@ -52,7 +52,6 @@ func (c *grpcOtelConfig) handleRPC(ctx context.Context, rs stats.RPCStats, isSer if span == nil { return } - //var metricAttrs []coreAttr.KeyValue var ( messageId int64 ) diff --git a/pkg/rules/grpc/grpc_data_type.go b/pkg/rules/grpc/grpc_data_type.go index d3d26fb6..43686a75 100644 --- a/pkg/rules/grpc/grpc_data_type.go +++ b/pkg/rules/grpc/grpc_data_type.go @@ -21,8 +21,9 @@ import ( var grpcClientInstrument = BuildGrpcClientInstrumenter() type grpcRequest struct { - methodName string - propagators propagation.TextMapCarrier + methodName string + serverAddress string + propagators propagation.TextMapCarrier } type grpcResponse struct { diff --git a/pkg/rules/grpc/grpc_new_client_setup.go b/pkg/rules/grpc/grpc_new_client_setup.go index 35cb91fe..6866b48f 100644 --- a/pkg/rules/grpc/grpc_new_client_setup.go +++ b/pkg/rules/grpc/grpc_new_client_setup.go @@ -35,6 +35,7 @@ func grpcNewClientOnExit(call api.CallContext, cc *grpc.ClientConn, err error) { } type clientNewHandler struct { + serverAddr string *grpcOtelConfig } @@ -49,7 +50,8 @@ func NewClientNewHandler(opts ...Option) stats.Handler { // TagRPC can attach some information to the given context. func (h *clientNewHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo) context.Context { nCtx := grpcClientInstrument.Start(ctx, grpcRequest{ - methodName: info.FullMethodName, + methodName: info.FullMethodName, + serverAddress: h.serverAddr, }) gctx := gRPCContext{ methodName: info.FullMethodName, @@ -59,12 +61,12 @@ func (h *clientNewHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo) c // HandleRPC processes the RPC stats. func (h *clientNewHandler) HandleRPC(ctx context.Context, rs stats.RPCStats) { - isServer := false - h.handleRPC(ctx, rs, isServer) + h.handleRPC(ctx, rs, false) } // TagConn can attach some information to the given context. func (h *clientNewHandler) TagConn(ctx context.Context, info *stats.ConnTagInfo) context.Context { + h.serverAddr = info.RemoteAddr.String() return ctx } diff --git a/pkg/rules/grpc/grpc_otel_instrumenter.go b/pkg/rules/grpc/grpc_otel_instrumenter.go index a32fa50a..e1ad1e02 100644 --- a/pkg/rules/grpc/grpc_otel_instrumenter.go +++ b/pkg/rules/grpc/grpc_otel_instrumenter.go @@ -54,6 +54,10 @@ func (g grpcAttrsGetter) GetMethod(request grpcRequest) string { return fullMethodName[slashIndex+1:] } +func (g grpcAttrsGetter) GetServerAddress(request grpcRequest) string { + return request.serverAddress +} + type grpcStatusCodeExtractor[REQUEST grpcRequest, RESPONSE grpcResponse] struct { } @@ -79,6 +83,7 @@ func BuildGrpcClientInstrumenter() instrumenter.Instrumenter[grpcRequest, grpcRe Name: utils.GRPC_CLIENT_SCOPE_NAME, Version: version.Tag, }). + AddOperationListeners(rpc.RpcClientMetrics("grpc.client")). BuildInstrumenter() } @@ -92,5 +97,6 @@ func BuildGrpcServerInstrumenter() instrumenter.Instrumenter[grpcRequest, grpcRe Name: utils.GRPC_SERVER_SCOPE_NAME, Version: version.Tag, }). + AddOperationListeners(rpc.RpcServerMetrics("grpc.server")). BuildInstrumenter() } diff --git a/pkg/rules/grpc/grpc_server_setup.go b/pkg/rules/grpc/grpc_server_setup.go index 840f2372..0d32da8d 100644 --- a/pkg/rules/grpc/grpc_server_setup.go +++ b/pkg/rules/grpc/grpc_server_setup.go @@ -52,11 +52,13 @@ func NewServerHandler(opts ...Option) stats.Handler { } type serverHandler struct { + serverAddr string *grpcOtelConfig } // TagConn can attach some information to the given context. func (h *serverHandler) TagConn(ctx context.Context, info *stats.ConnTagInfo) context.Context { + h.serverAddr = info.LocalAddr.String() return ctx } @@ -73,7 +75,8 @@ func (h *serverHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo) cont md = metadata.MD{} } nCtx := grpcServerInstrument.Start(ctx, grpcRequest{ - methodName: info.FullMethodName, + methodName: info.FullMethodName, + serverAddress: h.serverAddr, propagators: &grpcMetadataSupplier{ metadata: &md, }, @@ -88,6 +91,5 @@ func (h *serverHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo) cont // HandleRPC processes the RPC stats. func (h *serverHandler) HandleRPC(ctx context.Context, rs stats.RPCStats) { - isServer := true - h.handleRPC(ctx, rs, isServer) + h.handleRPC(ctx, rs, true) } diff --git a/pkg/rules/http/net_http_otel_instrumenter.go b/pkg/rules/http/net_http_otel_instrumenter.go index 73dac4c8..9ae48a42 100644 --- a/pkg/rules/http/net_http_otel_instrumenter.go +++ b/pkg/rules/http/net_http_otel_instrumenter.go @@ -205,7 +205,7 @@ func BuildNetHttpClientOtelInstrumenter() *instrumenter.PropagatingToDownstreamI networkExtractor := net.NetworkAttrsExtractor[*netHttpRequest, *netHttpResponse, net.NetworkAttrsGetter[*netHttpRequest, *netHttpResponse]]{Getter: clientGetter} return builder.Init().SetSpanStatusExtractor(http.HttpClientSpanStatusExtractor[*netHttpRequest, *netHttpResponse]{Getter: clientGetter}).SetSpanNameExtractor(&http.HttpClientSpanNameExtractor[*netHttpRequest, *netHttpResponse]{Getter: clientGetter}). SetSpanKindExtractor(&instrumenter.AlwaysClientExtractor[*netHttpRequest]{}). - AddOperationListeners(http.HttpClientMetrics(), http.HttpClientMetrics()). + AddOperationListeners(http.HttpClientMetrics("net.http.client")). SetInstrumentationScope(instrumentation.Scope{ Name: utils.NET_HTTP_CLIENT_SCOPE_NAME, Version: version.Tag, @@ -226,7 +226,7 @@ func BuildNetHttpServerOtelInstrumenter() *instrumenter.PropagatingFromUpstreamI urlExtractor := net.UrlAttrsExtractor[*netHttpRequest, *netHttpResponse, net.UrlAttrsGetter[*netHttpRequest]]{Getter: serverGetter} return builder.Init().SetSpanStatusExtractor(http.HttpServerSpanStatusExtractor[*netHttpRequest, *netHttpResponse]{Getter: serverGetter}).SetSpanNameExtractor(&http.HttpServerSpanNameExtractor[*netHttpRequest, *netHttpResponse]{Getter: serverGetter}). SetSpanKindExtractor(&instrumenter.AlwaysServerExtractor[*netHttpRequest]{}). - AddOperationListeners(http.HttpServerMetrics()). + AddOperationListeners(http.HttpServerMetrics("net.http.server")). SetInstrumentationScope(instrumentation.Scope{ Name: utils.NET_HTTP_SERVER_SCOPE_NAME, Version: version.Tag, diff --git a/pkg/rules/kitex/kitex_otel_instrumenter.go b/pkg/rules/kitex/kitex_otel_instrumenter.go index ed77e416..cd630447 100644 --- a/pkg/rules/kitex/kitex_otel_instrumenter.go +++ b/pkg/rules/kitex/kitex_otel_instrumenter.go @@ -19,9 +19,7 @@ import ( "github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/inst-api/utils" "github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/inst-api/version" "github.com/cloudwego/kitex/pkg/rpcinfo" - "github.com/cloudwego/kitex/pkg/stats" "go.opentelemetry.io/otel/sdk/instrumentation" - "time" "github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/inst-api-semconv/instrumenter/rpc" "github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/inst-api/instrumenter" @@ -49,6 +47,13 @@ func (g kitexAttrsGetter) GetMethod(ri rpcinfo.RPCInfo) string { return ri.Invocation().ServiceName() + "/" + ri.Invocation().MethodName() } +func (g kitexAttrsGetter) GetServerAddress(request rpcinfo.RPCInfo) string { + if request.To() != nil && request.To().Address() != nil { + return request.To().Address().String() + } + return "" +} + func BuildKitexClientInstrumenter() instrumenter.Instrumenter[rpcinfo.RPCInfo, rpcinfo.RPCInfo] { builder := instrumenter.Builder[rpcinfo.RPCInfo, rpcinfo.RPCInfo]{} clientGetter := kitexAttrsGetter{} @@ -88,10 +93,3 @@ func parseRPCError(ri rpcinfo.RPCInfo) (panicMsg, panicStack string, err error) } return } - -func getStartTimeOrNow(ri rpcinfo.RPCInfo) time.Time { - if event := ri.Stats().GetEvent(stats.RPCStart); event != nil { - return event.Time() - } - return time.Now() -} diff --git a/pkg/rules/kratos/grpc/kratos_internal_setup.go b/pkg/rules/kratos/grpc/kratos_internal_setup.go index 2ad68a4d..71d190e3 100644 --- a/pkg/rules/kratos/grpc/kratos_internal_setup.go +++ b/pkg/rules/kratos/grpc/kratos_internal_setup.go @@ -27,14 +27,14 @@ import ( "github.com/go-kratos/kratos/v2/transport/http" ) -const OTEL_INSTRUMENTATION_KRATOS_EXPERIMENTAL_SPAN_ATTRIBUTES = "OTEL_INSTRUMENTATION_KRATOS_EXPERIMENTAL_SPAN_ATTRIBUTES" +const OTEL_INSTRUMENTATION_KRATOS_EXPERIMENTAL_SPAN_ENABLE = "OTEL_INSTRUMENTATION_KRATOS_EXPERIMENTAL_SPAN_ENABLE" var kratosEnabler = instrumenter.NewDefaultInstrumentEnabler() var kratosInternalInstrument = BuildKratosInternalInstrumenter() func kratosNewGRPCServiceOnEnter(call api.CallContext, opts ...grpc.ServerOption) { - if os.Getenv(OTEL_INSTRUMENTATION_KRATOS_EXPERIMENTAL_SPAN_ATTRIBUTES) != "true" { + if os.Getenv(OTEL_INSTRUMENTATION_KRATOS_EXPERIMENTAL_SPAN_ENABLE) != "true" { return } opts = append(opts, AddGRPCMiddleware(ServerTracingMiddleWare())) diff --git a/pkg/rules/kratos/http/kratos_internal_setup.go b/pkg/rules/kratos/http/kratos_internal_setup.go index 78b5d5d0..b111b452 100644 --- a/pkg/rules/kratos/http/kratos_internal_setup.go +++ b/pkg/rules/kratos/http/kratos_internal_setup.go @@ -26,12 +26,12 @@ import ( "github.com/go-kratos/kratos/v2/transport/http" ) -const OTEL_INSTRUMENTATION_KRATOS_EXPERIMENTAL_SPAN_ATTRIBUTES = "OTEL_INSTRUMENTATION_KRATOS_EXPERIMENTAL_SPAN_ATTRIBUTES" +const OTEL_INSTRUMENTATION_KRATOS_EXPERIMENTAL_SPAN_ENABLE = "OTEL_INSTRUMENTATION_KRATOS_EXPERIMENTAL_SPAN_ENABLE" var kratosInternalInstrument = BuildKratosInternalInstrumenter() func kratosNewHTTPServiceOnEnter(call api.CallContext, opts ...http.ServerOption) { - if os.Getenv(OTEL_INSTRUMENTATION_KRATOS_EXPERIMENTAL_SPAN_ATTRIBUTES) != "true" { + if os.Getenv(OTEL_INSTRUMENTATION_KRATOS_EXPERIMENTAL_SPAN_ENABLE) != "true" { return } opts = append(opts, AddHTTPMiddleware(ServerTracingMiddleWare())) diff --git a/pkg/rules/nacos/config/nacos_go_client_config_setup.go b/pkg/rules/nacos/config/nacos_go_client_config_setup.go new file mode 100644 index 00000000..18a7f9ee --- /dev/null +++ b/pkg/rules/nacos/config/nacos_go_client_config_setup.go @@ -0,0 +1,183 @@ +// Copyright (c) 2024 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package config + +import ( + "context" + "errors" + "github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/api" + "github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/inst-api-semconv/instrumenter/experimental" + "github.com/nacos-group/nacos-sdk-go/v2/clients/cache" + "github.com/nacos-group/nacos-sdk-go/v2/clients/config_client" + "github.com/nacos-group/nacos-sdk-go/v2/clients/nacos_client" + "github.com/nacos-group/nacos-sdk-go/v2/common/nacos_error" + "github.com/nacos-group/nacos-sdk-go/v2/common/nacos_server" + "github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc" + "github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc/rpc_request" + "github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc/rpc_response" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric" + "log" + "reflect" + "strconv" + "time" + "unsafe" +) + +func beforeNewConfigClient(call api.CallContext, nc nacos_client.INacosClient) { + if !experimental.NacosEnabler.Enable() { + return + } + param, err := nc.GetClientConfig() + if err != nil { + return + } + call.SetKeyData("namespace", param.NamespaceId) + call.SetKeyData("region", param.RegionId) + call.SetKeyData("appName", param.AppName) + call.SetKeyData("appKey", param.AppKey) + call.SetKeyData("userName", param.Username) +} + +func afterNewConfigClient(call api.CallContext, client *config_client.ConfigClient, err error) { + if !experimental.NacosEnabler.Enable() { + return + } + if client == nil { + return + } + // get reference for cache map + t := reflect.ValueOf(client) + if t.Kind() == reflect.Ptr { + t = t.Elem() + } else { + return + } + cacheMapField := t.FieldByName("cacheMap") + if cacheMapField.IsValid() { + cf := reflect.NewAt(cacheMapField.Type(), unsafe.Pointer(cacheMapField.UnsafeAddr())).Elem() + cacheMap, ok := cf.Interface().(cache.ConcurrentMap) + if !ok { + return + } + attrSet := attribute.NewSet(attribute.KeyValue{ + Key: "namespace", + Value: attribute.StringValue(call.GetKeyData("namespace").(string)), + }, attribute.KeyValue{ + Key: "region", + Value: attribute.StringValue(call.GetKeyData("region").(string)), + }, attribute.KeyValue{ + Key: "appName", + Value: attribute.StringValue(call.GetKeyData("appName").(string)), + }, attribute.KeyValue{ + Key: "appKey", + Value: attribute.StringValue(call.GetKeyData("appKey").(string)), + }, attribute.KeyValue{ + Key: "userName", + Value: attribute.StringValue(call.GetKeyData("userName").(string)), + }) + reg, err := experimental.GlobalMeter.RegisterCallback(func(ctx context.Context, observer metric.Observer) error { + observer.ObserveInt64(experimental.ClientConfigCacheMapSize, int64(cacheMap.Count()), metric.WithAttributeSet(attrSet)) + return nil + }, experimental.ClientConfigCacheMapSize) + if err != nil { + log.Printf("[otel nacos] failed to register metrics for config info holder, %v\n", err) + } else { + client.OtelReg = reg + } + } +} + +func beforeConfigClientClose(call api.CallContext, sc *config_client.ConfigClient) { + if !experimental.NacosEnabler.Enable() { + return + } + if sc.OtelReg == nil { + return + } + if reg, ok := sc.OtelReg.(metric.Registration); ok { + err := reg.Unregister() + if err != nil { + log.Printf("[otel nacos] failed to unregister metrics for config info holder, %v", err) + } + } +} + +func beforeCallConfigServer(call api.CallContext, server *nacos_server.NacosServer, api string, params map[string]string, newHeaders map[string]string, + method string, curServer string, contextPath string, timeoutMS uint64) { + if !experimental.NacosEnabler.Enable() { + return + } + call.SetKeyData("ts", time.Now().UnixMilli()) + call.SetKeyData("method", method) + call.SetKeyData("type", contextPath+api) +} + +func afterCallConfigServer(call api.CallContext, result string, err error) { + if !experimental.NacosEnabler.Enable() { + return + } + method := call.GetKeyData("method").(string) + t := call.GetKeyData("ts").(int64) + tpe := call.GetKeyData("type").(string) + code := "200" + if err != nil { + var nacosErr *nacos_error.NacosError + errors.As(err, &nacosErr) + code = nacosErr.ErrorCode() + } + set := attribute.NewSet(attribute.KeyValue{ + Key: "method", + Value: attribute.StringValue(method), + }, attribute.KeyValue{ + Key: "type", + Value: attribute.StringValue(tpe), + }, attribute.KeyValue{ + Key: "status", + Value: attribute.StringValue(code), + }) + experimental.ClientConfigRequestDuration.Record(context.Background(), float64(time.Now().UnixMilli()-t), metric.WithAttributeSet(set)) +} + +func beforeRequestProxy(call api.CallContext, cp *config_client.ConfigProxy, rpcClient *rpc.RpcClient, request rpc_request.IRequest, timeoutMills uint64) { + if !experimental.NacosEnabler.Enable() { + return + } + call.SetKeyData("ts", time.Now().UnixMilli()) + call.SetKeyData("request", request) +} + +func afterRequestProxy(call api.CallContext, resp rpc_response.IResponse, err error) { + if !experimental.NacosEnabler.Enable() { + return + } + t := call.GetKeyData("ts").(int64) + req := call.GetKeyData("request").(rpc_request.IRequest) + code := "NA" + if resp != nil { + code = strconv.Itoa(resp.GetResultCode()) + } + set := attribute.NewSet(attribute.KeyValue{ + Key: "method", + Value: attribute.StringValue("GRPC"), + }, attribute.KeyValue{ + Key: "type", + Value: attribute.StringValue(req.GetRequestType()), + }, attribute.KeyValue{ + Key: "status", + Value: attribute.StringValue(code), + }) + experimental.ClientConfigRequestDuration.Record(context.Background(), float64(time.Now().UnixMilli()-t), metric.WithAttributeSet(set)) +} diff --git a/pkg/rules/nacos/dom/nacos_go_client_dom_setup.go b/pkg/rules/nacos/dom/nacos_go_client_dom_setup.go new file mode 100644 index 00000000..5338b1ba --- /dev/null +++ b/pkg/rules/nacos/dom/nacos_go_client_dom_setup.go @@ -0,0 +1,82 @@ +// Copyright (c) 2024 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package dom + +import ( + "context" + "github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/api" + "github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/inst-api-semconv/instrumenter/experimental" + "github.com/nacos-group/nacos-sdk-go/v2/clients/cache" + "github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client/naming_http" + "github.com/nacos-group/nacos-sdk-go/v2/common/constant" + "github.com/nacos-group/nacos-sdk-go/v2/common/nacos_server" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric" + "log" + "reflect" + "unsafe" +) + +func beforeNewBeatReactor(call api.CallContext, clientCfg constant.ClientConfig, nacosServer *nacos_server.NacosServer) { + if !experimental.NacosEnabler.Enable() { + return + } + call.SetKeyData("namespace", clientCfg.NamespaceId) + call.SetKeyData("region", clientCfg.RegionId) + call.SetKeyData("appName", clientCfg.AppName) + call.SetKeyData("appKey", clientCfg.AppKey) + call.SetKeyData("userName", clientCfg.Username) +} + +func afterNewBeatReactor(call api.CallContext, b naming_http.BeatReactor) { + if !experimental.NacosEnabler.Enable() { + return + } + t := reflect.ValueOf(&b).Elem() + beatMapField := t.FieldByName("beatMap") + if beatMapField.IsValid() { + bf := reflect.NewAt(beatMapField.Type(), unsafe.Pointer(beatMapField.UnsafeAddr())).Elem() + beatMap, ok := bf.Interface().(cache.ConcurrentMap) + if !ok { + return + } + attrSet := attribute.NewSet(attribute.KeyValue{ + Key: "namespace", + Value: attribute.StringValue(call.GetKeyData("namespace").(string)), + }, attribute.KeyValue{ + Key: "region", + Value: attribute.StringValue(call.GetKeyData("region").(string)), + }, attribute.KeyValue{ + Key: "appName", + Value: attribute.StringValue(call.GetKeyData("appName").(string)), + }, attribute.KeyValue{ + Key: "appKey", + Value: attribute.StringValue(call.GetKeyData("appKey").(string)), + }, attribute.KeyValue{ + Key: "userName", + Value: attribute.StringValue(call.GetKeyData("userName").(string)), + }) + reg, err := experimental.GlobalMeter.RegisterCallback(func(ctx context.Context, observer metric.Observer) error { + observer.ObserveInt64(experimental.ClientDomBeatMapSize, int64(beatMap.Count()), metric.WithAttributeSet(attrSet)) + return nil + }, experimental.ClientDomBeatMapSize) + if err != nil { + log.Printf("[otel nacos] failed to register metrics for beat map, %v\n", err) + } else { + b.OtelReg = reg + call.SetReturnVal(0, b) + } + } +} diff --git a/pkg/rules/nacos/service/nacos_go_client_service_setup.go b/pkg/rules/nacos/service/nacos_go_client_service_setup.go new file mode 100644 index 00000000..0cc9ac83 --- /dev/null +++ b/pkg/rules/nacos/service/nacos_go_client_service_setup.go @@ -0,0 +1,153 @@ +// Copyright (c) 2024 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package service + +import ( + "context" + "errors" + "github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/api" + "github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/inst-api-semconv/instrumenter/experimental" + "github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client" + "github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client/naming_cache" + "github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client/naming_grpc" + "github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client/naming_http" + "github.com/nacos-group/nacos-sdk-go/v2/common/nacos_error" + "github.com/nacos-group/nacos-sdk-go/v2/common/nacos_server" + "github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc/rpc_request" + "github.com/nacos-group/nacos-sdk-go/v2/common/remote/rpc/rpc_response" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric" + "log" + "reflect" + "strconv" + "time" + "unsafe" +) + +func beforeNamingHttpProxyCloseClient(call api.CallContext, proxy *naming_http.NamingHttpProxy) { + if !experimental.NacosEnabler.Enable() { + return + } + t := reflect.ValueOf(proxy) + if t.Kind() == reflect.Ptr { + t = t.Elem() + } else { + return + } + beatReactorField := t.FieldByName("beatReactor") + if beatReactorField.IsValid() && beatReactorField.CanInterface() { + beatReactorInterface := beatReactorField.Interface() + beatReactor, ok := beatReactorInterface.(naming_http.BeatReactor) + if ok { + if reg, ok := beatReactor.OtelReg.(metric.Registration); ok { + err := reg.Unregister() + if err != nil { + log.Printf("failed to unregister metrics for beat reactor, %v", err) + } + } + } + } +} + +func beforeNamingClientClose(call api.CallContext, sc *naming_client.NamingClient) { + if !experimental.NacosEnabler.Enable() { + return + } + t := reflect.ValueOf(sc) + if t.Kind() == reflect.Ptr { + t = t.Elem() + } else { + return + } + serviceInfoHolderField := t.FieldByName("serviceInfoHolder") + if serviceInfoHolderField.IsValid() { + holderPointer := serviceInfoHolderField.Pointer() + holder := (*naming_cache.ServiceInfoHolder)(unsafe.Pointer(holderPointer)) + if holder != nil { + if reg, ok := holder.OtelReg.(metric.Registration); ok { + err := reg.Unregister() + if err != nil { + log.Printf("failed to unregister metrics for service info holder, %v", err) + } + } + } + } +} + +func beforeRequestToServer(call api.CallContext, proxy *naming_grpc.NamingGrpcProxy, request rpc_request.IRequest) { + if !experimental.NacosEnabler.Enable() { + return + } + call.SetKeyData("ts", time.Now().UnixMilli()) + call.SetKeyData("request", request) +} + +func afterRequestToServer(call api.CallContext, resp rpc_response.IResponse, err error) { + if !experimental.NacosEnabler.Enable() { + return + } + t := call.GetKeyData("ts").(int64) + req := call.GetKeyData("request").(rpc_request.IRequest) + code := "NA" + if resp != nil { + code = strconv.Itoa(resp.GetResultCode()) + } + set := attribute.NewSet(attribute.KeyValue{ + Key: "method", + Value: attribute.StringValue("GRPC"), + }, attribute.KeyValue{ + Key: "type", + Value: attribute.StringValue(req.GetRequestType()), + }, attribute.KeyValue{ + Key: "status", + Value: attribute.StringValue(code), + }) + experimental.ClientNamingRequestDuration.Record(context.Background(), float64(time.Now().UnixMilli()-t), metric.WithAttributeSet(set)) +} + +func beforeCallServer(call api.CallContext, server *nacos_server.NacosServer, api string, params map[string]string, method string, curServer string, contextPath string) { + if !experimental.NacosEnabler.Enable() { + return + } + call.SetKeyData("ts", time.Now().UnixMilli()) + call.SetKeyData("method", method) + call.SetKeyData("type", contextPath+api) +} + +func afterCallServer(call api.CallContext, result string, err error) { + if !experimental.NacosEnabler.Enable() { + return + } + method := call.GetKeyData("method").(string) + t := call.GetKeyData("ts").(int64) + tpe := call.GetKeyData("type").(string) + code := "200" + if err != nil { + var nacosErr *nacos_error.NacosError + errors.As(err, &nacosErr) + code = nacosErr.ErrorCode() + } + set := attribute.NewSet(attribute.KeyValue{ + Key: "method", + Value: attribute.StringValue(method), + }, attribute.KeyValue{ + Key: "type", + Value: attribute.StringValue(tpe), + }, attribute.KeyValue{ + Key: "status", + Value: attribute.StringValue(code), + }) + experimental.ClientNamingRequestDuration.Record(context.Background(), float64(time.Now().UnixMilli()-t), metric.WithAttributeSet(set)) +} diff --git a/pkg/rules/nacos/service_holder/nacos_go_client_service_holder_setup.go b/pkg/rules/nacos/service_holder/nacos_go_client_service_holder_setup.go new file mode 100644 index 00000000..6f25ee6f --- /dev/null +++ b/pkg/rules/nacos/service_holder/nacos_go_client_service_holder_setup.go @@ -0,0 +1,64 @@ +// Copyright (c) 2024 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package service_holder + +import ( + "context" + "github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/api" + "github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/inst-api-semconv/instrumenter/experimental" + "github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client/naming_cache" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric" + "log" + "strconv" +) + +func beforeNewServiceInfoHolder(call api.CallContext, namespace, cacheDir string, updateCacheWhenEmpty, notLoadCacheAtStart bool) { + if !experimental.NacosEnabler.Enable() { + return + } + call.SetKeyData("namespace", namespace) + call.SetKeyData("cacheDir", cacheDir) + call.SetKeyData("updateCacheWhenEmpty", strconv.FormatBool(updateCacheWhenEmpty)) + call.SetKeyData("notLoadCacheAtStart", strconv.FormatBool(notLoadCacheAtStart)) +} + +func afterNewServiceInfoHolder(call api.CallContext, holder *naming_cache.ServiceInfoHolder) { + if !experimental.NacosEnabler.Enable() { + return + } + reg, err := experimental.GlobalMeter.RegisterCallback(func(ctx context.Context, observer metric.Observer) error { + attrSet := attribute.NewSet(attribute.KeyValue{ + Key: "namespace", + Value: attribute.StringValue(call.GetKeyData("namespace").(string)), + }, attribute.KeyValue{ + Key: "cache.dir", + Value: attribute.StringValue(call.GetKeyData("cacheDir").(string)), + }, attribute.KeyValue{ + Key: "update.cache.when.empty", + Value: attribute.StringValue(call.GetKeyData("updateCacheWhenEmpty").(string)), + }, attribute.KeyValue{ + Key: "not.load.cache.at.start", + Value: attribute.StringValue(call.GetKeyData("notLoadCacheAtStart").(string)), + }) + observer.ObserveInt64(experimental.ClientServiceInfoMapSize, int64(holder.ServiceInfoMap.Count()), metric.WithAttributeSet(attrSet)) + return nil + }, experimental.ClientServiceInfoMapSize) + if err != nil { + log.Printf("[otel nacos] failed to register metrics for service info holder") + } else { + holder.OtelReg = reg + } +} diff --git a/pkg/rules/nacos2_1_0/service_holder/nacos_go_client_service_holder_setup_210.go b/pkg/rules/nacos2_1_0/service_holder/nacos_go_client_service_holder_setup_210.go new file mode 100644 index 00000000..e9ffac37 --- /dev/null +++ b/pkg/rules/nacos2_1_0/service_holder/nacos_go_client_service_holder_setup_210.go @@ -0,0 +1,69 @@ +// Copyright (c) 2024 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package service + +import ( + "context" + "github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/api" + "github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/inst-api-semconv/instrumenter/experimental" + "github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client/naming_cache" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric" + "log" + "strconv" +) + +func beforeNewServiceInfoHolder(call api.CallContext, namespace, cacheDir string, updateCacheWhenEmpty, notLoadCacheAtStart bool) { + if !experimental.NacosEnabler.Enable() { + return + } + call.SetKeyData("namespace", namespace) + call.SetKeyData("cacheDir", cacheDir) + call.SetKeyData("updateCacheWhenEmpty", strconv.FormatBool(updateCacheWhenEmpty)) + call.SetKeyData("notLoadCacheAtStart", strconv.FormatBool(notLoadCacheAtStart)) +} + +func afterNewServiceInfoHolder(call api.CallContext, holder *naming_cache.ServiceInfoHolder) { + if !experimental.NacosEnabler.Enable() { + return + } + reg, err := experimental.GlobalMeter.RegisterCallback(func(ctx context.Context, observer metric.Observer) error { + attrSet := attribute.NewSet(attribute.KeyValue{ + Key: "namespace", + Value: attribute.StringValue(call.GetKeyData("namespace").(string)), + }, attribute.KeyValue{ + Key: "cache.dir", + Value: attribute.StringValue(call.GetKeyData("cacheDir").(string)), + }, attribute.KeyValue{ + Key: "update.cache.when.empty", + Value: attribute.StringValue(call.GetKeyData("updateCacheWhenEmpty").(string)), + }, attribute.KeyValue{ + Key: "not.load.cache.at.start", + Value: attribute.StringValue(call.GetKeyData("notLoadCacheAtStart").(string)), + }) + var count int + holder.ServiceInfoMap.Range(func(key, value interface{}) bool { + count++ + return true + }) + observer.ObserveInt64(experimental.ClientServiceInfoMapSize, int64(count), metric.WithAttributeSet(attrSet)) + return nil + }, experimental.ClientServiceInfoMapSize) + if err != nil { + log.Printf("[otel nacos] failed to register metrics for service info holder") + } else { + holder.OtelReg = reg + } +} diff --git a/pkg/rules/nacos2_1_1/dom/nacos_go_client_dom_setup_211.go b/pkg/rules/nacos2_1_1/dom/nacos_go_client_dom_setup_211.go new file mode 100644 index 00000000..de7fa49b --- /dev/null +++ b/pkg/rules/nacos2_1_1/dom/nacos_go_client_dom_setup_211.go @@ -0,0 +1,82 @@ +// Copyright (c) 2024 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package dom + +import ( + "context" + "github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/api" + "github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/inst-api-semconv/instrumenter/experimental" + "github.com/nacos-group/nacos-sdk-go/v2/clients/cache" + "github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client/naming_http" + "github.com/nacos-group/nacos-sdk-go/v2/common/constant" + "github.com/nacos-group/nacos-sdk-go/v2/common/nacos_server" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric" + "log" + "reflect" + "unsafe" +) + +func beforeNewBeatReactor(call api.CallContext, ctx context.Context, clientCfg constant.ClientConfig, nacosServer *nacos_server.NacosServer) { + if !experimental.NacosEnabler.Enable() { + return + } + call.SetKeyData("namespace", clientCfg.NamespaceId) + call.SetKeyData("region", clientCfg.RegionId) + call.SetKeyData("appName", clientCfg.AppName) + call.SetKeyData("appKey", clientCfg.AppKey) + call.SetKeyData("userName", clientCfg.Username) +} + +func afterNewBeatReactor(call api.CallContext, b naming_http.BeatReactor) { + if !experimental.NacosEnabler.Enable() { + return + } + t := reflect.ValueOf(&b).Elem() + beatMapField := t.FieldByName("beatMap") + if beatMapField.IsValid() { + bf := reflect.NewAt(beatMapField.Type(), unsafe.Pointer(beatMapField.UnsafeAddr())).Elem() + beatMap, ok := bf.Interface().(cache.ConcurrentMap) + if !ok { + return + } + attrSet := attribute.NewSet(attribute.KeyValue{ + Key: "namespace", + Value: attribute.StringValue(call.GetKeyData("namespace").(string)), + }, attribute.KeyValue{ + Key: "region", + Value: attribute.StringValue(call.GetKeyData("region").(string)), + }, attribute.KeyValue{ + Key: "appName", + Value: attribute.StringValue(call.GetKeyData("appName").(string)), + }, attribute.KeyValue{ + Key: "appKey", + Value: attribute.StringValue(call.GetKeyData("appKey").(string)), + }, attribute.KeyValue{ + Key: "userName", + Value: attribute.StringValue(call.GetKeyData("userName").(string)), + }) + reg, err := experimental.GlobalMeter.RegisterCallback(func(ctx context.Context, observer metric.Observer) error { + observer.ObserveInt64(experimental.ClientDomBeatMapSize, int64(beatMap.Count()), metric.WithAttributeSet(attrSet)) + return nil + }, experimental.ClientDomBeatMapSize) + if err != nil { + log.Printf("[otel nacos] failed to register metrics for beat map, %v\n", err) + } else { + b.OtelReg = reg + call.SetReturnVal(0, b) + } + } +} diff --git a/test/helloworld/go.mod b/test/helloworld/go.mod index ec532ecc..38a3fc8d 100644 --- a/test/helloworld/go.mod +++ b/test/helloworld/go.mod @@ -1,6 +1,6 @@ module helloworld -go 1.22 +go 1.22.0 toolchain go1.22.7 diff --git a/test/kratos_tests.go b/test/kratos_tests.go index d5ddd760..c26353b3 100644 --- a/test/kratos_tests.go +++ b/test/kratos_tests.go @@ -33,13 +33,13 @@ func init() { func TestKratosGrpc(t *testing.T, env ...string) { UseApp("kratos/v2.6.3") RunGoBuild(t, "go", "build", "test_kratos_grpc.go", "server.go") - env = append(env, "OTEL_INSTRUMENTATION_KRATOS_EXPERIMENTAL_SPAN_ATTRIBUTES=true") + env = append(env, "OTEL_INSTRUMENTATION_KRATOS_EXPERIMENTAL_SPAN_ENABLE=true") RunApp(t, "test_kratos_grpc", env...) } func TestKratosHttp(t *testing.T, env ...string) { UseApp("kratos/v2.6.3") RunGoBuild(t, "go", "build", "test_kratos_http.go", "server.go") - env = append(env, "OTEL_INSTRUMENTATION_KRATOS_EXPERIMENTAL_SPAN_ATTRIBUTES=true") + env = append(env, "OTEL_INSTRUMENTATION_KRATOS_EXPERIMENTAL_SPAN_ENABLE=true") RunApp(t, "test_kratos_http", env...) } diff --git a/test/nacos/v2.0.0/go.mod b/test/nacos/v2.0.0/go.mod new file mode 100644 index 00000000..6706df3e --- /dev/null +++ b/test/nacos/v2.0.0/go.mod @@ -0,0 +1,46 @@ +module v2.0.0 + +go 1.22 + +replace github.com/alibaba/opentelemetry-go-auto-instrumentation => ../../../../opentelemetry-go-auto-instrumentation + +replace github.com/alibaba/opentelemetry-go-auto-instrumentation/test/verifier => ../../../../opentelemetry-go-auto-instrumentation/test/verifier + +require ( + github.com/alibaba/opentelemetry-go-auto-instrumentation/test/verifier v0.0.0-00010101000000-000000000000 + github.com/nacos-group/nacos-sdk-go/v2 v2.0.0 +) + +require ( + github.com/aliyun/alibaba-cloud-sdk-go v1.61.18 // indirect + github.com/buger/jsonparser v1.1.1 // indirect + github.com/go-errors/errors v1.0.1 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/golang/mock v1.3.1 // indirect + github.com/golang/protobuf v1.4.2 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af // indirect + github.com/json-iterator/go v1.1.6 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 // indirect + github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/toolkits/concurrent v0.0.0-20150624120057-a4371d70e3e3 // indirect + go.opentelemetry.io/otel v1.30.0 // indirect + go.opentelemetry.io/otel/metric v1.30.0 // indirect + go.opentelemetry.io/otel/sdk v1.30.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.30.0 // indirect + go.opentelemetry.io/otel/trace v1.30.0 // indirect + go.uber.org/atomic v1.6.0 // indirect + go.uber.org/multierr v1.5.0 // indirect + go.uber.org/zap v1.15.0 // indirect + golang.org/x/net v0.0.0-20190620200207-3b0461eec859 // indirect + golang.org/x/sys v0.26.0 // indirect + golang.org/x/text v0.3.0 // indirect + google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect + google.golang.org/grpc v1.36.1 // indirect + google.golang.org/protobuf v1.25.0 // indirect + gopkg.in/ini.v1 v1.42.0 // indirect + gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect +) diff --git a/test/nacos/v2.0.0/test_nacos_config.go b/test/nacos/v2.0.0/test_nacos_config.go new file mode 100644 index 00000000..d550fe41 --- /dev/null +++ b/test/nacos/v2.0.0/test_nacos_config.go @@ -0,0 +1,138 @@ +// Copyright (c) 2024 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "fmt" + "github.com/alibaba/opentelemetry-go-auto-instrumentation/test/verifier" + "github.com/nacos-group/nacos-sdk-go/v2/clients" + "github.com/nacos-group/nacos-sdk-go/v2/common/constant" + "github.com/nacos-group/nacos-sdk-go/v2/vo" + "go.opentelemetry.io/otel/sdk/metric/metricdata" + "os" + "strconv" + "time" +) + +func main() { + port := 8848 + if os.Getenv("NACOS_PORT") != "" { + port, _ = strconv.Atoi(os.Getenv("NACOS_PORT")) + } + sc := []constant.ServerConfig{ + *constant.NewServerConfig("127.0.0.1", uint64(port), constant.WithContextPath("/nacos")), + } + + //create ClientConfig + cc := *constant.NewClientConfig( + constant.WithNamespaceId(""), + constant.WithTimeoutMs(5000), + constant.WithNotLoadCacheAtStart(true), + constant.WithLogDir("/tmp/nacos/log"), + constant.WithCacheDir("/tmp/nacos/cache"), + constant.WithLogLevel("debug"), + ) + + // create config client + client, err := clients.NewConfigClient( + vo.NacosClientParam{ + ClientConfig: &cc, + ServerConfigs: sc, + }, + ) + + if err != nil { + panic(err) + } + + //publish config + //config key=dataId+group+namespaceId + _, err = client.PublishConfig(vo.ConfigParam{ + DataId: "test-data", + Group: "test-group", + Content: "hello world!", + }) + _, err = client.PublishConfig(vo.ConfigParam{ + DataId: "test-data-2", + Group: "test-group", + Content: "hello world!", + }) + if err != nil { + fmt.Printf("PublishConfig err:%+v \n", err) + } + time.Sleep(1 * time.Second) + //get config + content, err := client.GetConfig(vo.ConfigParam{ + DataId: "test-data", + Group: "test-group", + }) + fmt.Println("GetConfig,config :" + content) + + //Listen config change,key=dataId+group+namespaceId. + err = client.ListenConfig(vo.ConfigParam{ + DataId: "test-data", + Group: "test-group", + OnChange: func(namespace, group, dataId, data string) { + fmt.Println("config changed group:" + group + ", dataId:" + dataId + ", content:" + data) + }, + }) + + err = client.ListenConfig(vo.ConfigParam{ + DataId: "test-data-2", + Group: "test-group", + OnChange: func(namespace, group, dataId, data string) { + fmt.Println("config changed group:" + group + ", dataId:" + dataId + ", content:" + data) + }, + }) + + time.Sleep(1 * time.Second) + + _, err = client.PublishConfig(vo.ConfigParam{ + DataId: "test-data", + Group: "test-group", + Content: "test-listen", + }) + + time.Sleep(1 * time.Second) + + _, err = client.PublishConfig(vo.ConfigParam{ + DataId: "test-data-2", + Group: "test-group", + Content: "test-listen", + }) + + time.Sleep(10 * time.Second) + + verifier.WaitAndAssertMetrics(map[string]func(metricdata.ResourceMetrics){ + "nacos.client.configinfo.size": func(metrics metricdata.ResourceMetrics) { + if len(metrics.ScopeMetrics) == 0 { + panic("should not be empty metrics") + } + point := metrics.ScopeMetrics[0].Metrics[0].Data.(metricdata.Gauge[int64]) + if point.DataPoints[0].Value <= 0 { + panic("nacos.client.configinfo.size should not be negative") + } + }, + "nacos.client.config.request.duration": func(metrics metricdata.ResourceMetrics) { + if len(metrics.ScopeMetrics) == 0 { + panic("should not be empty metrics") + } + point := metrics.ScopeMetrics[0].Metrics[0].Data.(metricdata.Histogram[float64]) + if point.DataPoints[0].Count <= 0 { + panic("nacos.client.config.request.duration should not be negative") + } + }, + }) +} diff --git a/test/nacos/v2.0.0/test_nacos_service.go b/test/nacos/v2.0.0/test_nacos_service.go new file mode 100644 index 00000000..50ba4eef --- /dev/null +++ b/test/nacos/v2.0.0/test_nacos_service.go @@ -0,0 +1,237 @@ +// Copyright (c) 2024 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "fmt" + "github.com/alibaba/opentelemetry-go-auto-instrumentation/test/verifier" + "github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client" + "go.opentelemetry.io/otel/sdk/metric/metricdata" + "os" + "strconv" + "time" + + "github.com/nacos-group/nacos-sdk-go/v2/clients" + "github.com/nacos-group/nacos-sdk-go/v2/common/constant" + "github.com/nacos-group/nacos-sdk-go/v2/model" + "github.com/nacos-group/nacos-sdk-go/v2/util" + "github.com/nacos-group/nacos-sdk-go/v2/vo" +) + +func main() { + port := 8848 + if os.Getenv("NACOS_PORT") != "" { + port, _ = strconv.Atoi(os.Getenv("NACOS_PORT")) + } + //create ServerConfig + sc := []constant.ServerConfig{ + *constant.NewServerConfig("127.0.0.1", uint64(port), constant.WithContextPath("/nacos")), + } + + //create ClientConfig + cc := *constant.NewClientConfig( + constant.WithNamespaceId(""), + constant.WithTimeoutMs(5000), + constant.WithNotLoadCacheAtStart(true), + constant.WithLogDir("/tmp/nacos/log"), + constant.WithCacheDir("/tmp/nacos/cache"), + constant.WithLogLevel("debug"), + ) + + // create naming client + client, err := clients.NewNamingClient( + vo.NacosClientParam{ + ClientConfig: &cc, + ServerConfigs: sc, + }, + ) + + if err != nil { + panic(err) + } + + //Register + ExampleServiceClient_RegisterServiceInstance(client, vo.RegisterInstanceParam{ + Ip: "10.0.0.10", + Port: 8848, + ServiceName: "demo.go", + GroupName: "group-a", + ClusterName: "cluster-a", + Weight: 10, + Enable: true, + Healthy: true, + Ephemeral: true, + Metadata: map[string]string{"idc": "shanghai"}, + }) + + //DeRegister + ExampleServiceClient_DeRegisterServiceInstance(client, vo.DeregisterInstanceParam{ + Ip: "10.0.0.10", + Port: 8848, + ServiceName: "demo.go", + GroupName: "group-a", + Cluster: "cluster-a", + Ephemeral: true, //it must be true + }) + + //Register + ExampleServiceClient_RegisterServiceInstance(client, vo.RegisterInstanceParam{ + Ip: "10.0.0.10", + Port: 8848, + ServiceName: "demo.go", + GroupName: "group-a", + ClusterName: "cluster-a", + Weight: 10, + Enable: true, + Healthy: true, + Ephemeral: true, + Metadata: map[string]string{"idc": "shanghai"}, + }) + + time.Sleep(1 * time.Second) + + //Get service with serviceName, groupName , clusters + ExampleServiceClient_GetService(client, vo.GetServiceParam{ + ServiceName: "demo.go", + GroupName: "group-a", + Clusters: []string{"cluster-a"}, + }) + + //SelectAllInstance + //GroupName=DEFAULT_GROUP + ExampleServiceClient_SelectAllInstances(client, vo.SelectAllInstancesParam{ + ServiceName: "demo.go", + GroupName: "group-a", + Clusters: []string{"cluster-a"}, + }) + + //SelectInstances only return the instances of healthy=${HealthyOnly},enable=true and weight>0 + //ClusterName=DEFAULT,GroupName=DEFAULT_GROUP + ExampleServiceClient_SelectInstances(client, vo.SelectInstancesParam{ + ServiceName: "demo.go", + GroupName: "group-a", + Clusters: []string{"cluster-a"}, + }) + + //SelectOneHealthyInstance return one instance by WRR strategy for load balance + //And the instance should be health=true,enable=true and weight>0 + //ClusterName=DEFAULT,GroupName=DEFAULT_GROUP + ExampleServiceClient_SelectOneHealthyInstance(client, vo.SelectOneHealthInstanceParam{ + ServiceName: "demo.go", + GroupName: "group-a", + Clusters: []string{"cluster-a"}, + }) + + //Subscribe key=serviceName+groupName+cluster + //Note:We call add multiple SubscribeCallback with the same key. + param := &vo.SubscribeParam{ + ServiceName: "demo.go", + GroupName: "group-a", + SubscribeCallback: func(services []model.Instance, err error) { + fmt.Printf("callback return services:%s \n\n", util.ToJsonString(services)) + }, + } + ExampleServiceClient_Subscribe(client, param) + + ExampleServiceClient_RegisterServiceInstance(client, vo.RegisterInstanceParam{ + Ip: "10.0.0.10", + Port: 8848, + ServiceName: "demo.go", + GroupName: "group-a", + ClusterName: "cluster-a", + Weight: 10, + Enable: true, + Healthy: true, + Ephemeral: true, + Metadata: map[string]string{"idc": "beijing"}, + }) + //wait for client pull change from server + time.Sleep(3 * time.Second) + + ExampleServiceClient_UpdateServiceInstance(client, vo.UpdateInstanceParam{ + Ip: "10.0.0.11", //update ip + Port: 8848, + ServiceName: "demo.go", + GroupName: "group-a", + ClusterName: "cluster-a", + Weight: 10, + Enable: true, + Healthy: true, + Ephemeral: true, + Metadata: map[string]string{"idc": "beijing1"}, //update metadata + }) + + time.Sleep(10 * time.Second) + verifier.WaitAndAssertMetrics(map[string]func(metricdata.ResourceMetrics){ + "nacos.client.serviceinfo.size": func(metrics metricdata.ResourceMetrics) { + if len(metrics.ScopeMetrics) == 0 { + panic("should not be empty metrics") + } + }, + "nacos.client.dombeat.size": func(metrics metricdata.ResourceMetrics) { + if len(metrics.ScopeMetrics) == 0 { + panic("should not be empty metrics") + } + }, + "nacos.client.naming.request.duration": func(metrics metricdata.ResourceMetrics) { + if len(metrics.ScopeMetrics) == 0 { + panic("should not be empty metrics") + } + point := metrics.ScopeMetrics[0].Metrics[0].Data.(metricdata.Histogram[float64]) + if point.DataPoints[0].Count <= 0 { + panic("nacos.client.naming.request.duration should not be negative") + } + }, + }) +} + +func ExampleServiceClient_RegisterServiceInstance(client naming_client.INamingClient, param vo.RegisterInstanceParam) { + success, _ := client.RegisterInstance(param) + fmt.Printf("RegisterServiceInstance,param:%+v,result:%+v \n\n", param, success) +} + +func ExampleServiceClient_DeRegisterServiceInstance(client naming_client.INamingClient, param vo.DeregisterInstanceParam) { + success, _ := client.DeregisterInstance(param) + fmt.Printf("DeRegisterServiceInstance,param:%+v,result:%+v \n\n", param, success) +} + +func ExampleServiceClient_UpdateServiceInstance(client naming_client.INamingClient, param vo.UpdateInstanceParam) { + success, _ := client.UpdateInstance(param) + fmt.Printf("UpdateServiceInstance,param:%+v,result:%+v \n\n", param, success) +} + +func ExampleServiceClient_GetService(client naming_client.INamingClient, param vo.GetServiceParam) { + service, _ := client.GetService(param) + fmt.Printf("GetService,param:%+v, result:%+v \n\n", param, service) +} + +func ExampleServiceClient_SelectAllInstances(client naming_client.INamingClient, param vo.SelectAllInstancesParam) { + instances, _ := client.SelectAllInstances(param) + fmt.Printf("SelectAllInstance,param:%+v, result:%+v \n\n", param, instances) +} + +func ExampleServiceClient_SelectInstances(client naming_client.INamingClient, param vo.SelectInstancesParam) { + instances, _ := client.SelectInstances(param) + fmt.Printf("SelectInstances,param:%+v, result:%+v \n\n", param, instances) +} + +func ExampleServiceClient_SelectOneHealthyInstance(client naming_client.INamingClient, param vo.SelectOneHealthInstanceParam) { + instances, _ := client.SelectOneHealthyInstance(param) + fmt.Printf("SelectOneHealthyInstance,param:%+v, result:%+v \n\n", param, instances) +} + +func ExampleServiceClient_Subscribe(client naming_client.INamingClient, param *vo.SubscribeParam) { + client.Subscribe(param) +} diff --git a/test/nacos/v2.1.0/go.mod b/test/nacos/v2.1.0/go.mod new file mode 100644 index 00000000..d565d3be --- /dev/null +++ b/test/nacos/v2.1.0/go.mod @@ -0,0 +1,46 @@ +module v2.1.0 + +go 1.22 + +replace github.com/alibaba/opentelemetry-go-auto-instrumentation => ../../../../opentelemetry-go-auto-instrumentation + +replace github.com/alibaba/opentelemetry-go-auto-instrumentation/test/verifier => ../../../../opentelemetry-go-auto-instrumentation/test/verifier + +require ( + github.com/alibaba/opentelemetry-go-auto-instrumentation/test/verifier v0.0.0-00010101000000-000000000000 + github.com/nacos-group/nacos-sdk-go/v2 v2.1.0 +) + +require ( + github.com/aliyun/alibaba-cloud-sdk-go v1.61.1704 // indirect + github.com/buger/jsonparser v1.1.1 // indirect + github.com/go-errors/errors v1.0.1 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/golang/mock v1.6.0 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/toolkits/concurrent v0.0.0-20150624120057-a4371d70e3e3 // indirect + go.opentelemetry.io/otel v1.30.0 // indirect + go.opentelemetry.io/otel/metric v1.30.0 // indirect + go.opentelemetry.io/otel/sdk v1.30.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.30.0 // indirect + go.opentelemetry.io/otel/trace v1.30.0 // indirect + go.uber.org/atomic v1.7.0 // indirect + go.uber.org/multierr v1.6.0 // indirect + go.uber.org/zap v1.21.0 // indirect + golang.org/x/net v0.0.0-20210525063256-abc453219eb5 // indirect + golang.org/x/sys v0.26.0 // indirect + golang.org/x/text v0.3.6 // indirect + google.golang.org/genproto v0.0.0-20200825200019-8632dd797987 // indirect + google.golang.org/grpc v1.48.0 // indirect + google.golang.org/protobuf v1.27.1 // indirect + gopkg.in/ini.v1 v1.66.2 // indirect + gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect +) diff --git a/test/nacos/v2.1.0/test_nacos_config.go b/test/nacos/v2.1.0/test_nacos_config.go new file mode 100644 index 00000000..d550fe41 --- /dev/null +++ b/test/nacos/v2.1.0/test_nacos_config.go @@ -0,0 +1,138 @@ +// Copyright (c) 2024 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "fmt" + "github.com/alibaba/opentelemetry-go-auto-instrumentation/test/verifier" + "github.com/nacos-group/nacos-sdk-go/v2/clients" + "github.com/nacos-group/nacos-sdk-go/v2/common/constant" + "github.com/nacos-group/nacos-sdk-go/v2/vo" + "go.opentelemetry.io/otel/sdk/metric/metricdata" + "os" + "strconv" + "time" +) + +func main() { + port := 8848 + if os.Getenv("NACOS_PORT") != "" { + port, _ = strconv.Atoi(os.Getenv("NACOS_PORT")) + } + sc := []constant.ServerConfig{ + *constant.NewServerConfig("127.0.0.1", uint64(port), constant.WithContextPath("/nacos")), + } + + //create ClientConfig + cc := *constant.NewClientConfig( + constant.WithNamespaceId(""), + constant.WithTimeoutMs(5000), + constant.WithNotLoadCacheAtStart(true), + constant.WithLogDir("/tmp/nacos/log"), + constant.WithCacheDir("/tmp/nacos/cache"), + constant.WithLogLevel("debug"), + ) + + // create config client + client, err := clients.NewConfigClient( + vo.NacosClientParam{ + ClientConfig: &cc, + ServerConfigs: sc, + }, + ) + + if err != nil { + panic(err) + } + + //publish config + //config key=dataId+group+namespaceId + _, err = client.PublishConfig(vo.ConfigParam{ + DataId: "test-data", + Group: "test-group", + Content: "hello world!", + }) + _, err = client.PublishConfig(vo.ConfigParam{ + DataId: "test-data-2", + Group: "test-group", + Content: "hello world!", + }) + if err != nil { + fmt.Printf("PublishConfig err:%+v \n", err) + } + time.Sleep(1 * time.Second) + //get config + content, err := client.GetConfig(vo.ConfigParam{ + DataId: "test-data", + Group: "test-group", + }) + fmt.Println("GetConfig,config :" + content) + + //Listen config change,key=dataId+group+namespaceId. + err = client.ListenConfig(vo.ConfigParam{ + DataId: "test-data", + Group: "test-group", + OnChange: func(namespace, group, dataId, data string) { + fmt.Println("config changed group:" + group + ", dataId:" + dataId + ", content:" + data) + }, + }) + + err = client.ListenConfig(vo.ConfigParam{ + DataId: "test-data-2", + Group: "test-group", + OnChange: func(namespace, group, dataId, data string) { + fmt.Println("config changed group:" + group + ", dataId:" + dataId + ", content:" + data) + }, + }) + + time.Sleep(1 * time.Second) + + _, err = client.PublishConfig(vo.ConfigParam{ + DataId: "test-data", + Group: "test-group", + Content: "test-listen", + }) + + time.Sleep(1 * time.Second) + + _, err = client.PublishConfig(vo.ConfigParam{ + DataId: "test-data-2", + Group: "test-group", + Content: "test-listen", + }) + + time.Sleep(10 * time.Second) + + verifier.WaitAndAssertMetrics(map[string]func(metricdata.ResourceMetrics){ + "nacos.client.configinfo.size": func(metrics metricdata.ResourceMetrics) { + if len(metrics.ScopeMetrics) == 0 { + panic("should not be empty metrics") + } + point := metrics.ScopeMetrics[0].Metrics[0].Data.(metricdata.Gauge[int64]) + if point.DataPoints[0].Value <= 0 { + panic("nacos.client.configinfo.size should not be negative") + } + }, + "nacos.client.config.request.duration": func(metrics metricdata.ResourceMetrics) { + if len(metrics.ScopeMetrics) == 0 { + panic("should not be empty metrics") + } + point := metrics.ScopeMetrics[0].Metrics[0].Data.(metricdata.Histogram[float64]) + if point.DataPoints[0].Count <= 0 { + panic("nacos.client.config.request.duration should not be negative") + } + }, + }) +} diff --git a/test/nacos/v2.1.0/test_nacos_service.go b/test/nacos/v2.1.0/test_nacos_service.go new file mode 100644 index 00000000..50ba4eef --- /dev/null +++ b/test/nacos/v2.1.0/test_nacos_service.go @@ -0,0 +1,237 @@ +// Copyright (c) 2024 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "fmt" + "github.com/alibaba/opentelemetry-go-auto-instrumentation/test/verifier" + "github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client" + "go.opentelemetry.io/otel/sdk/metric/metricdata" + "os" + "strconv" + "time" + + "github.com/nacos-group/nacos-sdk-go/v2/clients" + "github.com/nacos-group/nacos-sdk-go/v2/common/constant" + "github.com/nacos-group/nacos-sdk-go/v2/model" + "github.com/nacos-group/nacos-sdk-go/v2/util" + "github.com/nacos-group/nacos-sdk-go/v2/vo" +) + +func main() { + port := 8848 + if os.Getenv("NACOS_PORT") != "" { + port, _ = strconv.Atoi(os.Getenv("NACOS_PORT")) + } + //create ServerConfig + sc := []constant.ServerConfig{ + *constant.NewServerConfig("127.0.0.1", uint64(port), constant.WithContextPath("/nacos")), + } + + //create ClientConfig + cc := *constant.NewClientConfig( + constant.WithNamespaceId(""), + constant.WithTimeoutMs(5000), + constant.WithNotLoadCacheAtStart(true), + constant.WithLogDir("/tmp/nacos/log"), + constant.WithCacheDir("/tmp/nacos/cache"), + constant.WithLogLevel("debug"), + ) + + // create naming client + client, err := clients.NewNamingClient( + vo.NacosClientParam{ + ClientConfig: &cc, + ServerConfigs: sc, + }, + ) + + if err != nil { + panic(err) + } + + //Register + ExampleServiceClient_RegisterServiceInstance(client, vo.RegisterInstanceParam{ + Ip: "10.0.0.10", + Port: 8848, + ServiceName: "demo.go", + GroupName: "group-a", + ClusterName: "cluster-a", + Weight: 10, + Enable: true, + Healthy: true, + Ephemeral: true, + Metadata: map[string]string{"idc": "shanghai"}, + }) + + //DeRegister + ExampleServiceClient_DeRegisterServiceInstance(client, vo.DeregisterInstanceParam{ + Ip: "10.0.0.10", + Port: 8848, + ServiceName: "demo.go", + GroupName: "group-a", + Cluster: "cluster-a", + Ephemeral: true, //it must be true + }) + + //Register + ExampleServiceClient_RegisterServiceInstance(client, vo.RegisterInstanceParam{ + Ip: "10.0.0.10", + Port: 8848, + ServiceName: "demo.go", + GroupName: "group-a", + ClusterName: "cluster-a", + Weight: 10, + Enable: true, + Healthy: true, + Ephemeral: true, + Metadata: map[string]string{"idc": "shanghai"}, + }) + + time.Sleep(1 * time.Second) + + //Get service with serviceName, groupName , clusters + ExampleServiceClient_GetService(client, vo.GetServiceParam{ + ServiceName: "demo.go", + GroupName: "group-a", + Clusters: []string{"cluster-a"}, + }) + + //SelectAllInstance + //GroupName=DEFAULT_GROUP + ExampleServiceClient_SelectAllInstances(client, vo.SelectAllInstancesParam{ + ServiceName: "demo.go", + GroupName: "group-a", + Clusters: []string{"cluster-a"}, + }) + + //SelectInstances only return the instances of healthy=${HealthyOnly},enable=true and weight>0 + //ClusterName=DEFAULT,GroupName=DEFAULT_GROUP + ExampleServiceClient_SelectInstances(client, vo.SelectInstancesParam{ + ServiceName: "demo.go", + GroupName: "group-a", + Clusters: []string{"cluster-a"}, + }) + + //SelectOneHealthyInstance return one instance by WRR strategy for load balance + //And the instance should be health=true,enable=true and weight>0 + //ClusterName=DEFAULT,GroupName=DEFAULT_GROUP + ExampleServiceClient_SelectOneHealthyInstance(client, vo.SelectOneHealthInstanceParam{ + ServiceName: "demo.go", + GroupName: "group-a", + Clusters: []string{"cluster-a"}, + }) + + //Subscribe key=serviceName+groupName+cluster + //Note:We call add multiple SubscribeCallback with the same key. + param := &vo.SubscribeParam{ + ServiceName: "demo.go", + GroupName: "group-a", + SubscribeCallback: func(services []model.Instance, err error) { + fmt.Printf("callback return services:%s \n\n", util.ToJsonString(services)) + }, + } + ExampleServiceClient_Subscribe(client, param) + + ExampleServiceClient_RegisterServiceInstance(client, vo.RegisterInstanceParam{ + Ip: "10.0.0.10", + Port: 8848, + ServiceName: "demo.go", + GroupName: "group-a", + ClusterName: "cluster-a", + Weight: 10, + Enable: true, + Healthy: true, + Ephemeral: true, + Metadata: map[string]string{"idc": "beijing"}, + }) + //wait for client pull change from server + time.Sleep(3 * time.Second) + + ExampleServiceClient_UpdateServiceInstance(client, vo.UpdateInstanceParam{ + Ip: "10.0.0.11", //update ip + Port: 8848, + ServiceName: "demo.go", + GroupName: "group-a", + ClusterName: "cluster-a", + Weight: 10, + Enable: true, + Healthy: true, + Ephemeral: true, + Metadata: map[string]string{"idc": "beijing1"}, //update metadata + }) + + time.Sleep(10 * time.Second) + verifier.WaitAndAssertMetrics(map[string]func(metricdata.ResourceMetrics){ + "nacos.client.serviceinfo.size": func(metrics metricdata.ResourceMetrics) { + if len(metrics.ScopeMetrics) == 0 { + panic("should not be empty metrics") + } + }, + "nacos.client.dombeat.size": func(metrics metricdata.ResourceMetrics) { + if len(metrics.ScopeMetrics) == 0 { + panic("should not be empty metrics") + } + }, + "nacos.client.naming.request.duration": func(metrics metricdata.ResourceMetrics) { + if len(metrics.ScopeMetrics) == 0 { + panic("should not be empty metrics") + } + point := metrics.ScopeMetrics[0].Metrics[0].Data.(metricdata.Histogram[float64]) + if point.DataPoints[0].Count <= 0 { + panic("nacos.client.naming.request.duration should not be negative") + } + }, + }) +} + +func ExampleServiceClient_RegisterServiceInstance(client naming_client.INamingClient, param vo.RegisterInstanceParam) { + success, _ := client.RegisterInstance(param) + fmt.Printf("RegisterServiceInstance,param:%+v,result:%+v \n\n", param, success) +} + +func ExampleServiceClient_DeRegisterServiceInstance(client naming_client.INamingClient, param vo.DeregisterInstanceParam) { + success, _ := client.DeregisterInstance(param) + fmt.Printf("DeRegisterServiceInstance,param:%+v,result:%+v \n\n", param, success) +} + +func ExampleServiceClient_UpdateServiceInstance(client naming_client.INamingClient, param vo.UpdateInstanceParam) { + success, _ := client.UpdateInstance(param) + fmt.Printf("UpdateServiceInstance,param:%+v,result:%+v \n\n", param, success) +} + +func ExampleServiceClient_GetService(client naming_client.INamingClient, param vo.GetServiceParam) { + service, _ := client.GetService(param) + fmt.Printf("GetService,param:%+v, result:%+v \n\n", param, service) +} + +func ExampleServiceClient_SelectAllInstances(client naming_client.INamingClient, param vo.SelectAllInstancesParam) { + instances, _ := client.SelectAllInstances(param) + fmt.Printf("SelectAllInstance,param:%+v, result:%+v \n\n", param, instances) +} + +func ExampleServiceClient_SelectInstances(client naming_client.INamingClient, param vo.SelectInstancesParam) { + instances, _ := client.SelectInstances(param) + fmt.Printf("SelectInstances,param:%+v, result:%+v \n\n", param, instances) +} + +func ExampleServiceClient_SelectOneHealthyInstance(client naming_client.INamingClient, param vo.SelectOneHealthInstanceParam) { + instances, _ := client.SelectOneHealthyInstance(param) + fmt.Printf("SelectOneHealthyInstance,param:%+v, result:%+v \n\n", param, instances) +} + +func ExampleServiceClient_Subscribe(client naming_client.INamingClient, param *vo.SubscribeParam) { + client.Subscribe(param) +} diff --git a/test/nacos_tests.go b/test/nacos_tests.go new file mode 100644 index 00000000..f8e34c21 --- /dev/null +++ b/test/nacos_tests.go @@ -0,0 +1,60 @@ +// Copyright (c) 2024 Alibaba Group Holding Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package test + +import ( + "testing" +) + +const nacos_dependency_name = "github.com/nacos-group/nacos-sdk-go/v2" +const nacos_module_name = "nacos" + +func init() { + TestCases = append(TestCases, NewGeneralTestCase("nacos-2.0.0-config-test", nacos_module_name, "v2.0.0", "v2.1.0", "1.18", "", TestNacos200Config), + NewMuzzleTestCase("nacos-2.0.0-muzzle", nacos_dependency_name, nacos_module_name, "v2.0.0", "v2.1.0", "1.18", "", []string{"test_nacos_config.go"}), + NewGeneralTestCase("nacos-2.0.0-service-test", nacos_module_name, "v2.0.0", "v2.1.0", "1.18", "", TestNacos200Service), + NewGeneralTestCase("nacos-2.1.0-config-test", nacos_module_name, "v2.1.0", "", "1.18", "", TestNacos210Config), + NewGeneralTestCase("nacos-2.1.0-service-test", nacos_module_name, "v2.1.0", "", "1.18", "", TestNacos210Service), + NewMuzzleTestCase("nacos-2.1.0-muzzle", nacos_dependency_name, nacos_module_name, "v2.1.0", "", "1.18", "", []string{"test_nacos_config.go"}), + NewLatestDepthTestCase("nacos-2.1.0-latestdepth-test", nacos_dependency_name, nacos_module_name, "", "", "1.18", "", TestNacos210Config)) +} + +func TestNacos200Config(t *testing.T, env ...string) { + UseApp("nacos/v2.0.0") + RunGoBuild(t, "go", "build", "test_nacos_config.go") + env = append(env, "OTEL_INSTRUMENTATION_NACOS_EXPERIMENTAL_METRICS_ENABLE=true") + RunApp(t, "test_nacos_config", env...) +} + +func TestNacos200Service(t *testing.T, env ...string) { + UseApp("nacos/v2.0.0") + RunGoBuild(t, "go", "build", "test_nacos_service.go") + env = append(env, "OTEL_INSTRUMENTATION_NACOS_EXPERIMENTAL_METRICS_ENABLE=true") + RunApp(t, "test_nacos_service", env...) +} + +func TestNacos210Config(t *testing.T, env ...string) { + UseApp("nacos/v2.1.0") + RunGoBuild(t, "go", "build", "test_nacos_config.go") + env = append(env, "OTEL_INSTRUMENTATION_NACOS_EXPERIMENTAL_METRICS_ENABLE=true") + RunApp(t, "test_nacos_config", env...) +} + +func TestNacos210Service(t *testing.T, env ...string) { + UseApp("nacos/v2.1.0") + RunGoBuild(t, "go", "build", "test_nacos_service.go") + env = append(env, "OTEL_INSTRUMENTATION_NACOS_EXPERIMENTAL_METRICS_ENABLE=true") + RunApp(t, "test_nacos_service", env...) +}