diff --git a/cache/memcache/go.mod b/cache/memcache/go.mod index 202851d7..2d513dfe 100644 --- a/cache/memcache/go.mod +++ b/cache/memcache/go.mod @@ -20,7 +20,7 @@ require ( github.com/klauspost/cpuid/v2 v2.2.9 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect golang.org/x/arch v0.11.0 // indirect - golang.org/x/sys v0.28.0 // indirect + golang.org/x/sys v0.47.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/cache/memcache/go.sum b/cache/memcache/go.sum index 8bd2c9f7..4268667a 100644 --- a/cache/memcache/go.sum +++ b/cache/memcache/go.sum @@ -38,8 +38,8 @@ golang.org/x/arch v0.11.0 h1:KXV8WWKCXm6tRpLirl2szsO5j/oOODwZf4hATmGVNs4= golang.org/x/arch v0.11.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= -golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/cache/redis/go.mod b/cache/redis/go.mod index 1f220609..8d507836 100644 --- a/cache/redis/go.mod +++ b/cache/redis/go.mod @@ -22,7 +22,7 @@ require ( github.com/klauspost/cpuid/v2 v2.2.9 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect golang.org/x/arch v0.11.0 // indirect - golang.org/x/sys v0.28.0 // indirect + golang.org/x/sys v0.47.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/cache/redis/go.sum b/cache/redis/go.sum index 898205f8..7176e0d0 100644 --- a/cache/redis/go.sum +++ b/cache/redis/go.sum @@ -46,8 +46,8 @@ golang.org/x/arch v0.11.0 h1:KXV8WWKCXm6tRpLirl2szsO5j/oOODwZf4hATmGVNs4= golang.org/x/arch v0.11.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= -golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/component/http/go.mod b/component/http/go.mod index 8d7d43a0..6e328c9b 100644 --- a/component/http/go.mod +++ b/component/http/go.mod @@ -6,6 +6,7 @@ require ( github.com/dobyte/due/v2 v2.5.8 github.com/go-openapi/runtime v0.28.0 github.com/gofiber/fiber/v3 v3.2.0 + github.com/valyala/fasthttp v1.70.0 ) require ( @@ -43,13 +44,12 @@ require ( github.com/tinylib/msgp v1.6.4 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect - github.com/valyala/fasthttp v1.70.0 // indirect go.mongodb.org/mongo-driver v1.16.0 // indirect golang.org/x/arch v0.11.0 // indirect golang.org/x/crypto v0.50.0 // indirect golang.org/x/net v0.53.0 // indirect golang.org/x/sync v0.20.0 // indirect - golang.org/x/sys v0.43.0 // indirect + golang.org/x/sys v0.47.0 // indirect golang.org/x/text v0.36.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/component/http/go.sum b/component/http/go.sum index 4275c9d0..2930c85c 100644 --- a/component/http/go.sum +++ b/component/http/go.sum @@ -2,8 +2,6 @@ dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk= github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= -github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ= -github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= github.com/andybalholm/brotli v1.2.1 h1:R+f5xP285VArJDRgowrfb9DqL18yVK0gKAW/F+eTWro= github.com/andybalholm/brotli v1.2.1/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= @@ -21,9 +19,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= -github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= -github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/fxamacker/cbor/v2 v2.9.1 h1:2rWm8B193Ll4VdjsJY28jxs70IdDsHRWgQYAI80+rMQ= +github.com/fxamacker/cbor/v2 v2.9.1/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/go-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC03zFCU= github.com/go-openapi/analysis v0.23.0/go.mod h1:9mz9ZWaSlV8TvjQHLl2mUW2PbZtemkE8yA5v22ohupo= github.com/go-openapi/errors v0.22.0 h1:c4xY/OLxUBSTiepAg3j/MHuAv5mJhnf53LLMWFB+u/w= @@ -44,16 +41,10 @@ github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+Gr github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3BumrGD58= github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ= -github.com/gofiber/fiber/v3 v3.0.0-rc.3 h1:h0KXuRHbivSslIpoHD1R/XjUsjcGwt+2vK0avFiYonA= -github.com/gofiber/fiber/v3 v3.0.0-rc.3/go.mod h1:LNBPuS/rGoUFlOyy03fXsWAeWfdGoT1QytwjRVNSVWo= github.com/gofiber/fiber/v3 v3.2.0 h1:g9+09D320foINPpCnR3ibQ5oBEFHjAWRRfDG1te54u8= github.com/gofiber/fiber/v3 v3.2.0/go.mod h1:FHOsc2Db7HhHpsE62QAaJlXVV1pNkbZEptZ4jtti7m4= -github.com/gofiber/schema v1.6.0 h1:rAgVDFwhndtC+hgV7Vu5ItQCn7eC2mBA4Eu1/ZTiEYY= -github.com/gofiber/schema v1.6.0/go.mod h1:WNZWpQx8LlPSK7ZaX0OqOh+nQo/eW2OevsXs1VZfs/s= github.com/gofiber/schema v1.7.1 h1:oSJBKdgP8JeIME4TQSAqlNKTU2iBB+2RNmKi8Nsc+TI= github.com/gofiber/schema v1.7.1/go.mod h1:A/X5Ffyru4p9eBdp99qu+nzviHzQiZ7odLT+TwxWhbk= -github.com/gofiber/utils/v2 v2.0.0-rc.2 h1:NvJTf7yMafTq16lUOJv70nr+HIOLNQcvGme/X+ftbW8= -github.com/gofiber/utils/v2 v2.0.0-rc.2/go.mod h1:gXins5o7up+BQFiubmO8aUJc/+Mhd7EKXIiAK5GBomI= github.com/gofiber/utils/v2 v2.0.4 h1:WwAxUA7L4MW2DjdEHF234lfqvBqd2vYYuBtA9TJq2ec= github.com/gofiber/utils/v2 v2.0.4/go.mod h1:GGERKU3Vhj5z6hS8YKvxL99A54DjOvTFZ0cjZnG4Lj4= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= @@ -64,8 +55,6 @@ github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8= github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/klauspost/compress v1.18.1 h1:bcSGx7UbpBqMChDtsF28Lw6v/G94LPrrbMbdC3JH2co= -github.com/klauspost/compress v1.18.1/go.mod h1:ZQFFVG+MdnR0P+l6wpXgIL4NTtwiKIdBnrBd8Nrxr+0= github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE= github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY= @@ -78,8 +67,6 @@ github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0 github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.21 h1:xYae+lCNBP7QuW4PUnNG61ffM4hVIfm+zUzDuSzYLGs= github.com/mattn/go-isatty v0.0.21/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= @@ -92,8 +79,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= -github.com/shamaton/msgpack/v2 v2.4.0 h1:O5Z08MRmbo0lA9o2xnQ4TXx6teJbPqEurqcCOQ8Oi/4= -github.com/shamaton/msgpack/v2 v2.4.0/go.mod h1:6khjYnkx73f7VQU7wjcFS9DFjs+59naVWJv1TB7qdOI= +github.com/shamaton/msgpack/v3 v3.1.0 h1:jsk0vEAqVvvS9+fTZ5/EcQ9tz860c9pWxJ4Iwecz8gU= +github.com/shamaton/msgpack/v3 v3.1.0/go.mod h1:DcQG8jrdrQCIxr3HlMYkiXdMhK+KfN2CitkyzsQV4uc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -104,16 +91,12 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/tinylib/msgp v1.5.0 h1:GWnqAE54wmnlFazjq2+vgr736Akg58iiHImh+kPY2pc= -github.com/tinylib/msgp v1.5.0/go.mod h1:cvjFkb4RiC8qSBOPMGPSzSAx47nAsfhLVTCZZNuHv5o= github.com/tinylib/msgp v1.6.4 h1:mOwYbyYDLPj35mkA2BjjYejgJk9BuHxDdvRnb6v2ZcQ= github.com/tinylib/msgp v1.6.4/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA= 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/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.68.0 h1:v12Nx16iepr8r9ySOwqI+5RBJ/DqTxhOy1HrHoDFnok= -github.com/valyala/fasthttp v1.68.0/go.mod h1:5EXiRfYQAoiO/khu4oU9VISC/eVY6JqmSpPJoHCKsz4= github.com/valyala/fasthttp v1.70.0 h1:LAhMGcWk13QZWm85+eg8ZBNbrq5mnkWFGbHMUJHIdXA= github.com/valyala/fasthttp v1.70.0/go.mod h1:oDZEHHkJ/Buyklg6uURmYs19442zFSnCIfX3j1FY3pE= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= @@ -124,23 +107,14 @@ go.mongodb.org/mongo-driver v1.16.0 h1:tpRsfBJMROVHKpdGyc1BBEzzjDUWjItxbVSZ8Ls4B go.mongodb.org/mongo-driver v1.16.0/go.mod h1:oB6AhJQvFQL4LEHyXi6aJzQJtBiTQHiAd83l0GdFaiw= golang.org/x/arch v0.11.0 h1:KXV8WWKCXm6tRpLirl2szsO5j/oOODwZf4hATmGVNs4= golang.org/x/arch v0.11.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= -golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU= -golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc= golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI= golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q= -golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= -golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA= golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs= golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= -golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI= -golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= -golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg= golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/component/otel/go.mod b/component/otel/go.mod new file mode 100644 index 00000000..ca5bf0ba --- /dev/null +++ b/component/otel/go.mod @@ -0,0 +1,45 @@ +module github.com/dobyte/due/component/otel/v2 + +go 1.25.0 + +require ( + github.com/dobyte/due/v2 v2.5.8 + go.opentelemetry.io/otel v1.45.0 + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.45.0 + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.45.0 + go.opentelemetry.io/otel/sdk v1.45.0 + go.opentelemetry.io/otel/sdk/metric v1.45.0 +) + +require ( + dario.cat/mergo v1.0.2 // indirect + github.com/BurntSushi/toml v1.6.0 // indirect + github.com/bytedance/gopkg v0.1.3 // indirect + github.com/bytedance/sonic v1.14.2 // indirect + github.com/bytedance/sonic/loader v0.4.0 // indirect + github.com/cenkalti/backoff/v5 v5.0.3 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/cloudwego/base64x v0.1.6 // indirect + github.com/fsnotify/fsnotify v1.9.0 // indirect + github.com/go-logr/logr v1.4.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 // indirect + github.com/jinzhu/copier v0.4.0 // indirect + github.com/klauspost/cpuid/v2 v2.2.9 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/otel/metric v1.45.0 // indirect + go.opentelemetry.io/otel/trace v1.45.0 // indirect + go.opentelemetry.io/proto/otlp v1.11.0 // indirect + golang.org/x/arch v0.11.0 // indirect + golang.org/x/net v0.57.0 // indirect + golang.org/x/sync v0.22.0 // indirect + golang.org/x/sys v0.47.0 // indirect + golang.org/x/text v0.40.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260803160001-6ac0973c030d // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260803160001-6ac0973c030d // indirect + google.golang.org/grpc v1.83.0 // indirect + google.golang.org/protobuf v1.36.11 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/component/otel/go.sum b/component/otel/go.sum new file mode 100644 index 00000000..1a401da3 --- /dev/null +++ b/component/otel/go.sum @@ -0,0 +1,106 @@ +dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= +dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= +github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk= +github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M= +github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM= +github.com/bytedance/sonic v1.14.2 h1:k1twIoe97C1DtYUo+fZQy865IuHia4PR5RPiuGPPIIE= +github.com/bytedance/sonic v1.14.2/go.mod h1:T80iDELeHiHKSc0C9tubFygiuXoGzrkjKzX2quAx980= +github.com/bytedance/sonic/loader v0.4.0 h1:olZ7lEqcxtZygCK9EKYKADnpQoYkRQxaeY2NYzevs+o= +github.com/bytedance/sonic/loader v0.4.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= +github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= +github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= +github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dobyte/due/v2 v2.5.8 h1:8+3PfDhLTljm5NR41dHST5YwvGIHNjk7Ah9vD+J37R8= +github.com/dobyte/due/v2 v2.5.8/go.mod h1:P6T6x6gDR2bFe8Sd5W4KzWq2GBHoHZq/DIFY/u9Ub94= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8= +github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 h1:5VipnvEpbqr2gA2VbM+nYVbkIF28c5ZQfqCBQ5g2xfk= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0/go.mod h1:Hyl3n6Twe1hvtd9XUXDec4pTvgMSEixRuQKPTMH2bNs= +github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8= +github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= +github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY= +github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +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= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/otel v1.45.0 h1:pdrWmLHofpubmArBv1LgFSv1Z0Ie/ppdZzu+kUN5EeU= +go.opentelemetry.io/otel v1.45.0/go.mod h1:XZxIqPapzEYnhNSScF5DIqXhm/rYi0FzCe2XddAwZfQ= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.45.0 h1:klTViGcsvLCd1xN3rZzfZ12NslC/OimbmR+k+A006RI= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.45.0/go.mod h1:jRsK04CWmXuY8A0O+wMpSf+t90RHZ53o5Qmxn2PQPfk= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.45.0 h1:pnxy6c/kvNBWdNNFzqpjuJLm9Hjhgk/Q0nY221rwuk0= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.45.0/go.mod h1:qw6YsFapotRwoDhXRZvljzaOvCQB7UfnafEJagpN2TA= +go.opentelemetry.io/otel/metric v1.45.0 h1:7Eg1uH7CJ5cXv9is6tnBe1FI6rj1nwUdbFypRm3br/M= +go.opentelemetry.io/otel/metric v1.45.0/go.mod h1:HAPbm1nd3p1PmFH7v2dR+6BjXxw+Lq4a2+pndMAm08s= +go.opentelemetry.io/otel/metric/x v0.67.0 h1:PcicCNZFkZ4bXfSooXdo3WN7RBOVOtjVdo1wD358Uns= +go.opentelemetry.io/otel/metric/x v0.67.0/go.mod h1:FBjCWZe6wgcqxcMtjdGiClDKXb2YxxXii0CXftE4QtI= +go.opentelemetry.io/otel/sdk v1.45.0 h1:4VVSMgQ83dUgW2aoX5f6JgLvHwIvzcuLnF9lUdCSpCw= +go.opentelemetry.io/otel/sdk v1.45.0/go.mod h1:Sr40LgXV7DsKMMJMKOhUWOgMWTfAaqvm2kF0g7ilwuA= +go.opentelemetry.io/otel/sdk/metric v1.45.0 h1:oVFszMfyj1Am6s24Vtc7wBb8BKLcwepJjNEYILuiE3o= +go.opentelemetry.io/otel/sdk/metric v1.45.0/go.mod h1:vUWUxDZvu1WVRj8JA8S0AdhsPrZoDpA2DdZauIh4mDA= +go.opentelemetry.io/otel/trace v1.45.0 h1:l/mP6Uv7oNO7/TblbhpbgMidxhq1uO/rPsikOyVhxag= +go.opentelemetry.io/otel/trace v1.45.0/go.mod h1:qoJJA2xNMnxRrdISU/kLtfUH2wNeQbiv+jhs/CxI8bc= +go.opentelemetry.io/proto/otlp v1.11.0 h1:5rrYs0Ykyj50sdU/JU0x8etU+LubXWb+gED6TbEdMIk= +go.opentelemetry.io/proto/otlp v1.11.0/go.mod h1:SmVizdCOAm3XBtG1g1NnOdhW6jtddT72hLMhv8VwA8E= +golang.org/x/arch v0.11.0 h1:KXV8WWKCXm6tRpLirl2szsO5j/oOODwZf4hATmGVNs4= +golang.org/x/arch v0.11.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= +golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE= +golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU= +golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= +golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs= +golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY= +gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= +gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= +google.golang.org/genproto/googleapis/api v0.0.0-20260803160001-6ac0973c030d h1:FarXi840EJWSHYTN3ERkADbPWjl307+FGrA22KAVjjc= +google.golang.org/genproto/googleapis/api v0.0.0-20260803160001-6ac0973c030d/go.mod h1:K/+WGbmBY7aNW1HDw1fJnKYo10i0DkAX6pows00dLig= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260803160001-6ac0973c030d h1:IL4hdHzcUv2l/gcg98/Rj3FbtE6axwqslOW8SW0C+S0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260803160001-6ac0973c030d/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +google.golang.org/grpc v1.83.0 h1:JeNZEKJFbQxArAMl+hiytHauacDNqJUllNfmIMmpqnQ= +google.golang.org/grpc v1.83.0/go.mod h1:kDyl6SKsiHKt0uylY5gtn5cEjkrIOhQOGDgIc4JGwzQ= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/component/otel/metrics/metrics.go b/component/otel/metrics/metrics.go new file mode 100644 index 00000000..6d669e9e --- /dev/null +++ b/component/otel/metrics/metrics.go @@ -0,0 +1,171 @@ +package metrics + +import ( + "context" + "fmt" + "net/url" + "strings" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + otlpgrpc "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc" + otlphttp "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp" + "go.opentelemetry.io/otel/sdk/metric" + "go.opentelemetry.io/otel/sdk/resource" + + "github.com/dobyte/due/v2/component" + "github.com/dobyte/due/v2/log" +) + +var _ component.Component = (*Metrics)(nil) + +// Metrics manages the OpenTelemetry metrics provider and OTLP export pipeline. +type Metrics struct { + component.Base + opts *options + provider *metric.MeterProvider +} + +// NewMetrics creates an OpenTelemetry metrics component. +func NewMetrics(opts ...Option) *Metrics { + o := defaultOptions() + for _, opt := range opts { + opt(o) + } + if o.endpoint == "" { + applyProtocolDefault(o) + } + return &Metrics{opts: o} +} + +// Name returns the component name. +func (*Metrics) Name() string { + return "metrics" +} + +// Init creates and installs the global MeterProvider when metrics are enabled. +func (m *Metrics) Init() { + if !m.opts.enabled || m.provider != nil { + return + } + if m.opts.protocol != ProtocolGRPC && m.opts.protocol != ProtocolHTTP { + panic(fmt.Sprintf("unsupported OTLP metrics protocol: %q", m.opts.protocol)) + } + + ctx := context.Background() + res, err := buildResource(ctx, m.opts) + if err != nil { + panic(fmt.Errorf("build OTEL metrics resource: %w", err)) + } + + exportCtx, cancel := context.WithTimeout(ctx, m.opts.timeout) + exporter, err := buildExporter(exportCtx, m.opts) + cancel() + if err != nil { + panic(fmt.Errorf("build OTLP metrics exporter: %w", err)) + } + + reader := metric.NewPeriodicReader( + exporter, + metric.WithInterval(m.opts.interval), + metric.WithTimeout(m.opts.timeout), + ) + m.provider = metric.NewMeterProvider( + metric.WithResource(res), + metric.WithReader(reader), + ) + otel.SetMeterProvider(m.provider) +} + +// Close flushes pending metrics while other components are closing. +func (m *Metrics) Close() { + if m.provider == nil { + return + } + + ctx, cancel := context.WithTimeout(context.Background(), m.opts.timeout) + defer cancel() + if err := m.provider.ForceFlush(ctx); err != nil { + log.Warnf("OTEL metrics force flush failed: %v", err) + } +} + +// Destroy shuts down the provider and releases the exporter resources. +func (m *Metrics) Destroy() { + if m.provider == nil { + return + } + + ctx, cancel := context.WithTimeout(context.Background(), m.opts.timeout) + defer cancel() + if err := m.provider.Shutdown(ctx); err != nil { + log.Warnf("OTEL metrics shutdown failed: %v", err) + } + m.provider = nil +} + +func buildResource(ctx context.Context, opts *options) (*resource.Resource, error) { + res := resource.DefaultWithContext(ctx) + var err error + if opts.resource != nil { + res, err = resource.Merge(res, opts.resource) + if err != nil { + return nil, fmt.Errorf("merge configured resource: %w", err) + } + } + + attrs := make([]attribute.KeyValue, 0, len(opts.resourceAttributes)+2) + attrs = append(attrs, opts.resourceAttributes...) + if opts.serviceName != "" { + attrs = append(attrs, attribute.String("service.name", opts.serviceName)) + } + if opts.serviceInstanceID != "" { + attrs = append(attrs, attribute.String("service.instance.id", opts.serviceInstanceID)) + } + if len(attrs) == 0 { + return res, nil + } + return resource.Merge(res, resource.NewSchemaless(attrs...)) +} + +func buildExporter(ctx context.Context, opts *options) (metric.Exporter, error) { + switch opts.protocol { + case ProtocolGRPC: + exporterOpts := make([]otlpgrpc.Option, 0, 4) + if strings.Contains(opts.endpoint, "://") { + exporterOpts = append(exporterOpts, otlpgrpc.WithEndpointURL(opts.endpoint)) + } else { + exporterOpts = append(exporterOpts, otlpgrpc.WithEndpoint(opts.endpoint)) + if opts.insecure { + exporterOpts = append(exporterOpts, otlpgrpc.WithInsecure()) + } + } + if len(opts.headers) > 0 { + exporterOpts = append(exporterOpts, otlpgrpc.WithHeaders(opts.headers)) + } + return otlpgrpc.New(ctx, exporterOpts...) + case ProtocolHTTP: + exporterOpts := make([]otlphttp.Option, 0, 4) + if strings.Contains(opts.endpoint, "://") { + endpointURL, err := url.Parse(opts.endpoint) + if err != nil { + return nil, fmt.Errorf("parse HTTP endpoint URL: %w", err) + } + exporterOpts = append(exporterOpts, otlphttp.WithEndpointURL(opts.endpoint)) + if endpointURL.Path == "" { + exporterOpts = append(exporterOpts, otlphttp.WithURLPath("/v1/metrics")) + } + } else { + exporterOpts = append(exporterOpts, otlphttp.WithEndpoint(opts.endpoint)) + if opts.insecure { + exporterOpts = append(exporterOpts, otlphttp.WithInsecure()) + } + } + if len(opts.headers) > 0 { + exporterOpts = append(exporterOpts, otlphttp.WithHeaders(opts.headers)) + } + return otlphttp.New(ctx, exporterOpts...) + default: + return nil, fmt.Errorf("unsupported OTLP metrics protocol: %q", opts.protocol) + } +} diff --git a/component/otel/metrics/metrics_test.go b/component/otel/metrics/metrics_test.go new file mode 100644 index 00000000..9c40da89 --- /dev/null +++ b/component/otel/metrics/metrics_test.go @@ -0,0 +1,227 @@ +package metrics + +import ( + "context" + "net/http" + "net/http/httptest" + "testing" + "time" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/sdk/metric/metricdata" + "go.opentelemetry.io/otel/sdk/resource" +) + +func TestDefaultOptionsDisabled(t *testing.T) { + opts := defaultOptions() + + if opts.enabled { + t.Fatal("metrics must be disabled by default") + } + if opts.serviceName != defaultServiceName { + t.Fatalf("service name = %q, want %q", opts.serviceName, defaultServiceName) + } + if opts.protocol != ProtocolGRPC { + t.Fatalf("protocol = %q, want %q", opts.protocol, ProtocolGRPC) + } + if opts.endpoint != defaultGRPCEndpoint { + t.Fatalf("endpoint = %q, want %q", opts.endpoint, defaultGRPCEndpoint) + } +} + +func TestMetricsName(t *testing.T) { + if got := NewMetrics().Name(); got != "metrics" { + t.Fatalf("name = %q, want metrics", got) + } +} + +func TestOptionsOverrideDefaults(t *testing.T) { + opts := defaultOptions() + WithEnabled(true)(opts) + WithServiceName("game-node")(opts) + WithServiceInstanceID("node-1")(opts) + WithProtocol(ProtocolHTTP)(opts) + WithEndpoint("collector:4318")(opts) + WithInsecure(false)(opts) + WithExportInterval(2 * time.Second)(opts) + WithExportTimeout(1 * time.Second)(opts) + + if !opts.enabled || opts.serviceName != "game-node" || opts.serviceInstanceID != "node-1" { + t.Fatalf("identity options were not applied: %+v", opts) + } + if opts.protocol != ProtocolHTTP || opts.endpoint != "collector:4318" || opts.insecure { + t.Fatalf("transport options were not applied: %+v", opts) + } + if opts.interval != 2*time.Second || opts.timeout != time.Second { + t.Fatalf("timing options were not applied: %+v", opts) + } +} + +func TestEndpointDefaultsFollowProtocol(t *testing.T) { + opts := defaultOptions() + opts.endpointSet = false + opts.endpoint = "" + WithProtocol(ProtocolHTTP)(opts) + applyProtocolDefault(opts) + + if opts.endpoint != defaultHTTPEndpoint { + t.Fatalf("HTTP endpoint = %q, want %q", opts.endpoint, defaultHTTPEndpoint) + } +} + +func TestExplicitEndpointHasPriorityRegardlessOfOptionOrder(t *testing.T) { + for _, apply := range []func(*options){ + func(opts *options) { + WithEndpoint("collector:4317")(opts) + WithProtocol(ProtocolHTTP)(opts) + }, + func(opts *options) { + WithProtocol(ProtocolHTTP)(opts) + WithEndpoint("collector:4317")(opts) + }, + } { + opts := defaultOptions() + apply(opts) + if opts.endpoint != "collector:4317" { + t.Fatalf("endpoint = %q, want explicit endpoint", opts.endpoint) + } + } +} + +func TestBuildResourceDueAttributesOverrideDefaults(t *testing.T) { + base := resource.NewWithAttributes("", attribute.String("service.name", "environment-name")) + opts := defaultOptions() + opts.serviceName = "due-service" + opts.serviceInstanceID = "instance-1" + opts.resource = base + opts.resourceAttributes = []attribute.KeyValue{ + attribute.String("deployment.environment.name", "test"), + } + + res, err := buildResource(context.Background(), opts) + if err != nil { + t.Fatalf("build resource: %v", err) + } + + attrs := make(map[string]any) + for _, kv := range res.Attributes() { + attrs[string(kv.Key)] = kv.Value.AsInterface() + } + if attrs["service.name"] != "due-service" { + t.Fatalf("service.name = %v, want due-service", attrs["service.name"]) + } + if attrs["service.instance.id"] != "instance-1" { + t.Fatalf("service.instance.id = %v, want instance-1", attrs["service.instance.id"]) + } + if attrs["deployment.environment.name"] != "test" { + t.Fatalf("deployment.environment.name = %v, want test", attrs["deployment.environment.name"]) + } +} + +func TestDisabledMetricsDoesNotReplaceGlobalProvider(t *testing.T) { + provider := otel.GetMeterProvider() + metrics := NewMetrics(WithProtocol(Protocol("invalid"))) + metrics.Init() + + if otel.GetMeterProvider() != provider { + t.Fatal("disabled metrics replaced the global meter provider") + } +} + +func TestBuildExporterProtocols(t *testing.T) { + for _, test := range []struct { + name string + protocol Protocol + endpoint string + }{ + {name: "grpc", protocol: ProtocolGRPC, endpoint: defaultGRPCEndpoint}, + {name: "http", protocol: ProtocolHTTP, endpoint: defaultHTTPEndpoint}, + } { + t.Run(test.name, func(t *testing.T) { + opts := defaultOptions() + opts.protocol = test.protocol + opts.endpoint = test.endpoint + opts.insecure = true + + exporter, err := buildExporter(context.Background(), opts) + if err != nil { + t.Fatalf("build %s exporter: %v", test.name, err) + } + if err := exporter.Shutdown(context.Background()); err != nil { + t.Fatalf("shutdown %s exporter: %v", test.name, err) + } + }) + } +} + +func TestHTTPExporterURLPath(t *testing.T) { + paths := make(chan string, 2) + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + paths <- r.URL.Path + w.WriteHeader(http.StatusOK) + })) + t.Cleanup(server.Close) + + for _, test := range []struct { + name string + suffix string + wantPath string + }{ + {name: "default metrics path", wantPath: "/v1/metrics"}, + {name: "explicit path", suffix: "/custom/metrics", wantPath: "/custom/metrics"}, + } { + t.Run(test.name, func(t *testing.T) { + opts := defaultOptions() + opts.protocol = ProtocolHTTP + opts.endpoint = server.URL + test.suffix + + exporter, err := buildExporter(context.Background(), opts) + if err != nil { + t.Fatalf("build HTTP exporter: %v", err) + } + t.Cleanup(func() { + if err := exporter.Shutdown(context.Background()); err != nil { + t.Errorf("shutdown HTTP exporter: %v", err) + } + }) + + if err := exporter.Export(context.Background(), &metricdata.ResourceMetrics{}); err != nil { + t.Fatalf("export metrics: %v", err) + } + if got := <-paths; got != test.wantPath { + t.Fatalf("request path = %q, want %q", got, test.wantPath) + } + }) + } +} + +func TestEnabledMetricsLifecycle(t *testing.T) { + m := NewMetrics( + WithEnabled(true), + WithProtocol(ProtocolHTTP), + WithEndpoint(defaultHTTPEndpoint), + WithInsecure(true), + WithExportInterval(time.Hour), + WithExportTimeout(time.Second), + ) + m.Init() + if m.provider == nil { + t.Fatal("enabled metrics did not create a provider") + } + provider := m.provider + m.Init() + if m.provider != provider { + t.Fatal("repeated init replaced the active provider") + } + if otel.GetMeterProvider() != m.provider { + t.Fatal("enabled metrics did not install its provider globally") + } + + m.Close() + m.Destroy() + m.Destroy() + if m.provider != nil { + t.Fatal("destroy did not release the provider") + } +} diff --git a/component/otel/metrics/options.go b/component/otel/metrics/options.go new file mode 100644 index 00000000..8963b423 --- /dev/null +++ b/component/otel/metrics/options.go @@ -0,0 +1,143 @@ +package metrics + +import ( + "maps" + "time" + + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/sdk/resource" + + "github.com/dobyte/due/v2/etc" +) + +// Protocol selects the OTLP metric exporter transport. +type Protocol string + +const ( + ProtocolGRPC Protocol = "grpc" + ProtocolHTTP Protocol = "http" +) + +const ( + defaultServiceName = "due" // 默认服务名称 + defaultGRPCEndpoint = "localhost:4317" // 默认 gRPC 导出端点 + defaultHTTPEndpoint = "localhost:4318" // 默认 HTTP 导出端点 + defaultExportInterval = 10 * time.Second // 默认指标导出间隔 + defaultExportTimeout = 3 * time.Second // 默认单次指标导出超时时间 +) +const ( + defaultEnabledKey = "etc.otel.enable" + defaultServiceNameKey = "etc.otel.serviceName" + defaultServiceInstanceIDKey = "etc.otel.serviceInstanceID" + + defaultEndpointKey = "etc.otel.metrics.endpoint" + defaultProtocolKey = "etc.otel.metrics.protocol" + defaultInsecureKey = "etc.otel.metrics.insecure" + defaultExportIntervalKey = "etc.otel.metrics.exportInterval" + defaultExportTimeoutKey = "etc.otel.metrics.exportTimeout" +) + +type Option func(o *options) + +type options struct { + // 启用指标导出 + enabled bool + serviceName string + serviceInstanceID string + endpoint string + endpointSet bool + protocol Protocol + insecure bool + interval time.Duration + timeout time.Duration + resource *resource.Resource + resourceAttributes []attribute.KeyValue + headers map[string]string +} + +func defaultOptions() *options { + opts := &options{ + enabled: etc.Get(defaultEnabledKey).Bool(), + serviceName: etc.Get(defaultServiceNameKey, defaultServiceName).String(), + protocol: Protocol(etc.Get(defaultProtocolKey, string(ProtocolGRPC)).String()), + insecure: etc.Get(defaultInsecureKey, true).Bool(), + interval: etc.Get(defaultExportIntervalKey, defaultExportInterval).Duration(), + timeout: etc.Get(defaultExportTimeoutKey, defaultExportTimeout).Duration(), + headers: make(map[string]string), + } + if value := etc.Get(defaultServiceInstanceIDKey).String(); value != "" { + opts.serviceInstanceID = value + } + if value := etc.Get(defaultEndpointKey).String(); value != "" { + opts.endpoint = value + opts.endpointSet = true + } + applyProtocolDefault(opts) + return opts +} + +func applyProtocolDefault(opts *options) { + if opts.endpoint != "" { + return + } + if opts.protocol == ProtocolHTTP { + opts.endpoint = defaultHTTPEndpoint + return + } + opts.endpoint = defaultGRPCEndpoint +} + +func WithEnabled(enabled bool) Option { + return func(o *options) { o.enabled = enabled } +} + +func WithServiceName(name string) Option { + return func(o *options) { o.serviceName = name } +} + +func WithServiceInstanceID(id string) Option { + return func(o *options) { o.serviceInstanceID = id } +} + +func WithEndpoint(endpoint string) Option { + return func(o *options) { + o.endpoint = endpoint + o.endpointSet = true + } +} + +func WithProtocol(protocol Protocol) Option { + return func(o *options) { + o.protocol = protocol + if !o.endpointSet { + o.endpoint = "" + applyProtocolDefault(o) + } + } +} + +func WithInsecure(insecure bool) Option { + return func(o *options) { o.insecure = insecure } +} + +func WithExportInterval(interval time.Duration) Option { + return func(o *options) { o.interval = interval } +} + +func WithExportTimeout(timeout time.Duration) Option { + return func(o *options) { o.timeout = timeout } +} + +func WithResource(res *resource.Resource) Option { + return func(o *options) { o.resource = res } +} + +func WithResourceAttributes(attrs ...attribute.KeyValue) Option { + return func(o *options) { o.resourceAttributes = append(o.resourceAttributes, attrs...) } +} + +func WithHeaders(headers map[string]string) Option { + return func(o *options) { + o.headers = maps.Clone(headers) + } +} diff --git a/config/consul/go.mod b/config/consul/go.mod index e63cb41f..207b4538 100644 --- a/config/consul/go.mod +++ b/config/consul/go.mod @@ -35,7 +35,7 @@ require ( golang.org/x/arch v0.11.0 // indirect golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 // indirect golang.org/x/sync v0.20.0 // indirect - golang.org/x/sys v0.29.0 // indirect + golang.org/x/sys v0.47.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/config/consul/go.sum b/config/consul/go.sum index b3602ca5..bbf77158 100644 --- a/config/consul/go.sum +++ b/config/consul/go.sum @@ -234,8 +234,8 @@ golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= -golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= diff --git a/config/etcd/go.mod b/config/etcd/go.mod index 2c216fd1..18c4e42d 100644 --- a/config/etcd/go.mod +++ b/config/etcd/go.mod @@ -29,7 +29,7 @@ require ( go.uber.org/zap v1.27.0 // indirect golang.org/x/arch v0.11.0 // indirect golang.org/x/net v0.45.0 // indirect - golang.org/x/sys v0.36.0 // indirect + golang.org/x/sys v0.47.0 // indirect golang.org/x/text v0.29.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb // indirect diff --git a/config/etcd/go.sum b/config/etcd/go.sum index 9145d463..bcad2df7 100644 --- a/config/etcd/go.sum +++ b/config/etcd/go.sum @@ -8,6 +8,8 @@ github.com/bytedance/sonic v1.14.2 h1:k1twIoe97C1DtYUo+fZQy865IuHia4PR5RPiuGPPII github.com/bytedance/sonic v1.14.2/go.mod h1:T80iDELeHiHKSc0C9tubFygiuXoGzrkjKzX2quAx980= github.com/bytedance/sonic/loader v0.4.0 h1:olZ7lEqcxtZygCK9EKYKADnpQoYkRQxaeY2NYzevs+o= github.com/bytedance/sonic/loader v0.4.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4= @@ -19,8 +21,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= -github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= -github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8= +github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -67,18 +69,18 @@ go.etcd.io/etcd/client/pkg/v3 v3.6.7 h1:vvzgyozz46q+TyeGBuFzVuI53/yd133CHceNb/Ah go.etcd.io/etcd/client/pkg/v3 v3.6.7/go.mod h1:2IVulJ3FZ/czIGl9T4lMF1uxzrhRahLqe+hSgy+Kh7Q= go.etcd.io/etcd/client/v3 v3.6.7 h1:9WqA5RpIBtdMxAy1ukXLAdtg2pAxNqW5NUoO2wQrE6U= go.etcd.io/etcd/client/v3 v3.6.7/go.mod h1:2XfROY56AXnUqGsvl+6k29wrwsSbEh1lAouQB1vHpeE= -go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= -go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= -go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= -go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= -go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= -go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= -go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= -go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk= -go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= -go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= -go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/otel v1.45.0 h1:pdrWmLHofpubmArBv1LgFSv1Z0Ie/ppdZzu+kUN5EeU= +go.opentelemetry.io/otel v1.45.0/go.mod h1:XZxIqPapzEYnhNSScF5DIqXhm/rYi0FzCe2XddAwZfQ= +go.opentelemetry.io/otel/metric v1.45.0 h1:7Eg1uH7CJ5cXv9is6tnBe1FI6rj1nwUdbFypRm3br/M= +go.opentelemetry.io/otel/metric v1.45.0/go.mod h1:HAPbm1nd3p1PmFH7v2dR+6BjXxw+Lq4a2+pndMAm08s= +go.opentelemetry.io/otel/sdk v1.45.0 h1:4VVSMgQ83dUgW2aoX5f6JgLvHwIvzcuLnF9lUdCSpCw= +go.opentelemetry.io/otel/sdk v1.45.0/go.mod h1:Sr40LgXV7DsKMMJMKOhUWOgMWTfAaqvm2kF0g7ilwuA= +go.opentelemetry.io/otel/sdk/metric v1.45.0 h1:oVFszMfyj1Am6s24Vtc7wBb8BKLcwepJjNEYILuiE3o= +go.opentelemetry.io/otel/sdk/metric v1.45.0/go.mod h1:vUWUxDZvu1WVRj8JA8S0AdhsPrZoDpA2DdZauIh4mDA= +go.opentelemetry.io/otel/trace v1.45.0 h1:l/mP6Uv7oNO7/TblbhpbgMidxhq1uO/rPsikOyVhxag= +go.opentelemetry.io/otel/trace v1.45.0/go.mod h1:qoJJA2xNMnxRrdISU/kLtfUH2wNeQbiv+jhs/CxI8bc= 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.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= @@ -106,8 +108,8 @@ golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= -golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= diff --git a/config/nacos/go.mod b/config/nacos/go.mod index b107f9f6..86aa7875 100644 --- a/config/nacos/go.mod +++ b/config/nacos/go.mod @@ -65,7 +65,7 @@ require ( golang.org/x/crypto v0.31.0 // indirect golang.org/x/net v0.33.0 // indirect golang.org/x/sync v0.20.0 // indirect - golang.org/x/sys v0.28.0 // indirect + golang.org/x/sys v0.47.0 // indirect golang.org/x/text v0.21.0 // indirect golang.org/x/time v0.1.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect diff --git a/config/nacos/go.sum b/config/nacos/go.sum index c7d72ff3..e33e2792 100644 --- a/config/nacos/go.sum +++ b/config/nacos/go.sum @@ -501,6 +501,7 @@ golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= diff --git a/crypto/ecc/go.mod b/crypto/ecc/go.mod index 2dd0f6ac..4e6a5124 100644 --- a/crypto/ecc/go.mod +++ b/crypto/ecc/go.mod @@ -24,7 +24,7 @@ require ( golang.org/x/arch v0.11.0 // indirect golang.org/x/crypto v0.11.0 // indirect golang.org/x/sync v0.20.0 // indirect - golang.org/x/sys v0.28.0 // indirect + golang.org/x/sys v0.47.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/crypto/ecc/go.sum b/crypto/ecc/go.sum index 7ee78821..40cca9de 100644 --- a/crypto/ecc/go.sum +++ b/crypto/ecc/go.sum @@ -52,6 +52,7 @@ golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/crypto/rsa/go.mod b/crypto/rsa/go.mod index 9a7b42bc..d34d8ba9 100644 --- a/crypto/rsa/go.mod +++ b/crypto/rsa/go.mod @@ -17,7 +17,7 @@ require ( github.com/twitchyliquid64/golang-asm v0.15.1 // indirect golang.org/x/arch v0.11.0 // indirect golang.org/x/sync v0.20.0 // indirect - golang.org/x/sys v0.28.0 // indirect + golang.org/x/sys v0.47.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/crypto/rsa/go.sum b/crypto/rsa/go.sum index f9f9f81f..23fa47a0 100644 --- a/crypto/rsa/go.sum +++ b/crypto/rsa/go.sum @@ -38,6 +38,7 @@ golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/eventbus/kafka/go.mod b/eventbus/kafka/go.mod index 579d5d04..47f3706c 100644 --- a/eventbus/kafka/go.mod +++ b/eventbus/kafka/go.mod @@ -42,7 +42,7 @@ require ( golang.org/x/crypto v0.28.0 // indirect golang.org/x/net v0.30.0 // indirect golang.org/x/sync v0.20.0 // indirect - golang.org/x/sys v0.28.0 // indirect + golang.org/x/sys v0.47.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/eventbus/kafka/go.sum b/eventbus/kafka/go.sum index ca27d050..dfedb9b8 100644 --- a/eventbus/kafka/go.sum +++ b/eventbus/kafka/go.sum @@ -114,6 +114,7 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= diff --git a/eventbus/nats/go.mod b/eventbus/nats/go.mod index a05ce385..d0b57001 100644 --- a/eventbus/nats/go.mod +++ b/eventbus/nats/go.mod @@ -26,7 +26,7 @@ require ( golang.org/x/arch v0.11.0 // indirect golang.org/x/crypto v0.28.0 // indirect golang.org/x/sync v0.20.0 // indirect - golang.org/x/sys v0.28.0 // indirect + golang.org/x/sys v0.47.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/eventbus/nats/go.sum b/eventbus/nats/go.sum index d680f667..e52150b5 100644 --- a/eventbus/nats/go.sum +++ b/eventbus/nats/go.sum @@ -52,6 +52,7 @@ golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/eventbus/redis/go.mod b/eventbus/redis/go.mod index 46597596..760cccd5 100644 --- a/eventbus/redis/go.mod +++ b/eventbus/redis/go.mod @@ -24,7 +24,7 @@ require ( github.com/twitchyliquid64/golang-asm v0.15.1 // indirect golang.org/x/arch v0.11.0 // indirect golang.org/x/sync v0.20.0 // indirect - golang.org/x/sys v0.28.0 // indirect + golang.org/x/sys v0.47.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/eventbus/redis/go.sum b/eventbus/redis/go.sum index cddf79f6..a930a22b 100644 --- a/eventbus/redis/go.sum +++ b/eventbus/redis/go.sum @@ -52,6 +52,7 @@ golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/go.mod b/go.mod index dc304a21..2455341e 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,9 @@ require ( github.com/jinzhu/copier v0.4.0 github.com/panjf2000/ants/v2 v2.11.4 github.com/shamaton/msgpack/v2 v2.4.0 + go.opentelemetry.io/otel v1.45.0 + go.opentelemetry.io/otel/metric v1.45.0 + go.opentelemetry.io/otel/sdk/metric v1.45.0 golang.org/x/sync v0.20.0 google.golang.org/protobuf v1.36.6 gopkg.in/yaml.v3 v3.0.1 @@ -19,10 +22,15 @@ require ( require ( github.com/bytedance/gopkg v0.1.3 // indirect github.com/bytedance/sonic/loader v0.4.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cloudwego/base64x v0.1.6 // indirect - github.com/google/go-cmp v0.6.0 // indirect + github.com/go-logr/logr v1.4.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/klauspost/cpuid/v2 v2.2.9 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/otel/sdk v1.45.0 // indirect + go.opentelemetry.io/otel/trace v1.45.0 // indirect golang.org/x/arch v0.11.0 // indirect - golang.org/x/sys v0.28.0 // indirect + golang.org/x/sys v0.47.0 // indirect ) diff --git a/go.sum b/go.sum index 9c04e87e..2ce70c58 100644 --- a/go.sum +++ b/go.sum @@ -8,6 +8,8 @@ github.com/bytedance/sonic v1.14.2 h1:k1twIoe97C1DtYUo+fZQy865IuHia4PR5RPiuGPPII github.com/bytedance/sonic v1.14.2/go.mod h1:T80iDELeHiHKSc0C9tubFygiuXoGzrkjKzX2quAx980= github.com/bytedance/sonic/loader v0.4.0 h1:olZ7lEqcxtZygCK9EKYKADnpQoYkRQxaeY2NYzevs+o= github.com/bytedance/sonic/loader v0.4.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -15,18 +17,29 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8= +github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8= github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY= github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/panjf2000/ants/v2 v2.11.4 h1:UJQbtN1jIcI5CYNocTj0fuAUYvsLjPoYi0YuhqV/Y48= github.com/panjf2000/ants/v2 v2.11.4/go.mod h1:8u92CYMUc6gyvTIw8Ru7Mt7+/ESnJahz5EVtqfrilek= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/shamaton/msgpack/v2 v2.4.0 h1:O5Z08MRmbo0lA9o2xnQ4TXx6teJbPqEurqcCOQ8Oi/4= github.com/shamaton/msgpack/v2 v2.4.0/go.mod h1:6khjYnkx73f7VQU7wjcFS9DFjs+59naVWJv1TB7qdOI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -36,20 +49,36 @@ github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= 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= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/otel v1.45.0 h1:pdrWmLHofpubmArBv1LgFSv1Z0Ie/ppdZzu+kUN5EeU= +go.opentelemetry.io/otel v1.45.0/go.mod h1:XZxIqPapzEYnhNSScF5DIqXhm/rYi0FzCe2XddAwZfQ= +go.opentelemetry.io/otel/metric v1.45.0 h1:7Eg1uH7CJ5cXv9is6tnBe1FI6rj1nwUdbFypRm3br/M= +go.opentelemetry.io/otel/metric v1.45.0/go.mod h1:HAPbm1nd3p1PmFH7v2dR+6BjXxw+Lq4a2+pndMAm08s= +go.opentelemetry.io/otel/metric/x v0.67.0 h1:PcicCNZFkZ4bXfSooXdo3WN7RBOVOtjVdo1wD358Uns= +go.opentelemetry.io/otel/metric/x v0.67.0/go.mod h1:FBjCWZe6wgcqxcMtjdGiClDKXb2YxxXii0CXftE4QtI= +go.opentelemetry.io/otel/sdk v1.45.0 h1:4VVSMgQ83dUgW2aoX5f6JgLvHwIvzcuLnF9lUdCSpCw= +go.opentelemetry.io/otel/sdk v1.45.0/go.mod h1:Sr40LgXV7DsKMMJMKOhUWOgMWTfAaqvm2kF0g7ilwuA= +go.opentelemetry.io/otel/sdk/metric v1.45.0 h1:oVFszMfyj1Am6s24Vtc7wBb8BKLcwepJjNEYILuiE3o= +go.opentelemetry.io/otel/sdk/metric v1.45.0/go.mod h1:vUWUxDZvu1WVRj8JA8S0AdhsPrZoDpA2DdZauIh4mDA= +go.opentelemetry.io/otel/trace v1.45.0 h1:l/mP6Uv7oNO7/TblbhpbgMidxhq1uO/rPsikOyVhxag= +go.opentelemetry.io/otel/trace v1.45.0/go.mod h1:qoJJA2xNMnxRrdISU/kLtfUH2wNeQbiv+jhs/CxI8bc= golang.org/x/arch v0.11.0 h1:KXV8WWKCXm6tRpLirl2szsO5j/oOODwZf4hATmGVNs4= golang.org/x/arch v0.11.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= -golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/locate/redis/go.mod b/locate/redis/go.mod index 6cc9ae35..6eff7d63 100644 --- a/locate/redis/go.mod +++ b/locate/redis/go.mod @@ -23,7 +23,7 @@ require ( github.com/klauspost/cpuid/v2 v2.2.9 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect golang.org/x/arch v0.11.0 // indirect - golang.org/x/sys v0.28.0 // indirect + golang.org/x/sys v0.47.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/locate/redis/go.sum b/locate/redis/go.sum index 67948c03..760e2d7e 100644 --- a/locate/redis/go.sum +++ b/locate/redis/go.sum @@ -50,6 +50,7 @@ golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/lock/memcache/go.mod b/lock/memcache/go.mod index 6ce00f77..c7f0ef32 100644 --- a/lock/memcache/go.mod +++ b/lock/memcache/go.mod @@ -21,7 +21,7 @@ require ( github.com/twitchyliquid64/golang-asm v0.15.1 // indirect golang.org/x/arch v0.11.0 // indirect golang.org/x/sync v0.20.0 // indirect - golang.org/x/sys v0.28.0 // indirect + golang.org/x/sys v0.47.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/lock/memcache/go.sum b/lock/memcache/go.sum index 9f86a8e8..92191459 100644 --- a/lock/memcache/go.sum +++ b/lock/memcache/go.sum @@ -42,6 +42,7 @@ golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/lock/redis/go.mod b/lock/redis/go.mod index 56e4efb2..158fbb65 100644 --- a/lock/redis/go.mod +++ b/lock/redis/go.mod @@ -23,7 +23,7 @@ require ( github.com/twitchyliquid64/golang-asm v0.15.1 // indirect golang.org/x/arch v0.11.0 // indirect golang.org/x/sync v0.20.0 // indirect - golang.org/x/sys v0.30.0 // indirect + golang.org/x/sys v0.47.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/lock/redis/go.sum b/lock/redis/go.sum index a6eff65b..cde4acc4 100644 --- a/lock/redis/go.sum +++ b/lock/redis/go.sum @@ -50,6 +50,7 @@ golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/log/aliyun/go.mod b/log/aliyun/go.mod index db08feec..ffb29191 100644 --- a/log/aliyun/go.mod +++ b/log/aliyun/go.mod @@ -15,7 +15,7 @@ require ( github.com/bytedance/sonic v1.14.2 // indirect github.com/bytedance/sonic/loader v0.4.0 // indirect github.com/cenkalti/backoff v2.2.1+incompatible // indirect - github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cloudwego/base64x v0.1.6 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dennwc/varint v1.0.0 // indirect @@ -46,7 +46,7 @@ require ( golang.org/x/exp v0.0.0-20221031165847-c99f073a8326 // indirect golang.org/x/net v0.30.0 // indirect golang.org/x/sync v0.20.0 // indirect - golang.org/x/sys v0.28.0 // indirect + golang.org/x/sys v0.47.0 // indirect google.golang.org/protobuf v1.36.6 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/log/aliyun/go.sum b/log/aliyun/go.sum index 105d3db4..e852805a 100644 --- a/log/aliyun/go.sum +++ b/log/aliyun/go.sum @@ -88,6 +88,7 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -447,6 +448,7 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/log/tencent/go.mod b/log/tencent/go.mod index 075c0457..dd0e8013 100644 --- a/log/tencent/go.mod +++ b/log/tencent/go.mod @@ -24,7 +24,7 @@ require ( go.uber.org/atomic v1.11.0 // indirect golang.org/x/arch v0.11.0 // indirect golang.org/x/sync v0.20.0 // indirect - golang.org/x/sys v0.28.0 // indirect + golang.org/x/sys v0.47.0 // indirect google.golang.org/protobuf v1.36.6 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/log/tencent/go.sum b/log/tencent/go.sum index f83830a4..3f52e0e2 100644 --- a/log/tencent/go.sum +++ b/log/tencent/go.sum @@ -68,6 +68,7 @@ golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= diff --git a/network/kcp/go.mod b/network/kcp/go.mod index 846b8d8d..a56e3a46 100644 --- a/network/kcp/go.mod +++ b/network/kcp/go.mod @@ -28,7 +28,7 @@ require ( golang.org/x/crypto v0.35.0 // indirect golang.org/x/net v0.36.0 // indirect golang.org/x/sync v0.20.0 // indirect - golang.org/x/sys v0.30.0 // indirect + golang.org/x/sys v0.47.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/network/kcp/go.sum b/network/kcp/go.sum index f536fee3..15132b05 100644 --- a/network/kcp/go.sum +++ b/network/kcp/go.sum @@ -101,6 +101,7 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/network/metrics.go b/network/metrics.go new file mode 100644 index 00000000..36e99330 --- /dev/null +++ b/network/metrics.go @@ -0,0 +1,248 @@ +package network + +import ( + "context" + "fmt" + "sync" + "sync/atomic" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric" +) + +const meterName = "due.network" + +const ( + metricMessage = "network_message" + metricError = "network_error" + metricReceiveBytes = "network_receive_bytes" + metricSendBytes = "network_send_bytes" + metricConnectionOpen = "network_connection_open" + metricConnectionClose = "network_connection_close" + metricConnectionActive = "network_connection_active" +) + +type Role string + +const ( + NetServer Role = "server" + NetClient Role = "client" +) + +type Operation string + +const ( + OperationAccept Operation = "accept" + OperationAuthorize Operation = "authorize" + OperationClose Operation = "close" + OperationDecode Operation = "decode" + OperationDial Operation = "dial" + OperationHeartbeat Operation = "heartbeat" + OperationListen Operation = "listen" + OperationQueue Operation = "queue" + OperationRead Operation = "read" + OperationResolve Operation = "resolve" + OperationWrite Operation = "write" +) + +type ErrorType string + +const ( + ErrorTypeConnectionLimit ErrorType = "connection_limit" + ErrorTypeIO ErrorType = "io" + ErrorTypeInvalidMessage ErrorType = "invalid_message" + ErrorTypeTimeout ErrorType = "timeout" + ErrorTypeTLS ErrorType = "tls" +) + +var ( + meter = otel.Meter(meterName) + + messages metric.Int64ObservableCounter + errorsTotal metric.Int64Counter + receiveBytes metric.Int64ObservableCounter + sendBytes metric.Int64ObservableCounter + connectionsOpen metric.Int64Counter + connectionsClose metric.Int64Counter + connectionsAlive metric.Int64UpDownCounter + + metricsContext = context.Background() + recorders sync.Map +) + +func init() { + var err error + + messages, err = meter.Int64ObservableCounter( + metricMessage, + metric.WithDescription("Counts successfully transferred application messages."), + metric.WithUnit("{message}"), + ) + if err != nil { + panic(fmt.Errorf("create network message counter: %w", err)) + } + + errorsTotal, err = meter.Int64Counter( + metricError, + metric.WithDescription("Counts network transport errors."), + metric.WithUnit("{error}"), + ) + if err != nil { + panic(fmt.Errorf("create network error counter: %w", err)) + } + + receiveBytes, err = meter.Int64ObservableCounter( + metricReceiveBytes, + metric.WithDescription("Counts received Due protocol frame bytes."), + metric.WithUnit("By"), + ) + if err != nil { + panic(fmt.Errorf("create network receive bytes counter: %w", err)) + } + + sendBytes, err = meter.Int64ObservableCounter( + metricSendBytes, + metric.WithDescription("Counts sent Due protocol frame bytes."), + metric.WithUnit("By"), + ) + if err != nil { + panic(fmt.Errorf("create network send bytes counter: %w", err)) + } + + connectionsOpen, err = meter.Int64Counter( + metricConnectionOpen, + metric.WithDescription("Counts opened network connections."), + metric.WithUnit("{connection}"), + ) + if err != nil { + panic(fmt.Errorf("create network connection open counter: %w", err)) + } + + connectionsClose, err = meter.Int64Counter( + metricConnectionClose, + metric.WithDescription("Counts closed network connections."), + metric.WithUnit("{connection}"), + ) + if err != nil { + panic(fmt.Errorf("create network connection close counter: %w", err)) + } + + connectionsAlive, err = meter.Int64UpDownCounter( + metricConnectionActive, + metric.WithDescription("Tracks currently active network connections."), + metric.WithUnit("{connection}"), + ) + if err != nil { + panic(fmt.Errorf("create network active connection counter: %w", err)) + } + + if _, err = meter.RegisterCallback(observeMetrics, messages, receiveBytes, sendBytes); err != nil { + panic(fmt.Errorf("register network metrics callback: %w", err)) + } +} + +type MetricsRecorder struct { + transportAttr attribute.KeyValue + roleAttr attribute.KeyValue + base metric.MeasurementOption + receive metric.MeasurementOption + send metric.MeasurementOption + receiveBytes atomic.Int64 + receiveMessages atomic.Int64 + sendBytes atomic.Int64 + sendMessages atomic.Int64 +} + +func NewMetricsRecorder(transport string, role Role) *MetricsRecorder { + key := metricsRecorderKey{transport: transport, role: role} + if recorder, ok := recorders.Load(key); ok { + return recorder.(*MetricsRecorder) + } + + transportAttr := attribute.String("network.transport", transport) + roleAttr := attribute.String("network.role", string(role)) + + recorder := &MetricsRecorder{ + transportAttr: transportAttr, + roleAttr: roleAttr, + base: metric.WithAttributeSet(attribute.NewSet( + transportAttr, + roleAttr, + )), + receive: metric.WithAttributeSet(attribute.NewSet( + transportAttr, + roleAttr, + attribute.String("network.direction", "receive"), + )), + send: metric.WithAttributeSet(attribute.NewSet( + transportAttr, + roleAttr, + attribute.String("network.direction", "send"), + )), + } + actual, _ := recorders.LoadOrStore(key, recorder) + return actual.(*MetricsRecorder) +} + +func (r *MetricsRecorder) Receive(size int, isMessage bool) { + if size <= 0 { + return + } + + r.receiveBytes.Add(int64(size)) + if isMessage { + r.receiveMessages.Add(1) + } +} + +func (r *MetricsRecorder) MessageReceived() { + r.receiveMessages.Add(1) +} + +func (r *MetricsRecorder) Send(size int, isMessage bool) { + if size <= 0 { + return + } + + r.sendBytes.Add(int64(size)) + if isMessage { + r.sendMessages.Add(1) + } +} + +func (r *MetricsRecorder) Error(operation Operation, errorType ErrorType) { + errorsTotal.Add(metricsContext, 1, metric.WithAttributes( + r.transportAttr, + r.roleAttr, + attribute.String("network.operation", string(operation)), + attribute.String("error.type", string(errorType)), + )) +} + +func (r *MetricsRecorder) ConnectionOpened() { + connectionsOpen.Add(metricsContext, 1, r.base) + connectionsAlive.Add(metricsContext, 1, r.base) +} + +func (r *MetricsRecorder) ConnectionClosed() { + connectionsClose.Add(metricsContext, 1, r.base) + connectionsAlive.Add(metricsContext, -1, r.base) +} + +type metricsRecorderKey struct { + transport string + role Role +} + +func observeMetrics(_ context.Context, observer metric.Observer) error { + recorders.Range(func(_, value any) bool { + recorder := value.(*MetricsRecorder) + observer.ObserveInt64(receiveBytes, recorder.receiveBytes.Load(), recorder.base) + observer.ObserveInt64(sendBytes, recorder.sendBytes.Load(), recorder.base) + observer.ObserveInt64(messages, recorder.receiveMessages.Load(), recorder.receive) + observer.ObserveInt64(messages, recorder.sendMessages.Load(), recorder.send) + return true + }) + return nil +} diff --git a/network/metrics_test.go b/network/metrics_test.go new file mode 100644 index 00000000..6ac68069 --- /dev/null +++ b/network/metrics_test.go @@ -0,0 +1,203 @@ +package network + +import ( + "context" + "os" + "testing" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/sdk/metric" + "go.opentelemetry.io/otel/sdk/metric/metricdata" +) + +var testMetricReader *metric.ManualReader + +func TestMain(m *testing.M) { + testMetricReader = metric.NewManualReader() + provider := metric.NewMeterProvider(metric.WithReader(testMetricReader)) + otel.SetMeterProvider(provider) + + code := m.Run() + _ = provider.Shutdown(context.Background()) + os.Exit(code) +} + +func TestMetricsRecorderRecordsNetworkEvents(t *testing.T) { + recorder := NewMetricsRecorder("tcp", NetServer) + recorder.Receive(128, true) + recorder.Receive(5, false) + recorder.Send(64, true) + recorder.Send(5, false) + recorder.Error(OperationRead, ErrorTypeIO) + recorder.ConnectionOpened() + recorder.ConnectionClosed() + + data := collectMetrics(t) + + message := metricByName(t, data, "network_message") + messageSum, ok := message.Data.(metricdata.Sum[int64]) + if !ok { + t.Fatalf("network_message data = %T, want Sum[int64]", message.Data) + } + if got := sumByDirection(messageSum, "receive"); got != 1 { + t.Fatalf("received message count = %d, want 1", got) + } + if got := sumByDirection(messageSum, "send"); got != 1 { + t.Fatalf("sent message count = %d, want 1", got) + } + + if got := sumMetric[int64](t, data, "network_receive_bytes"); got != 133 { + t.Fatalf("received bytes = %d, want 133", got) + } + if got := sumMetric[int64](t, data, "network_send_bytes"); got != 69 { + t.Fatalf("sent bytes = %d, want 69", got) + } + if got := sumMetric[int64](t, data, "network_error"); got != 1 { + t.Fatalf("error count = %d, want 1", got) + } + if got := sumMetric[int64](t, data, "network_connection_open"); got != 1 { + t.Fatalf("opened connection count = %d, want 1", got) + } + if got := sumMetric[int64](t, data, "network_connection_close"); got != 1 { + t.Fatalf("closed connection count = %d, want 1", got) + } + if got := sumMetric[int64](t, data, "network_connection_active"); got != 0 { + t.Fatalf("active connections = %d, want 0", got) + } + + if got := message.Unit; got != "{message}" { + t.Fatalf("message unit = %q, want {message}", got) + } + if got := metricByName(t, data, "network_receive_bytes").Unit; got != "By" { + t.Fatalf("receive bytes unit = %q, want By", got) + } +} + +func TestMetricsRecorderIgnoresNonPositiveMeasurements(t *testing.T) { + recorder := NewMetricsRecorder("tcp", NetClient) + recorder.Receive(0, true) + recorder.Send(-1, true) + + data := collectMetrics(t) + if got := sumMetricWithAttribute[int64](t, data, "network_receive_bytes", "network.role", "client"); got != 0 { + t.Fatalf("received bytes = %d, want 0", got) + } + if got := sumMetricWithAttribute[int64](t, data, "network_send_bytes", "network.role", "client"); got != 0 { + t.Fatalf("sent bytes = %d, want 0", got) + } + + if got := sumMessageByAttributes(t, data, "client", "receive"); got != 0 { + t.Fatalf("received message count = %d, want 0", got) + } + if got := sumMessageByAttributes(t, data, "client", "send"); got != 0 { + t.Fatalf("sent message count = %d, want 0", got) + } +} + +func TestNewMetricsRecorderReusesRecorderByTransportAndRole(t *testing.T) { + server := NewMetricsRecorder("tcp-cache-test", NetServer) + if got := NewMetricsRecorder("tcp-cache-test", NetServer); got != server { + t.Fatal("same transport and role must reuse metrics recorder") + } + if got := NewMetricsRecorder("tcp-cache-test", NetClient); got == server { + t.Fatal("different role must use a different metrics recorder") + } + if got := NewMetricsRecorder("ws-cache-test", NetServer); got == server { + t.Fatal("different transport must use a different metrics recorder") + } +} + +func collectMetrics(t *testing.T) metricdata.ResourceMetrics { + t.Helper() + var data metricdata.ResourceMetrics + if err := testMetricReader.Collect(context.Background(), &data); err != nil { + t.Fatalf("collect metrics: %v", err) + } + return data +} + +func metricByName(t *testing.T, data metricdata.ResourceMetrics, name string) metricdata.Metrics { + t.Helper() + for _, scope := range data.ScopeMetrics { + if scope.Scope.Name != "due.network" { + continue + } + for _, m := range scope.Metrics { + if m.Name == name { + return m + } + } + } + t.Fatalf("metric %q was not collected", name) + return metricdata.Metrics{} +} + +func sumMetric[T int64 | float64](t *testing.T, data metricdata.ResourceMetrics, name string) T { + return sumMetricWithAttribute[T](t, data, name, "", "") +} + +func sumMetricWithAttribute[T int64 | float64](t *testing.T, data metricdata.ResourceMetrics, name, key, want string) T { + t.Helper() + m := metricByName(t, data, name) + switch sum := m.Data.(type) { + case metricdata.Sum[int64]: + var total int64 + for _, point := range sum.DataPoints { + if key != "" { + value, ok := point.Attributes.Value(attribute.Key(key)) + if !ok || value.AsString() != want { + continue + } + } + total += point.Value + } + return T(total) + case metricdata.Sum[float64]: + var total float64 + for _, point := range sum.DataPoints { + if key != "" { + value, ok := point.Attributes.Value(attribute.Key(key)) + if !ok || value.AsString() != want { + continue + } + } + total += point.Value + } + return T(total) + default: + t.Fatalf("metric %q data = %T, want Sum", name, m.Data) + return 0 + } +} + +func sumByDirection(sum metricdata.Sum[int64], direction string) int64 { + var total int64 + for _, point := range sum.DataPoints { + if value, ok := point.Attributes.Value("network.direction"); ok && value.AsString() == direction { + total += point.Value + } + } + return total +} + +func sumMessageByAttributes(t *testing.T, data metricdata.ResourceMetrics, role, direction string) int64 { + t.Helper() + message := metricByName(t, data, "network_message") + messageSum, ok := message.Data.(metricdata.Sum[int64]) + if !ok { + t.Fatalf("network_message data = %T, want Sum[int64]", message.Data) + } + + var total int64 + for _, point := range messageSum.DataPoints { + if value, ok := point.Attributes.Value("network.role"); !ok || value.AsString() != role { + continue + } + if value, ok := point.Attributes.Value("network.direction"); !ok || value.AsString() != direction { + continue + } + total += point.Value + } + return total +} diff --git a/network/tcp/client.go b/network/tcp/client.go index 024923ee..05ee433d 100644 --- a/network/tcp/client.go +++ b/network/tcp/client.go @@ -12,6 +12,7 @@ import ( type client struct { opts *clientOptions // 配置 id atomic.Int64 // 连接ID + metrics *network.MetricsRecorder // 指标记录器 connectHandler network.ConnectHandler // 连接打开hook函数 disconnectHandler network.DisconnectHandler // 连接关闭hook函数 receiveHandler network.ReceiveHandler // 接收消息hook函数 @@ -25,7 +26,10 @@ func NewClient(opts ...ClientOption) network.Client { opt(o) } - return &client{opts: o} + return &client{ + opts: o, + metrics: network.NewMetricsRecorder(protocol, network.NetClient), + } } // Dial 拨号连接 @@ -43,22 +47,26 @@ func (c *client) Dial(addr ...string) (network.Conn, error) { tcpAddr, err := net.ResolveTCPAddr("tcp", address) if err != nil { + recordError(c.metrics, network.OperationResolve, err) return nil, err } if c.opts.caFile != "" { config, err := ctls.MakeTCPClientTLSConfig(c.opts.caFile, c.opts.serverName) if err != nil { + c.metrics.Error(network.OperationDial, network.ErrorTypeTLS) return nil, err } dialer := &net.Dialer{Timeout: c.opts.dialTimeout} if conn, err = tls.DialWithDialer(dialer, tcpAddr.Network(), tcpAddr.String(), config); err != nil { + recordError(c.metrics, network.OperationDial, err) return nil, err } } else { if conn, err = net.DialTimeout(tcpAddr.Network(), tcpAddr.String(), c.opts.dialTimeout); err != nil { + recordError(c.metrics, network.OperationDial, err) return nil, err } } diff --git a/network/tcp/client_conn.go b/network/tcp/client_conn.go index 356d7d84..78b656d0 100644 --- a/network/tcp/client_conn.go +++ b/network/tcp/client_conn.go @@ -47,6 +47,7 @@ func newClientConn(client *client, id int64, conn net.Conn) network.Conn { c.state.Store(int32(network.ConnOpened)) c.lastHeartbeatTime.Store(xtime.Now().UnixNano()) + c.client.metrics.ConnectionOpened() xcall.Go(c.read) @@ -87,6 +88,7 @@ func (c *clientConn) Unbind() { // Send 发送消息(同步) func (c *clientConn) Send(msg []byte) error { if err := c.checkState(); err != nil { + recordError(c.client.metrics, network.OperationWrite, err) return err } @@ -95,16 +97,20 @@ func (c *clientConn) Send(msg []byte) error { c.rw.RUnlock() if conn == nil { + recordError(c.client.metrics, network.OperationWrite, errors.ErrConnectionClosed) return errors.ErrConnectionClosed } - _, err := conn.Write(msg) + n, err := conn.Write(msg) + c.client.metrics.Send(n, err == nil && n == len(msg)) + recordError(c.client.metrics, network.OperationWrite, err) return err } // Push 发送消息(异步) func (c *clientConn) Push(msg []byte) error { if err := c.checkState(); err != nil { + recordError(c.client.metrics, network.OperationWrite, err) return err } @@ -112,6 +118,7 @@ func (c *clientConn) Push(msg []byte) error { defer c.rw.RUnlock() if c.conn == nil { + recordError(c.client.metrics, network.OperationWrite, errors.ErrConnectionClosed) return errors.ErrConnectionClosed } @@ -249,6 +256,8 @@ func (c *clientConn) doClose() error { c.rw.Unlock() err := conn.Close() + c.client.metrics.ConnectionClosed() + recordError(c.client.metrics, network.OperationClose, err) if c.client.disconnectHandler != nil { c.client.disconnectHandler(c) @@ -271,8 +280,9 @@ func (c *clientConn) read() { case <-c.close: return default: - data, err := packet.ReadMessage(conn) + data, err := readMessage(conn, c.client.metrics) if err != nil { + recordError(c.client.metrics, network.OperationRead, err) _ = c.forceClose() return } @@ -297,6 +307,7 @@ func (c *clientConn) read() { isHeartbeat, err := packet.CheckHeartbeat(data) if err != nil { + recordError(c.client.metrics, network.OperationDecode, err) log.Errorf("check heartbeat message error: %v", err) continue } @@ -306,6 +317,8 @@ func (c *clientConn) read() { continue } + c.client.metrics.MessageReceived() + if c.client.receiveHandler != nil { c.client.receiveHandler(c, data) } @@ -366,7 +379,10 @@ func (c *clientConn) doWrite(conn net.Conn, t *task) bool { return false } - if _, err := conn.Write(t.msg); err != nil { + n, err := conn.Write(t.msg) + c.client.metrics.Send(n, err == nil && n == len(t.msg)) + if err != nil { + recordError(c.client.metrics, network.OperationWrite, err) log.Errorf("write message error: %v", err) } @@ -378,6 +394,7 @@ func (c *clientConn) doHandleHeartbeat(conn net.Conn, t time.Time) bool { deadline := t.Add(-2 * c.client.opts.heartbeatInterval).UnixNano() if c.lastHeartbeatTime.Load() < deadline { + c.client.metrics.Error(network.OperationHeartbeat, network.ErrorTypeTimeout) log.Debugf("connection heartbeat timeout") _ = c.forceClose() return false @@ -387,9 +404,13 @@ func (c *clientConn) doHandleHeartbeat(conn net.Conn, t time.Time) bool { } if heartbeat, err := packet.PackHeartbeat(); err != nil { + recordError(c.client.metrics, network.OperationHeartbeat, err) log.Errorf("pack heartbeat message error: %v", err) } else { - if _, err = conn.Write(heartbeat); err != nil { + n, err := conn.Write(heartbeat) + c.client.metrics.Send(n, false) + if err != nil { + recordError(c.client.metrics, network.OperationHeartbeat, err) log.Errorf("write heartbeat message error: %v", err) } } @@ -419,6 +440,7 @@ func (c *clientConn) doWriteToQueue(queue chan *task, typ int8, msg ...[]byte) e select { case <-ctx.Done(): c.doRecycleToPool(t) + recordError(c.client.metrics, network.OperationQueue, ctx.Err()) return ctx.Err() case queue <- t: return nil diff --git a/network/tcp/client_options.go b/network/tcp/client_options.go index 36bb5ca7..74d434f1 100644 --- a/network/tcp/client_options.go +++ b/network/tcp/client_options.go @@ -23,6 +23,7 @@ const ( defaultClientDialTimeoutKey = "etc.network.tcp.client.dialTimeout" defaultClientWriteTimeoutKey = "etc.network.tcp.client.writeTimeout" defaultClientWriteQueueSizeKey = "etc.network.tcp.client.writeQueueSize" + defaultClientMetricsEnabledKey = "etc.network.tcp.client.metrics.enable" defaultClientHeartbeatIntervalKey = "etc.network.tcp.client.heartbeatInterval" ) @@ -36,12 +37,14 @@ type clientOptions struct { writeTimeout time.Duration // 写超时时间,默认无超时 writeQueueSize int // 写队列大小,默认1024 heartbeatInterval time.Duration // 心跳间隔时间,默认10s + metricsEnabled bool // 是否开启指标采集 } func defaultClientOptions() *clientOptions { opts := &clientOptions{} opts.caFile = etc.Get(defaultClientCAFileKey).String() opts.serverName = etc.Get(defaultClientServerNameKey).String() + opts.metricsEnabled = etc.Get(defaultClientMetricsEnabledKey).Bool() if addr := etc.Get(defaultClientAddrKey, defaultClientAddr).String(); addr != "" { opts.addr = addr @@ -141,3 +144,8 @@ func WithClientHeartbeatInterval(heartbeatInterval time.Duration) ClientOption { } } } + +// WithClientMetricsEnabled 设置是否开启指标采集 +func WithClientMetricsEnabled(enabled bool) ClientOption { + return func(o *clientOptions) { o.metricsEnabled = enabled } +} diff --git a/network/tcp/go.mod b/network/tcp/go.mod index d700c5c8..da40ab57 100644 --- a/network/tcp/go.mod +++ b/network/tcp/go.mod @@ -4,20 +4,33 @@ go 1.25.0 require github.com/dobyte/due/v2 v2.5.8 +require ( + go.opentelemetry.io/otel v1.45.0 + go.opentelemetry.io/otel/sdk/metric v1.45.0 +) + require ( dario.cat/mergo v1.0.2 // indirect github.com/BurntSushi/toml v1.6.0 // indirect github.com/bytedance/gopkg v0.1.3 // indirect github.com/bytedance/sonic v1.14.2 // indirect github.com/bytedance/sonic/loader v0.4.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cloudwego/base64x v0.1.6 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect + github.com/go-logr/logr v1.4.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/jinzhu/copier v0.4.0 // indirect github.com/klauspost/cpuid/v2 v2.2.9 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/otel/metric v1.45.0 // indirect + go.opentelemetry.io/otel/sdk v1.45.0 // indirect + go.opentelemetry.io/otel/trace v1.45.0 // indirect golang.org/x/arch v0.11.0 // indirect golang.org/x/sync v0.20.0 // indirect - golang.org/x/sys v0.28.0 // indirect + golang.org/x/sys v0.47.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/network/tcp/go.sum b/network/tcp/go.sum index f9f9f81f..15b21561 100644 --- a/network/tcp/go.sum +++ b/network/tcp/go.sum @@ -8,6 +8,8 @@ github.com/bytedance/sonic v1.14.2 h1:k1twIoe97C1DtYUo+fZQy865IuHia4PR5RPiuGPPII github.com/bytedance/sonic v1.14.2/go.mod h1:T80iDELeHiHKSc0C9tubFygiuXoGzrkjKzX2quAx980= github.com/bytedance/sonic/loader v0.4.0 h1:olZ7lEqcxtZygCK9EKYKADnpQoYkRQxaeY2NYzevs+o= github.com/bytedance/sonic/loader v0.4.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -15,12 +17,27 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8= +github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8= github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY= github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -28,18 +45,34 @@ github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= 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= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/otel v1.45.0 h1:pdrWmLHofpubmArBv1LgFSv1Z0Ie/ppdZzu+kUN5EeU= +go.opentelemetry.io/otel v1.45.0/go.mod h1:XZxIqPapzEYnhNSScF5DIqXhm/rYi0FzCe2XddAwZfQ= +go.opentelemetry.io/otel/metric v1.45.0 h1:7Eg1uH7CJ5cXv9is6tnBe1FI6rj1nwUdbFypRm3br/M= +go.opentelemetry.io/otel/metric v1.45.0/go.mod h1:HAPbm1nd3p1PmFH7v2dR+6BjXxw+Lq4a2+pndMAm08s= +go.opentelemetry.io/otel/metric/x v0.67.0 h1:PcicCNZFkZ4bXfSooXdo3WN7RBOVOtjVdo1wD358Uns= +go.opentelemetry.io/otel/metric/x v0.67.0/go.mod h1:FBjCWZe6wgcqxcMtjdGiClDKXb2YxxXii0CXftE4QtI= +go.opentelemetry.io/otel/sdk v1.45.0 h1:4VVSMgQ83dUgW2aoX5f6JgLvHwIvzcuLnF9lUdCSpCw= +go.opentelemetry.io/otel/sdk v1.45.0/go.mod h1:Sr40LgXV7DsKMMJMKOhUWOgMWTfAaqvm2kF0g7ilwuA= +go.opentelemetry.io/otel/sdk/metric v1.45.0 h1:oVFszMfyj1Am6s24Vtc7wBb8BKLcwepJjNEYILuiE3o= +go.opentelemetry.io/otel/sdk/metric v1.45.0/go.mod h1:vUWUxDZvu1WVRj8JA8S0AdhsPrZoDpA2DdZauIh4mDA= +go.opentelemetry.io/otel/trace v1.45.0 h1:l/mP6Uv7oNO7/TblbhpbgMidxhq1uO/rPsikOyVhxag= +go.opentelemetry.io/otel/trace v1.45.0/go.mod h1:qoJJA2xNMnxRrdISU/kLtfUH2wNeQbiv+jhs/CxI8bc= golang.org/x/arch v0.11.0 h1:KXV8WWKCXm6tRpLirl2szsO5j/oOODwZf4hATmGVNs4= golang.org/x/arch v0.11.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= -golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/network/tcp/metrics.go b/network/tcp/metrics.go new file mode 100644 index 00000000..dbf2252b --- /dev/null +++ b/network/tcp/metrics.go @@ -0,0 +1,76 @@ +package tcp + +import ( + "context" + "crypto/tls" + "crypto/x509" + "io" + "net" + + "github.com/dobyte/due/v2/errors" + "github.com/dobyte/due/v2/network" + "github.com/dobyte/due/v2/packet" +) + +type countingConn struct { + net.Conn + bytes int +} + +func (c *countingConn) Read(p []byte) (int, error) { + n, err := c.Conn.Read(p) + c.bytes += n + return n, err +} + +func readMessage(conn net.Conn, metrics *network.MetricsRecorder) ([]byte, error) { + reader := &countingConn{Conn: conn} + data, err := packet.ReadMessage(reader) + metrics.Receive(reader.bytes, false) + return data, err +} + +func recordError(metrics *network.MetricsRecorder, operation network.Operation, err error) { + if err == nil || isNormalClose(err) { + return + } + + errorType := network.ErrorTypeIO + switch { + case errors.Is(err, errors.ErrInvalidMessage): + errorType = network.ErrorTypeInvalidMessage + case errors.Is(err, errors.ErrTooManyConnection): + errorType = network.ErrorTypeConnectionLimit + case errors.Is(err, context.DeadlineExceeded): + errorType = network.ErrorTypeTimeout + case isTLSError(err): + errorType = network.ErrorTypeTLS + default: + var netErr net.Error + if errors.As(err, &netErr) && netErr.Timeout() { + errorType = network.ErrorTypeTimeout + } + } + + metrics.Error(operation, errorType) +} + +func isTLSError(err error) bool { + var ( + alertErr tls.AlertError + recordErr tls.RecordHeaderError + certificateErr x509.CertificateInvalidError + hostnameErr x509.HostnameError + authorityErr x509.UnknownAuthorityError + ) + + return errors.As(err, &alertErr) || + errors.As(err, &recordErr) || + errors.As(err, &certificateErr) || + errors.As(err, &hostnameErr) || + errors.As(err, &authorityErr) +} + +func isNormalClose(err error) bool { + return errors.Is(err, io.EOF) || errors.Is(err, net.ErrClosed) +} diff --git a/network/tcp/metrics_test.go b/network/tcp/metrics_test.go new file mode 100644 index 00000000..4ce64625 --- /dev/null +++ b/network/tcp/metrics_test.go @@ -0,0 +1,442 @@ +package tcp + +import ( + "context" + "crypto/tls" + "errors" + "fmt" + "io" + "net" + "os" + "testing" + "time" + + dueerrors "github.com/dobyte/due/v2/errors" + "github.com/dobyte/due/v2/etc" + "github.com/dobyte/due/v2/network" + "github.com/dobyte/due/v2/packet" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/sdk/metric" + "go.opentelemetry.io/otel/sdk/metric/metricdata" +) + +var testMetricReader *metric.ManualReader + +func TestMain(m *testing.M) { + testMetricReader = metric.NewManualReader() + provider := metric.NewMeterProvider(metric.WithReader(testMetricReader)) + otel.SetMeterProvider(provider) + + code := m.Run() + _ = provider.Shutdown(context.Background()) + os.Exit(code) +} + +func TestTCPMetrics(t *testing.T) { + before := collectMetrics(t) + + serverReceived := make(chan struct{}, 1) + serverClosed := make(chan struct{}, 1) + clientReceived := make(chan struct{}, 1) + clientClosed := make(chan struct{}, 1) + + server := NewServer( + WithServerAddr("127.0.0.1:0"), + WithServerHeartbeatInterval(0), + WithServerMetricsEnabled(true), + ).(*server) + server.OnReceive(func(conn network.Conn, data []byte) { + select { + case serverReceived <- struct{}{}: + default: + } + if err := conn.Send(data); err != nil { + t.Errorf("echo message: %v", err) + } + }) + server.OnDisconnect(func(network.Conn) { + select { + case serverClosed <- struct{}{}: + default: + } + }) + if err := server.Start(); err != nil { + t.Fatalf("start server: %v", err) + } + defer server.Stop() + + client := NewClient( + WithClientAddr(server.listener.Addr().String()), + WithClientHeartbeatInterval(0), + WithClientMetricsEnabled(true), + ).(*client) + client.OnReceive(func(network.Conn, []byte) { + select { + case clientReceived <- struct{}{}: + default: + } + }) + client.OnDisconnect(func(network.Conn) { + select { + case clientClosed <- struct{}{}: + default: + } + }) + + conn, err := client.Dial() + if err != nil { + t.Fatalf("dial server: %v", err) + } + defer conn.Close(true) + + message, err := packet.PackMessage(&packet.Message{Route: 1, Buffer: []byte("hello")}) + if err != nil { + t.Fatalf("pack message: %v", err) + } + + if err := conn.Send(message); err != nil { + t.Fatalf("send message: %v", err) + } + waitForSignal(t, serverReceived, "server receive") + waitForSignal(t, clientReceived, "client receive") + + if err := conn.Close(true); err != nil { + t.Fatalf("close client connection: %v", err) + } + waitForSignal(t, clientClosed, "client close") + waitForSignal(t, serverClosed, "server close") + + after := collectMetrics(t) + if got := deltaMetric(before, after, "network_message", "client", "send"); got != 1 { + t.Fatalf("client sent messages = %d, want 1", got) + } + if got := deltaMetric(before, after, "network_message", "client", "receive"); got != 1 { + t.Fatalf("client received messages = %d, want 1", got) + } + if got := deltaMetric(before, after, "network_message", "server", "receive"); got != 1 { + t.Fatalf("server received messages = %d, want 1", got) + } + if got := deltaMetric(before, after, "network_message", "server", "send"); got != 1 { + t.Fatalf("server sent messages = %d, want 1", got) + } + if got := deltaMetric(before, after, "network_send_bytes", "client", ""); got != int64(len(message)) { + t.Fatalf("client sent bytes = %d, want %d", got, len(message)) + } + if got := deltaMetric(before, after, "network_receive_bytes", "server", ""); got != int64(len(message)) { + t.Fatalf("server received bytes = %d, want %d", got, len(message)) + } + if got := deltaMetric(before, after, "network_send_bytes", "server", ""); got != int64(len(message)) { + t.Fatalf("server sent bytes = %d, want %d", got, len(message)) + } + if got := deltaMetric(before, after, "network_receive_bytes", "client", ""); got != int64(len(message)) { + t.Fatalf("client received bytes = %d, want %d", got, len(message)) + } + if got := deltaMetric(before, after, "network_connection_open", "client", ""); got != 1 { + t.Fatalf("client opened connections = %d, want 1", got) + } + if got := deltaMetric(before, after, "network_connection_open", "server", ""); got != 1 { + t.Fatalf("server opened connections = %d, want 1", got) + } + if got := deltaMetric(before, after, "network_connection_close", "client", ""); got != 1 { + t.Fatalf("client closed connections = %d, want 1", got) + } + if got := deltaMetric(before, after, "network_connection_close", "server", ""); got != 1 { + t.Fatalf("server closed connections = %d, want 1", got) + } + if got := deltaMetric(before, after, "network_error", "client", ""); got != 0 { + t.Fatalf("client errors = %d, want 0", got) + } + if got := deltaMetric(before, after, "network_error", "server", ""); got != 0 { + t.Fatalf("server errors = %d, want 0", got) + } +} + +func TestMetricsEnabledFollowsConfigAndOptions(t *testing.T) { + const ( + clientEnabledKey = "etc.network.tcp.client.metrics.enable" + serverEnabledKey = "etc.network.tcp.server.metrics.enable" + ) + + originalClientEnabled := etc.Get(clientEnabledKey).Bool() + originalServerEnabled := etc.Get(serverEnabledKey).Bool() + t.Cleanup(func() { + if err := etc.Set(clientEnabledKey, originalClientEnabled); err != nil { + t.Errorf("restore client metrics config: %v", err) + } + if err := etc.Set(serverEnabledKey, originalServerEnabled); err != nil { + t.Errorf("restore server metrics config: %v", err) + } + }) + + if err := etc.Set(clientEnabledKey, true); err != nil { + t.Fatalf("enable client metrics config: %v", err) + } + if err := etc.Set(serverEnabledKey, false); err != nil { + t.Fatalf("disable server metrics config: %v", err) + } + if got := NewServer().(*server).metrics; got != nil { + t.Fatal("server metrics must be disabled by config") + } + if got := NewClient().(*client).metrics; got == nil { + t.Fatal("client metrics must be enabled by config") + } + if got := NewServer(WithServerMetricsEnabled(true)).(*server).metrics; got == nil { + t.Fatal("server option must enable metrics") + } + if got := NewClient(WithClientMetricsEnabled(false)).(*client).metrics; got != nil { + t.Fatal("client option must disable metrics") + } + + if err := etc.Set(clientEnabledKey, false); err != nil { + t.Fatalf("disable client metrics config: %v", err) + } + if err := etc.Set(serverEnabledKey, true); err != nil { + t.Fatalf("enable server metrics config: %v", err) + } + if got := NewServer().(*server).metrics; got == nil { + t.Fatal("server metrics must be enabled by config") + } + if got := NewClient().(*client).metrics; got != nil { + t.Fatal("client metrics must be disabled by config") + } + if got := NewServer(WithServerMetricsEnabled(false)).(*server).metrics; got != nil { + t.Fatal("server option must disable metrics") + } + if got := NewClient(WithClientMetricsEnabled(true)).(*client).metrics; got == nil { + t.Fatal("client option must enable metrics") + } +} + +func TestReadTCPMessageRecordsPartialAndEmptyFrames(t *testing.T) { + frames := []struct { + name string + data []byte + wantErr bool + }{ + {name: "empty frame", data: []byte{0, 0, 0, 0}}, + {name: "partial header", data: []byte{0, 0}, wantErr: true}, + {name: "partial payload", data: []byte{0, 0, 0, 3, 1}, wantErr: true}, + } + + for _, enabled := range []bool{false, true} { + name := "disabled" + var recorder *network.MetricsRecorder + if enabled { + name = "enabled" + recorder = network.NewMetricsRecorder("tcp-test", network.NetClient) + } + + t.Run(name, func(t *testing.T) { + for _, frame := range frames { + t.Run(frame.name, func(t *testing.T) { + before := collectMetrics(t) + left, right := net.Pipe() + readerDone := make(chan error, 1) + go func() { + _, err := readMessage(left, recorder) + readerDone <- err + }() + + if _, err := right.Write(frame.data); err != nil { + t.Fatalf("write frame: %v", err) + } + if err := right.Close(); err != nil { + t.Fatalf("close writer: %v", err) + } + err := <-readerDone + _ = left.Close() + if (err != nil) != frame.wantErr { + t.Fatalf("read error = %v, want error %t", err, frame.wantErr) + } + + after := collectMetrics(t) + wantBytes := int64(0) + if enabled { + wantBytes = int64(len(frame.data)) + } + if got := deltaMetric(before, after, "network_receive_bytes", "client", ""); got != wantBytes { + t.Fatalf("received bytes = %d, want %d", got, wantBytes) + } + }) + } + }) + } +} + +func TestRecordErrorIgnoresDisabledMetrics(t *testing.T) { + recordError(nil, network.OperationRead, errors.New("read failed")) +} + +func TestRecordErrorClassifiesNetworkErrors(t *testing.T) { + recorder := network.NewMetricsRecorder("tcp-error-test", network.NetServer) + + tests := []struct { + name string + err error + errorType network.ErrorType + }{ + { + name: "invalid message", + err: fmt.Errorf("decode frame: %w", dueerrors.ErrInvalidMessage), + errorType: network.ErrorTypeInvalidMessage, + }, + { + name: "connection limit", + err: fmt.Errorf("accept connection: %w", dueerrors.ErrTooManyConnection), + errorType: network.ErrorTypeConnectionLimit, + }, + { + name: "deadline exceeded", + err: fmt.Errorf("read deadline: %w", context.DeadlineExceeded), + errorType: network.ErrorTypeTimeout, + }, + { + name: "net timeout", + err: timeoutError{}, + errorType: network.ErrorTypeTimeout, + }, + { + name: "tls", + err: tls.RecordHeaderError{}, + errorType: network.ErrorTypeTLS, + }, + { + name: "generic io", + err: errors.New("write failed"), + errorType: network.ErrorTypeIO, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + before := collectMetrics(t) + recordError(recorder, network.OperationRead, tt.err) + after := collectMetrics(t) + + if got := deltaErrorMetric(before, after, "server", network.OperationRead, tt.errorType); got != 1 { + t.Fatalf("error count = %d, want 1 for type %q", got, tt.errorType) + } + }) + } +} + +func TestRecordErrorIgnoresNormalClose(t *testing.T) { + recorder := network.NewMetricsRecorder("tcp-normal-close-test", network.NetServer) + + for _, err := range []error{io.EOF, net.ErrClosed} { + before := collectMetrics(t) + recordError(recorder, network.OperationRead, err) + after := collectMetrics(t) + + if got := deltaMetric(before, after, "network_error", "server", ""); got != 0 { + t.Fatalf("error count = %d, want 0 for %v", got, err) + } + } +} + +func collectMetrics(t *testing.T) metricdata.ResourceMetrics { + t.Helper() + var data metricdata.ResourceMetrics + if err := testMetricReader.Collect(context.Background(), &data); err != nil { + t.Fatalf("collect metrics: %v", err) + } + return data +} + +func deltaMetric(before, after metricdata.ResourceMetrics, name, role, direction string) int64 { + return sumMetric(after, name, role, direction) - sumMetric(before, name, role, direction) +} + +func deltaErrorMetric(before, after metricdata.ResourceMetrics, role string, operation network.Operation, errorType network.ErrorType) int64 { + return sumErrorMetric(after, role, operation, errorType) - sumErrorMetric(before, role, operation, errorType) +} + +func sumMetric(data metricdata.ResourceMetrics, name, role, direction string) int64 { + for _, scope := range data.ScopeMetrics { + if scope.Scope.Name != "due.network" { + continue + } + for _, metric := range scope.Metrics { + if metric.Name != name { + continue + } + sum, ok := metric.Data.(metricdata.Sum[int64]) + if !ok { + return 0 + } + var total int64 + for _, point := range sum.DataPoints { + if !hasAttribute(point.Attributes, "network.role", role) { + continue + } + if direction != "" && !hasAttribute(point.Attributes, "network.direction", direction) { + continue + } + total += point.Value + } + return total + } + } + return 0 +} + +func sumErrorMetric(data metricdata.ResourceMetrics, role string, operation network.Operation, errorType network.ErrorType) int64 { + for _, scope := range data.ScopeMetrics { + if scope.Scope.Name != "due.network" { + continue + } + for _, metric := range scope.Metrics { + if metric.Name != "network_error" { + continue + } + sum, ok := metric.Data.(metricdata.Sum[int64]) + if !ok { + return 0 + } + var total int64 + for _, point := range sum.DataPoints { + if !hasAttribute(point.Attributes, "network.role", role) { + continue + } + if !hasAttribute(point.Attributes, "network.operation", string(operation)) { + continue + } + if !hasAttribute(point.Attributes, "error.type", string(errorType)) { + continue + } + total += point.Value + } + return total + } + } + return 0 +} + +func hasAttribute(set attribute.Set, key, want string) bool { + value, ok := set.Value(attribute.Key(key)) + return ok && value.AsString() == want +} + +func waitForSignal(t *testing.T, signal <-chan struct{}, name string) { + t.Helper() + select { + case <-signal: + case <-time.After(5 * time.Second): + t.Fatalf("timed out waiting for %s", name) + } +} + +type timeoutError struct{} + +func (timeoutError) Error() string { + return "timeout" +} + +func (timeoutError) Timeout() bool { + return true +} + +func (timeoutError) Temporary() bool { + return false +} diff --git a/network/tcp/server.go b/network/tcp/server.go index 9117f0be..af333da8 100644 --- a/network/tcp/server.go +++ b/network/tcp/server.go @@ -11,6 +11,7 @@ import ( type server struct { opts *serverOptions // 配置 + metrics *network.MetricsRecorder // 指标记录器 listener net.Listener // 监听器 connMgr *serverConnMgr // 连接管理器 startHandler network.StartHandler // 服务器启动hook函数 @@ -30,6 +31,7 @@ func NewServer(opts ...ServerOption) network.Server { s := &server{} s.opts = o + s.metrics = network.NewMetricsRecorder(protocol, network.NetServer) s.connMgr = newServerConnMgr(s) return s @@ -104,22 +106,26 @@ func (s *server) OnReceive(handler network.ReceiveHandler) { func (s *server) init() error { addr, err := net.ResolveTCPAddr("tcp", s.opts.addr) if err != nil { + recordError(s.metrics, network.OperationResolve, err) return err } if s.opts.certFile != "" && s.opts.keyFile != "" { cert, err := tls.LoadX509KeyPair(s.opts.certFile, s.opts.keyFile) if err != nil { + s.metrics.Error(network.OperationListen, network.ErrorTypeTLS) return err } if s.listener, err = tls.Listen(addr.Network(), addr.String(), &tls.Config{ Certificates: []tls.Certificate{cert}, }); err != nil { + s.metrics.Error(network.OperationListen, network.ErrorTypeTLS) return err } } else { if s.listener, err = net.ListenTCP(addr.Network(), addr); err != nil { + recordError(s.metrics, network.OperationListen, err) return err } } @@ -134,6 +140,7 @@ func (s *server) serve() { for { conn, err := s.listener.Accept() if err != nil { + recordError(s.metrics, network.OperationAccept, err) if e, ok := err.(net.Error); ok && e.Timeout() { if tempDelay == 0 { tempDelay = 5 * time.Millisecond @@ -156,6 +163,7 @@ func (s *server) serve() { tempDelay = 0 if err = s.connMgr.allocate(conn); err != nil { + recordError(s.metrics, network.OperationAccept, err) log.Errorf("connection allocate error: %v", err) _ = conn.Close() } diff --git a/network/tcp/server_conn.go b/network/tcp/server_conn.go index bff463b3..8e0f67f3 100644 --- a/network/tcp/server_conn.go +++ b/network/tcp/server_conn.go @@ -66,6 +66,7 @@ func (c *serverConn) Unbind() { // Send 发送消息(同步) func (c *serverConn) Send(msg []byte) error { if err := c.checkState(); err != nil { + recordError(c.connMgr.server.metrics, network.OperationWrite, err) return err } @@ -74,16 +75,20 @@ func (c *serverConn) Send(msg []byte) error { c.rw.RUnlock() if conn == nil { + recordError(c.connMgr.server.metrics, network.OperationWrite, errors.ErrConnectionClosed) return errors.ErrConnectionClosed } - _, err := conn.Write(msg) + n, err := conn.Write(msg) + c.connMgr.server.metrics.Send(n, err == nil && n == len(msg)) + recordError(c.connMgr.server.metrics, network.OperationWrite, err) return err } // Push 发送消息(异步) func (c *serverConn) Push(msg []byte) error { if err := c.checkState(); err != nil { + recordError(c.connMgr.server.metrics, network.OperationWrite, err) return err } @@ -91,6 +96,7 @@ func (c *serverConn) Push(msg []byte) error { defer c.rw.RUnlock() if c.conn == nil { + recordError(c.connMgr.server.metrics, network.OperationWrite, errors.ErrConnectionClosed) return errors.ErrConnectionClosed } @@ -185,6 +191,7 @@ func (c *serverConn) checkAuthorize() { return } + c.connMgr.server.metrics.Error(network.OperationAuthorize, network.ErrorTypeTimeout) c.forceClose(true) })) if t, ok := timer.(*time.Timer); ok && t != nil { @@ -217,6 +224,7 @@ func (c *serverConn) init(cm *serverConnMgr, id int64, conn net.Conn) { c.close = make(chan struct{}) c.lastHeartbeatTime.Store(xtime.Now().UnixNano()) c.authorizeTimer.Store((*time.Timer)(nil)) + c.connMgr.server.metrics.ConnectionOpened() xcall.Go(c.read) @@ -290,6 +298,8 @@ func (c *serverConn) doClose(isNeedRecycle bool) error { c.rw.Unlock() err := conn.Close() + c.connMgr.server.metrics.ConnectionClosed() + recordError(c.connMgr.server.metrics, network.OperationClose, err) if c.connMgr.server.disconnectHandler != nil { c.connMgr.server.disconnectHandler(c) @@ -311,8 +321,9 @@ func (c *serverConn) read() { case <-c.close: return default: - data, err := packet.ReadMessage(conn) + data, err := readMessage(conn, c.connMgr.server.metrics) if err != nil { + recordError(c.connMgr.server.metrics, network.OperationRead, err) _ = c.forceClose(true) return } @@ -337,6 +348,7 @@ func (c *serverConn) read() { isHeartbeat, err := packet.CheckHeartbeat(data) if err != nil { + recordError(c.connMgr.server.metrics, network.OperationDecode, err) log.Errorf("check heartbeat message error: %v", err) continue } @@ -348,6 +360,7 @@ func (c *serverConn) read() { c.doSendHeartbeat(conn) } } else { + c.connMgr.server.metrics.MessageReceived() if c.connMgr.server.receiveHandler != nil { c.connMgr.server.receiveHandler(c, data) } @@ -409,7 +422,10 @@ func (c *serverConn) doWrite(conn net.Conn, t *task) bool { return false } - if _, err := conn.Write(t.msg); err != nil { + n, err := conn.Write(t.msg) + c.connMgr.server.metrics.Send(n, err == nil && n == len(t.msg)) + if err != nil { + recordError(c.connMgr.server.metrics, network.OperationWrite, err) log.Errorf("write message error: %v", err) } @@ -421,6 +437,7 @@ func (c *serverConn) doHandleHeartbeat(conn net.Conn, t time.Time) bool { deadline := t.Add(-2 * c.connMgr.server.opts.heartbeatInterval).UnixNano() if c.lastHeartbeatTime.Load() < deadline { + c.connMgr.server.metrics.Error(network.OperationHeartbeat, network.ErrorTypeTimeout) log.Debugf("connection heartbeat timeout, cid: %d", c.id) _ = c.forceClose(true) return false @@ -445,9 +462,13 @@ func (c *serverConn) isClosed() bool { // 发送心跳包 func (c *serverConn) doSendHeartbeat(conn net.Conn) { if heartbeat, err := packet.PackHeartbeat(); err != nil { + recordError(c.connMgr.server.metrics, network.OperationHeartbeat, err) log.Errorf("pack heartbeat message error: %v", err) } else { - if _, err = conn.Write(heartbeat); err != nil { + n, err := conn.Write(heartbeat) + c.connMgr.server.metrics.Send(n, false) + if err != nil { + recordError(c.connMgr.server.metrics, network.OperationHeartbeat, err) log.Errorf("write heartbeat message error: %v", err) } } @@ -474,6 +495,7 @@ func (c *serverConn) doWriteToQueue(queue chan *task, typ int8, msg ...[]byte) e select { case <-ctx.Done(): c.doRecycleToPool(t) + recordError(c.connMgr.server.metrics, network.OperationQueue, ctx.Err()) return ctx.Err() case queue <- t: return nil diff --git a/network/tcp/server_options.go b/network/tcp/server_options.go index 7699f8a2..324e6763 100644 --- a/network/tcp/server_options.go +++ b/network/tcp/server_options.go @@ -24,6 +24,7 @@ const ( defaultServerKeyFileKey = "etc.network.tcp.server.keyFile" defaultServerMaxConnNumKey = "etc.network.tcp.server.maxConnNum" defaultServerWriteTimeoutKey = "etc.network.tcp.server.writeTimeout" + defaultServerMetricsEnabledKey = "etc.network.tcp.server.metrics.enable" defaultServerWriteQueueSizeKey = "etc.network.tcp.server.writeQueueSize" defaultServerHeartbeatIntervalKey = "etc.network.tcp.server.heartbeatInterval" defaultServerHeartbeatMechanismKey = "etc.network.tcp.server.heartbeatMechanism" @@ -49,12 +50,14 @@ type serverOptions struct { heartbeatInterval time.Duration // 心跳检测间隔时间,默认10s heartbeatMechanism HeartbeatMechanism // 心跳机制,默认resp authorizeTimeout time.Duration // 授权超时时间,默认0s,不检测 + metricsEnabled bool // 是否开启指标采集 } func defaultServerOptions() *serverOptions { opts := &serverOptions{} opts.certFile = etc.Get(defaultServerCertFileKey).String() opts.keyFile = etc.Get(defaultServerKeyFileKey).String() + opts.metricsEnabled = etc.Get(defaultServerMetricsEnabledKey).Bool() if addr := etc.Get(defaultServerAddrKey, defaultServerAddr).String(); addr != "" { opts.addr = addr @@ -183,3 +186,8 @@ func WithServerAuthorizeTimeout(authorizeTimeout time.Duration) ServerOption { } } } + +// WithServerMetricsEnabled 设置是否开启指标采集 +func WithServerMetricsEnabled(enabled bool) ServerOption { + return func(o *serverOptions) { o.metricsEnabled = enabled } +} diff --git a/network/ws/go.mod b/network/ws/go.mod index 5c0c1c10..6a2526ca 100644 --- a/network/ws/go.mod +++ b/network/ws/go.mod @@ -20,7 +20,7 @@ require ( github.com/twitchyliquid64/golang-asm v0.15.1 // indirect golang.org/x/arch v0.11.0 // indirect golang.org/x/sync v0.20.0 // indirect - golang.org/x/sys v0.28.0 // indirect + golang.org/x/sys v0.47.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/network/ws/go.sum b/network/ws/go.sum index a322b945..517f2771 100644 --- a/network/ws/go.sum +++ b/network/ws/go.sum @@ -40,6 +40,7 @@ golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/registry/consul/go.mod b/registry/consul/go.mod index c050534d..ef83a809 100644 --- a/registry/consul/go.mod +++ b/registry/consul/go.mod @@ -35,7 +35,7 @@ require ( golang.org/x/arch v0.11.0 // indirect golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 // indirect golang.org/x/sync v0.20.0 // indirect - golang.org/x/sys v0.29.0 // indirect + golang.org/x/sys v0.47.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/registry/consul/go.sum b/registry/consul/go.sum index b3602ca5..407e96b8 100644 --- a/registry/consul/go.sum +++ b/registry/consul/go.sum @@ -236,6 +236,7 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= diff --git a/registry/etcd/go.mod b/registry/etcd/go.mod index af51026c..5a78843a 100644 --- a/registry/etcd/go.mod +++ b/registry/etcd/go.mod @@ -30,7 +30,7 @@ require ( golang.org/x/arch v0.11.0 // indirect golang.org/x/net v0.45.0 // indirect golang.org/x/sync v0.20.0 // indirect - golang.org/x/sys v0.36.0 // indirect + golang.org/x/sys v0.47.0 // indirect golang.org/x/text v0.29.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb // indirect diff --git a/registry/etcd/go.sum b/registry/etcd/go.sum index 9145d463..b267b645 100644 --- a/registry/etcd/go.sum +++ b/registry/etcd/go.sum @@ -108,6 +108,7 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= diff --git a/registry/nacos/go.mod b/registry/nacos/go.mod index d73f4c65..3e2aa4ff 100644 --- a/registry/nacos/go.mod +++ b/registry/nacos/go.mod @@ -65,7 +65,7 @@ require ( golang.org/x/arch v0.11.0 // indirect golang.org/x/crypto v0.31.0 // indirect golang.org/x/net v0.33.0 // indirect - golang.org/x/sys v0.28.0 // indirect + golang.org/x/sys v0.47.0 // indirect golang.org/x/text v0.21.0 // indirect golang.org/x/time v0.1.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect diff --git a/registry/nacos/go.sum b/registry/nacos/go.sum index c7d72ff3..e33e2792 100644 --- a/registry/nacos/go.sum +++ b/registry/nacos/go.sum @@ -501,6 +501,7 @@ golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= diff --git a/tidy.sh b/tidy.sh index 8b68993e..ac517e68 100644 --- a/tidy.sh +++ b/tidy.sh @@ -6,6 +6,7 @@ readonly modules=( "./cache/redis" "./cache/memcache" "./component/http" + "./component/otel" "./config/consul" "./config/etcd" "./config/nacos" diff --git a/transport/rpcx/go.mod b/transport/rpcx/go.mod index c30b9eba..7c1b30f9 100644 --- a/transport/rpcx/go.mod +++ b/transport/rpcx/go.mod @@ -70,7 +70,7 @@ require ( golang.org/x/exp v0.0.0-20250128144449-3edf0e91c1ae // indirect golang.org/x/mod v0.22.0 // indirect golang.org/x/net v0.34.0 // indirect - golang.org/x/sys v0.29.0 // indirect + golang.org/x/sys v0.47.0 // indirect golang.org/x/text v0.21.0 // indirect golang.org/x/tools v0.29.0 // indirect google.golang.org/protobuf v1.36.6 // indirect diff --git a/transport/rpcx/go.sum b/transport/rpcx/go.sum index 090a6850..9eedf4fe 100644 --- a/transport/rpcx/go.sum +++ b/transport/rpcx/go.sum @@ -226,6 +226,7 @@ golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=