From 8669512f4210d436146e7f7a3327aaca7a7597a5 Mon Sep 17 00:00:00 2001 From: John Guo Date: Sun, 24 Mar 2024 21:18:30 +0800 Subject: [PATCH] feat: add metric feature support in goframe (#3138) --- .github/workflows/ci-main.sh | 8 + cmd/gf/go.sum | 14 + .../internal/cmd/testdata/build/varmap/go.sum | 7 +- container/gtype/gtype.go | 8 +- container/gtype/gtype_any.go | 20 ++ container/gtype/gtype_z_unit_any_test.go | 74 ++++ .../drivers/mssql/mssql_z_unit_basic_test.go | 3 +- contrib/metric/otelmetric/README.MD | 160 +++++++++ contrib/metric/otelmetric/go.mod | 44 +++ contrib/metric/otelmetric/go.sum | 75 ++++ contrib/metric/otelmetric/otelmetric.go | 33 ++ .../metric/otelmetric/otelmetric_callback.go | 53 +++ .../otelmetric_meter_counter_performer.go | 65 ++++ .../otelmetric_meter_histogram_performer.go | 84 +++++ ...tric_meter_observable_counter_performer.go | 56 +++ ...metric_meter_observable_gauge_performer.go | 56 +++ ...ter_observable_updown_counter_performer.go | 57 ++++ .../otelmetric/otelmetric_meter_performer.go | 143 ++++++++ ...elmetric_meter_updown_counter_performer.go | 70 ++++ .../otelmetric/otelmetric_metric_callback.go | 56 +++ .../metric/otelmetric/otelmetric_provider.go | 145 ++++++++ contrib/metric/otelmetric/otelmetric_util.go | 170 ++++++++++ .../otelmetric/otelmetric_z_unit_http_test.go | 95 ++++++ .../otelmetric/otelmetric_z_unit_test.go | 320 ++++++++++++++++++ .../testdata/http.prometheus.metrics.txt | 141 ++++++++ contrib/nosql/redis/go.mod | 7 +- contrib/nosql/redis/go.sum | 15 +- contrib/registry/etcd/README.MD | 4 +- contrib/registry/file/README.MD | 4 +- contrib/registry/nacos/README.MD | 4 +- contrib/registry/polaris/README.MD | 4 +- contrib/trace/jaeger/go.mod | 2 +- contrib/trace/jaeger/go.sum | 9 +- contrib/trace/otlpgrpc/go.mod | 44 +-- contrib/trace/otlpgrpc/go.sum | 91 +++-- contrib/trace/otlphttp/go.mod | 45 ++- contrib/trace/otlphttp/go.sum | 90 +++-- database/gdb/gdb.go | 90 ++--- database/gdb/gdb_core.go | 2 +- database/gdb/gdb_core_stats.go | 45 +++ database/gdb/gdb_core_trace.go | 6 +- database/gdb/gdb_core_underlying.go | 2 +- example/go.mod | 97 +++--- example/go.sum | 143 ++++---- example/httpserver/proxy/main.go | 36 +- example/metric/basic/main.go | 139 ++++++++ example/metric/callback/main.go | 81 +++++ example/metric/dynamic_attributes/main.go | 90 +++++ example/metric/global_attributes/main.go | 95 ++++++ example/metric/http_client/main.go | 46 +++ example/metric/http_server/main.go | 52 +++ example/metric/meter_attributes/main.go | 91 +++++ example/metric/prometheus/prometheus.go | 6 + example/trace/http-with-db/client/client.go | 9 +- go.mod | 7 +- go.sum | 15 +- net/gclient/gclient.go | 2 +- net/gclient/gclient_chain.go | 1 + net/gclient/gclient_config.go | 1 + net/gclient/gclient_metrics.go | 277 +++++++++++++++ ...nt_tracing.go => gclient_observability.go} | 42 ++- net/gclient/gclient_request.go | 13 +- net/gclient/gclient_tracer.go | 158 +++++++++ net/gclient/gclient_tracer_metrics.go | 176 ++++++++++ net/gclient/gclient_tracer_noop.go | 124 +++++++ net/gclient/gclient_tracer_tracing.go | 231 +++++++++++++ net/gclient/gclient_tracing_tracer.go | 175 ---------- net/ghttp/ghttp_middleware_tracing.go | 8 +- net/ghttp/ghttp_request.go | 20 +- net/ghttp/ghttp_response.go | 46 +-- net/ghttp/ghttp_response_write.go | 8 +- net/ghttp/ghttp_response_writer.go | 72 ---- net/ghttp/ghttp_server_handler.go | 122 ++++--- net/ghttp/ghttp_server_log.go | 17 +- net/ghttp/ghttp_server_metric.go | 256 ++++++++++++++ net/ghttp/ghttp_server_websocket.go | 2 + .../response/response_buffer_writer.go | 95 ++++++ .../internal/response/response_writer.go | 19 +- net/gtrace/gtrace.go | 4 +- os/gmetric/gmetric.go | 284 ++++++++++++++++ os/gmetric/gmetric_attribute.go | 109 ++++++ os/gmetric/gmetric_attribute_map.go | 49 +++ os/gmetric/gmetric_global_attributes.go | 80 +++++ os/gmetric/gmetric_meter.go | 42 +++ os/gmetric/gmetric_meter_callback.go | 51 +++ os/gmetric/gmetric_meter_counter.go | 72 ++++ os/gmetric/gmetric_meter_histogram.go | 77 +++++ os/gmetric/gmetric_meter_metric_info.go | 78 +++++ os/gmetric/gmetric_meter_metric_instrument.go | 31 ++ .../gmetric_meter_observable_counter.go | 72 ++++ os/gmetric/gmetric_meter_observable_gauge.go | 72 ++++ ...gmetric_meter_observable_updown_counter.go | 72 ++++ os/gmetric/gmetric_meter_updown_counter.go | 72 ++++ os/gmetric/gmetric_metric.go | 47 +++ os/gmetric/gmetric_noop_counter_performer.go | 23 ++ .../gmetric_noop_histogram_performer.go | 18 + ...etric_noop_observable_counter_performer.go | 17 + ...gmetric_noop_observable_gauge_performer.go | 17 + ...oop_observable_updown_counter_performer.go | 19 ++ .../gmetric_noop_updown_counter_performer.go | 26 ++ os/gmetric/gmetric_provider.go | 91 +++++ os/gmetric/gmetric_z_unit_internal_test.go | 112 ++++++ os/gmetric/gmetric_z_unit_test.go | 79 +++++ 103 files changed, 6056 insertions(+), 751 deletions(-) create mode 100644 container/gtype/gtype_any.go create mode 100644 container/gtype/gtype_z_unit_any_test.go create mode 100644 contrib/metric/otelmetric/README.MD create mode 100644 contrib/metric/otelmetric/go.mod create mode 100644 contrib/metric/otelmetric/go.sum create mode 100644 contrib/metric/otelmetric/otelmetric.go create mode 100644 contrib/metric/otelmetric/otelmetric_callback.go create mode 100644 contrib/metric/otelmetric/otelmetric_meter_counter_performer.go create mode 100644 contrib/metric/otelmetric/otelmetric_meter_histogram_performer.go create mode 100644 contrib/metric/otelmetric/otelmetric_meter_observable_counter_performer.go create mode 100644 contrib/metric/otelmetric/otelmetric_meter_observable_gauge_performer.go create mode 100644 contrib/metric/otelmetric/otelmetric_meter_observable_updown_counter_performer.go create mode 100644 contrib/metric/otelmetric/otelmetric_meter_performer.go create mode 100644 contrib/metric/otelmetric/otelmetric_meter_updown_counter_performer.go create mode 100644 contrib/metric/otelmetric/otelmetric_metric_callback.go create mode 100644 contrib/metric/otelmetric/otelmetric_provider.go create mode 100644 contrib/metric/otelmetric/otelmetric_util.go create mode 100644 contrib/metric/otelmetric/otelmetric_z_unit_http_test.go create mode 100644 contrib/metric/otelmetric/otelmetric_z_unit_test.go create mode 100644 contrib/metric/otelmetric/testdata/http.prometheus.metrics.txt create mode 100644 database/gdb/gdb_core_stats.go create mode 100644 example/metric/basic/main.go create mode 100644 example/metric/callback/main.go create mode 100644 example/metric/dynamic_attributes/main.go create mode 100644 example/metric/global_attributes/main.go create mode 100644 example/metric/http_client/main.go create mode 100644 example/metric/http_server/main.go create mode 100644 example/metric/meter_attributes/main.go create mode 100644 net/gclient/gclient_metrics.go rename net/gclient/{gclient_tracing.go => gclient_observability.go} (77%) create mode 100644 net/gclient/gclient_tracer.go create mode 100644 net/gclient/gclient_tracer_metrics.go create mode 100644 net/gclient/gclient_tracer_noop.go create mode 100644 net/gclient/gclient_tracer_tracing.go delete mode 100644 net/gclient/gclient_tracing_tracer.go delete mode 100644 net/ghttp/ghttp_response_writer.go create mode 100644 net/ghttp/ghttp_server_metric.go create mode 100644 net/ghttp/internal/response/response_buffer_writer.go create mode 100644 os/gmetric/gmetric.go create mode 100644 os/gmetric/gmetric_attribute.go create mode 100644 os/gmetric/gmetric_attribute_map.go create mode 100644 os/gmetric/gmetric_global_attributes.go create mode 100644 os/gmetric/gmetric_meter.go create mode 100644 os/gmetric/gmetric_meter_callback.go create mode 100644 os/gmetric/gmetric_meter_counter.go create mode 100644 os/gmetric/gmetric_meter_histogram.go create mode 100644 os/gmetric/gmetric_meter_metric_info.go create mode 100644 os/gmetric/gmetric_meter_metric_instrument.go create mode 100644 os/gmetric/gmetric_meter_observable_counter.go create mode 100644 os/gmetric/gmetric_meter_observable_gauge.go create mode 100644 os/gmetric/gmetric_meter_observable_updown_counter.go create mode 100644 os/gmetric/gmetric_meter_updown_counter.go create mode 100644 os/gmetric/gmetric_metric.go create mode 100644 os/gmetric/gmetric_noop_counter_performer.go create mode 100644 os/gmetric/gmetric_noop_histogram_performer.go create mode 100644 os/gmetric/gmetric_noop_observable_counter_performer.go create mode 100644 os/gmetric/gmetric_noop_observable_gauge_performer.go create mode 100644 os/gmetric/gmetric_noop_observable_updown_counter_performer.go create mode 100644 os/gmetric/gmetric_noop_updown_counter_performer.go create mode 100644 os/gmetric/gmetric_provider.go create mode 100644 os/gmetric/gmetric_z_unit_internal_test.go create mode 100644 os/gmetric/gmetric_z_unit_test.go diff --git a/.github/workflows/ci-main.sh b/.github/workflows/ci-main.sh index e143bce55..7fdcf9a96 100644 --- a/.github/workflows/ci-main.sh +++ b/.github/workflows/ci-main.sh @@ -70,6 +70,14 @@ for file in `find . -name go.mod`; do fi fi + # package otelmetric needs golang >= v1.20 + if [ "otelmetric" = $(basename $dirpath) ]; then + if ! go version|grep -qE "go1.[2-9][0-9]"; then + echo "ignore otelmetric as go version: $(go version)" + continue 1 + fi + fi + cd $dirpath go mod tidy go build ./... diff --git a/cmd/gf/go.sum b/cmd/gf/go.sum index 349b148ec..997134972 100644 --- a/cmd/gf/go.sum +++ b/cmd/gf/go.sum @@ -38,6 +38,20 @@ github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiU github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/gogf/gf/contrib/drivers/clickhouse/v2 v2.6.4 h1:ggkj93Ln7ULOyoDGIGaphkg/qV8zDDSbkFVn6EljjNQ= +github.com/gogf/gf/contrib/drivers/clickhouse/v2 v2.6.4/go.mod h1:J7CWaKo4mIYv1BqfHFpTPAjs5Sm7eZrCjA2UMAfM98E= +github.com/gogf/gf/contrib/drivers/mssql/v2 v2.6.4 h1:S22r+bWoSSESNJh/IT9tpbgDhmWltxztiQ5ONn75BRo= +github.com/gogf/gf/contrib/drivers/mssql/v2 v2.6.4/go.mod h1:NFhR+wl4BF1W/ddH9hKXARL+g3nP+r1qeAgxqOl75bA= +github.com/gogf/gf/contrib/drivers/mysql/v2 v2.6.4 h1:ScG3YcYMDEP/UrwNtwQPt2noySa5ZpoV7BxrwaeBaro= +github.com/gogf/gf/contrib/drivers/mysql/v2 v2.6.4/go.mod h1:oFFE9u1zHkxVXk7ZkNipXQR9JFyDZDiixZy243ywhfQ= +github.com/gogf/gf/contrib/drivers/oracle/v2 v2.6.4 h1:9bOuNKHZ/ZBNuuZT2Qc0V+ypkKI2k8ZkSwcnqH+ikEY= +github.com/gogf/gf/contrib/drivers/oracle/v2 v2.6.4/go.mod h1:tK8W1hDZdwjfWh3GlNdYoBWLs6unfey2+XW8FFnWICM= +github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.6.4 h1:a+zIohGG1mv+1/9lBb/C0coM+MbmPYOWKo2SL1tJhTU= +github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.6.4/go.mod h1:I7TGm/kilpNGwT8h8Frc5vGWRLgn5Vn9ce2gIBHcfbg= +github.com/gogf/gf/contrib/drivers/sqlite/v2 v2.6.4 h1:eJYZCJIqybMV6oJY7J7clWTNqx0yzZI7CaUn31igBGc= +github.com/gogf/gf/contrib/drivers/sqlite/v2 v2.6.4/go.mod h1:3q/YKn6NL7KkUdT6nw93eM5d1QjmX/+b+CPiIm/aLiA= +github.com/gogf/gf/v2 v2.6.4 h1:w7HXdH9mcTsn/aE13CkaDbRArmAL1KS3FuQqDi6u74Y= +github.com/gogf/gf/v2 v2.6.4/go.mod h1:x2XONYcI4hRQ/4gMNbWHmZrNzSEIg20s2NULbzom5k0= github.com/gogf/selfupdate v0.0.0-20231215043001-5c48c528462f h1:7xfXR/BhG3JDqO1s45n65Oyx9t4E/UqDOXep6jXdLCM= github.com/gogf/selfupdate v0.0.0-20231215043001-5c48c528462f/go.mod h1:HnYoio6S7VaFJdryKcD/r9HgX+4QzYfr00XiXUo/xz0= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= diff --git a/cmd/gf/internal/cmd/testdata/build/varmap/go.sum b/cmd/gf/internal/cmd/testdata/build/varmap/go.sum index 56cf07dd7..f36cf06eb 100644 --- a/cmd/gf/internal/cmd/testdata/build/varmap/go.sum +++ b/cmd/gf/internal/cmd/testdata/build/varmap/go.sum @@ -3,18 +3,17 @@ github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyM github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/grokify/html-strip-tags-go v0.0.1 h1:0fThFwLbW7P/kOiTBs03FsJSV9RM2M/Q/MOnCQxKMo0= github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= +github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= go.opentelemetry.io/otel v1.14.0 h1:/79Huy8wbf5DnIPhemGB+zEPVwnN6fuQybr/SRXa6hM= go.opentelemetry.io/otel v1.14.0/go.mod h1:o4buv+dJzx8rohcUeRmWUZhqupFvzWis188WlggnNeU= diff --git a/container/gtype/gtype.go b/container/gtype/gtype.go index d6711f2a1..2e6a11291 100644 --- a/container/gtype/gtype.go +++ b/container/gtype/gtype.go @@ -7,8 +7,8 @@ // Package gtype provides high performance and concurrent-safe basic variable types. package gtype -// New is alias of NewInterface. -// See NewInterface. -func New(value ...interface{}) *Interface { - return NewInterface(value...) +// New is alias of NewAny. +// See NewAny, NewInterface. +func New(value ...interface{}) *Any { + return NewAny(value...) } diff --git a/container/gtype/gtype_any.go b/container/gtype/gtype_any.go new file mode 100644 index 000000000..6e9d2439c --- /dev/null +++ b/container/gtype/gtype_any.go @@ -0,0 +1,20 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gtype + +// Any is a struct for concurrent-safe operation for type any. +type Any = Interface + +// NewAny creates and returns a concurrent-safe object for any type, +// with given initial value `value`. +func NewAny(value ...any) *Any { + t := &Any{} + if len(value) > 0 && value[0] != nil { + t.value.Store(value[0]) + } + return t +} diff --git a/container/gtype/gtype_z_unit_any_test.go b/container/gtype/gtype_z_unit_any_test.go new file mode 100644 index 000000000..ce5eb5f3e --- /dev/null +++ b/container/gtype/gtype_z_unit_any_test.go @@ -0,0 +1,74 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gtype_test + +import ( + "testing" + + "github.com/gogf/gf/v2/container/gtype" + "github.com/gogf/gf/v2/internal/json" + "github.com/gogf/gf/v2/test/gtest" + "github.com/gogf/gf/v2/util/gconv" +) + +func Test_Any(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + t1 := Temp{Name: "gf", Age: 18} + t2 := Temp{Name: "gf", Age: 19} + i := gtype.New(t1) + iClone := i.Clone() + t.AssertEQ(iClone.Set(t2), t1) + t.AssertEQ(iClone.Val().(Temp), t2) + + // empty param test + i1 := gtype.New() + t.AssertEQ(i1.Val(), nil) + + i2 := gtype.New("gf") + t.AssertEQ(i2.String(), "gf") + copyVal := i2.DeepCopy() + i2.Set("goframe") + t.AssertNE(copyVal, iClone.Val()) + i2 = nil + copyVal = i2.DeepCopy() + t.AssertNil(copyVal) + }) +} + +func Test_Any_JSON(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + s := "i love gf" + i := gtype.New(s) + b1, err1 := json.Marshal(i) + b2, err2 := json.Marshal(i.Val()) + t.Assert(err1, nil) + t.Assert(err2, nil) + t.Assert(b1, b2) + + i2 := gtype.New() + err := json.UnmarshalUseNumber(b2, &i2) + t.AssertNil(err) + t.Assert(i2.Val(), s) + }) +} + +func Test_Any_UnmarshalValue(t *testing.T) { + type V struct { + Name string + Var *gtype.Any + } + gtest.C(t, func(t *gtest.T) { + var v *V + err := gconv.Struct(map[string]any{ + "name": "john", + "var": "123", + }, &v) + t.AssertNil(err) + t.Assert(v.Name, "john") + t.Assert(v.Var.Val(), "123") + }) +} diff --git a/contrib/drivers/mssql/mssql_z_unit_basic_test.go b/contrib/drivers/mssql/mssql_z_unit_basic_test.go index 87bb85fe4..40d39f988 100644 --- a/contrib/drivers/mssql/mssql_z_unit_basic_test.go +++ b/contrib/drivers/mssql/mssql_z_unit_basic_test.go @@ -236,7 +236,8 @@ func Test_DB_Insert(t *testing.T) { t.Assert(one["PASSPORT"].String(), "user_3") t.Assert(one["PASSWORD"].String(), "25d55ad283aa400af464c76d713c07ad") t.Assert(one["NICKNAME"].String(), "name_3") - t.Assert(one["CREATE_TIME"].GTime(), timeNow) + t.AssertNE(one["CREATE_TIME"].GTime(), nil) + t.AssertLT(timeNow.Sub(one["CREATE_TIME"].GTime()), 3) // *struct timeNow = gtime.Now() diff --git a/contrib/metric/otelmetric/README.MD b/contrib/metric/otelmetric/README.MD new file mode 100644 index 000000000..501496e5b --- /dev/null +++ b/contrib/metric/otelmetric/README.MD @@ -0,0 +1,160 @@ +# GoFrame Metric In OpenTelemetry + +## Installation + +``` +go get -u -v github.com/gogf/gf/contrib/metric/otelmetric/v2 +``` + +suggested using `go.mod`: + +``` +require github.com/gogf/gf/contrib/metric/otelmetric/v2 latest +``` + +## Example + +### [basic](../../../example/metric/basic/main.go) + +```go +package main + +import ( + "context" + + "github.com/prometheus/client_golang/prometheus/promhttp" + "go.opentelemetry.io/otel/exporters/prometheus" + "go.opentelemetry.io/otel/sdk/metric" + + "github.com/gogf/gf/contrib/metric/otelmetric/v2" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/net/ghttp" + "github.com/gogf/gf/v2/os/gctx" + "github.com/gogf/gf/v2/os/gmetric" +) + +var ( + meter = gmetric.GetGlobalProvider().Meter(gmetric.MeterOption{ + Instrument: "github.com/gogf/gf/example/metric/basic", + InstrumentVersion: "v1.0", + }) + counter = meter.MustCounter( + "goframe.metric.demo.counter", + gmetric.MetricOption{ + Help: "This is a simple demo for Counter usage", + Unit: "bytes", + Attributes: gmetric.Attributes{ + gmetric.NewAttribute("const_label_1", 1), + }, + }, + ) + upDownCounter = meter.MustUpDownCounter( + "goframe.metric.demo.updown_counter", + gmetric.MetricOption{ + Help: "This is a simple demo for UpDownCounter usage", + Unit: "%", + Attributes: gmetric.Attributes{ + gmetric.NewAttribute("const_label_2", 2), + }, + }, + ) + histogram = meter.MustHistogram( + "goframe.metric.demo.histogram", + gmetric.MetricOption{ + Help: "This is a simple demo for histogram usage", + Unit: "ms", + Attributes: gmetric.Attributes{ + gmetric.NewAttribute("const_label_3", 3), + }, + Buckets: []float64{0, 10, 20, 50, 100, 500, 1000, 2000, 5000, 10000}, + }, + ) + observableCounter = meter.MustObservableCounter( + "goframe.metric.demo.observable_counter", + gmetric.MetricOption{ + Help: "This is a simple demo for ObservableCounter usage", + Unit: "%", + Attributes: gmetric.Attributes{ + gmetric.NewAttribute("const_label_4", 4), + }, + }, + ) + observableUpDownCounter = meter.MustObservableUpDownCounter( + "goframe.metric.demo.observable_updown_counter", + gmetric.MetricOption{ + Help: "This is a simple demo for ObservableUpDownCounter usage", + Unit: "%", + Attributes: gmetric.Attributes{ + gmetric.NewAttribute("const_label_5", 5), + }, + }, + ) + observableGauge = meter.MustObservableGauge( + "goframe.metric.demo.observable_gauge", + gmetric.MetricOption{ + Help: "This is a simple demo for ObservableGauge usage", + Unit: "%", + Attributes: gmetric.Attributes{ + gmetric.NewAttribute("const_label_6", 6), + }, + }, + ) +) + +func main() { + var ctx = gctx.New() + + // Callback for observable metrics. + meter.MustRegisterCallback(func(ctx context.Context, obs gmetric.Observer) error { + obs.Observe(observableCounter, 10) + obs.Observe(observableUpDownCounter, 20) + obs.Observe(observableGauge, 30) + return nil + }, observableCounter, observableUpDownCounter, observableGauge) + + // Prometheus exporter to export metrics as Prometheus format. + exporter, err := prometheus.New( + prometheus.WithoutCounterSuffixes(), + prometheus.WithoutUnits(), + ) + if err != nil { + g.Log().Fatal(ctx, err) + } + + // OpenTelemetry provider. + provider := otelmetric.MustProvider(metric.WithReader(exporter)) + provider.SetAsGlobal() + defer provider.Shutdown(ctx) + + // Counter. + counter.Inc(ctx) + counter.Add(ctx, 10) + + // UpDownCounter. + upDownCounter.Inc(ctx) + upDownCounter.Add(ctx, 10) + upDownCounter.Dec(ctx) + + // Record values for histogram. + histogram.Record(1) + histogram.Record(20) + histogram.Record(30) + histogram.Record(101) + histogram.Record(2000) + histogram.Record(9000) + histogram.Record(20000) + + // HTTP Server for metrics exporting. + s := g.Server() + s.BindHandler("/metrics", ghttp.WrapH(promhttp.Handler())) + s.SetPort(8000) + s.Run() +} +``` + +### [more examples](../../../example/metric/) + +## License + +`GoFrame Polaris` is licensed under the [MIT License](../../../LICENSE), 100% free and open-source, forever. + diff --git a/contrib/metric/otelmetric/go.mod b/contrib/metric/otelmetric/go.mod new file mode 100644 index 000000000..784879aa8 --- /dev/null +++ b/contrib/metric/otelmetric/go.mod @@ -0,0 +1,44 @@ +module github.com/gogf/gf/contrib/metric/otelmetric/v2 + +go 1.20 + +require ( + github.com/gogf/gf/v2 v2.6.1 + github.com/prometheus/client_golang v1.19.0 + go.opentelemetry.io/contrib/instrumentation/runtime v0.49.0 + go.opentelemetry.io/otel v1.24.0 + go.opentelemetry.io/otel/exporters/prometheus v0.46.0 + go.opentelemetry.io/otel/metric v1.24.0 + go.opentelemetry.io/otel/sdk v1.24.0 + go.opentelemetry.io/otel/sdk/metric v1.24.0 +) + +require ( + github.com/BurntSushi/toml v1.3.2 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/clbanning/mxj/v2 v2.7.0 // indirect + github.com/fatih/color v1.15.0 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/go-logr/logr v1.4.1 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/gorilla/websocket v1.5.0 // indirect + github.com/grokify/html-strip-tags-go v0.0.1 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.17 // indirect + github.com/mattn/go-runewidth v0.0.9 // indirect + github.com/olekukonko/tablewriter v0.0.5 // indirect + github.com/prometheus/client_model v0.6.0 // indirect + github.com/prometheus/common v0.48.0 // indirect + github.com/prometheus/procfs v0.12.0 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect + go.opentelemetry.io/otel/trace v1.24.0 // indirect + golang.org/x/net v0.20.0 // indirect + golang.org/x/sys v0.18.0 // indirect + golang.org/x/text v0.14.0 // indirect + google.golang.org/protobuf v1.32.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + +replace github.com/gogf/gf/v2 => ../../../ diff --git a/contrib/metric/otelmetric/go.sum b/contrib/metric/otelmetric/go.sum new file mode 100644 index 000000000..5f6ae2da9 --- /dev/null +++ b/contrib/metric/otelmetric/go.sum @@ -0,0 +1,75 @@ +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME= +github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= +github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/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.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grokify/html-strip-tags-go v0.0.1 h1:0fThFwLbW7P/kOiTBs03FsJSV9RM2M/Q/MOnCQxKMo0= +github.com/grokify/html-strip-tags-go v0.0.1/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= +github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= +github.com/prometheus/client_model v0.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos= +github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOAGQPoaGhyTchlyt8= +github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= +github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +go.opentelemetry.io/contrib/instrumentation/runtime v0.49.0 h1:dg9y+7ArpumB6zwImJv47RHfdgOGQ1EMkzP5vLkEnTU= +go.opentelemetry.io/contrib/instrumentation/runtime v0.49.0/go.mod h1:Ul4MtXqu/hJBM+v7a6dCF0nHwckPMLpIpLeCi4+zfdw= +go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= +go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= +go.opentelemetry.io/otel/exporters/prometheus v0.46.0 h1:I8WIFXR351FoLJYuloU4EgXbtNX2URfU/85pUPheIEQ= +go.opentelemetry.io/otel/exporters/prometheus v0.46.0/go.mod h1:ztwVUHe5DTR/1v7PeuGRnU5Bbd4QKYwApWmuutKsJSs= +go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= +go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= +go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw= +go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg= +go.opentelemetry.io/otel/sdk/metric v1.24.0 h1:yyMQrPzF+k88/DbH7o4FMAs80puqd+9osbiBrJrz/w8= +go.opentelemetry.io/otel/sdk/metric v1.24.0/go.mod h1:I6Y5FjH6rvEnTTAYQz3Mmv2kl6Ek5IIrmwTLqMrrOE0= +go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= +go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= +golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= +google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +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/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/contrib/metric/otelmetric/otelmetric.go b/contrib/metric/otelmetric/otelmetric.go new file mode 100644 index 000000000..e42e2fa07 --- /dev/null +++ b/contrib/metric/otelmetric/otelmetric.go @@ -0,0 +1,33 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +// Package otelmetric provides metric functionalities using OpenTelemetry metric. +package otelmetric + +import ( + "go.opentelemetry.io/otel/sdk/metric" + + "github.com/gogf/gf/v2/os/gmetric" +) + +// NewProvider creates and returns a metrics provider. +func NewProvider(option ...metric.Option) (gmetric.Provider, error) { + provider, err := newProvider(option...) + if err != nil { + return nil, err + } + return provider, nil +} + +// MustProvider creates and returns a metrics provider. +// It panics if any error occurs. +func MustProvider(option ...metric.Option) gmetric.Provider { + provider, err := NewProvider(option...) + if err != nil { + panic(err) + } + return provider +} diff --git a/contrib/metric/otelmetric/otelmetric_callback.go b/contrib/metric/otelmetric/otelmetric_callback.go new file mode 100644 index 000000000..5ca690d4c --- /dev/null +++ b/contrib/metric/otelmetric/otelmetric_callback.go @@ -0,0 +1,53 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package otelmetric + +import ( + "go.opentelemetry.io/otel/metric" + + "github.com/gogf/gf/v2/os/gmetric" +) + +// localObserver implements interface gmetric.Observer. +type localObserver struct { + metric.Observer + gmetric.MeterOption +} + +// newObserver creates and returns gmetric.Observer. +func newObserver(observer metric.Observer, meterOption gmetric.MeterOption) gmetric.Observer { + return &localObserver{ + Observer: observer, + MeterOption: meterOption, + } +} + +// Observe observes the value for certain initialized Metric. +// It adds the value to total result if the observed Metrics is type of Counter. +// It sets the value as the result if the observed Metrics is type of Gauge. +func (l *localObserver) Observe(om gmetric.ObservableMetric, value float64, option ...gmetric.Option) { + var ( + m = om.(gmetric.Metric) + constOption = getConstOptionByMetric(l.MeterOption, m) + dynamicOption = getDynamicOptionByMetricOption(option...) + globalAttributesOption = getGlobalAttributesOption(gmetric.GetGlobalAttributesOption{ + Instrument: m.Info().Instrument().Name(), + InstrumentVersion: m.Info().Instrument().Version(), + }) + observeOptions = make([]metric.ObserveOption, 0) + ) + if globalAttributesOption != nil { + observeOptions = append(observeOptions, globalAttributesOption) + } + if constOption != nil { + observeOptions = append(observeOptions, constOption) + } + if dynamicOption != nil { + observeOptions = append(observeOptions, dynamicOption) + } + l.Observer.ObserveFloat64(metricToFloat64Observable(m), value, observeOptions...) +} diff --git a/contrib/metric/otelmetric/otelmetric_meter_counter_performer.go b/contrib/metric/otelmetric/otelmetric_meter_counter_performer.go new file mode 100644 index 000000000..23955d353 --- /dev/null +++ b/contrib/metric/otelmetric/otelmetric_meter_counter_performer.go @@ -0,0 +1,65 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package otelmetric + +import ( + "context" + + "go.opentelemetry.io/otel/metric" + + "github.com/gogf/gf/v2/encoding/gjson" + "github.com/gogf/gf/v2/errors/gcode" + "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/os/gmetric" +) + +// localCounterPerformer is an implementer for interface gmetric.CounterPerformer. +type localCounterPerformer struct { + gmetric.MeterOption + gmetric.MetricOption + metric.Float64Counter + constOption metric.MeasurementOption +} + +// newCounterPerformer creates and returns a CounterPerformer that truly takes action to implement Counter. +func (l *localMeterPerformer) newCounterPerformer( + meter metric.Meter, + metricName string, + metricOption gmetric.MetricOption, +) (gmetric.CounterPerformer, error) { + var ( + options = []metric.Float64CounterOption{ + metric.WithDescription(metricOption.Help), + metric.WithUnit(metricOption.Unit), + } + ) + counter, err := meter.Float64Counter(metricName, options...) + if err != nil { + return nil, gerror.WrapCodef( + gcode.CodeInternalError, + err, + `create Float64Counter "%s" failed with option: %s`, + metricName, gjson.MustEncodeString(metricOption), + ) + } + return &localCounterPerformer{ + MetricOption: metricOption, + MeterOption: l.MeterOption, + Float64Counter: counter, + constOption: genConstOptionForMetric(l.MeterOption, metricOption), + }, nil +} + +// Inc increments the counter by 1. +func (l *localCounterPerformer) Inc(ctx context.Context, option ...gmetric.Option) { + l.Add(ctx, 1, option...) +} + +// Add adds the given value to the counter. It panics if the value is < 0. +func (l *localCounterPerformer) Add(ctx context.Context, increment float64, option ...gmetric.Option) { + l.Float64Counter.Add(ctx, increment, generateAddOptions(l.MeterOption, l.constOption, option...)...) +} diff --git a/contrib/metric/otelmetric/otelmetric_meter_histogram_performer.go b/contrib/metric/otelmetric/otelmetric_meter_histogram_performer.go new file mode 100644 index 000000000..fddede757 --- /dev/null +++ b/contrib/metric/otelmetric/otelmetric_meter_histogram_performer.go @@ -0,0 +1,84 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package otelmetric + +import ( + "context" + + "go.opentelemetry.io/otel/metric" + + "github.com/gogf/gf/v2/encoding/gjson" + "github.com/gogf/gf/v2/errors/gcode" + "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/os/gmetric" +) + +// localHistogramPerformer is an implementer for interface HistogramPerformer. +type localHistogramPerformer struct { + gmetric.MeterOption + gmetric.MetricOption + metric.Float64Histogram + constOption metric.MeasurementOption +} + +// newHistogramPerformer creates and returns a HistogramPerformer that truly takes action to implement Histogram. +func (l *localMeterPerformer) newHistogramPerformer( + meter metric.Meter, + metricName string, + metricOption gmetric.MetricOption, +) (gmetric.HistogramPerformer, error) { + histogram, err := meter.Float64Histogram( + metricName, + metric.WithDescription(metricOption.Help), + metric.WithUnit(metricOption.Unit), + metric.WithExplicitBucketBoundaries(metricOption.Buckets...), + ) + if err != nil { + return nil, gerror.WrapCodef( + gcode.CodeInternalError, + err, + `create Float64Histogram "%s" failed with option: %s`, + metricName, gjson.MustEncodeString(metricOption), + ) + } + return &localHistogramPerformer{ + MeterOption: l.MeterOption, + MetricOption: metricOption, + Float64Histogram: histogram, + constOption: genConstOptionForMetric(l.MeterOption, metricOption), + }, nil +} + +// Record adds a single value to the histogram. The value is usually positive or zero. +func (l *localHistogramPerformer) Record(increment float64, option ...gmetric.Option) { + l.Float64Histogram.Record( + context.Background(), + increment, + l.generateRecordOptions(option...)..., + ) +} + +func (l *localHistogramPerformer) generateRecordOptions(option ...gmetric.Option) []metric.RecordOption { + var ( + dynamicOption = getDynamicOptionByMetricOption(option...) + recordOptions = make([]metric.RecordOption, 0) + globalAttributesOption = getGlobalAttributesOption(gmetric.GetGlobalAttributesOption{ + Instrument: l.MeterOption.Instrument, + InstrumentVersion: l.MeterOption.InstrumentVersion, + }) + ) + if globalAttributesOption != nil { + recordOptions = append(recordOptions, globalAttributesOption) + } + if l.constOption != nil { + recordOptions = append(recordOptions, l.constOption) + } + if dynamicOption != nil { + recordOptions = append(recordOptions, dynamicOption) + } + return recordOptions +} diff --git a/contrib/metric/otelmetric/otelmetric_meter_observable_counter_performer.go b/contrib/metric/otelmetric/otelmetric_meter_observable_counter_performer.go new file mode 100644 index 000000000..b4111fa79 --- /dev/null +++ b/contrib/metric/otelmetric/otelmetric_meter_observable_counter_performer.go @@ -0,0 +1,56 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package otelmetric + +import ( + "context" + + "go.opentelemetry.io/otel/metric" + + "github.com/gogf/gf/v2/encoding/gjson" + "github.com/gogf/gf/v2/errors/gcode" + "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/os/gmetric" +) + +// localCounterPerformer is an implementer for interface CounterPerformer. +type localObservableCounterPerformer struct { + gmetric.ObservableMetric + metric.Float64ObservableCounter +} + +// newCounterPerformer creates and returns a CounterPerformer that truly takes action to implement Counter. +func (l *localMeterPerformer) newObservableCounterPerformer( + meter metric.Meter, + metricName string, + metricOption gmetric.MetricOption, +) (gmetric.ObservableCounterPerformer, error) { + var ( + options = []metric.Float64ObservableCounterOption{ + metric.WithDescription(metricOption.Help), + metric.WithUnit(metricOption.Unit), + } + ) + if metricOption.Callback != nil { + callback := metric.WithFloat64Callback(func(ctx context.Context, observer metric.Float64Observer) error { + return metricOption.Callback(ctx, l.newMetricObserver(metricOption, observer)) + }) + options = append(options, callback) + } + counter, err := meter.Float64ObservableCounter(metricName, options...) + if err != nil { + return nil, gerror.WrapCodef( + gcode.CodeInternalError, + err, + `create Float64ObservableCounter "%s" failed with option: %s`, + metricName, gjson.MustEncodeString(metricOption), + ) + } + return &localObservableCounterPerformer{ + Float64ObservableCounter: counter, + }, nil +} diff --git a/contrib/metric/otelmetric/otelmetric_meter_observable_gauge_performer.go b/contrib/metric/otelmetric/otelmetric_meter_observable_gauge_performer.go new file mode 100644 index 000000000..7ff414791 --- /dev/null +++ b/contrib/metric/otelmetric/otelmetric_meter_observable_gauge_performer.go @@ -0,0 +1,56 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package otelmetric + +import ( + "context" + + "go.opentelemetry.io/otel/metric" + + "github.com/gogf/gf/v2/encoding/gjson" + "github.com/gogf/gf/v2/errors/gcode" + "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/os/gmetric" +) + +// localGaugePerformer is an implementer for interface GaugePerformer. +type localObservableGaugePerformer struct { + gmetric.ObservableMetric + metric.Float64ObservableGauge +} + +// newGaugePerformer creates and returns a GaugePerformer that truly takes action to implement Gauge. +func (l *localMeterPerformer) newObservableGaugePerformer( + meter metric.Meter, + metricName string, + metricOption gmetric.MetricOption, +) (gmetric.ObservableGaugePerformer, error) { + var ( + options = []metric.Float64ObservableGaugeOption{ + metric.WithDescription(metricOption.Help), + metric.WithUnit(metricOption.Unit), + } + ) + if metricOption.Callback != nil { + callback := metric.WithFloat64Callback(func(ctx context.Context, observer metric.Float64Observer) error { + return metricOption.Callback(ctx, l.newMetricObserver(metricOption, observer)) + }) + options = append(options, callback) + } + gauge, err := meter.Float64ObservableGauge(metricName, options...) + if err != nil { + return nil, gerror.WrapCodef( + gcode.CodeInternalError, + err, + `create Float64ObservableGauge "%s" failed with option: %s`, + metricName, gjson.MustEncodeString(metricOption), + ) + } + return &localObservableGaugePerformer{ + Float64ObservableGauge: gauge, + }, nil +} diff --git a/contrib/metric/otelmetric/otelmetric_meter_observable_updown_counter_performer.go b/contrib/metric/otelmetric/otelmetric_meter_observable_updown_counter_performer.go new file mode 100644 index 000000000..7f4312316 --- /dev/null +++ b/contrib/metric/otelmetric/otelmetric_meter_observable_updown_counter_performer.go @@ -0,0 +1,57 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package otelmetric + +import ( + "context" + + "go.opentelemetry.io/otel/metric" + + "github.com/gogf/gf/v2/encoding/gjson" + "github.com/gogf/gf/v2/errors/gcode" + "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/os/gmetric" +) + +// localObservableUpDownCounterPerformer is an implementer for interface CounterPerformer. +type localObservableUpDownCounterPerformer struct { + gmetric.ObservableMetric + metric.Float64ObservableUpDownCounter +} + +// newObservableUpDownCounterPerformer creates and returns a UpDownCounterPerformer that truly takes action to +// implement ObservableUpDownCounter. +func (l *localMeterPerformer) newObservableUpDownCounterPerformer( + meter metric.Meter, + metricName string, + metricOption gmetric.MetricOption, +) (gmetric.ObservableUpDownCounterPerformer, error) { + var ( + options = []metric.Float64ObservableUpDownCounterOption{ + metric.WithDescription(metricOption.Help), + metric.WithUnit(metricOption.Unit), + } + ) + if metricOption.Callback != nil { + callback := metric.WithFloat64Callback(func(ctx context.Context, observer metric.Float64Observer) error { + return metricOption.Callback(ctx, l.newMetricObserver(metricOption, observer)) + }) + options = append(options, callback) + } + counter, err := meter.Float64ObservableUpDownCounter(metricName, options...) + if err != nil { + return nil, gerror.WrapCodef( + gcode.CodeInternalError, + err, + `create Float64ObservableUpDownCounter "%s" failed with option: %s`, + metricName, gjson.MustEncodeString(metricOption), + ) + } + return &localObservableUpDownCounterPerformer{ + Float64ObservableUpDownCounter: counter, + }, nil +} diff --git a/contrib/metric/otelmetric/otelmetric_meter_performer.go b/contrib/metric/otelmetric/otelmetric_meter_performer.go new file mode 100644 index 000000000..574bb199b --- /dev/null +++ b/contrib/metric/otelmetric/otelmetric_meter_performer.go @@ -0,0 +1,143 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package otelmetric + +import ( + "context" + "fmt" + "reflect" + + otelmetric "go.opentelemetry.io/otel/metric" + "go.opentelemetry.io/otel/sdk/metric" + + "github.com/gogf/gf/v2/container/gset" + "github.com/gogf/gf/v2/errors/gcode" + "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/os/gmetric" +) + +// localMeterPerformer implements interface gmetric.Performer. +type localMeterPerformer struct { + gmetric.MeterOption + *metric.MeterProvider +} + +// newPerformer creates and returns gmetric.Meter. +func newMeterPerformer(provider *metric.MeterProvider, option gmetric.MeterOption) gmetric.MeterPerformer { + meterPerformer := &localMeterPerformer{ + MeterOption: option, + MeterProvider: provider, + } + return meterPerformer +} + +// CounterPerformer creates and returns a CounterPerformer that performs +// the operations for Counter metric. +func (l *localMeterPerformer) CounterPerformer(name string, option gmetric.MetricOption) (gmetric.CounterPerformer, error) { + return l.newCounterPerformer(l.createMeter(), name, option) +} + +// UpDownCounterPerformer creates and returns a UpDownCounterPerformer that performs +// the operations for UpDownCounter metric. +func (l *localMeterPerformer) UpDownCounterPerformer(name string, option gmetric.MetricOption) (gmetric.UpDownCounterPerformer, error) { + return l.newUpDownCounterPerformer(l.createMeter(), name, option) +} + +// HistogramPerformer creates and returns a HistogramPerformer that performs +// the operations for Histogram metric. +func (l *localMeterPerformer) HistogramPerformer(name string, option gmetric.MetricOption) (gmetric.HistogramPerformer, error) { + return l.newHistogramPerformer(l.createMeter(), name, option) +} + +// ObservableCounterPerformer creates and returns an ObservableMetric that performs +// the operations for ObservableCounter metric. +func (l *localMeterPerformer) ObservableCounterPerformer(name string, option gmetric.MetricOption) (gmetric.ObservableMetric, error) { + return l.newObservableCounterPerformer(l.createMeter(), name, option) +} + +// ObservableUpDownCounterPerformer creates and returns an ObservableMetric that performs +// the operations for ObservableUpDownCounter metric. +func (l *localMeterPerformer) ObservableUpDownCounterPerformer(name string, option gmetric.MetricOption) (gmetric.ObservableMetric, error) { + return l.newObservableUpDownCounterPerformer(l.createMeter(), name, option) +} + +// ObservableGaugePerformer creates and returns an ObservableMetric that performs +// the operations for ObservableGauge metric. +func (l *localMeterPerformer) ObservableGaugePerformer(name string, option gmetric.MetricOption) (gmetric.ObservableMetric, error) { + return l.newObservableGaugePerformer(l.createMeter(), name, option) +} + +// RegisterCallback registers callback on certain metrics. +// A callback is bound to certain component and version, it is called when the associated metrics are read. +// Multiple callbacks on the same component and version will be called by their registered sequence. +func (l *localMeterPerformer) RegisterCallback( + callback gmetric.Callback, observableMetrics ...gmetric.ObservableMetric, +) error { + var metrics = make([]gmetric.Metric, 0) + for _, v := range observableMetrics { + m, ok := v.(gmetric.Metric) + if !ok { + return gerror.NewCodef( + gcode.CodeInvalidParameter, + `invalid metric parameter "%s" for RegisterCallback, which does not implement interface Metric`, + reflect.TypeOf(v).String(), + ) + } + metrics = append(metrics, m) + } + // group the metric by instrument and instrument version. + var ( + instrumentSet = gset.NewStrSet() + underlyingMeterMap = map[otelmetric.Meter][]otelmetric.Observable{} + ) + for _, m := range metrics { + var meter = l.Meter( + m.Info().Instrument().Name(), + otelmetric.WithInstrumentationVersion(m.Info().Instrument().Version()), + ) + instrumentSet.Add(fmt.Sprintf( + `%s@%s`, + m.Info().Instrument().Name(), + m.Info().Instrument().Version(), + )) + if _, ok := underlyingMeterMap[meter]; !ok { + underlyingMeterMap[meter] = make([]otelmetric.Observable, 0) + } + underlyingMeterMap[meter] = append(underlyingMeterMap[meter], metricToFloat64Observable(m)) + } + if len(underlyingMeterMap) > 1 { + return gerror.NewCodef( + gcode.CodeInvalidParameter, + `multiple instrument or instrument version metrics used in the same callback: %s`, + instrumentSet.Join(","), + ) + } + // do callback registering. + for meter, observables := range underlyingMeterMap { + _, err := meter.RegisterCallback( + func(ctx context.Context, observer otelmetric.Observer) error { + return callback(ctx, newObserver(observer, l.MeterOption)) + }, + observables..., + ) + if err != nil { + return gerror.WrapCode( + gcode.CodeInternalError, err, + `RegisterCallback failed`, + ) + } + } + return nil +} + +// createMeter creates and returns an OpenTelemetry Meter. +func (l *localMeterPerformer) createMeter() otelmetric.Meter { + return l.Meter( + l.Instrument, + otelmetric.WithInstrumentationVersion(l.InstrumentVersion), + ) +} diff --git a/contrib/metric/otelmetric/otelmetric_meter_updown_counter_performer.go b/contrib/metric/otelmetric/otelmetric_meter_updown_counter_performer.go new file mode 100644 index 000000000..83d8316cc --- /dev/null +++ b/contrib/metric/otelmetric/otelmetric_meter_updown_counter_performer.go @@ -0,0 +1,70 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package otelmetric + +import ( + "context" + + "go.opentelemetry.io/otel/metric" + + "github.com/gogf/gf/v2/encoding/gjson" + "github.com/gogf/gf/v2/errors/gcode" + "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/os/gmetric" +) + +// localUpDownCounterPerformer is an implementer for interface gmetric.UpDownCounterPerformer. +type localUpDownCounterPerformer struct { + gmetric.MeterOption + gmetric.MetricOption + metric.Float64UpDownCounter + constOption metric.MeasurementOption +} + +// newUpDownCounterPerformer creates and returns a CounterPerformer that truly takes action to implement Counter. +func (l *localMeterPerformer) newUpDownCounterPerformer( + meter metric.Meter, + metricName string, + metricOption gmetric.MetricOption, +) (gmetric.UpDownCounterPerformer, error) { + var ( + options = []metric.Float64UpDownCounterOption{ + metric.WithDescription(metricOption.Help), + metric.WithUnit(metricOption.Unit), + } + ) + counter, err := meter.Float64UpDownCounter(metricName, options...) + if err != nil { + return nil, gerror.WrapCodef( + gcode.CodeInternalError, + err, + `create Float64Counter "%s" failed with config: %s`, + metricName, gjson.MustEncodeString(metricOption), + ) + } + return &localUpDownCounterPerformer{ + MeterOption: l.MeterOption, + MetricOption: metricOption, + Float64UpDownCounter: counter, + constOption: genConstOptionForMetric(l.MeterOption, metricOption), + }, nil +} + +// Inc increments the counter by 1. +func (l *localUpDownCounterPerformer) Inc(ctx context.Context, option ...gmetric.Option) { + l.Add(ctx, 1, option...) +} + +// Dec decrements the counter by 1. +func (l *localUpDownCounterPerformer) Dec(ctx context.Context, option ...gmetric.Option) { + l.Add(ctx, -1, option...) +} + +// Add adds the given value to the counter. +func (l *localUpDownCounterPerformer) Add(ctx context.Context, increment float64, option ...gmetric.Option) { + l.Float64UpDownCounter.Add(ctx, increment, generateAddOptions(l.MeterOption, l.constOption, option...)...) +} diff --git a/contrib/metric/otelmetric/otelmetric_metric_callback.go b/contrib/metric/otelmetric/otelmetric_metric_callback.go new file mode 100644 index 000000000..0efbc6218 --- /dev/null +++ b/contrib/metric/otelmetric/otelmetric_metric_callback.go @@ -0,0 +1,56 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package otelmetric + +import ( + "go.opentelemetry.io/otel/metric" + + "github.com/gogf/gf/v2/os/gmetric" +) + +// localMetricObserver implements interface gmetric.CallbackObserver. +type localMetricObserver struct { + gmetric.MeterOption + gmetric.MetricOption + metric.Float64Observer +} + +func (l *localMeterPerformer) newMetricObserver( + metricOption gmetric.MetricOption, + float64Observer metric.Float64Observer, +) gmetric.MetricObserver { + return &localMetricObserver{ + MeterOption: l.MeterOption, + MetricOption: metricOption, + Float64Observer: float64Observer, + } +} + +// Observe observes the value for certain initialized Metric. +// It adds the value to total result if the observed Metrics is type of Counter. +// It sets the value as the result if the observed Metrics is type of Gauge. +func (l *localMetricObserver) Observe(value float64, option ...gmetric.Option) { + var ( + constOption = genConstOptionForMetric(l.MeterOption, l.MetricOption) + dynamicOption = getDynamicOptionByMetricOption(option...) + globalAttributesOption = getGlobalAttributesOption(gmetric.GetGlobalAttributesOption{ + Instrument: l.Instrument, + InstrumentVersion: l.InstrumentVersion, + }) + observeOptions = make([]metric.ObserveOption, 0) + ) + if globalAttributesOption != nil { + observeOptions = append(observeOptions, globalAttributesOption) + } + if constOption != nil { + observeOptions = append(observeOptions, constOption) + } + if dynamicOption != nil { + observeOptions = append(observeOptions, dynamicOption) + } + l.Float64Observer.Observe(value, observeOptions...) +} diff --git a/contrib/metric/otelmetric/otelmetric_provider.go b/contrib/metric/otelmetric/otelmetric_provider.go new file mode 100644 index 000000000..653a1bed3 --- /dev/null +++ b/contrib/metric/otelmetric/otelmetric_provider.go @@ -0,0 +1,145 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package otelmetric + +import ( + "time" + + "go.opentelemetry.io/contrib/instrumentation/runtime" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/sdk/instrumentation" + "go.opentelemetry.io/otel/sdk/metric" + + "github.com/gogf/gf/v2/errors/gcode" + "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/os/gmetric" +) + +// localProvider implements interface gmetric.Provider. +type localProvider struct { + *metric.MeterProvider +} + +// newProvider creates and returns an object that implements gmetric.Provider. +// DO NOT set this as global provider internally. +func newProvider(options ...metric.Option) (gmetric.Provider, error) { + // TODO global logger set for otel. + // otel.SetLogger() + + var ( + err error + metrics = gmetric.GetAllMetrics() + builtinViews = createViewsForBuiltInMetrics() + callbacks = gmetric.GetRegisteredCallbacks() + ) + options = append(options, metric.WithView(builtinViews...)) + provider := &localProvider{ + // MeterProvider is the core object that can create otel metrics. + MeterProvider: metric.NewMeterProvider(options...), + } + + if err = provider.initializeMetrics(metrics); err != nil { + return nil, err + } + + if err = provider.initializeCallback(callbacks); err != nil { + return nil, err + } + + // builtin metrics: golang. + err = runtime.Start( + runtime.WithMinimumReadMemStatsInterval(time.Second), + runtime.WithMeterProvider(provider), + ) + if err != nil { + return nil, gerror.WrapCode( + gcode.CodeInternalError, err, `start built-in runtime metrics failed`, + ) + } + + return provider, nil +} + +// SetAsGlobal sets current provider as global meter provider for current process, +// which makes the following metrics creating on this Provider, especially the metrics created in runtime. +func (l *localProvider) SetAsGlobal() { + gmetric.SetGlobalProvider(l) + otel.SetMeterProvider(l) +} + +// MeterPerformer creates and returns a MeterPerformer. +// A Performer can produce types of Metric performer. +func (l *localProvider) MeterPerformer(option gmetric.MeterOption) gmetric.MeterPerformer { + return newMeterPerformer(l.MeterProvider, option) +} + +// createViewsForBuiltInMetrics creates and returns views for builtin metrics. +func createViewsForBuiltInMetrics() []metric.View { + var views = make([]metric.View, 0) + views = append(views, metric.NewView( + metric.Instrument{ + Name: "process.runtime.go.gc.pause_ns", + Scope: instrumentation.Scope{ + Name: runtime.ScopeName, + Version: runtime.Version(), + }, + }, + metric.Stream{ + Aggregation: metric.AggregationExplicitBucketHistogram{ + Boundaries: []float64{ + 500, 1000, 5000, 10000, 50000, 100000, 500000, 1000000, + }, + }, + }, + )) + views = append(views, metric.NewView( + metric.Instrument{ + Name: "runtime.uptime", + Scope: instrumentation.Scope{ + Name: runtime.ScopeName, + Version: runtime.Version(), + }, + }, + metric.Stream{ + Name: "process.runtime.uptime", + }, + )) + return views +} + +// initializeMetrics initializes all metrics in provider creating. +// The initialization replaces the underlying metric performer using noop-performer with truly performer +// that implements operations for types of metric. +func (l *localProvider) initializeMetrics(metrics []gmetric.Metric) error { + for _, m := range metrics { + if initializer, ok := m.(gmetric.MetricInitializer); ok { + if err := initializer.Init(l); err != nil { + return err + } + } + } + return nil +} + +func (l *localProvider) initializeCallback(callbackItems []gmetric.CallbackItem) error { + var err error + for _, callbackItem := range callbackItems { + if callbackItem.Provider != nil { + continue + } + if len(callbackItem.Metrics) == 0 { + continue + } + callbackItem.Provider = l + if err = l.MeterPerformer(callbackItem.MeterOption).RegisterCallback( + callbackItem.Callback, callbackItem.Metrics..., + ); err != nil { + return err + } + } + return nil +} diff --git a/contrib/metric/otelmetric/otelmetric_util.go b/contrib/metric/otelmetric/otelmetric_util.go new file mode 100644 index 000000000..0051cb6d5 --- /dev/null +++ b/contrib/metric/otelmetric/otelmetric_util.go @@ -0,0 +1,170 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package otelmetric + +import ( + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric" + + "github.com/gogf/gf/v2/errors/gcode" + "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/os/gmetric" + "github.com/gogf/gf/v2/util/gconv" +) + +func generateAddOptions( + meterOption gmetric.MeterOption, constOption metric.MeasurementOption, option ...gmetric.Option, +) []metric.AddOption { + var ( + addOptions = make([]metric.AddOption, 0) + globalAttributesOption = getGlobalAttributesOption(gmetric.GetGlobalAttributesOption{ + Instrument: meterOption.Instrument, + InstrumentVersion: meterOption.InstrumentVersion, + }) + ) + if constOption != nil { + addOptions = append(addOptions, constOption) + } + if globalAttributesOption != nil { + addOptions = append(addOptions, globalAttributesOption) + } + if len(option) > 0 { + addOptions = append( + addOptions, + metric.WithAttributes(attributesToKeyValues(option[0].Attributes)...), + ) + } + return addOptions +} + +func getGlobalAttributesOption(option gmetric.GetGlobalAttributesOption) metric.MeasurementOption { + var ( + globalAttributesOption metric.MeasurementOption + globalAttributes = gmetric.GetGlobalAttributes(gmetric.GetGlobalAttributesOption{}) + instrumentAttributes gmetric.Attributes + ) + if option.Instrument != "" { + instrumentAttributes = gmetric.GetGlobalAttributes(option) + } + if len(globalAttributes) > 0 { + globalAttributesOption = metric.WithAttributes(attributesToKeyValues(globalAttributes)...) + } + if len(instrumentAttributes) > 0 { + globalAttributesOption = metric.WithAttributes(attributesToKeyValues(instrumentAttributes)...) + } + return globalAttributesOption +} + +func getDynamicOptionByMetricOption(option ...gmetric.Option) metric.MeasurementOption { + var ( + usedOption gmetric.Option + dynamicOption metric.MeasurementOption + ) + if len(option) > 0 { + usedOption = option[0] + } + if len(usedOption.Attributes) > 0 { + dynamicOption = metric.WithAttributes(attributesToKeyValues(usedOption.Attributes)...) + } + return dynamicOption +} + +func genConstOptionForMetric( + meterOption gmetric.MeterOption, + metricOption gmetric.MetricOption, +) metric.MeasurementOption { + return genConstOptionForMetricByAttributes(meterOption.Attributes, metricOption.Attributes) +} + +func getConstOptionByMetric(meterOption gmetric.MeterOption, m gmetric.Metric) metric.MeasurementOption { + return genConstOptionForMetricByAttributes(meterOption.Attributes, m.Info().Attributes()) +} + +func genConstOptionForMetricByAttributes( + meterAttrs gmetric.Attributes, + metricAttrs gmetric.Attributes, +) metric.MeasurementOption { + var ( + constOption metric.MeasurementOption + attributes = make([]attribute.KeyValue, 0) + ) + if len(meterAttrs) > 0 { + attributes = append(attributes, attributesToKeyValues(meterAttrs)...) + } + if len(metricAttrs) > 0 { + attributes = append(attributes, attributesToKeyValues(metricAttrs)...) + } + constOption = metric.WithAttributes(attributes...) + return constOption +} + +func metricToFloat64Observable(m gmetric.Metric) metric.Float64Observable { + performer := m.(gmetric.PerformerExporter).Performer() + switch m.Info().Type() { + case gmetric.MetricTypeObservableCounter: + return performer.(*localObservableCounterPerformer).Float64ObservableCounter + + case gmetric.MetricTypeObservableUpDownCounter: + return performer.(*localObservableUpDownCounterPerformer).Float64ObservableUpDownCounter + + case gmetric.MetricTypeObservableGauge: + return performer.(*localObservableGaugePerformer).Float64ObservableGauge + + default: + panic(gerror.NewCode( + gcode.CodeInvalidParameter, + `Histogram is not support for converting to metric.Float64Observable`, + )) + } + return nil +} + +// attributesToKeyValues converts attributes to OpenTelemetry key-value pair attributes. +func attributesToKeyValues(attrs gmetric.Attributes) []attribute.KeyValue { + var keyValues = make([]attribute.KeyValue, 0) + for _, attr := range attrs { + keyValues = append(keyValues, attributeToKeyValue(attr)) + } + return keyValues +} + +// attributeToKeyValue converts attribute to OpenTelemetry key-value pair attribute. +func attributeToKeyValue(attr gmetric.Attribute) attribute.KeyValue { + var ( + key = string(attr.Key()) + value = attr.Value() + ) + switch result := value.(type) { + case bool: + return attribute.Bool(key, result) + case []bool: + return attribute.BoolSlice(key, result) + + case int: + return attribute.Int(key, result) + case []int: + return attribute.IntSlice(key, result) + + case int64: + return attribute.Int64(key, result) + case []int64: + return attribute.Int64Slice(key, result) + + case float64: + return attribute.Float64(key, result) + case []float64: + return attribute.Float64Slice(key, result) + + case string: + return attribute.String(key, result) + case []string: + return attribute.StringSlice(key, result) + + default: + return attribute.String(key, gconv.String(value)) + } +} diff --git a/contrib/metric/otelmetric/otelmetric_z_unit_http_test.go b/contrib/metric/otelmetric/otelmetric_z_unit_http_test.go new file mode 100644 index 000000000..129f925ae --- /dev/null +++ b/contrib/metric/otelmetric/otelmetric_z_unit_http_test.go @@ -0,0 +1,95 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package otelmetric_test + +import ( + "fmt" + "testing" + "time" + + "github.com/prometheus/client_golang/prometheus/promhttp" + "go.opentelemetry.io/otel/exporters/prometheus" + "go.opentelemetry.io/otel/sdk/metric" + + "github.com/gogf/gf/contrib/metric/otelmetric/v2" + "github.com/gogf/gf/v2" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/net/ghttp" + "github.com/gogf/gf/v2/os/gctx" + "github.com/gogf/gf/v2/os/gmetric" + "github.com/gogf/gf/v2/test/gtest" + "github.com/gogf/gf/v2/text/gregex" + "github.com/gogf/gf/v2/text/gstr" + "github.com/gogf/gf/v2/util/guid" +) + +func Test_HTTP_Server(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + s := g.Server(guid.S()) + s.BindHandler("/user/:id", func(r *ghttp.Request) { + r.Response.Write("user") + }) + s.BindHandler("/order/:id", func(r *ghttp.Request) { + r.Response.Write("order") + }) + s.BindHandler("/metrics", ghttp.WrapH(promhttp.Handler())) + s.SetDumpRouterMap(false) + s.Start() + defer s.Shutdown() + + time.Sleep(100 * time.Millisecond) + + var ctx = gctx.New() + // Prometheus exporter to export metrics as Prometheus format. + exporter, err := prometheus.New( + prometheus.WithoutCounterSuffixes(), + prometheus.WithoutUnits(), + ) + if err != nil { + g.Log().Fatal(ctx, err) + } + + // OpenTelemetry provider. + provider := otelmetric.MustProvider(metric.WithReader(exporter)) + defer provider.Shutdown(ctx) + + gmetric.SetGlobalProvider(provider) + defer gmetric.SetGlobalProvider(nil) + + c := g.Client() + c.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())) + + c.GetContent(ctx, "/user/1") + c.PutContent(ctx, "/user/1", "123") + c.PostContent(ctx, "/user/2", "123") + c.DeleteContent(ctx, "/user/3") + c.GetContent(ctx, "/order/1") + c.PutContent(ctx, "/order/1", "1234") + c.PostContent(ctx, "/order/2", "1234") + c.DeleteContent(ctx, "/order/3") + + var ( + metricsContent = c.GetContent(ctx, "/metrics") + expectContent = gtest.DataContent("http.prometheus.metrics.txt") + ) + expectContent, _ = gregex.ReplaceString( + `otel_scope_version=".+?"`, + fmt.Sprintf(`otel_scope_version="%s"`, gf.VERSION), + expectContent, + ) + expectContent, _ = gregex.ReplaceString( + `server_port=".+?"`, + fmt.Sprintf(`server_port="%d"`, s.GetListenedPort()), + expectContent, + ) + //fmt.Println(metricsContent) + for _, line := range gstr.SplitAndTrim(expectContent, "\n") { + //fmt.Println(line) + t.Assert(gstr.Contains(metricsContent, line), true) + } + }) +} diff --git a/contrib/metric/otelmetric/otelmetric_z_unit_test.go b/contrib/metric/otelmetric/otelmetric_z_unit_test.go new file mode 100644 index 000000000..db1d398cb --- /dev/null +++ b/contrib/metric/otelmetric/otelmetric_z_unit_test.go @@ -0,0 +1,320 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package otelmetric_test + +import ( + "context" + "testing" + + "go.opentelemetry.io/otel/sdk/metric" + "go.opentelemetry.io/otel/sdk/metric/metricdata" + + "github.com/gogf/gf/contrib/metric/otelmetric/v2" + "github.com/gogf/gf/v2/encoding/gjson" + "github.com/gogf/gf/v2/os/gctx" + "github.com/gogf/gf/v2/os/gmetric" + "github.com/gogf/gf/v2/test/gtest" + "github.com/gogf/gf/v2/text/gstr" +) + +func Test_Basic(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + var ( + ctx = gctx.New() + meterV11 = gmetric.GetGlobalProvider().Meter(gmetric.MeterOption{ + Instrument: "github.com/gogf/gf/example/metric/basic", + InstrumentVersion: "v1.1", + }) + meterV12 = gmetric.GetGlobalProvider().Meter(gmetric.MeterOption{ + Instrument: "github.com/gogf/gf/example/metric/basic", + InstrumentVersion: "v1.2", + }) + meterV13 = gmetric.GetGlobalProvider().Meter(gmetric.MeterOption{ + Instrument: "github.com/gogf/gf/example/metric/basic", + InstrumentVersion: "v1.3", + }) + meterV14 = gmetric.GetGlobalProvider().Meter(gmetric.MeterOption{ + Instrument: "github.com/gogf/gf/example/metric/basic", + InstrumentVersion: "v1.4", + }) + counter = meterV11.MustCounter( + "goframe.metric.demo.counter", + gmetric.MetricOption{ + Help: "This is a simple demo for Counter usage", + Unit: "%", + Attributes: gmetric.Attributes{ + gmetric.NewAttribute("const_label_1", 1), + }, + }, + ) + upDownCounter = meterV12.MustUpDownCounter( + "goframe.metric.demo.updown_counter", + gmetric.MetricOption{ + Help: "This is a simple demo for UpDownCounter usage", + Unit: "%", + Attributes: gmetric.Attributes{ + gmetric.NewAttribute("const_label_2", 2), + }, + }, + ) + histogram = meterV13.MustHistogram( + "goframe.metric.demo.histogram", + gmetric.MetricOption{ + Help: "This is a simple demo for histogram usage", + Unit: "ms", + Attributes: gmetric.Attributes{ + gmetric.NewAttribute("const_label_3", 3), + }, + Buckets: []float64{0, 10, 20, 50, 100, 500, 1000, 2000, 5000, 10000}, + }, + ) + observableCounter = meterV14.MustObservableCounter( + "goframe.metric.demo.observable_counter", + gmetric.MetricOption{ + Help: "This is a simple demo for ObservableCounter usage", + Unit: "%", + Attributes: gmetric.Attributes{ + gmetric.NewAttribute("const_label_4", 4), + }, + }, + ) + observableUpDownCounter = meterV14.MustObservableUpDownCounter( + "goframe.metric.demo.observable_updown_counter", + gmetric.MetricOption{ + Help: "This is a simple demo for ObservableUpDownCounter usage", + Unit: "%", + Attributes: gmetric.Attributes{ + gmetric.NewAttribute("const_label_5", 5), + }, + }, + ) + observableGauge = meterV14.MustObservableGauge( + "goframe.metric.demo.observable_gauge", + gmetric.MetricOption{ + Help: "This is a simple demo for ObservableGauge usage", + Unit: "%", + Attributes: gmetric.Attributes{ + gmetric.NewAttribute("const_label_6", 6), + }, + }, + ) + ) + + meterV14.MustRegisterCallback(func(ctx context.Context, obs gmetric.Observer) error { + obs.Observe(observableCounter, 10, gmetric.Option{ + Attributes: gmetric.Attributes{gmetric.NewAttribute("dynamic_label_4", "4")}, + }) + obs.Observe(observableUpDownCounter, 20, gmetric.Option{ + Attributes: gmetric.Attributes{gmetric.NewAttribute("dynamic_label_5", "5")}, + }) + obs.Observe(observableGauge, 30, gmetric.Option{ + Attributes: gmetric.Attributes{gmetric.NewAttribute("dynamic_label_6", "6")}, + }) + return nil + }, observableCounter, observableUpDownCounter, observableGauge) + + reader := metric.NewManualReader() + + // OpenTelemetry provider. + provider := otelmetric.MustProvider(metric.WithReader(reader)) + defer provider.Shutdown(ctx) + + // Counter. + counter.Inc(ctx) + counter.Add(ctx, 10, gmetric.Option{ + Attributes: gmetric.Attributes{gmetric.NewAttribute("dynamic_label_1", "1")}, + }) + + upDownCounter.Add(ctx, 10) + upDownCounter.Dec(ctx, gmetric.Option{ + Attributes: gmetric.Attributes{gmetric.NewAttribute("dynamic_label_2", "2")}, + }) + + // Record values for histogram. + histogram.Record(1) + histogram.Record(20) + histogram.Record(30) + histogram.Record(101) + histogram.Record(2000) + histogram.Record(9000) + histogram.Record(20000) + + histogramOption := gmetric.Option{ + Attributes: gmetric.Attributes{gmetric.NewAttribute("dynamic_label_3", "3")}, + } + histogram.Record(100, histogramOption) + histogram.Record(200, histogramOption) + + rm := metricdata.ResourceMetrics{} + err := reader.Collect(ctx, &rm) + t.AssertNil(err) + + metricsJsonContent := gjson.MustEncodeString(rm) + + t.Assert(len(rm.ScopeMetrics), 5) + t.Assert(gstr.Count(metricsJsonContent, `goframe.metric.demo.counter`), 1) + t.Assert(gstr.Count(metricsJsonContent, `goframe.metric.demo.updown_counter`), 1) + t.Assert(gstr.Count(metricsJsonContent, `goframe.metric.demo.histogram`), 1) + t.Assert(gstr.Count(metricsJsonContent, `goframe.metric.demo.observable_counter`), 1) + t.Assert(gstr.Count(metricsJsonContent, `goframe.metric.demo.observable_updown_counter"`), 1) + t.Assert(gstr.Count(metricsJsonContent, `goframe.metric.demo.observable_gauge`), 1) + t.Assert(gstr.Count(metricsJsonContent, `{"Key":"const_label_2","Value":{"Type":"INT64","Value":2}}`), 2) + t.Assert(gstr.Count(metricsJsonContent, `{"Key":"dynamic_label_2","Value":{"Type":"STRING","Value":"2"}}`), 1) + t.Assert(gstr.Count(metricsJsonContent, `{"Key":"const_label_3","Value":{"Type":"INT64","Value":3}}`), 2) + t.Assert(gstr.Count(metricsJsonContent, `"Count":7,"Bounds":[0,10,20,50,100,500,1000,2000,5000,10000],"BucketCounts":[0,1,1,1,0,1,0,1,0,1,1],"Min":1,"Max":20000,"Sum":31152`), 1) + t.Assert(gstr.Count(metricsJsonContent, `{"Key":"const_label_3","Value":{"Type":"INT64","Value":3}}`), 2) + t.Assert(gstr.Count(metricsJsonContent, `{"Key":"dynamic_label_3","Value":{"Type":"STRING","Value":"3"}}`), 1) + t.Assert(gstr.Count(metricsJsonContent, `"Count":2,"Bounds":[0,10,20,50,100,500,1000,2000,5000,10000],"BucketCounts":[0,0,0,0,1,1,0,0,0,0,0],"Min":100,"Max":200,"Sum":300`), 1) + t.Assert(gstr.Count(metricsJsonContent, `{"Key":"const_label_4","Value":{"Type":"INT64","Value":4}}`), 1) + t.Assert(gstr.Count(metricsJsonContent, `{"Key":"dynamic_label_4","Value":{"Type":"STRING","Value":"4"}}`), 1) + t.Assert(gstr.Count(metricsJsonContent, `{"Key":"const_label_5","Value":{"Type":"INT64","Value":5}}`), 1) + t.Assert(gstr.Count(metricsJsonContent, `{"Key":"dynamic_label_5","Value":{"Type":"STRING","Value":"5"}}`), 1) + t.Assert(gstr.Count(metricsJsonContent, `{"Key":"const_label_6","Value":{"Type":"INT64","Value":6}}`), 1) + t.Assert(gstr.Count(metricsJsonContent, `{"Key":"dynamic_label_6","Value":{"Type":"STRING","Value":"6"}}`), 1) + t.Assert(gstr.Count(metricsJsonContent, `{"Key":"const_label_1","Value":{"Type":"INT64","Value":1}}`), 2) + t.Assert(gstr.Count(metricsJsonContent, `{"Key":"dynamic_label_1","Value":{"Type":"STRING","Value":"1"}}`), 1) + }) +} + +func Test_GlobalAttributes(t *testing.T) { + gmetric.SetGlobalAttributes(gmetric.Attributes{ + gmetric.NewAttribute("g1", 1), + }, gmetric.SetGlobalAttributesOption{ + Instrument: "github.com/gogf/gf/example/metric/basic", + InstrumentVersion: "v1.1", + InstrumentPattern: "", + }) + gmetric.SetGlobalAttributes(gmetric.Attributes{ + gmetric.NewAttribute("g2", 2), + }, gmetric.SetGlobalAttributesOption{ + Instrument: "github.com/gogf/gf/example/metric/basic", + InstrumentVersion: "v1.3", + InstrumentPattern: "", + }) + gtest.C(t, func(t *gtest.T) { + var ( + ctx = gctx.New() + meterV11 = gmetric.GetGlobalProvider().Meter(gmetric.MeterOption{ + Instrument: "github.com/gogf/gf/example/metric/basic", + InstrumentVersion: "v1.1", + }) + meterV12 = gmetric.GetGlobalProvider().Meter(gmetric.MeterOption{ + Instrument: "github.com/gogf/gf/example/metric/basic", + InstrumentVersion: "v1.2", + }) + meterV13 = gmetric.GetGlobalProvider().Meter(gmetric.MeterOption{ + Instrument: "github.com/gogf/gf/example/metric/basic", + InstrumentVersion: "v1.3", + }) + counter = meterV11.MustCounter( + "goframe.metric.demo.counter", + gmetric.MetricOption{ + Help: "This is a simple demo for Counter usage", + Unit: "%", + Attributes: gmetric.Attributes{ + gmetric.NewAttribute("const_label_1", 1), + }, + }, + ) + + histogram = meterV12.MustHistogram( + "goframe.metric.demo.histogram", + gmetric.MetricOption{ + Help: "This is a simple demo for histogram usage", + Unit: "ms", + Attributes: gmetric.Attributes{ + gmetric.NewAttribute("const_label_2", 2), + }, + Buckets: []float64{0, 10, 20, 50, 100, 500, 1000, 2000, 5000, 10000}, + }, + ) + + observableCounter = meterV13.MustObservableCounter( + "goframe.metric.demo.observable_counter", + gmetric.MetricOption{ + Help: "This is a simple demo for ObservableCounter usage", + Unit: "%", + Attributes: gmetric.Attributes{ + gmetric.NewAttribute("const_label_3", 3), + }, + }, + ) + + observableGauge = meterV13.MustObservableGauge( + "goframe.metric.demo.observable_gauge", + gmetric.MetricOption{ + Help: "This is a simple demo for ObservableGauge usage", + Unit: "%", + Attributes: gmetric.Attributes{ + gmetric.NewAttribute("const_label_4", 4), + }, + }, + ) + ) + + meterV13.MustRegisterCallback(func(ctx context.Context, obs gmetric.Observer) error { + obs.Observe(observableCounter, 10, gmetric.Option{ + Attributes: gmetric.Attributes{gmetric.NewAttribute("dynamic_label_3", "3")}, + }) + obs.Observe(observableGauge, 10, gmetric.Option{ + Attributes: gmetric.Attributes{gmetric.NewAttribute("dynamic_label_4", "4")}, + }) + return nil + }, observableCounter, observableGauge) + + reader := metric.NewManualReader() + + // OpenTelemetry provider. + provider := otelmetric.MustProvider(metric.WithReader(reader)) + defer provider.Shutdown(ctx) + + // Add value for counter. + counter.Inc(ctx) + counter.Add(ctx, 10, gmetric.Option{ + Attributes: gmetric.Attributes{gmetric.NewAttribute("dynamic_label_1", "1")}, + }) + + // Record values for histogram. + histogram.Record(1) + histogram.Record(20) + histogram.Record(30) + histogram.Record(101) + histogram.Record(2000) + histogram.Record(9000) + histogram.Record(20000) + + histogramOption := gmetric.Option{ + Attributes: gmetric.Attributes{gmetric.NewAttribute("dynamic_label_2", "2")}, + } + histogram.Record(100, histogramOption) + histogram.Record(200, histogramOption) + + rm := metricdata.ResourceMetrics{} + err := reader.Collect(ctx, &rm) + t.AssertNil(err) + + metricsJsonContent := gjson.MustEncodeString(rm) + t.Assert(len(rm.ScopeMetrics), 4) + t.Assert(gstr.Count(metricsJsonContent, `goframe.metric.demo.counter`), 1) + t.Assert(gstr.Count(metricsJsonContent, `goframe.metric.demo.histogram`), 1) + t.Assert(gstr.Count(metricsJsonContent, `goframe.metric.demo.observable_counter`), 1) + t.Assert(gstr.Count(metricsJsonContent, `goframe.metric.demo.observable_gauge`), 1) + t.Assert(gstr.Count(metricsJsonContent, `goframe.metric.demo.observable_gauge`), 1) + t.Assert(gstr.Count(metricsJsonContent, `{"Key":"const_label_1","Value":{"Type":"INT64","Value":1}}`), 2) + t.Assert(gstr.Count(metricsJsonContent, `{"Key":"g1","Value":{"Type":"INT64","Value":1}}`), 2) + t.Assert(gstr.Count(metricsJsonContent, `{"Key":"dynamic_label_1","Value":{"Type":"STRING","Value":"1"}}`), 1) + t.Assert(gstr.Count(metricsJsonContent, `{"Key":"const_label_2","Value":{"Type":"INT64","Value":2}}`), 2) + t.Assert(gstr.Count(metricsJsonContent, `{"Key":"dynamic_label_2","Value":{"Type":"STRING","Value":"2"}}`), 1) + t.Assert(gstr.Count(metricsJsonContent, `"Count":2,"Bounds":[0,10,20,50,100,500,1000,2000,5000,10000],"BucketCounts":[0,0,0,0,1,1,0,0,0,0,0],"Min":100,"Max":200,"Sum":300`), 1) + t.Assert(gstr.Count(metricsJsonContent, `"Count":7,"Bounds":[0,10,20,50,100,500,1000,2000,5000,10000],"BucketCounts":[0,1,1,1,0,1,0,1,0,1,1],"Min":1,"Max":20000,"Sum":31152`), 1) + t.Assert(gstr.Count(metricsJsonContent, `{"Key":"const_label_3","Value":{"Type":"INT64","Value":3}}`), 1) + t.Assert(gstr.Count(metricsJsonContent, `{"Key":"dynamic_label_3","Value":{"Type":"STRING","Value":"3"}}`), 1) + t.Assert(gstr.Count(metricsJsonContent, `{"Key":"g2","Value":{"Type":"INT64","Value":2}}`), 2) + t.Assert(gstr.Count(metricsJsonContent, `{"Key":"const_label_4","Value":{"Type":"INT64","Value":4}}`), 1) + t.Assert(gstr.Count(metricsJsonContent, `{"Key":"dynamic_label_4","Value":{"Type":"STRING","Value":"4"}}`), 1) + }) +} diff --git a/contrib/metric/otelmetric/testdata/http.prometheus.metrics.txt b/contrib/metric/otelmetric/testdata/http.prometheus.metrics.txt new file mode 100644 index 000000000..60d51c0d5 --- /dev/null +++ b/contrib/metric/otelmetric/testdata/http.prometheus.metrics.txt @@ -0,0 +1,141 @@ + +# HELP http_client_connection_duration Measures the connection establish duration of client requests. +# TYPE http_client_connection_duration histogram +http_client_connection_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="1"} +http_client_connection_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="5"} +http_client_connection_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="10"} +http_client_connection_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="25"} +http_client_connection_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="50"} +http_client_connection_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="75"} +http_client_connection_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="100"} +http_client_connection_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="250"} +http_client_connection_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="500"} +http_client_connection_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="750"} +http_client_connection_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="1000"} +http_client_connection_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="2500"} +http_client_connection_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="5000"} +http_client_connection_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="7500"} +http_client_connection_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="10000"} +http_client_connection_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="30000"} +http_client_connection_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="60000"} +http_client_connection_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="+Inf"} +http_client_connection_duration_sum{otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730"} +http_client_connection_duration_count{otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730"} 9 +# HELP http_client_request_active Number of active client requests. +# TYPE http_client_request_active gauge +http_client_request_active{http_request_method="DELETE",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} 0 +http_client_request_active{http_request_method="GET",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} 1 +http_client_request_active{http_request_method="POST",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} 0 +http_client_request_active{http_request_method="PUT",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} 0 +# HELP http_client_request_body_size Outgoing request bytes total. +# TYPE http_client_request_body_size counter +http_client_request_body_size{http_request_method="POST",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} 7 +http_client_request_body_size{http_request_method="PUT",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} 7 +# HELP http_client_request_duration Measures the duration of client requests. +# TYPE http_client_request_duration histogram +http_client_request_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="1"} +http_client_request_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="5"} +http_client_request_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="10"} +http_client_request_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="25"} +http_client_request_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="50"} +http_client_request_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="75"} +http_client_request_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="100"} +http_client_request_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="250"} +http_client_request_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="500"} +http_client_request_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="750"} +http_client_request_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="1000"} +http_client_request_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="2500"} +http_client_request_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="5000"} +http_client_request_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="7500"} +http_client_request_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="10000"} +http_client_request_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="30000"} +http_client_request_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="60000"} +http_client_request_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="+Inf"} +http_client_request_duration_sum{otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730"} +http_client_request_duration_count{otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730"} 8 +# HELP http_client_request_duration_total Total execution duration of request. +# TYPE http_client_request_duration_total counter +http_client_request_duration_total{http_request_method="DELETE",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} +http_client_request_duration_total{http_request_method="GET",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} +http_client_request_duration_total{http_request_method="POST",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} +http_client_request_duration_total{http_request_method="PUT",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} +# HELP http_client_request_total Total processed request number. +# TYPE http_client_request_total counter +http_client_request_total{http_request_method="DELETE",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} 2 +http_client_request_total{http_request_method="GET",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} 2 +http_client_request_total{http_request_method="POST",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} 2 +http_client_request_total{http_request_method="PUT",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/gclient.Client",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} 2 +# HELP http_server_request_active Number of active server requests. +# TYPE http_server_request_active gauge +http_server_request_active{http_request_method="DELETE",http_route="/order/:id",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} 0 +http_server_request_active{http_request_method="DELETE",http_route="/user/:id",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} 0 +http_server_request_active{http_request_method="GET",http_route="/metrics",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} 1 +http_server_request_active{http_request_method="GET",http_route="/order/:id",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} 0 +http_server_request_active{http_request_method="GET",http_route="/user/:id",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} 0 +http_server_request_active{http_request_method="POST",http_route="/order/:id",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} 0 +http_server_request_active{http_request_method="POST",http_route="/user/:id",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} 0 +http_server_request_active{http_request_method="PUT",http_route="/order/:id",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} 0 +http_server_request_active{http_request_method="PUT",http_route="/user/:id",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} 0 +# HELP http_server_request_body_size Incoming request bytes total. +# TYPE http_server_request_body_size counter +http_server_request_body_size{http_request_method="DELETE",http_route="/order/:id",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} 0 +http_server_request_body_size{http_request_method="DELETE",http_route="/user/:id",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} 0 +http_server_request_body_size{http_request_method="GET",http_route="/metrics",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} 0 +http_server_request_body_size{http_request_method="GET",http_route="/order/:id",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} 0 +http_server_request_body_size{http_request_method="GET",http_route="/user/:id",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} 0 +http_server_request_body_size{http_request_method="POST",http_route="/order/:id",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} 4 +http_server_request_body_size{http_request_method="POST",http_route="/user/:id",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} 3 +http_server_request_body_size{http_request_method="PUT",http_route="/order/:id",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} 4 +http_server_request_body_size{http_request_method="PUT",http_route="/user/:id",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} 3 +# HELP http_server_request_duration Measures the duration of inbound request. +# TYPE http_server_request_duration histogram +http_server_request_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="1"} +http_server_request_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="5"} +http_server_request_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="10"} +http_server_request_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="25"} +http_server_request_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="50"} +http_server_request_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="75"} +http_server_request_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="100"} +http_server_request_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="250"} +http_server_request_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="500"} +http_server_request_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="750"} +http_server_request_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="1000"} +http_server_request_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="2500"} +http_server_request_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="5000"} +http_server_request_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="7500"} +http_server_request_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="10000"} +http_server_request_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="30000"} +http_server_request_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="60000"} +http_server_request_duration_bucket{otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",le="+Inf"} +http_server_request_duration_sum{otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730"} +http_server_request_duration_count{otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730"} +# HELP http_server_request_duration_total Total execution duration of request. +# TYPE http_server_request_duration_total counter +http_server_request_duration_total{error_code="0",http_request_method="DELETE",http_response_status_code="200",http_route="/order/:id",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} +http_server_request_duration_total{error_code="0",http_request_method="DELETE",http_response_status_code="200",http_route="/user/:id",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} +http_server_request_duration_total{error_code="0",http_request_method="GET",http_response_status_code="200",http_route="/order/:id",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} +http_server_request_duration_total{error_code="0",http_request_method="GET",http_response_status_code="200",http_route="/user/:id",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} +http_server_request_duration_total{error_code="0",http_request_method="POST",http_response_status_code="200",http_route="/order/:id",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} +http_server_request_duration_total{error_code="0",http_request_method="POST",http_response_status_code="200",http_route="/user/:id",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} +http_server_request_duration_total{error_code="0",http_request_method="PUT",http_response_status_code="200",http_route="/order/:id",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} +http_server_request_duration_total{error_code="0",http_request_method="PUT",http_response_status_code="200",http_route="/user/:id",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} +# HELP http_server_request_total Total processed request number. +# TYPE http_server_request_total counter +http_server_request_total{error_code="0",http_request_method="DELETE",http_response_status_code="200",http_route="/order/:id",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} 1 +http_server_request_total{error_code="0",http_request_method="DELETE",http_response_status_code="200",http_route="/user/:id",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} 1 +http_server_request_total{error_code="0",http_request_method="GET",http_response_status_code="200",http_route="/order/:id",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} 1 +http_server_request_total{error_code="0",http_request_method="GET",http_response_status_code="200",http_route="/user/:id",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} 1 +http_server_request_total{error_code="0",http_request_method="POST",http_response_status_code="200",http_route="/order/:id",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} 1 +http_server_request_total{error_code="0",http_request_method="POST",http_response_status_code="200",http_route="/user/:id",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} 1 +http_server_request_total{error_code="0",http_request_method="PUT",http_response_status_code="200",http_route="/order/:id",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} 1 +http_server_request_total{error_code="0",http_request_method="PUT",http_response_status_code="200",http_route="/user/:id",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} 1 +# HELP http_server_response_body_size Response bytes total. +# TYPE http_server_response_body_size counter +http_server_response_body_size{error_code="0",http_request_method="DELETE",http_response_status_code="200",http_route="/order/:id",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} 5 +http_server_response_body_size{error_code="0",http_request_method="DELETE",http_response_status_code="200",http_route="/user/:id",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} 4 +http_server_response_body_size{error_code="0",http_request_method="GET",http_response_status_code="200",http_route="/order/:id",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} 5 +http_server_response_body_size{error_code="0",http_request_method="GET",http_response_status_code="200",http_route="/user/:id",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} 4 +http_server_response_body_size{error_code="0",http_request_method="POST",http_response_status_code="200",http_route="/order/:id",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} 5 +http_server_response_body_size{error_code="0",http_request_method="POST",http_response_status_code="200",http_route="/user/:id",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} 4 +http_server_response_body_size{error_code="0",http_request_method="PUT",http_response_status_code="200",http_route="/order/:id",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} 5 +http_server_response_body_size{error_code="0",http_request_method="PUT",http_response_status_code="200",http_route="/user/:id",network_protocol_version="1.1",otel_scope_name="github.com/gogf/gf/v2/net/ghttp.Server",otel_scope_version="v2.6.4",server_address="127.0.0.1",server_port="62730",url_schema="http"} 4 diff --git a/contrib/nosql/redis/go.mod b/contrib/nosql/redis/go.mod index e321e40b5..a987c24c7 100644 --- a/contrib/nosql/redis/go.mod +++ b/contrib/nosql/redis/go.mod @@ -16,16 +16,15 @@ require ( github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/fatih/color v1.15.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect - github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/logr v1.2.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/gorilla/websocket v1.5.0 // indirect github.com/grokify/html-strip-tags-go v0.0.1 // indirect github.com/magiconair/properties v1.8.6 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/mattn/go-isatty v0.0.17 // indirect + github.com/mattn/go-runewidth v0.0.9 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect - github.com/rivo/uniseg v0.4.4 // indirect go.opentelemetry.io/otel/sdk v1.14.0 // indirect golang.org/x/net v0.17.0 // indirect golang.org/x/sys v0.13.0 // indirect diff --git a/contrib/nosql/redis/go.sum b/contrib/nosql/redis/go.sum index 1b23b4c20..a962d232f 100644 --- a/contrib/nosql/redis/go.sum +++ b/contrib/nosql/redis/go.sum @@ -14,8 +14,8 @@ github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBD github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= -github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 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.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= @@ -28,19 +28,15 @@ github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPK github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -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.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= -github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/redis/go-redis/v9 v9.2.1 h1:WlYJg71ODF0dVspZZCpYmoF1+U1Jjk9Rwd7pq6QmlCg= github.com/redis/go-redis/v9 v9.2.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= -github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= -github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= go.opentelemetry.io/otel v1.14.0 h1:/79Huy8wbf5DnIPhemGB+zEPVwnN6fuQybr/SRXa6hM= go.opentelemetry.io/otel v1.14.0/go.mod h1:o4buv+dJzx8rohcUeRmWUZhqupFvzWis188WlggnNeU= @@ -51,7 +47,6 @@ go.opentelemetry.io/otel/trace v1.14.0/go.mod h1:8avnQLK+CG77yNLUae4ea2JDQ6iT+go golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= 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.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= diff --git a/contrib/registry/etcd/README.MD b/contrib/registry/etcd/README.MD index a1cac7693..b2421d676 100644 --- a/contrib/registry/etcd/README.MD +++ b/contrib/registry/etcd/README.MD @@ -18,7 +18,7 @@ require github.com/gogf/gf/contrib/registry/etcd/v2 latest ### Reference example -[server](example/registry/etcd/server/main.go) +[server](../../../example/registry/etcd/http/server/server.go) ```go package main @@ -41,7 +41,7 @@ func main() { } ``` -[client](example/registry/etcd/client/main.go) +[client](../../../example/registry/etcd/http/client/client.go) ```go package main diff --git a/contrib/registry/file/README.MD b/contrib/registry/file/README.MD index adbd6029b..885af9aac 100644 --- a/contrib/registry/file/README.MD +++ b/contrib/registry/file/README.MD @@ -18,7 +18,7 @@ require github.com/gogf/gf/contrib/registry/file/v2 latest ### Reference example -[server](example/registry/file/server/main.go) +[server](../../../example/registry/file/server/server.go) ```go package main @@ -43,7 +43,7 @@ func main() { ``` -[client](example/registry/file/client/main.go) +[client](../../../example/registry/file/client/client.go) ```go package main diff --git a/contrib/registry/nacos/README.MD b/contrib/registry/nacos/README.MD index 216f28a71..6c76a77d1 100644 --- a/contrib/registry/nacos/README.MD +++ b/contrib/registry/nacos/README.MD @@ -18,7 +18,7 @@ require github.com/gogf/gf/contrib/registry/nacos/v2 latest ### Reference example -[server](example/registry/nacos/server/main.go) +[server](../../../example/registry/nacos/http/server/main.go) ```go package main @@ -43,7 +43,7 @@ func main() { } ``` -[client](example/registry/nacos/client/main.go) +[client](../../../example/registry/nacos/http/client/main.go) ```go package main diff --git a/contrib/registry/polaris/README.MD b/contrib/registry/polaris/README.MD index 9785577be..31719e676 100644 --- a/contrib/registry/polaris/README.MD +++ b/contrib/registry/polaris/README.MD @@ -20,7 +20,7 @@ require github.com/gogf/gf/contrib/registry/polaris/v2 latest ### Reference example -[server](example/registry/polaris/server/main.go) +[server](../../../example/registry/polaris/server/server.go) ```go package main @@ -49,7 +49,7 @@ func main() { } ``` -[client](example/registry/polaris/client/main.go) +[client](../../../example/registry/polaris/client/client.go) ```go package main diff --git a/contrib/trace/jaeger/go.mod b/contrib/trace/jaeger/go.mod index 3a6b2703e..7542437a5 100644 --- a/contrib/trace/jaeger/go.mod +++ b/contrib/trace/jaeger/go.mod @@ -10,7 +10,7 @@ require ( ) require ( - github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/logr v1.2.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect go.opentelemetry.io/otel/trace v1.14.0 // indirect golang.org/x/sys v0.13.0 // indirect diff --git a/contrib/trace/jaeger/go.sum b/contrib/trace/jaeger/go.sum index 96f073451..cf25cfa40 100644 --- a/contrib/trace/jaeger/go.sum +++ b/contrib/trace/jaeger/go.sum @@ -4,8 +4,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= -github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 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.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= @@ -13,11 +13,10 @@ github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWm github.com/grokify/html-strip-tags-go v0.0.1 h1:0fThFwLbW7P/kOiTBs03FsJSV9RM2M/Q/MOnCQxKMo0= github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= +github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= go.opentelemetry.io/otel v1.14.0 h1:/79Huy8wbf5DnIPhemGB+zEPVwnN6fuQybr/SRXa6hM= diff --git a/contrib/trace/otlpgrpc/go.mod b/contrib/trace/otlpgrpc/go.mod index 8475cde88..8615050c4 100644 --- a/contrib/trace/otlpgrpc/go.mod +++ b/contrib/trace/otlpgrpc/go.mod @@ -3,41 +3,41 @@ module github.com/gogf/gf/contrib/trace/otlpgrpc/v2 go 1.20 require ( - github.com/gogf/gf/v2 v2.6.4 - go.opentelemetry.io/otel v1.19.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 - go.opentelemetry.io/otel/sdk v1.19.0 - google.golang.org/grpc v1.59.0 + github.com/gogf/gf/v2 v2.6.1 + go.opentelemetry.io/otel v1.22.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.22.0 + go.opentelemetry.io/otel/sdk v1.22.0 + google.golang.org/grpc v1.60.1 ) require ( - github.com/BurntSushi/toml v1.2.0 // indirect + github.com/BurntSushi/toml v1.3.2 // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/clbanning/mxj/v2 v2.7.0 // indirect - github.com/fatih/color v1.15.0 // indirect + github.com/fatih/color v1.16.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect - github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/logr v1.4.1 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/golang/protobuf v1.5.3 // indirect - github.com/gorilla/websocket v1.5.0 // indirect - github.com/grokify/html-strip-tags-go v0.0.1 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 // indirect - github.com/magiconair/properties v1.8.6 // indirect + github.com/gorilla/websocket v1.5.1 // indirect + github.com/grokify/html-strip-tags-go v0.1.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 // indirect + github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/rivo/uniseg v0.4.4 // indirect - go.opentelemetry.io/otel/metric v1.19.0 // indirect - go.opentelemetry.io/otel/trace v1.19.0 // indirect - go.opentelemetry.io/proto/otlp v1.0.0 // indirect - golang.org/x/net v0.17.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b // indirect - google.golang.org/protobuf v1.31.0 // indirect + go.opentelemetry.io/otel/metric v1.22.0 // indirect + go.opentelemetry.io/otel/trace v1.22.0 // indirect + go.opentelemetry.io/proto/otlp v1.1.0 // indirect + golang.org/x/net v0.20.0 // indirect + golang.org/x/sys v0.16.0 // indirect + golang.org/x/text v0.14.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240116215550-a9fa1716bcac // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac // indirect + google.golang.org/protobuf v1.32.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/contrib/trace/otlpgrpc/go.sum b/contrib/trace/otlpgrpc/go.sum index 979a8865c..5e6c4218b 100644 --- a/contrib/trace/otlpgrpc/go.sum +++ b/contrib/trace/otlpgrpc/go.sum @@ -1,35 +1,34 @@ -github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0= -github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME= github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= -github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= -github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/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/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grokify/html-strip-tags-go v0.0.1 h1:0fThFwLbW7P/kOiTBs03FsJSV9RM2M/Q/MOnCQxKMo0= -github.com/grokify/html-strip-tags-go v0.0.1/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 h1:RtRsiaGvWxcwd8y3BiRZxsylPT8hLWZ5SPcfI+3IDNk= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0/go.mod h1:TzP6duP4Py2pHLVPPQp42aoYI92+PCrVotyR5e8Vqlk= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= +github.com/grokify/html-strip-tags-go v0.1.0 h1:03UrQLjAny8xci+R+qjCce/MYnpNXCtgzltlQbOBae4= +github.com/grokify/html-strip-tags-go v0.1.0/go.mod h1:ZdzgfHEzAfz9X6Xe5eBLVblWIxXfYSQ40S/VKrAOGpc= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 h1:Wqo399gCIufwto+VfwCSvsnfGpF/w5E9CNxSwbpD6No= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0/go.mod h1:qmOFXW2epJhM0qSnUUYpldc7gVz2KMQwJ/QYCDIa7XU= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= -github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= @@ -46,41 +45,41 @@ github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs= -go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 h1:3d+S281UTjM+AbF31XSOYn1qXn3BgIdWl8HNEpx08Jk= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0/go.mod h1:0+KuTDyKL4gjKCF75pHOX4wuzYDUZYfAQdSu43o+Z2I= -go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE= -go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8= -go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o= -go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A= -go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg= -go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo= -go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= -go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= -go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +go.opentelemetry.io/otel v1.22.0 h1:xS7Ku+7yTFvDfDraDIJVpw7XPyuHlB9MCiqqX5mcJ6Y= +go.opentelemetry.io/otel v1.22.0/go.mod h1:eoV4iAi3Ea8LkAEI9+GFT44O6T/D0GWAVFyZVCC6pMI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0 h1:9M3+rhx7kZCIQQhQRYaZCdNu1V73tm4TvXs2ntl98C4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0/go.mod h1:noq80iT8rrHP1SfybmPiRGc9dc5M8RPmGvtwo7Oo7tc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.22.0 h1:H2JFgRcGiyHg7H7bwcwaQJYrNFqCqrbTQ8K4p1OvDu8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.22.0/go.mod h1:WfCWp1bGoYK8MeULtI15MmQVczfR+bFkk0DF3h06QmQ= +go.opentelemetry.io/otel/metric v1.22.0 h1:lypMQnGyJYeuYPhOM/bgjbFM6WE44W1/T45er4d8Hhg= +go.opentelemetry.io/otel/metric v1.22.0/go.mod h1:evJGjVpZv0mQ5QBRJoBF64yMuOf4xCWdXjK8pzFvliY= +go.opentelemetry.io/otel/sdk v1.22.0 h1:6coWHw9xw7EfClIC/+O31R8IY3/+EiRFHevmHafB2Gw= +go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc= +go.opentelemetry.io/otel/trace v1.22.0 h1:Hg6pPujv0XG9QaVbGOBVHunyuLcCC3jN7WEhPx83XD0= +go.opentelemetry.io/otel/trace v1.22.0/go.mod h1:RbbHXVqKES9QhzZq/fE5UnOSILqRt40a21sPw2He1xo= +go.opentelemetry.io/proto/otlp v1.1.0 h1:2Di21piLrCqJ3U3eXGCTPHE9R8Nh+0uglSnOyxikMeI= +go.opentelemetry.io/proto/otlp v1.1.0/go.mod h1:GpBHCBWiqvVLDqmHZsoMM3C5ySeKTC7ej/RNTae6MdY= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= 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.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto v0.0.0-20231012201019-e917dd12ba7a h1:fwgW9j3vHirt4ObdHoYNwuO24BEZjSzbh+zPaNWoiY8= -google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b h1:CIC2YMXmIhYw6evmhPxBKJ4fmLbOFtXQN/GV3XOZR8k= -google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:IBQ646DjkDkvUIsVq/cc03FUFQ9wbZu7yE396YcL870= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b h1:ZlWIi1wSK56/8hn4QcBp/j9M7Gt3U/3hZw3mC7vDICo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:swOH3j0KzcDDgGUWr+SNpyTen5YrXjS3eyPzFYKc6lc= -google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= -google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= +google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917 h1:nz5NESFLZbJGPFxDT/HCn+V1mZ8JGNoY4nUpmW/Y2eg= +google.golang.org/genproto/googleapis/api v0.0.0-20240116215550-a9fa1716bcac h1:OZkkudMUu9LVQMCoRUbI/1p5VCo9BOrlvkqMvWtqa6s= +google.golang.org/genproto/googleapis/api v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:B5xPO//w8qmBDjGReYLpR6UJPnkldGkCSMoH/2vxJeg= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac h1:nUQEQmH/csSvFECKYRv6HWEyypysidKl2I6Qpsglq/0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:daQN87bsDqDoe316QbbvX60nMoJQa4r6Ds0ZuoAe5yA= +google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU= +google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= +google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= 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/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/contrib/trace/otlphttp/go.mod b/contrib/trace/otlphttp/go.mod index ea3e1ed4f..a9727f3b0 100644 --- a/contrib/trace/otlphttp/go.mod +++ b/contrib/trace/otlphttp/go.mod @@ -3,42 +3,41 @@ module github.com/gogf/gf/contrib/trace/otlphttp/v2 go 1.20 require ( - github.com/gogf/gf/v2 v2.6.4 - go.opentelemetry.io/otel v1.19.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 - go.opentelemetry.io/otel/sdk v1.19.0 + github.com/gogf/gf/v2 v2.6.1 + go.opentelemetry.io/otel v1.22.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.22.0 + go.opentelemetry.io/otel/sdk v1.22.0 ) require ( - github.com/BurntSushi/toml v1.2.0 // indirect + github.com/BurntSushi/toml v1.3.2 // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/clbanning/mxj/v2 v2.7.0 // indirect - github.com/fatih/color v1.15.0 // indirect + github.com/fatih/color v1.16.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect - github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/logr v1.4.1 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/golang/protobuf v1.5.3 // indirect - github.com/gorilla/websocket v1.5.0 // indirect - github.com/grokify/html-strip-tags-go v0.0.1 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 // indirect - github.com/magiconair/properties v1.8.6 // indirect + github.com/gorilla/websocket v1.5.1 // indirect + github.com/grokify/html-strip-tags-go v0.1.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 // indirect + github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/rivo/uniseg v0.4.4 // indirect - go.opentelemetry.io/otel/metric v1.19.0 // indirect - go.opentelemetry.io/otel/trace v1.19.0 // indirect - go.opentelemetry.io/proto/otlp v1.0.0 // indirect - golang.org/x/net v0.17.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect - google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b // indirect - google.golang.org/grpc v1.59.0 // indirect - google.golang.org/protobuf v1.31.0 // indirect + go.opentelemetry.io/otel/metric v1.22.0 // indirect + go.opentelemetry.io/otel/trace v1.22.0 // indirect + go.opentelemetry.io/proto/otlp v1.1.0 // indirect + golang.org/x/net v0.20.0 // indirect + golang.org/x/sys v0.16.0 // indirect + golang.org/x/text v0.14.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240116215550-a9fa1716bcac // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac // indirect + google.golang.org/grpc v1.60.1 // indirect + google.golang.org/protobuf v1.32.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/contrib/trace/otlphttp/go.sum b/contrib/trace/otlphttp/go.sum index 98fc45252..302fae375 100644 --- a/contrib/trace/otlphttp/go.sum +++ b/contrib/trace/otlphttp/go.sum @@ -1,35 +1,34 @@ -github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0= -github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME= github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= -github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= -github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/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/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grokify/html-strip-tags-go v0.0.1 h1:0fThFwLbW7P/kOiTBs03FsJSV9RM2M/Q/MOnCQxKMo0= -github.com/grokify/html-strip-tags-go v0.0.1/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 h1:RtRsiaGvWxcwd8y3BiRZxsylPT8hLWZ5SPcfI+3IDNk= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0/go.mod h1:TzP6duP4Py2pHLVPPQp42aoYI92+PCrVotyR5e8Vqlk= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= +github.com/grokify/html-strip-tags-go v0.1.0 h1:03UrQLjAny8xci+R+qjCce/MYnpNXCtgzltlQbOBae4= +github.com/grokify/html-strip-tags-go v0.1.0/go.mod h1:ZdzgfHEzAfz9X6Xe5eBLVblWIxXfYSQ40S/VKrAOGpc= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 h1:Wqo399gCIufwto+VfwCSvsnfGpF/w5E9CNxSwbpD6No= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0/go.mod h1:qmOFXW2epJhM0qSnUUYpldc7gVz2KMQwJ/QYCDIa7XU= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= -github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= @@ -46,41 +45,40 @@ github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs= -go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU= -go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE= -go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8= -go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o= -go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A= -go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg= -go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo= -go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= -go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +go.opentelemetry.io/otel v1.22.0 h1:xS7Ku+7yTFvDfDraDIJVpw7XPyuHlB9MCiqqX5mcJ6Y= +go.opentelemetry.io/otel v1.22.0/go.mod h1:eoV4iAi3Ea8LkAEI9+GFT44O6T/D0GWAVFyZVCC6pMI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0 h1:9M3+rhx7kZCIQQhQRYaZCdNu1V73tm4TvXs2ntl98C4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0/go.mod h1:noq80iT8rrHP1SfybmPiRGc9dc5M8RPmGvtwo7Oo7tc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.22.0 h1:FyjCyI9jVEfqhUh2MoSkmolPjfh5fp2hnV0b0irxH4Q= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.22.0/go.mod h1:hYwym2nDEeZfG/motx0p7L7J1N1vyzIThemQsb4g2qY= +go.opentelemetry.io/otel/metric v1.22.0 h1:lypMQnGyJYeuYPhOM/bgjbFM6WE44W1/T45er4d8Hhg= +go.opentelemetry.io/otel/metric v1.22.0/go.mod h1:evJGjVpZv0mQ5QBRJoBF64yMuOf4xCWdXjK8pzFvliY= +go.opentelemetry.io/otel/sdk v1.22.0 h1:6coWHw9xw7EfClIC/+O31R8IY3/+EiRFHevmHafB2Gw= +go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc= +go.opentelemetry.io/otel/trace v1.22.0 h1:Hg6pPujv0XG9QaVbGOBVHunyuLcCC3jN7WEhPx83XD0= +go.opentelemetry.io/otel/trace v1.22.0/go.mod h1:RbbHXVqKES9QhzZq/fE5UnOSILqRt40a21sPw2He1xo= +go.opentelemetry.io/proto/otlp v1.1.0 h1:2Di21piLrCqJ3U3eXGCTPHE9R8Nh+0uglSnOyxikMeI= +go.opentelemetry.io/proto/otlp v1.1.0/go.mod h1:GpBHCBWiqvVLDqmHZsoMM3C5ySeKTC7ej/RNTae6MdY= +golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= 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.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b h1:+YaDE2r2OG8t/z5qmsh7Y+XXwCbvadxxZ0YY6mTdrVA= -google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:CgAqfJo+Xmu0GwA0411Ht3OU3OntXwsGmrmjI8ioGXI= -google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b h1:CIC2YMXmIhYw6evmhPxBKJ4fmLbOFtXQN/GV3XOZR8k= -google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:IBQ646DjkDkvUIsVq/cc03FUFQ9wbZu7yE396YcL870= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b h1:ZlWIi1wSK56/8hn4QcBp/j9M7Gt3U/3hZw3mC7vDICo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:swOH3j0KzcDDgGUWr+SNpyTen5YrXjS3eyPzFYKc6lc= -google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= -google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= +google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917 h1:nz5NESFLZbJGPFxDT/HCn+V1mZ8JGNoY4nUpmW/Y2eg= +google.golang.org/genproto/googleapis/api v0.0.0-20240116215550-a9fa1716bcac h1:OZkkudMUu9LVQMCoRUbI/1p5VCo9BOrlvkqMvWtqa6s= +google.golang.org/genproto/googleapis/api v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:B5xPO//w8qmBDjGReYLpR6UJPnkldGkCSMoH/2vxJeg= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac h1:nUQEQmH/csSvFECKYRv6HWEyypysidKl2I6Qpsglq/0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:daQN87bsDqDoe316QbbvX60nMoJQa4r6Ds0ZuoAe5yA= +google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU= +google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= +google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= 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/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index 3212614d9..9145ddeea 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -12,7 +12,6 @@ package gdb import ( "context" "database/sql" - "fmt" "time" "github.com/gogf/gf/v2/container/garray" @@ -169,6 +168,7 @@ type DB interface { // Utility methods. // =========================================================================== + Stats(ctx context.Context) []StatsItem // See Core.Stats. GetCtx() context.Context // See Core.GetCtx. GetCore() *Core // See Core.GetCore GetChars() (charLeft string, charRight string) // See Core.GetChars. @@ -247,6 +247,15 @@ type TX interface { RollbackTo(point string) error } +// StatsItem defines the stats information for a configuration node. +type StatsItem interface { + // Node returns the configuration node info. + Node() ConfigNode + + // Stats returns the connection stat for current node. + Stats() sql.DBStats +} + // Core is the base struct for database management. type Core struct { db DB // DB interface object. @@ -255,7 +264,7 @@ type Core struct { schema string // Custom schema for this object. debug *gtype.Bool // Enable debug mode for the database, which can be changed in runtime. cache *gcache.Cache // Cache manager, SQL result cache only. - links *gmap.StrAnyMap // links caches all created links by node. + links *gmap.Map // links caches all created links by node. logger glog.ILogger // Logger for logging functionality. config *ConfigNode // Current config node. dynamicConfig dynamicConfig // Dynamic configurations, which can be changed in runtime. @@ -275,7 +284,7 @@ type DoCommitInput struct { Link Link Sql string Args []interface{} - Type string + Type SqlType IsTransaction bool } @@ -307,7 +316,7 @@ type Link interface { // Sql is the sql recording struct. type Sql struct { Sql string // SQL string(may contain reserved char '?'). - Type string // SQL operation type. + Type SqlType // SQL operation type. Args []interface{} // Arguments for this sql. Format string // Formatted sql which contains arguments in the sql. Error error // Execution result. @@ -417,16 +426,18 @@ const ( InsertOnDuplicateKeyUpdate = "ON DUPLICATE KEY UPDATE" ) +type SqlType string + const ( - SqlTypeBegin = "DB.Begin" - SqlTypeTXCommit = "TX.Commit" - SqlTypeTXRollback = "TX.Rollback" - SqlTypeExecContext = "DB.ExecContext" - SqlTypeQueryContext = "DB.QueryContext" - SqlTypePrepareContext = "DB.PrepareContext" - SqlTypeStmtExecContext = "DB.Statement.ExecContext" - SqlTypeStmtQueryContext = "DB.Statement.QueryContext" - SqlTypeStmtQueryRowContext = "DB.Statement.QueryRowContext" + SqlTypeBegin SqlType = "DB.Begin" + SqlTypeTXCommit SqlType = "TX.Commit" + SqlTypeTXRollback SqlType = "TX.Rollback" + SqlTypeExecContext SqlType = "DB.ExecContext" + SqlTypeQueryContext SqlType = "DB.QueryContext" + SqlTypePrepareContext SqlType = "DB.PrepareContext" + SqlTypeStmtExecContext SqlType = "DB.Statement.ExecContext" + SqlTypeStmtQueryContext SqlType = "DB.Statement.QueryContext" + SqlTypeStmtQueryRowContext SqlType = "DB.Statement.QueryRowContext" ) type LocalType string @@ -579,7 +590,7 @@ func newDBByConfigNode(node *ConfigNode, group string) (db DB, err error) { group: group, debug: gtype.NewBool(), cache: gcache.New(), - links: gmap.NewStrAnyMap(true), + links: gmap.New(true), logger: glog.New(), config: node, dynamicConfig: dynamicConfig{ @@ -738,31 +749,34 @@ func (c *Core) getSqlDb(master bool, schema ...string) (sqlDb *sql.DB, err error internalData.ConfigNode = node } // Cache the underlying connection pool object by node. - instanceNameByNode := fmt.Sprintf(`%+v`, node) - instanceValue := c.links.GetOrSetFuncLock(instanceNameByNode, func() interface{} { - if sqlDb, err = c.db.Open(node); err != nil { - return nil + var ( + instanceCacheFunc = func() interface{} { + if sqlDb, err = c.db.Open(node); err != nil { + return nil + } + if sqlDb == nil { + return nil + } + if c.dynamicConfig.MaxIdleConnCount > 0 { + sqlDb.SetMaxIdleConns(c.dynamicConfig.MaxIdleConnCount) + } else { + sqlDb.SetMaxIdleConns(defaultMaxIdleConnCount) + } + if c.dynamicConfig.MaxOpenConnCount > 0 { + sqlDb.SetMaxOpenConns(c.dynamicConfig.MaxOpenConnCount) + } else { + sqlDb.SetMaxOpenConns(defaultMaxOpenConnCount) + } + if c.dynamicConfig.MaxConnLifeTime > 0 { + sqlDb.SetConnMaxLifetime(c.dynamicConfig.MaxConnLifeTime) + } else { + sqlDb.SetConnMaxLifetime(defaultMaxConnLifeTime) + } + return sqlDb } - if sqlDb == nil { - return nil - } - if c.dynamicConfig.MaxIdleConnCount > 0 { - sqlDb.SetMaxIdleConns(c.dynamicConfig.MaxIdleConnCount) - } else { - sqlDb.SetMaxIdleConns(defaultMaxIdleConnCount) - } - if c.dynamicConfig.MaxOpenConnCount > 0 { - sqlDb.SetMaxOpenConns(c.dynamicConfig.MaxOpenConnCount) - } else { - sqlDb.SetMaxOpenConns(defaultMaxOpenConnCount) - } - if c.dynamicConfig.MaxConnLifeTime > 0 { - sqlDb.SetConnMaxLifetime(c.dynamicConfig.MaxConnLifeTime) - } else { - sqlDb.SetConnMaxLifetime(defaultMaxConnLifeTime) - } - return sqlDb - }) + // it here uses node value not pointer as the cache key, in case of oracle ORA-12516 error. + instanceValue = c.links.GetOrSetFuncLock(*node, instanceCacheFunc) + ) if instanceValue != nil && sqlDb == nil { // It reads from instance map. sqlDb = instanceValue.(*sql.DB) diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index bf58e7ce2..73cfb541d 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -106,7 +106,7 @@ func (c *Core) Close(ctx context.Context) (err error) { if err = c.cache.Close(ctx); err != nil { return err } - c.links.LockFunc(func(m map[string]interface{}) { + c.links.LockFunc(func(m map[any]any) { for k, v := range m { if db, ok := v.(*sql.DB); ok { err = db.Close() diff --git a/database/gdb/gdb_core_stats.go b/database/gdb/gdb_core_stats.go new file mode 100644 index 000000000..8b64f14e8 --- /dev/null +++ b/database/gdb/gdb_core_stats.go @@ -0,0 +1,45 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. +// + +package gdb + +import ( + "context" + "database/sql" +) + +type localStatsItem struct { + node *ConfigNode + stats sql.DBStats +} + +// Node returns the configuration node info. +func (item *localStatsItem) Node() ConfigNode { + return *item.node +} + +// Stats returns the connection stat for current node. +func (item *localStatsItem) Stats() sql.DBStats { + return item.stats +} + +// Stats retrieves and returns the pool stat for all nodes that have been established. +func (c *Core) Stats(ctx context.Context) []StatsItem { + var items = make([]StatsItem, 0) + c.links.Iterator(func(k, v any) bool { + var ( + node = k.(ConfigNode) + sqlDB = v.(*sql.DB) + ) + items = append(items, &localStatsItem{ + node: &node, + stats: sqlDB.Stats(), + }) + return true + }) + return items +} diff --git a/database/gdb/gdb_core_trace.go b/database/gdb/gdb_core_trace.go index 12e06050b..a380dd804 100644 --- a/database/gdb/gdb_core_trace.go +++ b/database/gdb/gdb_core_trace.go @@ -13,7 +13,7 @@ import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" - semconv "go.opentelemetry.io/otel/semconv/v1.4.0" + semconv "go.opentelemetry.io/otel/semconv/v1.18.0" "go.opentelemetry.io/otel/trace" "github.com/gogf/gf/v2/net/gtrace" @@ -48,7 +48,7 @@ func (c *Core) traceSpanEnd(ctx context.Context, span trace.Span, sql *Sql) { labels = append(labels, gtrace.CommonLabels()...) labels = append(labels, attribute.String(traceAttrDbType, c.db.GetConfig().Type), - semconv.DBStatementKey.String(sql.Format), + semconv.DBStatement(sql.Format), ) if c.db.GetConfig().Host != "" { labels = append(labels, attribute.String(traceAttrDbHost, c.db.GetConfig().Host)) @@ -81,6 +81,6 @@ func (c *Core) traceSpanEnd(ctx context.Context, span trace.Span, sql *Sql) { )) } } - events = append(events, attribute.String(traceEventDbExecutionType, sql.Type)) + events = append(events, attribute.String(traceEventDbExecutionType, string(sql.Type))) span.AddEvent(traceEventDbExecution, trace.WithAttributes(events...)) } diff --git a/database/gdb/gdb_core_underlying.go b/database/gdb/gdb_core_underlying.go index 1d212975f..f574a1948 100644 --- a/database/gdb/gdb_core_underlying.go +++ b/database/gdb/gdb_core_underlying.go @@ -174,7 +174,7 @@ func (c *Core) DoCommit(ctx context.Context, in DoCommitInput) (out DoCommitOutp // Trace span start. tr := otel.GetTracerProvider().Tracer(traceInstrumentName, trace.WithInstrumentationVersion(gf.VERSION)) - ctx, span := tr.Start(ctx, in.Type, trace.WithSpanKind(trace.SpanKindInternal)) + ctx, span := tr.Start(ctx, string(in.Type), trace.WithSpanKind(trace.SpanKindInternal)) defer span.End() // Execution cased by type. diff --git a/example/go.mod b/example/go.mod index b2dfd8329..ee9c232e9 100644 --- a/example/go.mod +++ b/example/go.mod @@ -3,34 +3,37 @@ module github.com/gogf/gf/example go 1.18 require ( - github.com/gogf/gf/contrib/config/apollo/v2 v2.6.4 - github.com/gogf/gf/contrib/config/consul/v2 v2.6.4 - github.com/gogf/gf/contrib/config/kubecm/v2 v2.6.4 - github.com/gogf/gf/contrib/config/nacos/v2 v2.6.4 - github.com/gogf/gf/contrib/config/polaris/v2 v2.6.4 - github.com/gogf/gf/contrib/drivers/mysql/v2 v2.6.4 - github.com/gogf/gf/contrib/nosql/redis/v2 v2.6.4 - github.com/gogf/gf/contrib/registry/etcd/v2 v2.6.4 + github.com/gogf/gf/contrib/config/apollo/v2 v2.6.1 + github.com/gogf/gf/contrib/config/consul/v2 v2.6.1 + github.com/gogf/gf/contrib/config/kubecm/v2 v2.6.1 + github.com/gogf/gf/contrib/config/nacos/v2 v2.6.1 + github.com/gogf/gf/contrib/config/polaris/v2 v2.6.1 + github.com/gogf/gf/contrib/drivers/mysql/v2 v2.6.1 + github.com/gogf/gf/contrib/metric/otelmetric/v2 v2.6.1 + github.com/gogf/gf/contrib/nosql/redis/v2 v2.6.1 + github.com/gogf/gf/contrib/registry/etcd/v2 v2.6.1 github.com/gogf/gf/contrib/registry/file/v2 v2.6.4 - github.com/gogf/gf/contrib/registry/nacos/v2 v2.6.4 - github.com/gogf/gf/contrib/registry/polaris/v2 v2.6.4 - github.com/gogf/gf/contrib/rpc/grpcx/v2 v2.6.4 - github.com/gogf/gf/contrib/trace/otlpgrpc/v2 v2.6.4 - github.com/gogf/gf/contrib/trace/otlphttp/v2 v2.6.4 + github.com/gogf/gf/contrib/registry/nacos/v2 v2.6.1 + github.com/gogf/gf/contrib/registry/polaris/v2 v2.6.1 + github.com/gogf/gf/contrib/rpc/grpcx/v2 v2.6.1 + github.com/gogf/gf/contrib/trace/otlpgrpc/v2 v2.6.1 + github.com/gogf/gf/contrib/trace/otlphttp/v2 v2.6.1 github.com/gogf/gf/v2 v2.6.4 github.com/hashicorp/consul/api v1.24.0 github.com/hashicorp/go-cleanhttp v0.5.2 github.com/nacos-group/nacos-sdk-go/v2 v2.2.5 github.com/polarismesh/polaris-go v1.5.5 - github.com/prometheus/client_golang v1.17.0 - golang.org/x/time v0.3.0 - google.golang.org/grpc v1.59.0 - google.golang.org/protobuf v1.31.0 + github.com/prometheus/client_golang v1.19.0 + go.opentelemetry.io/otel/exporters/prometheus v0.46.0 + go.opentelemetry.io/otel/sdk/metric v1.24.0 + golang.org/x/time v0.5.0 + google.golang.org/grpc v1.60.1 + google.golang.org/protobuf v1.32.0 k8s.io/client-go v0.27.4 ) require ( - github.com/BurntSushi/toml v1.2.0 // indirect + github.com/BurntSushi/toml v1.3.2 // indirect github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68 // indirect github.com/alibabacloud-go/tea v1.1.17 // indirect github.com/alibabacloud-go/tea-utils v1.4.4 // indirect @@ -50,9 +53,9 @@ require ( github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dlclark/regexp2 v1.7.0 // indirect github.com/emicklei/go-restful/v3 v3.9.0 // indirect - github.com/fatih/color v1.15.0 // indirect + github.com/fatih/color v1.16.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect - github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/logr v1.4.1 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/jsonreference v0.20.1 // indirect @@ -62,12 +65,12 @@ require ( github.com/golang/mock v1.6.0 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/gnostic v0.5.7-v3refs // indirect - github.com/google/go-cmp v0.5.9 // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.1.0 // indirect github.com/google/uuid v1.3.1 // indirect - github.com/gorilla/websocket v1.5.0 // indirect - github.com/grokify/html-strip-tags-go v0.0.1 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 // indirect + github.com/gorilla/websocket v1.5.1 // indirect + github.com/grokify/html-strip-tags-go v0.1.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-hclog v1.5.0 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect @@ -81,12 +84,11 @@ require ( github.com/josharian/intern v1.0.0 // indirect github.com/joy999/nacos-sdk-go v0.0.0-20231120071639-10a34b3e7288 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/magiconair/properties v1.8.6 // indirect + github.com/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect @@ -98,9 +100,9 @@ require ( github.com/pelletier/go-toml v1.9.3 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/polarismesh/specification v1.4.1 // indirect - github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect - github.com/prometheus/common v0.44.0 // indirect - github.com/prometheus/procfs v0.11.1 // indirect + github.com/prometheus/client_model v0.6.0 // indirect + github.com/prometheus/common v0.48.0 // indirect + github.com/prometheus/procfs v0.12.0 // indirect github.com/redis/go-redis/v9 v9.2.1 // indirect github.com/rivo/uniseg v0.4.4 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect @@ -113,29 +115,30 @@ require ( go.etcd.io/etcd/api/v3 v3.5.7 // indirect go.etcd.io/etcd/client/pkg/v3 v3.5.7 // indirect go.etcd.io/etcd/client/v3 v3.5.7 // indirect - go.opentelemetry.io/otel v1.19.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 // indirect - go.opentelemetry.io/otel/metric v1.19.0 // indirect - go.opentelemetry.io/otel/sdk v1.19.0 // indirect - go.opentelemetry.io/otel/trace v1.19.0 // indirect - go.opentelemetry.io/proto/otlp v1.0.0 // indirect + go.opentelemetry.io/contrib/instrumentation/runtime v0.49.0 // indirect + go.opentelemetry.io/otel v1.24.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.22.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.22.0 // indirect + go.opentelemetry.io/otel/metric v1.24.0 // indirect + go.opentelemetry.io/otel/sdk v1.24.0 // indirect + go.opentelemetry.io/otel/trace v1.24.0 // indirect + go.opentelemetry.io/proto/otlp v1.1.0 // indirect go.uber.org/atomic v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.26.0 // indirect - golang.org/x/crypto v0.17.0 // indirect + golang.org/x/crypto v0.18.0 // indirect golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect - golang.org/x/net v0.17.0 // indirect - golang.org/x/oauth2 v0.13.0 // indirect - golang.org/x/sync v0.4.0 // indirect - golang.org/x/sys v0.15.0 // indirect - golang.org/x/term v0.15.0 // indirect + golang.org/x/net v0.20.0 // indirect + golang.org/x/oauth2 v0.16.0 // indirect + golang.org/x/sync v0.6.0 // indirect + golang.org/x/sys v0.18.0 // indirect + golang.org/x/term v0.16.0 // indirect golang.org/x/text v0.14.0 // indirect google.golang.org/appengine v1.6.8 // indirect - google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b // indirect + google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240116215550-a9fa1716bcac // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect @@ -158,10 +161,10 @@ replace ( github.com/gogf/gf/contrib/config/nacos/v2 => ../contrib/config/nacos/ github.com/gogf/gf/contrib/config/polaris/v2 => ../contrib/config/polaris/ github.com/gogf/gf/contrib/drivers/mysql/v2 => ../contrib/drivers/mysql/ + github.com/gogf/gf/contrib/metric/otelmetric/v2 => ../contrib/metric/otelmetric github.com/gogf/gf/contrib/nosql/redis/v2 => ../contrib/nosql/redis/ github.com/gogf/gf/contrib/registry/etcd/v2 => ../contrib/registry/etcd/ github.com/gogf/gf/contrib/registry/file/v2 => ../contrib/registry/file/ - github.com/gogf/gf/contrib/registry/nacos/v2 => ../contrib/registry/nacos/ github.com/gogf/gf/contrib/registry/polaris/v2 => ../contrib/registry/polaris/ github.com/gogf/gf/contrib/rpc/grpcx/v2 => ../contrib/rpc/grpcx/ github.com/gogf/gf/contrib/trace/otlpgrpc/v2 => ../contrib/trace/otlpgrpc diff --git a/example/go.sum b/example/go.sum index 59f63f64f..213adfc22 100644 --- a/example/go.sum +++ b/example/go.sum @@ -169,8 +169,8 @@ cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1V cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0= -github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= @@ -271,8 +271,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= -github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= @@ -289,8 +289,8 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= -github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/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/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= @@ -304,13 +304,14 @@ github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogf/gf/contrib/registry/nacos/v2 v2.6.1 h1:yWmm+1yjk8JDh1CUYjAvOV7pBN400xJluw07w7U1GsI= +github.com/gogf/gf/contrib/registry/nacos/v2 v2.6.1/go.mod h1:1j1dsYPFByfzK9UHk7XBqRu5jNKEDO3hgwgwmThfa5c= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -370,8 +371,8 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +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/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -412,13 +413,13 @@ github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqE github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grokify/html-strip-tags-go v0.0.1 h1:0fThFwLbW7P/kOiTBs03FsJSV9RM2M/Q/MOnCQxKMo0= -github.com/grokify/html-strip-tags-go v0.0.1/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= +github.com/grokify/html-strip-tags-go v0.1.0 h1:03UrQLjAny8xci+R+qjCce/MYnpNXCtgzltlQbOBae4= +github.com/grokify/html-strip-tags-go v0.1.0/go.mod h1:ZdzgfHEzAfz9X6Xe5eBLVblWIxXfYSQ40S/VKrAOGpc= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 h1:RtRsiaGvWxcwd8y3BiRZxsylPT8hLWZ5SPcfI+3IDNk= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0/go.mod h1:TzP6duP4Py2pHLVPPQp42aoYI92+PCrVotyR5e8Vqlk= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 h1:Wqo399gCIufwto+VfwCSvsnfGpF/w5E9CNxSwbpD6No= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0/go.mod h1:qmOFXW2epJhM0qSnUUYpldc7gVz2KMQwJ/QYCDIa7XU= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/api v1.24.0 h1:u2XyStA2j0jnCiVUU7Qyrt8idjRn4ORhK6DlvZ3bWhA= github.com/hashicorp/consul/api v1.24.0/go.mod h1:NZJGRFYruc/80wYowkPFCp1LbGmJC9L8izrwfyVx/Wg= @@ -509,8 +510,8 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= -github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= -github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= 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.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= @@ -532,8 +533,6 @@ github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY= @@ -597,29 +596,29 @@ github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3O github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= -github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= +github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= +github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 h1:v7DLqVdK4VrYkVD5diGdl4sxJurKJEMnODWRJlxV9oM= -github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/client_model v0.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos= +github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOAGQPoaGhyTchlyt8= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= -github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= +github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= +github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI= -github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/redis/go-redis/v9 v9.2.1 h1:WlYJg71ODF0dVspZZCpYmoF1+U1Jjk9Rwd7pq6QmlCg= github.com/redis/go-redis/v9 v9.2.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= @@ -627,7 +626,7 @@ github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= @@ -702,29 +701,35 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs= -go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 h1:3d+S281UTjM+AbF31XSOYn1qXn3BgIdWl8HNEpx08Jk= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0/go.mod h1:0+KuTDyKL4gjKCF75pHOX4wuzYDUZYfAQdSu43o+Z2I= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU= -go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE= -go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8= -go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o= -go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A= -go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg= -go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo= +go.opentelemetry.io/contrib/instrumentation/runtime v0.49.0 h1:dg9y+7ArpumB6zwImJv47RHfdgOGQ1EMkzP5vLkEnTU= +go.opentelemetry.io/contrib/instrumentation/runtime v0.49.0/go.mod h1:Ul4MtXqu/hJBM+v7a6dCF0nHwckPMLpIpLeCi4+zfdw= +go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= +go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0 h1:9M3+rhx7kZCIQQhQRYaZCdNu1V73tm4TvXs2ntl98C4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0/go.mod h1:noq80iT8rrHP1SfybmPiRGc9dc5M8RPmGvtwo7Oo7tc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.22.0 h1:H2JFgRcGiyHg7H7bwcwaQJYrNFqCqrbTQ8K4p1OvDu8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.22.0/go.mod h1:WfCWp1bGoYK8MeULtI15MmQVczfR+bFkk0DF3h06QmQ= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.22.0 h1:FyjCyI9jVEfqhUh2MoSkmolPjfh5fp2hnV0b0irxH4Q= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.22.0/go.mod h1:hYwym2nDEeZfG/motx0p7L7J1N1vyzIThemQsb4g2qY= +go.opentelemetry.io/otel/exporters/prometheus v0.46.0 h1:I8WIFXR351FoLJYuloU4EgXbtNX2URfU/85pUPheIEQ= +go.opentelemetry.io/otel/exporters/prometheus v0.46.0/go.mod h1:ztwVUHe5DTR/1v7PeuGRnU5Bbd4QKYwApWmuutKsJSs= +go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= +go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= +go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw= +go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg= +go.opentelemetry.io/otel/sdk/metric v1.24.0 h1:yyMQrPzF+k88/DbH7o4FMAs80puqd+9osbiBrJrz/w8= +go.opentelemetry.io/otel/sdk/metric v1.24.0/go.mod h1:I6Y5FjH6rvEnTTAYQz3Mmv2kl6Ek5IIrmwTLqMrrOE0= +go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= +go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= -go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= +go.opentelemetry.io/proto/otlp v1.1.0 h1:2Di21piLrCqJ3U3eXGCTPHE9R8Nh+0uglSnOyxikMeI= +go.opentelemetry.io/proto/otlp v1.1.0/go.mod h1:GpBHCBWiqvVLDqmHZsoMM3C5ySeKTC7ej/RNTae6MdY= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= @@ -743,8 +748,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -840,8 +845,8 @@ golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -866,8 +871,8 @@ golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7Lm golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY= -golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0= +golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= +golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -881,8 +886,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= -golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -968,13 +973,13 @@ golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= -golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= +golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -991,8 +996,8 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1219,12 +1224,12 @@ google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006/go.mod h1:ht8XFiar google.golang.org/genproto v0.0.0-20220926165614-551eb538f295/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= -google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b h1:+YaDE2r2OG8t/z5qmsh7Y+XXwCbvadxxZ0YY6mTdrVA= -google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:CgAqfJo+Xmu0GwA0411Ht3OU3OntXwsGmrmjI8ioGXI= -google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b h1:CIC2YMXmIhYw6evmhPxBKJ4fmLbOFtXQN/GV3XOZR8k= -google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:IBQ646DjkDkvUIsVq/cc03FUFQ9wbZu7yE396YcL870= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b h1:ZlWIi1wSK56/8hn4QcBp/j9M7Gt3U/3hZw3mC7vDICo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:swOH3j0KzcDDgGUWr+SNpyTen5YrXjS3eyPzFYKc6lc= +google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917 h1:nz5NESFLZbJGPFxDT/HCn+V1mZ8JGNoY4nUpmW/Y2eg= +google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917/go.mod h1:pZqR+glSb11aJ+JQcczCvgf47+duRuzNSKqE8YAQnV0= +google.golang.org/genproto/googleapis/api v0.0.0-20240116215550-a9fa1716bcac h1:OZkkudMUu9LVQMCoRUbI/1p5VCo9BOrlvkqMvWtqa6s= +google.golang.org/genproto/googleapis/api v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:B5xPO//w8qmBDjGReYLpR6UJPnkldGkCSMoH/2vxJeg= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac h1:nUQEQmH/csSvFECKYRv6HWEyypysidKl2I6Qpsglq/0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:daQN87bsDqDoe316QbbvX60nMoJQa4r6Ds0ZuoAe5yA= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1260,8 +1265,8 @@ google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACu google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= -google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= -google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= +google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU= +google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -1278,8 +1283,8 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= +google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/example/httpserver/proxy/main.go b/example/httpserver/proxy/main.go index c39caf0a7..520e0dbed 100644 --- a/example/httpserver/proxy/main.go +++ b/example/httpserver/proxy/main.go @@ -16,28 +16,28 @@ import ( ) const ( - PortOfServer1 = 8198 - PortOfServer2 = 8199 - UpStream = "http://127.0.0.1:8198" + PortOfServerBackend = 8198 + PortOfServerProxy = 8199 + UpStream = "http://127.0.0.1:8198" ) -// StartServer1 starts Server1: A simple http server for demo. -func StartServer1() { - s := g.Server(1) +// StartServerBackend starts `backend`: A simple http server for demo. +func StartServerBackend() { + s := g.Server("backend") s.BindHandler("/*", func(r *ghttp.Request) { - r.Response.Write("response from server 1") + r.Response.Write("response from server backend") }) s.BindHandler("/user/1", func(r *ghttp.Request) { - r.Response.Write("user info from server 1") + r.Response.Write("user info from server backend") }) - s.SetPort(PortOfServer1) + s.SetPort(PortOfServerBackend) s.Run() } -// StartServer2 starts Server2: -// All requests to Server2 are directly redirected to Server1. -func StartServer2() { - s := g.Server(2) +// StartServerProxy starts `proxy`: +// All requests to `proxy` of route `/proxy/*` are directly redirected to `backend`. +func StartServerProxy() { + s := g.Server("proxy") u, _ := url.Parse(UpStream) proxy := httputil.NewSingleHostReverseProxy(u) proxy.ErrorHandler = func(writer http.ResponseWriter, request *http.Request, e error) { @@ -49,15 +49,15 @@ func StartServer2() { proxyToPath = "/" + r.Get("url").String() ) r.Request.URL.Path = proxyToPath - g.Log().Infof(r.Context(), `server2:"%s" -> server1:"%s"`, originalPath, proxyToPath) + g.Log().Infof(r.Context(), `proxy:"%s" -> backend:"%s"`, originalPath, proxyToPath) r.MakeBodyRepeatableRead(false) - proxy.ServeHTTP(r.Response.Writer.RawWriter(), r.Request) + proxy.ServeHTTP(r.Response.Writer, r.Request) }) - s.SetPort(PortOfServer2) + s.SetPort(PortOfServerProxy) s.Run() } func main() { - go StartServer1() - StartServer2() + go StartServerBackend() + StartServerProxy() } diff --git a/example/metric/basic/main.go b/example/metric/basic/main.go new file mode 100644 index 000000000..354108d88 --- /dev/null +++ b/example/metric/basic/main.go @@ -0,0 +1,139 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package main + +import ( + "context" + + "github.com/prometheus/client_golang/prometheus/promhttp" + "go.opentelemetry.io/otel/exporters/prometheus" + "go.opentelemetry.io/otel/sdk/metric" + + "github.com/gogf/gf/contrib/metric/otelmetric/v2" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/net/ghttp" + "github.com/gogf/gf/v2/os/gctx" + "github.com/gogf/gf/v2/os/gmetric" +) + +var ( + meter = gmetric.GetGlobalProvider().Meter(gmetric.MeterOption{ + Instrument: "github.com/gogf/gf/example/metric/basic", + InstrumentVersion: "v1.0", + }) + counter = meter.MustCounter( + "goframe.metric.demo.counter", + gmetric.MetricOption{ + Help: "This is a simple demo for Counter usage", + Unit: "bytes", + Attributes: gmetric.Attributes{ + gmetric.NewAttribute("const_label_1", 1), + }, + }, + ) + upDownCounter = meter.MustUpDownCounter( + "goframe.metric.demo.updown_counter", + gmetric.MetricOption{ + Help: "This is a simple demo for UpDownCounter usage", + Unit: "%", + Attributes: gmetric.Attributes{ + gmetric.NewAttribute("const_label_2", 2), + }, + }, + ) + histogram = meter.MustHistogram( + "goframe.metric.demo.histogram", + gmetric.MetricOption{ + Help: "This is a simple demo for histogram usage", + Unit: "ms", + Attributes: gmetric.Attributes{ + gmetric.NewAttribute("const_label_3", 3), + }, + Buckets: []float64{0, 10, 20, 50, 100, 500, 1000, 2000, 5000, 10000}, + }, + ) + observableCounter = meter.MustObservableCounter( + "goframe.metric.demo.observable_counter", + gmetric.MetricOption{ + Help: "This is a simple demo for ObservableCounter usage", + Unit: "%", + Attributes: gmetric.Attributes{ + gmetric.NewAttribute("const_label_4", 4), + }, + }, + ) + observableUpDownCounter = meter.MustObservableUpDownCounter( + "goframe.metric.demo.observable_updown_counter", + gmetric.MetricOption{ + Help: "This is a simple demo for ObservableUpDownCounter usage", + Unit: "%", + Attributes: gmetric.Attributes{ + gmetric.NewAttribute("const_label_5", 5), + }, + }, + ) + observableGauge = meter.MustObservableGauge( + "goframe.metric.demo.observable_gauge", + gmetric.MetricOption{ + Help: "This is a simple demo for ObservableGauge usage", + Unit: "%", + Attributes: gmetric.Attributes{ + gmetric.NewAttribute("const_label_6", 6), + }, + }, + ) +) + +func main() { + var ctx = gctx.New() + + // Callback for observable metrics. + meter.MustRegisterCallback(func(ctx context.Context, obs gmetric.Observer) error { + obs.Observe(observableCounter, 10) + obs.Observe(observableUpDownCounter, 20) + obs.Observe(observableGauge, 30) + return nil + }, observableCounter, observableUpDownCounter, observableGauge) + + // Prometheus exporter to export metrics as Prometheus format. + exporter, err := prometheus.New( + prometheus.WithoutCounterSuffixes(), + prometheus.WithoutUnits(), + ) + if err != nil { + g.Log().Fatal(ctx, err) + } + + // OpenTelemetry provider. + provider := otelmetric.MustProvider(metric.WithReader(exporter)) + provider.SetAsGlobal() + defer provider.Shutdown(ctx) + + // Counter. + counter.Inc(ctx) + counter.Add(ctx, 10) + + // UpDownCounter. + upDownCounter.Inc(ctx) + upDownCounter.Add(ctx, 10) + upDownCounter.Dec(ctx) + + // Record values for histogram. + histogram.Record(1) + histogram.Record(20) + histogram.Record(30) + histogram.Record(101) + histogram.Record(2000) + histogram.Record(9000) + histogram.Record(20000) + + // HTTP Server for metrics exporting. + s := g.Server() + s.BindHandler("/metrics", ghttp.WrapH(promhttp.Handler())) + s.SetPort(8000) + s.Run() +} diff --git a/example/metric/callback/main.go b/example/metric/callback/main.go new file mode 100644 index 000000000..263f5c12b --- /dev/null +++ b/example/metric/callback/main.go @@ -0,0 +1,81 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package main + +import ( + "context" + + "github.com/prometheus/client_golang/prometheus/promhttp" + "go.opentelemetry.io/otel/exporters/prometheus" + "go.opentelemetry.io/otel/sdk/metric" + + "github.com/gogf/gf/contrib/metric/otelmetric/v2" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/net/ghttp" + "github.com/gogf/gf/v2/os/gctx" + "github.com/gogf/gf/v2/os/gmetric" +) + +var ( + meter = gmetric.GetGlobalProvider().Meter(gmetric.MeterOption{ + Instrument: "github.com/gogf/gf/example/metric/basic", + InstrumentVersion: "v1.0", + }) + counter = meter.MustCounter( + "goframe.metric.demo.counter", + gmetric.MetricOption{ + Help: "This is a simple demo for Counter usage", + Unit: "%", + Attributes: gmetric.Attributes{ + gmetric.NewAttribute("const_label_1", 1), + }, + }, + ) + + _ = meter.MustObservableCounter( + "goframe.metric.demo.observable_counter", + gmetric.MetricOption{ + Help: "This is a simple demo for ObservableCounter usage", + Unit: "%", + Attributes: gmetric.Attributes{ + gmetric.NewAttribute("const_label_3", 3), + }, + Callback: func(ctx context.Context, obs gmetric.MetricObserver) error { + obs.Observe(10) + return nil + }, + }, + ) +) + +func main() { + var ctx = gctx.New() + + // Prometheus exporter to export metrics as Prometheus format. + exporter, err := prometheus.New( + prometheus.WithoutCounterSuffixes(), + prometheus.WithoutUnits(), + ) + if err != nil { + g.Log().Fatal(ctx, err) + } + + // OpenTelemetry provider. + provider := otelmetric.MustProvider(metric.WithReader(exporter)) + provider.SetAsGlobal() + defer provider.Shutdown(ctx) + + // Add value for counter. + counter.Inc(ctx) + counter.Add(ctx, 10) + + // HTTP Server for metrics exporting. + s := g.Server() + s.BindHandler("/metrics", ghttp.WrapH(promhttp.Handler())) + s.SetPort(8000) + s.Run() +} diff --git a/example/metric/dynamic_attributes/main.go b/example/metric/dynamic_attributes/main.go new file mode 100644 index 000000000..004756661 --- /dev/null +++ b/example/metric/dynamic_attributes/main.go @@ -0,0 +1,90 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package main + +import ( + "context" + + "github.com/prometheus/client_golang/prometheus/promhttp" + "go.opentelemetry.io/otel/exporters/prometheus" + "go.opentelemetry.io/otel/sdk/metric" + + "github.com/gogf/gf/contrib/metric/otelmetric/v2" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/net/ghttp" + "github.com/gogf/gf/v2/os/gctx" + "github.com/gogf/gf/v2/os/gmetric" +) + +var ( + meter = gmetric.GetGlobalProvider().Meter(gmetric.MeterOption{ + Instrument: "github.com/gogf/gf/example/metric/basic", + InstrumentVersion: "v1.0", + }) + counter = meter.MustCounter( + "goframe.metric.demo.counter", + gmetric.MetricOption{ + Help: "This is a simple demo for Counter usage", + Unit: "bytes", + Attributes: gmetric.Attributes{ + gmetric.NewAttribute("const_label_1", 1), + }, + }, + ) + observableCounter = meter.MustObservableCounter( + "goframe.metric.demo.observable_counter", + gmetric.MetricOption{ + Help: "This is a simple demo for ObservableCounter usage", + Unit: "%", + Attributes: gmetric.Attributes{ + gmetric.NewAttribute("const_label_4", 4), + }, + }, + ) +) + +func main() { + var ctx = gctx.New() + + // Callback for observable metrics. + meter.MustRegisterCallback(func(ctx context.Context, obs gmetric.Observer) error { + obs.Observe(observableCounter, 10, gmetric.Option{ + Attributes: gmetric.Attributes{ + gmetric.NewAttribute("dynamic_label_1", 1), + }, + }) + return nil + }, observableCounter) + + // Prometheus exporter to export metrics as Prometheus format. + exporter, err := prometheus.New( + prometheus.WithoutCounterSuffixes(), + prometheus.WithoutUnits(), + ) + if err != nil { + g.Log().Fatal(ctx, err) + } + + // OpenTelemetry provider. + provider := otelmetric.MustProvider(metric.WithReader(exporter)) + provider.SetAsGlobal() + defer provider.Shutdown(ctx) + + // Counter. + counter.Inc(ctx) + counter.Add(ctx, 10, gmetric.Option{ + Attributes: gmetric.Attributes{ + gmetric.NewAttribute("dynamic_label_2", 2), + }, + }) + + // HTTP Server for metrics exporting. + s := g.Server() + s.BindHandler("/metrics", ghttp.WrapH(promhttp.Handler())) + s.SetPort(8000) + s.Run() +} diff --git a/example/metric/global_attributes/main.go b/example/metric/global_attributes/main.go new file mode 100644 index 000000000..1684afede --- /dev/null +++ b/example/metric/global_attributes/main.go @@ -0,0 +1,95 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package main + +import ( + "context" + + "github.com/prometheus/client_golang/prometheus/promhttp" + "go.opentelemetry.io/otel/exporters/prometheus" + "go.opentelemetry.io/otel/sdk/metric" + + "github.com/gogf/gf/contrib/metric/otelmetric/v2" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/net/ghttp" + "github.com/gogf/gf/v2/os/gctx" + "github.com/gogf/gf/v2/os/gmetric" +) + +const ( + instrument = "github.com/gogf/gf/example/metric/basic" + instrumentVersion = "v1.0" +) + +var ( + meter = gmetric.GetGlobalProvider().Meter(gmetric.MeterOption{ + Instrument: instrument, + InstrumentVersion: instrumentVersion, + }) + counter = meter.MustCounter( + "goframe.metric.demo.counter", + gmetric.MetricOption{ + Help: "This is a simple demo for Counter usage", + Unit: "bytes", + Attributes: gmetric.Attributes{ + gmetric.NewAttribute("const_label_1", 1), + }, + }, + ) + observableCounter = meter.MustObservableCounter( + "goframe.metric.demo.observable_counter", + gmetric.MetricOption{ + Help: "This is a simple demo for ObservableCounter usage", + Unit: "%", + Attributes: gmetric.Attributes{ + gmetric.NewAttribute("const_label_2", 2), + }, + }, + ) +) + +func main() { + var ctx = gctx.New() + + gmetric.SetGlobalAttributes(gmetric.Attributes{ + gmetric.NewAttribute("g1", 1), + }, gmetric.SetGlobalAttributesOption{ + Instrument: instrument, + InstrumentVersion: instrumentVersion, + InstrumentPattern: "", + }) + + // Callback for observable metrics. + meter.MustRegisterCallback(func(ctx context.Context, obs gmetric.Observer) error { + obs.Observe(observableCounter, 10) + return nil + }, observableCounter) + + // Prometheus exporter to export metrics as Prometheus format. + exporter, err := prometheus.New( + prometheus.WithoutCounterSuffixes(), + prometheus.WithoutUnits(), + ) + if err != nil { + g.Log().Fatal(ctx, err) + } + + // OpenTelemetry provider. + provider := otelmetric.MustProvider(metric.WithReader(exporter)) + provider.SetAsGlobal() + defer provider.Shutdown(ctx) + + // Counter. + counter.Inc(ctx) + counter.Add(ctx, 10) + + // HTTP Server for metrics exporting. + s := g.Server() + s.BindHandler("/metrics", ghttp.WrapH(promhttp.Handler())) + s.SetPort(8000) + s.Run() +} diff --git a/example/metric/http_client/main.go b/example/metric/http_client/main.go new file mode 100644 index 000000000..4729639e6 --- /dev/null +++ b/example/metric/http_client/main.go @@ -0,0 +1,46 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package main + +import ( + "github.com/gogf/gf/contrib/metric/otelmetric/v2" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/net/ghttp" + "github.com/gogf/gf/v2/os/gctx" + "github.com/prometheus/client_golang/prometheus/promhttp" + "go.opentelemetry.io/otel/exporters/prometheus" + "go.opentelemetry.io/otel/sdk/metric" +) + +func main() { + var ctx = gctx.New() + + // Prometheus exporter to export metrics as Prometheus format. + exporter, err := prometheus.New( + prometheus.WithoutCounterSuffixes(), + prometheus.WithoutUnits(), + ) + if err != nil { + g.Log().Fatal(ctx, err) + } + + // OpenTelemetry provider. + provider := otelmetric.MustProvider(metric.WithReader(exporter)) + provider.SetAsGlobal() + defer provider.Shutdown(ctx) + + // A simple http client request for demonstration purpose only. + url := `https://github.com/gogf/gf` + content := g.Client().GetContent(ctx, url) + g.Log().Infof(ctx, `content length from "%s": %d`, url, len(content)) + + // A simple http server for metrics export. + s := g.Server() + s.BindHandler("/metrics", ghttp.WrapH(promhttp.Handler())) + s.SetPort(8000) + s.Run() +} diff --git a/example/metric/http_server/main.go b/example/metric/http_server/main.go new file mode 100644 index 000000000..db86b8106 --- /dev/null +++ b/example/metric/http_server/main.go @@ -0,0 +1,52 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package main + +import ( + "github.com/prometheus/client_golang/prometheus/promhttp" + "go.opentelemetry.io/otel/exporters/prometheus" + "go.opentelemetry.io/otel/sdk/metric" + "time" + + "github.com/gogf/gf/contrib/metric/otelmetric/v2" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/net/ghttp" + "github.com/gogf/gf/v2/os/gctx" +) + +func main() { + var ctx = gctx.New() + + // Prometheus exporter to export metrics as Prometheus format. + exporter, err := prometheus.New( + prometheus.WithoutCounterSuffixes(), + prometheus.WithoutUnits(), + ) + if err != nil { + g.Log().Fatal(ctx, err) + } + + // OpenTelemetry provider. + provider := otelmetric.MustProvider(metric.WithReader(exporter)) + provider.SetAsGlobal() + defer provider.Shutdown(ctx) + + s := g.Server() + s.BindHandler("/", func(r *ghttp.Request) { + r.Response.Write("ok") + }) + s.BindHandler("/error", func(r *ghttp.Request) { + panic("error") + }) + s.BindHandler("/sleep", func(r *ghttp.Request) { + time.Sleep(time.Second * 5) + r.Response.Write("ok") + }) + s.BindHandler("/metrics", ghttp.WrapH(promhttp.Handler())) + s.SetPort(8000) + s.Run() +} diff --git a/example/metric/meter_attributes/main.go b/example/metric/meter_attributes/main.go new file mode 100644 index 000000000..5a42da838 --- /dev/null +++ b/example/metric/meter_attributes/main.go @@ -0,0 +1,91 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package main + +import ( + "context" + + "github.com/prometheus/client_golang/prometheus/promhttp" + "go.opentelemetry.io/otel/exporters/prometheus" + "go.opentelemetry.io/otel/sdk/metric" + + "github.com/gogf/gf/contrib/metric/otelmetric/v2" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/net/ghttp" + "github.com/gogf/gf/v2/os/gctx" + "github.com/gogf/gf/v2/os/gmetric" +) + +const ( + instrument = "github.com/gogf/gf/example/metric/basic" + instrumentVersion = "v1.0" +) + +var ( + meter = gmetric.GetGlobalProvider().Meter(gmetric.MeterOption{ + Instrument: instrument, + InstrumentVersion: instrumentVersion, + Attributes: gmetric.Attributes{ + gmetric.NewAttribute("meter_label_1", 1), + gmetric.NewAttribute("meter_label_2", 2), + }, + }) + counter = meter.MustCounter( + "goframe.metric.demo.counter", + gmetric.MetricOption{ + Help: "This is a simple demo for Counter usage", + Unit: "bytes", + Attributes: gmetric.Attributes{ + gmetric.NewAttribute("const_label_1", 1), + }, + }, + ) + observableCounter = meter.MustObservableCounter( + "goframe.metric.demo.observable_counter", + gmetric.MetricOption{ + Help: "This is a simple demo for ObservableCounter usage", + Unit: "%", + Attributes: gmetric.Attributes{ + gmetric.NewAttribute("const_label_2", 2), + }, + }, + ) +) + +func main() { + var ctx = gctx.New() + + // Callback for observable metrics. + meter.MustRegisterCallback(func(ctx context.Context, obs gmetric.Observer) error { + obs.Observe(observableCounter, 10) + return nil + }, observableCounter) + + // Prometheus exporter to export metrics as Prometheus format. + exporter, err := prometheus.New( + prometheus.WithoutCounterSuffixes(), + prometheus.WithoutUnits(), + ) + if err != nil { + g.Log().Fatal(ctx, err) + } + + // OpenTelemetry provider. + provider := otelmetric.MustProvider(metric.WithReader(exporter)) + provider.SetAsGlobal() + defer provider.Shutdown(ctx) + + // Counter. + counter.Inc(ctx) + counter.Add(ctx, 10) + + // HTTP Server for metrics exporting. + s := g.Server() + s.BindHandler("/metrics", ghttp.WrapH(promhttp.Handler())) + s.SetPort(8000) + s.Run() +} diff --git a/example/metric/prometheus/prometheus.go b/example/metric/prometheus/prometheus.go index d76dc31ee..5b2f44c7a 100644 --- a/example/metric/prometheus/prometheus.go +++ b/example/metric/prometheus/prometheus.go @@ -1,3 +1,9 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + package main import ( diff --git a/example/trace/http-with-db/client/client.go b/example/trace/http-with-db/client/client.go index 826744382..c4f4ec5ae 100644 --- a/example/trace/http-with-db/client/client.go +++ b/example/trace/http-with-db/client/client.go @@ -32,6 +32,7 @@ func main() { StartRequests() } +// StartRequests starts requests. func StartRequests() { ctx, span := gtrace.NewSpan(gctx.New(), "StartRequests") defer span.End() @@ -43,7 +44,7 @@ func StartRequests() { // Add user info. var insertRes = struct { ghttp.DefaultHandlerResponse - Data struct{ Id int64 } `json:"data"` + Data struct{ ID int64 } `json:"data"` }{} err = client.PostVar(ctx, "http://127.0.0.1:8199/user/insert", g.Map{ "name": "john", @@ -52,7 +53,7 @@ func StartRequests() { panic(err) } g.Log().Info(ctx, "insert result:", insertRes) - if insertRes.Data.Id == 0 { + if insertRes.Data.ID == 0 { g.Log().Error(ctx, "retrieve empty id string") return } @@ -63,7 +64,7 @@ func StartRequests() { Data struct{ User gdb.Record } `json:"data"` }{} err = client.GetVar(ctx, "http://127.0.0.1:8199/user/query", g.Map{ - "id": insertRes.Data.Id, + "id": insertRes.Data.ID, }).Scan(&queryRes) if err != nil { panic(err) @@ -75,7 +76,7 @@ func StartRequests() { ghttp.DefaultHandlerResponse }{} err = client.PostVar(ctx, "http://127.0.0.1:8199/user/delete", g.Map{ - "id": insertRes.Data.Id, + "id": insertRes.Data.ID, }).Scan(&deleteRes) if err != nil { panic(err) diff --git a/go.mod b/go.mod index 19b62ba10..f5ed180ef 100644 --- a/go.mod +++ b/go.mod @@ -20,11 +20,10 @@ require ( ) require ( - github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/logr v1.2.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-runewidth v0.0.15 // indirect - github.com/rivo/uniseg v0.4.4 // indirect + github.com/mattn/go-isatty v0.0.17 // indirect + github.com/mattn/go-runewidth v0.0.9 // indirect golang.org/x/sys v0.13.0 // indirect ) diff --git a/go.sum b/go.sum index f083621d9..1b22ab371 100644 --- a/go.sum +++ b/go.sum @@ -8,8 +8,8 @@ github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBD github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= -github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 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.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= @@ -22,17 +22,13 @@ github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPK github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -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.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= -github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= -github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= go.opentelemetry.io/otel v1.14.0 h1:/79Huy8wbf5DnIPhemGB+zEPVwnN6fuQybr/SRXa6hM= go.opentelemetry.io/otel v1.14.0/go.mod h1:o4buv+dJzx8rohcUeRmWUZhqupFvzWis188WlggnNeU= @@ -43,7 +39,6 @@ go.opentelemetry.io/otel/trace v1.14.0/go.mod h1:8avnQLK+CG77yNLUae4ea2JDQ6iT+go golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= 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.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= diff --git a/net/gclient/gclient.go b/net/gclient/gclient.go index d0a0fba05..44b99e2a3 100644 --- a/net/gclient/gclient.go +++ b/net/gclient/gclient.go @@ -76,7 +76,7 @@ func New() *Client { } c.header[httpHeaderUserAgent] = defaultClientAgent // It enables OpenTelemetry for client in default. - c.Use(internalMiddlewareTracing, internalMiddlewareDiscovery) + c.Use(internalMiddlewareObservability, internalMiddlewareDiscovery) return c } diff --git a/net/gclient/gclient_chain.go b/net/gclient/gclient_chain.go index fce78fba1..7b0b6c643 100644 --- a/net/gclient/gclient_chain.go +++ b/net/gclient/gclient_chain.go @@ -101,6 +101,7 @@ func (c *Client) BasicAuth(user, pass string) *Client { // Retry is a chaining function, // which sets retry count and interval when failure for next request. +// TODO removed. func (c *Client) Retry(retryCount int, retryInterval time.Duration) *Client { newClient := c.Clone() newClient.SetRetry(retryCount, retryInterval) diff --git a/net/gclient/gclient_config.go b/net/gclient/gclient_config.go index f842ab4e7..baa090220 100644 --- a/net/gclient/gclient_config.go +++ b/net/gclient/gclient_config.go @@ -108,6 +108,7 @@ func (c *Client) SetBasicAuth(user, pass string) *Client { } // SetRetry sets retry count and interval. +// TODO removed. func (c *Client) SetRetry(retryCount int, retryInterval time.Duration) *Client { c.retryCount = retryCount c.retryInterval = retryInterval diff --git a/net/gclient/gclient_metrics.go b/net/gclient/gclient_metrics.go new file mode 100644 index 000000000..2b81a9b9b --- /dev/null +++ b/net/gclient/gclient_metrics.go @@ -0,0 +1,277 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gclient + +import ( + "net/http" + + "github.com/gogf/gf/v2" + "github.com/gogf/gf/v2/os/gmetric" + "github.com/gogf/gf/v2/os/gtime" + "github.com/gogf/gf/v2/text/gstr" +) + +type localMetricManager struct { + HttpClientRequestActive gmetric.UpDownCounter + HttpClientRequestTotal gmetric.Counter + HttpClientRequestDuration gmetric.Histogram + HttpClientRequestDurationTotal gmetric.Counter + HttpClientConnectionDuration gmetric.Histogram + HttpClientRequestBodySize gmetric.Counter + HttpClientResponseBodySize gmetric.Counter +} + +const ( + metricAttrKeyServerAddress = "server.address" + metricAttrKeyServerPort = "server.port" + metricAttrKeyUrlSchema = "url.schema" + metricAttrKeyHttpRequestMethod = "http.request.method" + metricAttrKeyHttpResponseStatusCode = "http.response.status_code" + metricAttrKeyNetworkProtocolVersion = "network.protocol.version" +) + +var ( + // metricManager for http client metrics. + metricManager = newMetricManager() +) + +func newMetricManager() *localMetricManager { + meter := gmetric.GetGlobalProvider().Meter(gmetric.MeterOption{ + Instrument: instrumentName, + InstrumentVersion: gf.VERSION, + }) + durationBuckets := []float64{ + 1, + 5, + 10, + 25, + 50, + 75, + 100, + 250, + 500, + 750, + 1000, + 2500, + 5000, + 7500, + 10000, + 30000, + 60000, + } + mm := &localMetricManager{ + HttpClientRequestDuration: meter.MustHistogram( + "http.client.request.duration", + gmetric.MetricOption{ + Help: "Measures the duration of client requests.", + Unit: "ms", + Attributes: gmetric.Attributes{}, + Buckets: durationBuckets, + }, + ), + HttpClientRequestTotal: meter.MustCounter( + "http.client.request.total", + gmetric.MetricOption{ + Help: "Total processed request number.", + Unit: "", + Attributes: gmetric.Attributes{}, + }, + ), + HttpClientRequestActive: meter.MustUpDownCounter( + "http.client.request.active", + gmetric.MetricOption{ + Help: "Number of active client requests.", + Unit: "", + Attributes: gmetric.Attributes{}, + }, + ), + HttpClientRequestDurationTotal: meter.MustCounter( + "http.client.request.duration_total", + gmetric.MetricOption{ + Help: "Total execution duration of request.", + Unit: "ms", + Attributes: gmetric.Attributes{}, + }, + ), + HttpClientRequestBodySize: meter.MustCounter( + "http.client.request.body_size", + gmetric.MetricOption{ + Help: "Outgoing request bytes total.", + Unit: "bytes", + Attributes: gmetric.Attributes{}, + }, + ), + HttpClientResponseBodySize: meter.MustCounter( + "http.client.response.body_size", + gmetric.MetricOption{ + Help: "Response bytes total.", + Unit: "bytes", + Attributes: gmetric.Attributes{}, + }, + ), + HttpClientConnectionDuration: meter.MustHistogram( + "http.client.connection_duration", + gmetric.MetricOption{ + Help: "Measures the connection establish duration of client requests.", + Unit: "ms", + Attributes: gmetric.Attributes{}, + Buckets: durationBuckets, + }, + ), + } + return mm +} + +func (m *localMetricManager) GetMetricOptionForHistogram(r *http.Request) gmetric.Option { + attrMap := m.GetMetricAttributeMap(r) + return gmetric.Option{ + Attributes: attrMap.Pick( + metricAttrKeyServerAddress, + metricAttrKeyServerPort, + ), + } +} + +func (m *localMetricManager) GetMetricOptionForHistogramByMap(attrMap gmetric.AttributeMap) gmetric.Option { + return gmetric.Option{ + Attributes: attrMap.Pick( + metricAttrKeyServerAddress, + metricAttrKeyServerPort, + ), + } +} + +func (m *localMetricManager) GetMetricOptionForRequest(r *http.Request) gmetric.Option { + attrMap := m.GetMetricAttributeMap(r) + return m.GetMetricOptionForRequestByMap(attrMap) +} + +func (m *localMetricManager) GetMetricOptionForRequestByMap(attrMap gmetric.AttributeMap) gmetric.Option { + return gmetric.Option{ + Attributes: attrMap.Pick( + metricAttrKeyServerAddress, + metricAttrKeyServerPort, + metricAttrKeyHttpRequestMethod, + metricAttrKeyUrlSchema, + metricAttrKeyNetworkProtocolVersion, + ), + } +} + +func (m *localMetricManager) GetMetricOptionForResponseByMap(attrMap gmetric.AttributeMap) gmetric.Option { + return gmetric.Option{ + Attributes: attrMap.Pick( + metricAttrKeyServerAddress, + metricAttrKeyServerPort, + metricAttrKeyHttpRequestMethod, + metricAttrKeyHttpResponseStatusCode, + metricAttrKeyUrlSchema, + metricAttrKeyNetworkProtocolVersion, + ), + } +} + +func (m *localMetricManager) GetMetricAttributeMap(r *http.Request) gmetric.AttributeMap { + var ( + serverAddress string + serverPort string + protocolVersion string + attrMap = make(gmetric.AttributeMap) + ) + serverAddress, serverPort = gstr.List2(r.Host, ":") + if serverPort == "" { + _, serverPort = gstr.List2(r.RemoteAddr, ":") + } + if serverPort == "" { + serverPort = "80" + if r.URL.Scheme == "https" { + serverPort = "443" + } + } + if array := gstr.Split(r.Proto, "/"); len(array) > 1 { + protocolVersion = array[1] + } + attrMap.Sets(gmetric.AttributeMap{ + metricAttrKeyServerAddress: serverAddress, + metricAttrKeyServerPort: serverPort, + metricAttrKeyUrlSchema: r.URL.Scheme, + metricAttrKeyHttpRequestMethod: r.Method, + metricAttrKeyNetworkProtocolVersion: protocolVersion, + }) + if r.Response != nil { + attrMap.Sets(gmetric.AttributeMap{ + metricAttrKeyHttpResponseStatusCode: r.Response.Status, + }) + } + return attrMap +} + +func (c *Client) handleMetricsBeforeRequest(r *http.Request) { + if !gmetric.IsEnabled() { + return + } + + var ( + ctx = r.Context() + attrMap = metricManager.GetMetricAttributeMap(r) + requestOption = metricManager.GetMetricOptionForRequestByMap(attrMap) + requestBodySize = float64(r.ContentLength) + ) + metricManager.HttpClientRequestActive.Inc( + ctx, + requestOption, + ) + if requestBodySize > 0 { + metricManager.HttpClientRequestBodySize.Add( + ctx, + requestBodySize, + requestOption, + ) + } +} + +func (c *Client) handleMetricsAfterRequestDone(r *http.Request, requestStartTime *gtime.Time) { + if !gmetric.IsEnabled() { + return + } + + var ( + ctx = r.Context() + attrMap = metricManager.GetMetricAttributeMap(r) + duration = float64(gtime.Now().Sub(requestStartTime).Milliseconds()) + requestOption = metricManager.GetMetricOptionForRequestByMap(attrMap) + responseOption = metricManager.GetMetricOptionForResponseByMap(attrMap) + histogramOption = metricManager.GetMetricOptionForHistogramByMap(attrMap) + ) + metricManager.HttpClientRequestActive.Dec( + ctx, + requestOption, + ) + metricManager.HttpClientRequestTotal.Inc( + ctx, + responseOption, + ) + metricManager.HttpClientRequestDuration.Record( + duration, + histogramOption, + ) + metricManager.HttpClientRequestDurationTotal.Add( + ctx, + duration, + responseOption, + ) + if r.Response != nil { + var responseBodySize = float64(r.Response.ContentLength) + if responseBodySize > 0 { + metricManager.HttpClientResponseBodySize.Add( + ctx, + responseBodySize, + responseOption, + ) + } + } +} diff --git a/net/gclient/gclient_tracing.go b/net/gclient/gclient_observability.go similarity index 77% rename from net/gclient/gclient_tracing.go rename to net/gclient/gclient_observability.go index 961775adf..6b0323f18 100644 --- a/net/gclient/gclient_tracing.go +++ b/net/gclient/gclient_observability.go @@ -24,11 +24,12 @@ import ( "github.com/gogf/gf/v2/internal/utils" "github.com/gogf/gf/v2/net/gtrace" "github.com/gogf/gf/v2/os/gctx" + "github.com/gogf/gf/v2/os/gmetric" "github.com/gogf/gf/v2/util/gconv" ) const ( - tracingInstrumentName = "github.com/gogf/gf/v2/net/gclient.Client" + instrumentName = "github.com/gogf/gf/v2/net/gclient.Client" tracingAttrHttpAddressRemote = "http.address.remote" tracingAttrHttpAddressLocal = "http.address.local" tracingAttrHttpDnsStart = "http.dns.start" @@ -45,8 +46,8 @@ const ( tracingMiddlewareHandled gctx.StrKey = `MiddlewareClientTracingHandled` ) -// internalMiddlewareTracing is a client middleware that enables tracing feature using standards of OpenTelemetry. -func internalMiddlewareTracing(c *Client, r *http.Request) (response *Response, err error) { +// internalMiddlewareObservability is a client middleware that enables observability feature. +func internalMiddlewareObservability(c *Client, r *http.Request) (response *Response, err error) { var ctx = r.Context() // Mark this request is handled by server tracing middleware, // to avoid repeated handling by the same middleware. @@ -56,7 +57,7 @@ func internalMiddlewareTracing(c *Client, r *http.Request) (response *Response, ctx = context.WithValue(ctx, tracingMiddlewareHandled, 1) tr := otel.GetTracerProvider().Tracer( - tracingInstrumentName, + instrumentName, trace.WithInstrumentationVersion(gf.VERSION), ) ctx, span := tr.Start(ctx, r.URL.String(), trace.WithSpanKind(trace.SpanKindClient)) @@ -67,20 +68,33 @@ func internalMiddlewareTracing(c *Client, r *http.Request) (response *Response, // Inject tracing content into http header. otel.GetTextMapPropagator().Inject(ctx, propagation.HeaderCarrier(r.Header)) + // Inject ClientTrace into context for http request. + var ( + httpClientTracer *httptrace.ClientTrace + baseClientTracer = newClientTracerNoop() + isUsingDefaultProvider = gtrace.IsUsingDefaultProvider() + ) + // Tracing. + if !isUsingDefaultProvider { + baseClientTracer = newClientTracerTracing(ctx, span, r) + } + // Metrics. + if gmetric.IsEnabled() { + baseClientTracer = newClientTracerMetrics(r, baseClientTracer) + } + httpClientTracer = newClientTracer(baseClientTracer) + r = r.WithContext( + httptrace.WithClientTrace( + ctx, httpClientTracer, + ), + ) + response, err = c.Next(r) + // If it is now using default trace provider, it then does no complex tracing jobs. - if gtrace.IsUsingDefaultProvider() { - response, err = c.Next(r) + if isUsingDefaultProvider { return } - // Continue client handler executing. - response, err = c.Next( - r.WithContext( - httptrace.WithClientTrace( - ctx, newClientTrace(ctx, span, r), - ), - ), - ) if err != nil { span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, err)) } diff --git a/net/gclient/gclient_request.go b/net/gclient/gclient_request.go index ecaa467bb..53aa744c1 100644 --- a/net/gclient/gclient_request.go +++ b/net/gclient/gclient_request.go @@ -23,6 +23,7 @@ import ( "github.com/gogf/gf/v2/internal/json" "github.com/gogf/gf/v2/internal/utils" "github.com/gogf/gf/v2/os/gfile" + "github.com/gogf/gf/v2/os/gtime" "github.com/gogf/gf/v2/text/gregex" "github.com/gogf/gf/v2/text/gstr" "github.com/gogf/gf/v2/util/gconv" @@ -121,12 +122,19 @@ func (c *Client) PostForm(ctx context.Context, url string, data map[string]strin // else it uses "application/x-www-form-urlencoded". It also automatically detects the post // content for JSON format, and for that it automatically sets the Content-Type as // "application/json". -func (c *Client) DoRequest(ctx context.Context, method, url string, data ...interface{}) (resp *Response, err error) { +func (c *Client) DoRequest( + ctx context.Context, method, url string, data ...interface{}, +) (resp *Response, err error) { + var requestStartTime = gtime.Now() req, err := c.prepareRequest(ctx, method, url, data...) if err != nil { return nil, err } + // Metrics. + c.handleMetricsBeforeRequest(req) + defer c.handleMetricsAfterRequestDone(req, requestStartTime) + // Client middleware. if len(c.middlewareHandler) > 0 { mdlHandlers := make([]HandlerFunc, 0, len(c.middlewareHandler)+1) @@ -144,6 +152,9 @@ func (c *Client) DoRequest(ctx context.Context, method, url string, data ...inte } else { resp, err = c.callRequest(req) } + if resp != nil && resp.Response != nil { + req.Response = resp.Response + } return resp, err } diff --git a/net/gclient/gclient_tracer.go b/net/gclient/gclient_tracer.go new file mode 100644 index 000000000..6b0e9e3e4 --- /dev/null +++ b/net/gclient/gclient_tracer.go @@ -0,0 +1,158 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gclient + +import ( + "crypto/tls" + "net/http/httptrace" + "net/textproto" +) + +type clientTracer struct { + *httptrace.ClientTrace +} + +// newClientTracer creates and returns object of httptrace.ClientTrace. +func newClientTracer(baseClientTracer *httptrace.ClientTrace) *httptrace.ClientTrace { + c := &clientTracer{ + ClientTrace: baseClientTracer, + } + return &httptrace.ClientTrace{ + GetConn: c.GetConn, + GotConn: c.GotConn, + PutIdleConn: c.PutIdleConn, + GotFirstResponseByte: c.GotFirstResponseByte, + Got100Continue: c.Got100Continue, + Got1xxResponse: c.Got1xxResponse, + DNSStart: c.DNSStart, + DNSDone: c.DNSDone, + ConnectStart: c.ConnectStart, + ConnectDone: c.ConnectDone, + TLSHandshakeStart: c.TLSHandshakeStart, + TLSHandshakeDone: c.TLSHandshakeDone, + WroteHeaderField: c.WroteHeaderField, + WroteHeaders: c.WroteHeaders, + Wait100Continue: c.Wait100Continue, + WroteRequest: c.WroteRequest, + } +} + +// GetConn is called before a connection is created or +// retrieved from an idle pool. The hostPort is the +// "host:port" of the target or proxy. GetConn is called even +// if there's already an idle cached connection available. +func (ct *clientTracer) GetConn(hostPort string) { + ct.ClientTrace.GetConn(hostPort) +} + +// GotConn is called after a successful connection is +// obtained. There is no hook for failure to obtain a +// connection; instead, use the error from +// Transport.RoundTrip. +func (ct *clientTracer) GotConn(info httptrace.GotConnInfo) { + ct.ClientTrace.GotConn(info) +} + +// PutIdleConn is called when the connection is returned to +// the idle pool. If err is nil, the connection was +// successfully returned to the idle pool. If err is non-nil, +// it describes why not. PutIdleConn is not called if +// connection reuse is disabled via Transport.DisableKeepAlives. +// PutIdleConn is called before the caller's Response.Body.Close +// call returns. +// For HTTP/2, this hook is not currently used. +func (ct *clientTracer) PutIdleConn(err error) { + ct.ClientTrace.PutIdleConn(err) +} + +// GotFirstResponseByte is called when the first byte of the response +// headers is available. +func (ct *clientTracer) GotFirstResponseByte() { + ct.ClientTrace.GotFirstResponseByte() +} + +// Got100Continue is called if the server replies with a "100 +// Continue" response. +func (ct *clientTracer) Got100Continue() { + ct.ClientTrace.Got100Continue() +} + +// Got1xxResponse is called for each 1xx informational response header +// returned before the final non-1xx response. Got1xxResponse is called +// for "100 Continue" responses, even if Got100Continue is also defined. +// If it returns an error, the client request is aborted with that error value. +func (ct *clientTracer) Got1xxResponse(code int, header textproto.MIMEHeader) error { + return ct.ClientTrace.Got1xxResponse(code, header) +} + +// DNSStart is called when a DNS lookup begins. +func (ct *clientTracer) DNSStart(info httptrace.DNSStartInfo) { + ct.ClientTrace.DNSStart(info) +} + +// DNSDone is called when a DNS lookup ends. +func (ct *clientTracer) DNSDone(info httptrace.DNSDoneInfo) { + ct.ClientTrace.DNSDone(info) +} + +// ConnectStart is called when a new connection's Dial begins. +// If net.Dialer.DualStack (IPv6 "Happy Eyeballs") support is +// enabled, this may be called multiple times. +func (ct *clientTracer) ConnectStart(network, addr string) { + ct.ClientTrace.ConnectStart(network, addr) +} + +// ConnectDone is called when a new connection's Dial +// completes. The provided err indicates whether the +// connection completed successfully. +// If net.Dialer.DualStack ("Happy Eyeballs") support is +// enabled, this may be called multiple times. +func (ct *clientTracer) ConnectDone(network, addr string, err error) { + ct.ClientTrace.ConnectDone(network, addr, err) +} + +// TLSHandshakeStart is called when the TLS handshake is started. When +// connecting to an HTTPS site via an HTTP proxy, the handshake happens +// after the CONNECT request is processed by the proxy. +func (ct *clientTracer) TLSHandshakeStart() { + ct.ClientTrace.TLSHandshakeStart() +} + +// TLSHandshakeDone is called after the TLS handshake with either the +// successful handshake's connection state, or a non-nil error on handshake +// failure. +func (ct *clientTracer) TLSHandshakeDone(state tls.ConnectionState, err error) { + ct.ClientTrace.TLSHandshakeDone(state, err) +} + +// WroteHeaderField is called after the Transport has written +// each request header. At the time of this call the values +// might be buffered and not yet written to the network. +func (ct *clientTracer) WroteHeaderField(key string, value []string) { + ct.ClientTrace.WroteHeaderField(key, value) +} + +// WroteHeaders is called after the Transport has written +// all request headers. +func (ct *clientTracer) WroteHeaders() { + ct.ClientTrace.WroteHeaders() +} + +// Wait100Continue is called if the Request specified +// "Expect: 100-continue" and the Transport has written the +// request headers but is waiting for "100 Continue" from the +// server before writing the request body. +func (ct *clientTracer) Wait100Continue() { + ct.ClientTrace.Wait100Continue() +} + +// WroteRequest is called with the result of writing the +// request and any body. It may be called multiple times +// in the case of retried requests. +func (ct *clientTracer) WroteRequest(info httptrace.WroteRequestInfo) { + ct.ClientTrace.WroteRequest(info) +} diff --git a/net/gclient/gclient_tracer_metrics.go b/net/gclient/gclient_tracer_metrics.go new file mode 100644 index 000000000..3af9ef3cb --- /dev/null +++ b/net/gclient/gclient_tracer_metrics.go @@ -0,0 +1,176 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gclient + +import ( + "crypto/tls" + "net/http" + "net/http/httptrace" + "net/textproto" + + "github.com/gogf/gf/v2/os/gtime" +) + +type clientTracerMetrics struct { + *httptrace.ClientTrace + Request *http.Request + ConnectStartTime *gtime.Time +} + +// newClientTracerMetrics creates and returns object of httptrace.ClientTrace. +func newClientTracerMetrics(request *http.Request, baseClientTracer *httptrace.ClientTrace) *httptrace.ClientTrace { + c := &clientTracerMetrics{ + Request: request, + ClientTrace: baseClientTracer, + } + return &httptrace.ClientTrace{ + GetConn: c.GetConn, + GotConn: c.GotConn, + PutIdleConn: c.PutIdleConn, + GotFirstResponseByte: c.GotFirstResponseByte, + Got100Continue: c.Got100Continue, + Got1xxResponse: c.Got1xxResponse, + DNSStart: c.DNSStart, + DNSDone: c.DNSDone, + ConnectStart: c.ConnectStart, + ConnectDone: c.ConnectDone, + TLSHandshakeStart: c.TLSHandshakeStart, + TLSHandshakeDone: c.TLSHandshakeDone, + WroteHeaderField: c.WroteHeaderField, + WroteHeaders: c.WroteHeaders, + Wait100Continue: c.Wait100Continue, + WroteRequest: c.WroteRequest, + } +} + +// GetConn is called before a connection is created or +// retrieved from an idle pool. The hostPort is the +// "host:port" of the target or proxy. GetConn is called even +// if there's already an idle cached connection available. +func (ct *clientTracerMetrics) GetConn(hostPort string) { + ct.ClientTrace.GetConn(hostPort) +} + +// GotConn is called after a successful connection is +// obtained. There is no hook for failure to obtain a +// connection; instead, use the error from +// Transport.RoundTrip. +func (ct *clientTracerMetrics) GotConn(info httptrace.GotConnInfo) { + ct.ClientTrace.GotConn(info) +} + +// PutIdleConn is called when the connection is returned to +// the idle pool. If err is nil, the connection was +// successfully returned to the idle pool. If err is non-nil, +// it describes why not. PutIdleConn is not called if +// connection reuse is disabled via Transport.DisableKeepAlives. +// PutIdleConn is called before the caller's Response.Body.Close +// call returns. +// For HTTP/2, this hook is not currently used. +func (ct *clientTracerMetrics) PutIdleConn(err error) { + ct.ClientTrace.PutIdleConn(err) +} + +// GotFirstResponseByte is called when the first byte of the response +// headers is available. +func (ct *clientTracerMetrics) GotFirstResponseByte() { + ct.ClientTrace.GotFirstResponseByte() +} + +// Got100Continue is called if the server replies with a "100 +// Continue" response. +func (ct *clientTracerMetrics) Got100Continue() { + ct.ClientTrace.Got100Continue() +} + +// Got1xxResponse is called for each 1xx informational response header +// returned before the final non-1xx response. Got1xxResponse is called +// for "100 Continue" responses, even if Got100Continue is also defined. +// If it returns an error, the client request is aborted with that error value. +func (ct *clientTracerMetrics) Got1xxResponse(code int, header textproto.MIMEHeader) error { + return ct.ClientTrace.Got1xxResponse(code, header) +} + +// DNSStart is called when a DNS lookup begins. +func (ct *clientTracerMetrics) DNSStart(info httptrace.DNSStartInfo) { + ct.ClientTrace.DNSStart(info) +} + +// DNSDone is called when a DNS lookup ends. +func (ct *clientTracerMetrics) DNSDone(info httptrace.DNSDoneInfo) { + ct.ClientTrace.DNSDone(info) +} + +// ConnectStart is called when a new connection's Dial begins. +// If net.Dialer.DualStack (IPv6 "Happy Eyeballs") support is +// enabled, this may be called multiple times. +func (ct *clientTracerMetrics) ConnectStart(network, addr string) { + if ct.Request.RemoteAddr == "" { + ct.Request.RemoteAddr = addr + } + ct.ConnectStartTime = gtime.Now() + ct.ClientTrace.ConnectStart(network, addr) +} + +// ConnectDone is called when a new connection's Dial +// completes. The provided err indicates whether the +// connection completed successfully. +// If net.Dialer.DualStack ("Happy Eyeballs") support is +// enabled, this may be called multiple times. +func (ct *clientTracerMetrics) ConnectDone(network, addr string, err error) { + var ( + duration = float64(gtime.Now().Sub(ct.ConnectStartTime).Milliseconds()) + durationOption = metricManager.GetMetricOptionForHistogram(ct.Request) + ) + metricManager.HttpClientConnectionDuration.Record( + duration, + durationOption, + ) + ct.ClientTrace.ConnectDone(network, addr, err) +} + +// TLSHandshakeStart is called when the TLS handshake is started. When +// connecting to an HTTPS site via an HTTP proxy, the handshake happens +// after the CONNECT request is processed by the proxy. +func (ct *clientTracerMetrics) TLSHandshakeStart() { + ct.ClientTrace.TLSHandshakeStart() +} + +// TLSHandshakeDone is called after the TLS handshake with either the +// successful handshake's connection state, or a non-nil error on handshake +// failure. +func (ct *clientTracerMetrics) TLSHandshakeDone(state tls.ConnectionState, err error) { + ct.ClientTrace.TLSHandshakeDone(state, err) +} + +// WroteHeaderField is called after the Transport has written +// each request header. At the time of this call the values +// might be buffered and not yet written to the network. +func (ct *clientTracerMetrics) WroteHeaderField(key string, value []string) { + ct.ClientTrace.WroteHeaderField(key, value) +} + +// WroteHeaders is called after the Transport has written +// all request headers. +func (ct *clientTracerMetrics) WroteHeaders() { + ct.ClientTrace.WroteHeaders() +} + +// Wait100Continue is called if the Request specified +// "Expect: 100-continue" and the Transport has written the +// request headers but is waiting for "100 Continue" from the +// server before writing the request body. +func (ct *clientTracerMetrics) Wait100Continue() { + ct.ClientTrace.Wait100Continue() +} + +// WroteRequest is called with the result of writing the +// request and any body. It may be called multiple times +// in the case of retried requests. +func (ct *clientTracerMetrics) WroteRequest(info httptrace.WroteRequestInfo) { + ct.ClientTrace.WroteRequest(info) +} diff --git a/net/gclient/gclient_tracer_noop.go b/net/gclient/gclient_tracer_noop.go new file mode 100644 index 000000000..e047f1954 --- /dev/null +++ b/net/gclient/gclient_tracer_noop.go @@ -0,0 +1,124 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gclient + +import ( + "crypto/tls" + "net/http/httptrace" + "net/textproto" +) + +type clientTracerNoop struct{} + +// newClientTracerNoop creates and returns object of httptrace.ClientTrace. +func newClientTracerNoop() *httptrace.ClientTrace { + c := &clientTracerNoop{} + return &httptrace.ClientTrace{ + GetConn: c.GetConn, + GotConn: c.GotConn, + PutIdleConn: c.PutIdleConn, + GotFirstResponseByte: c.GotFirstResponseByte, + Got100Continue: c.Got100Continue, + Got1xxResponse: c.Got1xxResponse, + DNSStart: c.DNSStart, + DNSDone: c.DNSDone, + ConnectStart: c.ConnectStart, + ConnectDone: c.ConnectDone, + TLSHandshakeStart: c.TLSHandshakeStart, + TLSHandshakeDone: c.TLSHandshakeDone, + WroteHeaderField: c.WroteHeaderField, + WroteHeaders: c.WroteHeaders, + Wait100Continue: c.Wait100Continue, + WroteRequest: c.WroteRequest, + } +} + +// GetConn is called before a connection is created or +// retrieved from an idle pool. The hostPort is the +// "host:port" of the target or proxy. GetConn is called even +// if there's already an idle cached connection available. +func (*clientTracerNoop) GetConn(hostPort string) {} + +// GotConn is called after a successful connection is +// obtained. There is no hook for failure to obtain a +// connection; instead, use the error from +// Transport.RoundTrip. +func (*clientTracerNoop) GotConn(httptrace.GotConnInfo) {} + +// PutIdleConn is called when the connection is returned to +// the idle pool. If err is nil, the connection was +// successfully returned to the idle pool. If err is non-nil, +// it describes why not. PutIdleConn is not called if +// connection reuse is disabled via Transport.DisableKeepAlives. +// PutIdleConn is called before the caller's Response.Body.Close +// call returns. +// For HTTP/2, this hook is not currently used. +func (*clientTracerNoop) PutIdleConn(err error) {} + +// GotFirstResponseByte is called when the first byte of the response +// headers is available. +func (*clientTracerNoop) GotFirstResponseByte() {} + +// Got100Continue is called if the server replies with a "100 +// Continue" response. +func (*clientTracerNoop) Got100Continue() {} + +// Got1xxResponse is called for each 1xx informational response header +// returned before the final non-1xx response. Got1xxResponse is called +// for "100 Continue" responses, even if Got100Continue is also defined. +// If it returns an error, the client request is aborted with that error value. +func (*clientTracerNoop) Got1xxResponse(code int, header textproto.MIMEHeader) error { + return nil +} + +// DNSStart is called when a DNS lookup begins. +func (*clientTracerNoop) DNSStart(httptrace.DNSStartInfo) {} + +// DNSDone is called when a DNS lookup ends. +func (*clientTracerNoop) DNSDone(httptrace.DNSDoneInfo) {} + +// ConnectStart is called when a new connection's Dial begins. +// If net.Dialer.DualStack (IPv6 "Happy Eyeballs") support is +// enabled, this may be called multiple times. +func (*clientTracerNoop) ConnectStart(network, addr string) {} + +// ConnectDone is called when a new connection's Dial +// completes. The provided err indicates whether the +// connection completed successfully. +// If net.Dialer.DualStack ("Happy Eyeballs") support is +// enabled, this may be called multiple times. +func (*clientTracerNoop) ConnectDone(network, addr string, err error) {} + +// TLSHandshakeStart is called when the TLS handshake is started. When +// connecting to an HTTPS site via an HTTP proxy, the handshake happens +// after the CONNECT request is processed by the proxy. +func (*clientTracerNoop) TLSHandshakeStart() {} + +// TLSHandshakeDone is called after the TLS handshake with either the +// successful handshake's connection state, or a non-nil error on handshake +// failure. +func (*clientTracerNoop) TLSHandshakeDone(tls.ConnectionState, error) {} + +// WroteHeaderField is called after the Transport has written +// each request header. At the time of this call the values +// might be buffered and not yet written to the network. +func (*clientTracerNoop) WroteHeaderField(key string, value []string) {} + +// WroteHeaders is called after the Transport has written +// all request headers. +func (*clientTracerNoop) WroteHeaders() {} + +// Wait100Continue is called if the Request specified +// "Expect: 100-continue" and the Transport has written the +// request headers but is waiting for "100 Continue" from the +// server before writing the request body. +func (*clientTracerNoop) Wait100Continue() {} + +// WroteRequest is called with the result of writing the +// request and any body. It may be called multiple times +// in the case of retried requests. +func (*clientTracerNoop) WroteRequest(httptrace.WroteRequestInfo) {} diff --git a/net/gclient/gclient_tracer_tracing.go b/net/gclient/gclient_tracer_tracing.go new file mode 100644 index 000000000..6c61ebe69 --- /dev/null +++ b/net/gclient/gclient_tracer_tracing.go @@ -0,0 +1,231 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gclient + +import ( + "context" + "crypto/tls" + "fmt" + "io" + "net/http" + "net/http/httptrace" + "net/textproto" + "strings" + "sync" + + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/trace" + + "github.com/gogf/gf/v2/internal/utils" + "github.com/gogf/gf/v2/net/gtrace" + "github.com/gogf/gf/v2/util/gconv" +) + +// clientTracerTracing is used for implementing httptrace.ClientTrace. +type clientTracerTracing struct { + context.Context + span trace.Span + request *http.Request + requestBody []byte + headers map[string]interface{} + mtx sync.Mutex +} + +// newClientTracerTracing creates and returns object of httptrace.ClientTrace. +func newClientTracerTracing( + ctx context.Context, + span trace.Span, + request *http.Request, +) *httptrace.ClientTrace { + ct := &clientTracerTracing{ + Context: ctx, + span: span, + request: request, + headers: make(map[string]interface{}), + } + + reqBodyContent, _ := io.ReadAll(ct.request.Body) + ct.requestBody = reqBodyContent + ct.request.Body = utils.NewReadCloser(reqBodyContent, false) + + return &httptrace.ClientTrace{ + GetConn: ct.GetConn, + GotConn: ct.GotConn, + PutIdleConn: ct.PutIdleConn, + GotFirstResponseByte: ct.GotFirstResponseByte, + Got100Continue: ct.Got100Continue, + Got1xxResponse: ct.Got1xxResponse, + DNSStart: ct.DNSStart, + DNSDone: ct.DNSDone, + ConnectStart: ct.ConnectStart, + ConnectDone: ct.ConnectDone, + TLSHandshakeStart: ct.TLSHandshakeStart, + TLSHandshakeDone: ct.TLSHandshakeDone, + WroteHeaderField: ct.WroteHeaderField, + WroteHeaders: ct.WroteHeaders, + Wait100Continue: ct.Wait100Continue, + WroteRequest: ct.WroteRequest, + } +} + +// GetConn is called before a connection is created or +// retrieved from an idle pool. The hostPort is the +// "host:port" of the target or proxy. GetConn is called even +// if there's already an idle cached connection available. +func (ct *clientTracerTracing) GetConn(host string) {} + +// GotConn is called after a successful connection is +// obtained. There is no hook for failure to obtain a +// connection; instead, use the error from +// Transport.RoundTrip. +func (ct *clientTracerTracing) GotConn(info httptrace.GotConnInfo) { + remoteAddr := "" + if info.Conn.RemoteAddr() != nil { + remoteAddr = info.Conn.RemoteAddr().String() + } + localAddr := "" + if info.Conn.LocalAddr() != nil { + localAddr = info.Conn.LocalAddr().String() + } + ct.span.SetAttributes( + attribute.String(tracingAttrHttpAddressRemote, remoteAddr), + attribute.String(tracingAttrHttpAddressLocal, localAddr), + ) +} + +// PutIdleConn is called when the connection is returned to +// the idle pool. If err is nil, the connection was +// successfully returned to the idle pool. If err is non-nil, +// it describes why not. PutIdleConn is not called if +// connection reuse is disabled via Transport.DisableKeepAlives. +// PutIdleConn is called before the caller's Response.Body.Close +// call returns. +// For HTTP/2, this hook is not currently used. +func (ct *clientTracerTracing) PutIdleConn(err error) { + if err != nil { + ct.span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, err)) + } +} + +// GotFirstResponseByte is called when the first byte of the response +// headers is available. +func (ct *clientTracerTracing) GotFirstResponseByte() {} + +// Got100Continue is called if the server replies with a "100 +// Continue" response. +func (ct *clientTracerTracing) Got100Continue() {} + +// Got1xxResponse is called for each 1xx informational response header +// returned before the final non-1xx response. Got1xxResponse is called +// for "100 Continue" responses, even if Got100Continue is also defined. +// If it returns an error, the client request is aborted with that error value. +func (ct *clientTracerTracing) Got1xxResponse(code int, header textproto.MIMEHeader) error { + return nil +} + +// DNSStart is called when a DNS lookup begins. +func (ct *clientTracerTracing) DNSStart(info httptrace.DNSStartInfo) { + ct.span.SetAttributes( + attribute.String(tracingAttrHttpDnsStart, info.Host), + ) +} + +// DNSDone is called when a DNS lookup ends. +func (ct *clientTracerTracing) DNSDone(info httptrace.DNSDoneInfo) { + var buffer strings.Builder + for _, v := range info.Addrs { + if buffer.Len() != 0 { + buffer.WriteString(",") + } + buffer.WriteString(v.String()) + } + if info.Err != nil { + ct.span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, info.Err)) + } + ct.span.SetAttributes( + attribute.String(tracingAttrHttpDnsDone, buffer.String()), + ) +} + +// ConnectStart is called when a new connection's Dial begins. +// If net.Dialer.DualStack (IPv6 "Happy Eyeballs") support is +// enabled, this may be called multiple times. +func (ct *clientTracerTracing) ConnectStart(network, addr string) { + ct.span.SetAttributes( + attribute.String(tracingAttrHttpConnectStart, network+"@"+addr), + ) +} + +// ConnectDone is called when a new connection's Dial +// completes. The provided err indicates whether the +// connection completed successfully. +// If net.Dialer.DualStack ("Happy Eyeballs") support is +// enabled, this may be called multiple times. +func (ct *clientTracerTracing) ConnectDone(network, addr string, err error) { + if err != nil { + ct.span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, err)) + } + ct.span.SetAttributes( + attribute.String(tracingAttrHttpConnectDone, network+"@"+addr), + ) +} + +// TLSHandshakeStart is called when the TLS handshake is started. When +// connecting to an HTTPS site via an HTTP proxy, the handshake happens +// after the CONNECT request is processed by the proxy. +func (ct *clientTracerTracing) TLSHandshakeStart() {} + +// TLSHandshakeDone is called after the TLS handshake with either the +// successful handshake's connection state, or a non-nil error on handshake +// failure. +func (ct *clientTracerTracing) TLSHandshakeDone(_ tls.ConnectionState, err error) { + if err != nil { + ct.span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, err)) + } +} + +// WroteHeaderField is called after the Transport has written +// each request header. At the time of this call the values +// might be buffered and not yet written to the network. +func (ct *clientTracerTracing) WroteHeaderField(k string, v []string) { + if len(v) > 1 { + ct.headers[k] = v + } else if len(v) == 1 { + ct.headers[k] = v[0] + } +} + +// WroteHeaders is called after the Transport has written +// all request headers. +func (ct *clientTracerTracing) WroteHeaders() {} + +// Wait100Continue is called if the Request specified +// "Expect: 100-continue" and the Transport has written the +// request headers but is waiting for "100 Continue" from the +// server before writing the request body. +func (ct *clientTracerTracing) Wait100Continue() {} + +// WroteRequest is called with the result of writing the +// request and any body. It may be called multiple times +// in the case of retried requests. +func (ct *clientTracerTracing) WroteRequest(info httptrace.WroteRequestInfo) { + if info.Err != nil { + ct.span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, info.Err)) + } + + reqBodyContent, err := gtrace.SafeContentForHttp(ct.requestBody, ct.request.Header) + if err != nil { + ct.span.SetStatus(codes.Error, fmt.Sprintf(`converting safe content failed: %s`, err.Error())) + } + + ct.span.AddEvent(tracingEventHttpRequest, trace.WithAttributes( + attribute.String(tracingEventHttpRequestHeaders, gconv.String(ct.headers)), + attribute.String(tracingEventHttpRequestBaggage, gtrace.GetBaggageMap(ct.Context).String()), + attribute.String(tracingEventHttpRequestBody, reqBodyContent), + )) +} diff --git a/net/gclient/gclient_tracing_tracer.go b/net/gclient/gclient_tracing_tracer.go deleted file mode 100644 index 8e9d87a67..000000000 --- a/net/gclient/gclient_tracing_tracer.go +++ /dev/null @@ -1,175 +0,0 @@ -// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. -// -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, -// You can obtain one at https://github.com/gogf/gf. - -package gclient - -import ( - "context" - "crypto/tls" - "fmt" - "io" - "net/http" - "net/http/httptrace" - "net/textproto" - "strings" - "sync" - - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/codes" - "go.opentelemetry.io/otel/trace" - - "github.com/gogf/gf/v2/internal/utils" - "github.com/gogf/gf/v2/net/gtrace" - "github.com/gogf/gf/v2/util/gconv" -) - -// clientTracer is used for implementing httptrace.ClientTrace. -type clientTracer struct { - context.Context - span trace.Span - request *http.Request - requestBody []byte - headers map[string]interface{} - mtx sync.Mutex -} - -// newClientTrace creates and returns object of newClientTrace. -func newClientTrace(ctx context.Context, span trace.Span, request *http.Request) *httptrace.ClientTrace { - ct := &clientTracer{ - Context: ctx, - span: span, - request: request, - headers: make(map[string]interface{}), - } - - reqBodyContent, _ := io.ReadAll(ct.request.Body) - ct.requestBody = reqBodyContent - ct.request.Body = utils.NewReadCloser(reqBodyContent, false) - - return &httptrace.ClientTrace{ - GetConn: ct.getConn, - GotConn: ct.gotConn, - PutIdleConn: ct.putIdleConn, - GotFirstResponseByte: ct.gotFirstResponseByte, - Got100Continue: ct.got100Continue, - Got1xxResponse: ct.got1xxResponse, - DNSStart: ct.dnsStart, - DNSDone: ct.dnsDone, - ConnectStart: ct.connectStart, - ConnectDone: ct.connectDone, - TLSHandshakeStart: ct.tlsHandshakeStart, - TLSHandshakeDone: ct.tlsHandshakeDone, - WroteHeaderField: ct.wroteHeaderField, - WroteHeaders: ct.wroteHeaders, - Wait100Continue: ct.wait100Continue, - WroteRequest: ct.wroteRequest, - } -} - -func (ct *clientTracer) getConn(host string) {} - -func (ct *clientTracer) gotConn(info httptrace.GotConnInfo) { - remoteAddr := "" - if info.Conn.RemoteAddr() != nil { - remoteAddr = info.Conn.RemoteAddr().String() - } - localAddr := "" - if info.Conn.LocalAddr() != nil { - localAddr = info.Conn.LocalAddr().String() - } - ct.span.SetAttributes( - attribute.String(tracingAttrHttpAddressRemote, remoteAddr), - attribute.String(tracingAttrHttpAddressLocal, localAddr), - ) -} - -func (ct *clientTracer) putIdleConn(err error) { - if err != nil { - ct.span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, err)) - } -} - -func (ct *clientTracer) dnsStart(info httptrace.DNSStartInfo) { - ct.span.SetAttributes( - attribute.String(tracingAttrHttpDnsStart, info.Host), - ) -} - -func (ct *clientTracer) dnsDone(info httptrace.DNSDoneInfo) { - var buffer strings.Builder - for _, v := range info.Addrs { - if buffer.Len() != 0 { - buffer.WriteString(",") - } - buffer.WriteString(v.String()) - } - if info.Err != nil { - ct.span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, info.Err)) - } - ct.span.SetAttributes( - attribute.String(tracingAttrHttpDnsDone, buffer.String()), - ) -} - -func (ct *clientTracer) connectStart(network, addr string) { - ct.span.SetAttributes( - attribute.String(tracingAttrHttpConnectStart, network+"@"+addr), - ) -} - -func (ct *clientTracer) connectDone(network, addr string, err error) { - if err != nil { - ct.span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, err)) - } - ct.span.SetAttributes( - attribute.String(tracingAttrHttpConnectDone, network+"@"+addr), - ) -} - -func (ct *clientTracer) tlsHandshakeStart() {} - -func (ct *clientTracer) tlsHandshakeDone(_ tls.ConnectionState, err error) { - if err != nil { - ct.span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, err)) - } -} - -func (ct *clientTracer) wroteHeaderField(k string, v []string) { - if len(v) > 1 { - ct.headers[k] = v - } else if len(v) == 1 { - ct.headers[k] = v[0] - } -} - -func (ct *clientTracer) wroteHeaders() {} - -func (ct *clientTracer) wroteRequest(info httptrace.WroteRequestInfo) { - if info.Err != nil { - ct.span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, info.Err)) - } - - reqBodyContent, err := gtrace.SafeContentForHttp(ct.requestBody, ct.request.Header) - if err != nil { - ct.span.SetStatus(codes.Error, fmt.Sprintf(`converting safe content failed: %s`, err.Error())) - } - - ct.span.AddEvent(tracingEventHttpRequest, trace.WithAttributes( - attribute.String(tracingEventHttpRequestHeaders, gconv.String(ct.headers)), - attribute.String(tracingEventHttpRequestBaggage, gtrace.GetBaggageMap(ct.Context).String()), - attribute.String(tracingEventHttpRequestBody, reqBodyContent), - )) -} - -func (ct *clientTracer) got100Continue() {} - -func (ct *clientTracer) wait100Continue() {} - -func (ct *clientTracer) gotFirstResponseByte() {} - -func (ct *clientTracer) got1xxResponse(code int, header textproto.MIMEHeader) error { - return nil -} diff --git a/net/ghttp/ghttp_middleware_tracing.go b/net/ghttp/ghttp_middleware_tracing.go index b1e6fbaca..f5d125183 100644 --- a/net/ghttp/ghttp_middleware_tracing.go +++ b/net/ghttp/ghttp_middleware_tracing.go @@ -27,7 +27,7 @@ import ( ) const ( - tracingInstrumentName = "github.com/gogf/gf/v2/net/ghttp.Server" + instrumentName = "github.com/gogf/gf/v2/net/ghttp.Server" tracingEventHttpRequest = "http.request" tracingEventHttpRequestHeaders = "http.request.headers" tracingEventHttpRequestBaggage = "http.request.baggage" @@ -55,7 +55,7 @@ func internalMiddlewareServerTracing(r *Request) { var ( span trace.Span tr = otel.GetTracerProvider().Tracer( - tracingInstrumentName, + instrumentName, trace.WithInstrumentationVersion(gf.VERSION), ) ) @@ -104,8 +104,8 @@ func internalMiddlewareServerTracing(r *Request) { r.Middleware.Next() // parse after set route as span name - if r.Router.Uri != defaultMiddlewarePattern || r.Router.RegNames != nil { - span.SetName(r.Router.Uri) + if handler := r.GetServeHandler(); handler != nil && handler.Handler.Router != nil { + span.SetName(handler.Handler.Router.Uri) } // Error logging. diff --git a/net/ghttp/ghttp_request.go b/net/ghttp/ghttp_request.go index f48dcafab..73801995b 100644 --- a/net/ghttp/ghttp_request.go +++ b/net/ghttp/ghttp_request.go @@ -30,8 +30,8 @@ type Request struct { Session *gsession.Session // Session. Response *Response // Corresponding Response of this request. Router *Router // Matched Router for this request. Note that it's not available in HOOK handler. - EnterTime int64 // Request starting time in milliseconds. - LeaveTime int64 // Request to end time in milliseconds. + EnterTime *gtime.Time // Request starting time in milliseconds. + LeaveTime *gtime.Time // Request to end time in milliseconds. Middleware *middleware // Middleware manager. StaticFile *staticFile // Static file object for static file serving. @@ -76,7 +76,7 @@ func newRequest(s *Server, r *http.Request, w http.ResponseWriter) *Request { Server: s, Request: r, Response: newResponse(s, w), - EnterTime: gtime.TimestampMilli(), + EnterTime: gtime.Now(), originUrlPath: r.URL.Path, } request.Cookie = GetCookie(request) @@ -117,6 +117,8 @@ func newRequest(s *Server, r *http.Request, w http.ResponseWriter) *Request { // WebSocket upgrades current request as a websocket request. // It returns a new WebSocket object if success, or the error if failure. // Note that the request should be a websocket request, or it will surely fail upgrading. +// +// Deprecated: will be removed in the future, please use third-party websocket library instead. func (r *Request) WebSocket() (*WebSocket, error) { if conn, err := wsUpGrader.Upgrade(r.Response.Writer, r.Request, nil); err == nil { return &WebSocket{ @@ -217,6 +219,18 @@ func (r *Request) GetRemoteIp() string { return r.RemoteAddr } +// GetSchema returns the schema of this request. +func (r *Request) GetSchema() string { + var ( + scheme = "http" + proto = r.Header.Get("X-Forwarded-Proto") + ) + if r.TLS != nil || gstr.Equal(proto, "https") { + scheme = "https" + } + return scheme +} + // GetUrl returns current URL of this request. func (r *Request) GetUrl() string { var ( diff --git a/net/ghttp/ghttp_response.go b/net/ghttp/ghttp_response.go index 44bb3bb7f..69ca86d5e 100644 --- a/net/ghttp/ghttp_response.go +++ b/net/ghttp/ghttp_response.go @@ -8,7 +8,6 @@ package ghttp import ( - "bytes" "fmt" "io" "net/http" @@ -24,22 +23,17 @@ import ( // Response is the http response manager. // Note that it implements the http.ResponseWriter interface with buffering feature. type Response struct { - *ResponseWriter // Underlying ResponseWriter. - Server *Server // Parent server. - Writer *ResponseWriter // Alias of ResponseWriter. - Request *Request // According request. + *response.BufferWriter // Underlying ResponseWriter. + Server *Server // Parent server. + Request *Request // According request. } // newResponse creates and returns a new Response object. func newResponse(s *Server, w http.ResponseWriter) *Response { r := &Response{ - Server: s, - ResponseWriter: &ResponseWriter{ - writer: response.NewWriter(w), - buffer: bytes.NewBuffer(nil), - }, + Server: s, + BufferWriter: response.NewBufferWriter(w), } - r.Writer = r.ResponseWriter return r } @@ -119,32 +113,6 @@ func (r *Response) RedirectBack(code ...int) { r.RedirectTo(r.Request.GetReferer(), code...) } -// Buffer returns the buffered content as []byte. -func (r *Response) Buffer() []byte { - return r.buffer.Bytes() -} - -// BufferString returns the buffered content as string. -func (r *Response) BufferString() string { - return r.buffer.String() -} - -// BufferLength returns the length of the buffered content. -func (r *Response) BufferLength() int { - return r.buffer.Len() -} - -// SetBuffer overwrites the buffer with `data`. -func (r *Response) SetBuffer(data []byte) { - r.buffer.Reset() - r.buffer.Write(data) -} - -// ClearBuffer clears the response buffer. -func (r *Response) ClearBuffer() { - r.buffer.Reset() -} - // ServeContent replies to the request using the content in the // provided ReadSeeker. The main benefit of ServeContent over io.Copy // is that it handles Range requests properly, sets the MIME type, and @@ -153,7 +121,7 @@ func (r *Response) ClearBuffer() { // // See http.ServeContent func (r *Response) ServeContent(name string, modTime time.Time, content io.ReadSeeker) { - http.ServeContent(r.Writer.RawWriter(), r.Request.Request, name, modTime, content) + http.ServeContent(r.RawWriter(), r.Request.Request, name, modTime, content) } // Flush outputs the buffer content to the client and clears the buffer. @@ -162,5 +130,5 @@ func (r *Response) Flush() { if r.Server.config.ServerAgent != "" { r.Header().Set("Server", r.Server.config.ServerAgent) } - r.Writer.Flush() + r.BufferWriter.Flush() } diff --git a/net/ghttp/ghttp_response_write.go b/net/ghttp/ghttp_response_write.go index 05b5c113f..a6edbf648 100644 --- a/net/ghttp/ghttp_response_write.go +++ b/net/ghttp/ghttp_response_write.go @@ -19,7 +19,7 @@ import ( // Write writes `content` to the response buffer. func (r *Response) Write(content ...interface{}) { - if r.writer.IsHijacked() || len(content) == 0 { + if r.IsHijacked() || len(content) == 0 { return } if r.Status == 0 { @@ -28,11 +28,11 @@ func (r *Response) Write(content ...interface{}) { for _, v := range content { switch value := v.(type) { case []byte: - r.buffer.Write(value) + _, _ = r.BufferWriter.Write(value) case string: - r.buffer.WriteString(value) + _, _ = r.BufferWriter.WriteString(value) default: - r.buffer.WriteString(gconv.String(v)) + _, _ = r.BufferWriter.WriteString(gconv.String(v)) } } } diff --git a/net/ghttp/ghttp_response_writer.go b/net/ghttp/ghttp_response_writer.go deleted file mode 100644 index 9ed6f6a28..000000000 --- a/net/ghttp/ghttp_response_writer.go +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. -// -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, -// You can obtain one at https://github.com/gogf/gf. -// - -package ghttp - -import ( - "bufio" - "bytes" - "net" - "net/http" - - "github.com/gogf/gf/v2/net/ghttp/internal/response" -) - -// ResponseWriter is the custom writer for http response. -type ResponseWriter struct { - Status int // HTTP status. - writer *response.Writer // The underlying ResponseWriter. - buffer *bytes.Buffer // The output buffer. -} - -// RawWriter returns the underlying ResponseWriter. -func (w *ResponseWriter) RawWriter() http.ResponseWriter { - return w.writer -} - -// Header implements the interface function of http.ResponseWriter.Header. -func (w *ResponseWriter) Header() http.Header { - return w.writer.Header() -} - -// Write implements the interface function of http.ResponseWriter.Write. -func (w *ResponseWriter) Write(data []byte) (int, error) { - w.buffer.Write(data) - return len(data), nil -} - -// WriteHeader implements the interface of http.ResponseWriter.WriteHeader. -func (w *ResponseWriter) WriteHeader(status int) { - w.Status = status -} - -// Hijack implements the interface function of http.Hijacker.Hijack. -func (w *ResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { - return w.writer.Hijack() -} - -// Flush outputs the buffer to clients and clears the buffer. -func (w *ResponseWriter) Flush() { - if w.writer.IsHijacked() { - return - } - - if w.Status != 0 && !w.writer.IsHeaderWrote() { - w.writer.WriteHeader(w.Status) - } - // Default status text output. - if w.Status != http.StatusOK && w.buffer.Len() == 0 { - w.buffer.WriteString(http.StatusText(w.Status)) - } - if w.buffer.Len() > 0 { - _, _ = w.writer.Write(w.buffer.Bytes()) - w.buffer.Reset() - if flusher, ok := w.RawWriter().(http.Flusher); ok { - flusher.Flush() - } - } -} diff --git a/net/ghttp/ghttp_server_handler.go b/net/ghttp/ghttp_server_handler.go index da4863032..27ca64e14 100644 --- a/net/ghttp/ghttp_server_handler.go +++ b/net/ghttp/ghttp_server_handler.go @@ -40,52 +40,11 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { } } - // Create a new request object. - request := newRequest(s, r, w) - - // Get sessionId before user handler - sessionId := request.GetSessionId() - - defer func() { - request.LeaveTime = gtime.TimestampMilli() - // error log handling. - if request.error != nil { - s.handleErrorLog(request.error, request) - } else { - if exception := recover(); exception != nil { - request.Response.WriteStatus(http.StatusInternalServerError) - if v, ok := exception.(error); ok { - if code := gerror.Code(v); code != gcode.CodeNil { - s.handleErrorLog(v, request) - } else { - s.handleErrorLog(gerror.WrapCodeSkip(gcode.CodeInternalPanic, 1, v, ""), request) - } - } else { - s.handleErrorLog(gerror.NewCodeSkipf(gcode.CodeInternalPanic, 1, "%+v", exception), request) - } - } - } - // access log handling. - s.handleAccessLog(request) - // Close the session, which automatically update the TTL - // of the session if it exists. - if err := request.Session.Close(); err != nil { - intlog.Errorf(request.Context(), `%+v`, err) - } - - // Close the request and response body - // to release the file descriptor in time. - err := request.Request.Body.Close() - if err != nil { - intlog.Errorf(request.Context(), `%+v`, err) - } - if request.Request.Response != nil { - err = request.Request.Response.Body.Close() - if err != nil { - intlog.Errorf(request.Context(), `%+v`, err) - } - } - }() + var ( + request = newRequest(s, r, w) // Create a new request object. + sessionId = request.GetSessionId() // Get sessionId before user handler + ) + defer s.handleAfterRequestDone(request) // ============================================================ // Priority: @@ -112,6 +71,9 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { request.isFileRequest = false } + // Metrics. + s.handleMetricsBeforeRequest(request) + // HOOK - BeforeServe s.callHookHandler(HookBeforeServe, request) @@ -149,9 +111,19 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { s.callHookHandler(HookBeforeOutput, request) } + // Response handling. + s.handleResponse(request, sessionId) + + // HOOK - AfterOutput + if !request.IsExited() { + s.callHookHandler(HookAfterOutput, request) + } +} + +func (s *Server) handleResponse(request *Request, sessionId string) { // HTTP status checking. if request.Response.Status == 0 { - if request.StaticFile != nil || request.Middleware.served || request.Response.buffer.Len() > 0 { + if request.StaticFile != nil || request.Middleware.served || request.Response.BufferLength() > 0 { request.Response.WriteHeader(http.StatusOK) } else if err := request.GetError(); err != nil { if request.Response.BufferLength() == 0 { @@ -195,10 +167,56 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { request.Cookie.Flush() // Output the buffer content to the client. request.Response.Flush() - // HOOK - AfterOutput - if !request.IsExited() { - s.callHookHandler(HookAfterOutput, request) +} + +func (s *Server) handleAfterRequestDone(request *Request) { + request.LeaveTime = gtime.Now() + // error log handling. + if request.error != nil { + s.handleErrorLog(request.error, request) + } else { + if exception := recover(); exception != nil { + request.Response.WriteStatus(http.StatusInternalServerError) + if v, ok := exception.(error); ok { + if code := gerror.Code(v); code != gcode.CodeNil { + s.handleErrorLog(v, request) + } else { + s.handleErrorLog( + gerror.WrapCodeSkip(gcode.CodeInternalPanic, 1, v, ""), + request, + ) + } + } else { + s.handleErrorLog( + gerror.NewCodeSkipf(gcode.CodeInternalPanic, 1, "%+v", exception), + request, + ) + } + } } + // access log handling. + s.handleAccessLog(request) + // Close the session, which automatically update the TTL + // of the session if it exists. + if err := request.Session.Close(); err != nil { + intlog.Errorf(request.Context(), `%+v`, err) + } + + // Close the request and response body + // to release the file descriptor in time. + err := request.Request.Body.Close() + if err != nil { + intlog.Errorf(request.Context(), `%+v`, err) + } + if request.Request.Response != nil { + err = request.Request.Response.Body.Close() + if err != nil { + intlog.Errorf(request.Context(), `%+v`, err) + } + } + + // Metrics. + s.handleMetricsAfterRequestDone(request) } // searchStaticFile searches the file with given URI. @@ -287,7 +305,9 @@ func (s *Server) serveFile(r *Request, f *staticFile, allowIndex ...bool) { r.Response.WriteStatus(http.StatusForbidden) return } - defer file.Close() + defer func() { + _ = file.Close() + }() // Clear the response buffer before file serving. // It ignores all custom buffer content and uses the file content. diff --git a/net/ghttp/ghttp_server_log.go b/net/ghttp/ghttp_server_log.go index 2b67156bd..9f4e917df 100644 --- a/net/ghttp/ghttp_server_log.go +++ b/net/ghttp/ghttp_server_log.go @@ -21,18 +21,13 @@ func (s *Server) handleAccessLog(r *Request) { return } var ( - scheme = "http" - proto = r.Header.Get("X-Forwarded-Proto") + scheme = r.GetSchema() loggerInstanceKey = fmt.Sprintf(`Acccess Logger Of Server:%s`, s.instance) ) - - if r.TLS != nil || gstr.Equal(proto, "https") { - scheme = "https" - } content := fmt.Sprintf( `%d "%s %s %s %s %s" %.3f, %s, "%s", "%s"`, r.Response.Status, r.Method, scheme, r.Host, r.URL.String(), r.Proto, - float64(r.LeaveTime-r.EnterTime)/1000, + float64(r.LeaveTime.Sub(r.EnterTime).Milliseconds())/1000, r.GetClientIp(), r.Referer(), r.UserAgent(), ) logger := instance.GetOrSetFuncLock(loggerInstanceKey, func() interface{} { @@ -53,22 +48,18 @@ func (s *Server) handleErrorLog(err error, r *Request) { } var ( code = gerror.Code(err) - scheme = "http" + scheme = r.GetSchema() codeDetail = code.Detail() - proto = r.Header.Get("X-Forwarded-Proto") loggerInstanceKey = fmt.Sprintf(`Error Logger Of Server:%s`, s.instance) codeDetailStr string ) - if r.TLS != nil || gstr.Equal(proto, "https") { - scheme = "https" - } if codeDetail != nil { codeDetailStr = gstr.Replace(fmt.Sprintf(`%+v`, codeDetail), "\n", " ") } content := fmt.Sprintf( `%d "%s %s %s %s %s" %.3f, %s, "%s", "%s", %d, "%s", "%+v"`, r.Response.Status, r.Method, scheme, r.Host, r.URL.String(), r.Proto, - float64(r.LeaveTime-r.EnterTime)/1000, + float64(r.LeaveTime.Sub(r.EnterTime))/1000, r.GetClientIp(), r.Referer(), r.UserAgent(), code.Code(), code.Message(), codeDetailStr, ) diff --git a/net/ghttp/ghttp_server_metric.go b/net/ghttp/ghttp_server_metric.go new file mode 100644 index 000000000..b9eb458e4 --- /dev/null +++ b/net/ghttp/ghttp_server_metric.go @@ -0,0 +1,256 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package ghttp + +import ( + "net" + "net/http" + + "github.com/gogf/gf/v2" + "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/os/gmetric" + "github.com/gogf/gf/v2/text/gstr" +) + +type localMetricManager struct { + HttpServerRequestActive gmetric.UpDownCounter + HttpServerRequestTotal gmetric.Counter + HttpServerRequestDuration gmetric.Histogram + HttpServerRequestDurationTotal gmetric.Counter + HttpServerRequestBodySize gmetric.Counter + HttpServerResponseBodySize gmetric.Counter +} + +const ( + metricAttrKeyServerAddress = "server.address" + metricAttrKeyServerPort = "server.port" + metricAttrKeyHttpRoute = "http.route" + metricAttrKeyUrlSchema = "url.schema" + metricAttrKeyHttpRequestMethod = "http.request.method" + metricAttrKeyErrorCode = "error.code" + metricAttrKeyHttpResponseStatusCode = "http.response.status_code" + metricAttrKeyNetworkProtocolVersion = "network.protocol.version" +) + +var ( + // metricManager for http server metrics. + metricManager = newMetricManager() +) + +func newMetricManager() *localMetricManager { + meter := gmetric.GetGlobalProvider().Meter(gmetric.MeterOption{ + Instrument: instrumentName, + InstrumentVersion: gf.VERSION, + }) + mm := &localMetricManager{ + HttpServerRequestDuration: meter.MustHistogram( + "http.server.request.duration", + gmetric.MetricOption{ + Help: "Measures the duration of inbound request.", + Unit: "ms", + Attributes: gmetric.Attributes{}, + Buckets: []float64{ + 1, + 5, + 10, + 25, + 50, + 75, + 100, + 250, + 500, + 750, + 1000, + 2500, + 5000, + 7500, + 10000, + 30000, + 60000, + }, + }, + ), + HttpServerRequestTotal: meter.MustCounter( + "http.server.request.total", + gmetric.MetricOption{ + Help: "Total processed request number.", + Unit: "", + Attributes: gmetric.Attributes{}, + }, + ), + HttpServerRequestActive: meter.MustUpDownCounter( + "http.server.request.active", + gmetric.MetricOption{ + Help: "Number of active server requests.", + Unit: "", + Attributes: gmetric.Attributes{}, + }, + ), + HttpServerRequestDurationTotal: meter.MustCounter( + "http.server.request.duration_total", + gmetric.MetricOption{ + Help: "Total execution duration of request.", + Unit: "ms", + Attributes: gmetric.Attributes{}, + }, + ), + HttpServerRequestBodySize: meter.MustCounter( + "http.server.request.body_size", + gmetric.MetricOption{ + Help: "Incoming request bytes total.", + Unit: "bytes", + Attributes: gmetric.Attributes{}, + }, + ), + HttpServerResponseBodySize: meter.MustCounter( + "http.server.response.body_size", + gmetric.MetricOption{ + Help: "Response bytes total.", + Unit: "bytes", + Attributes: gmetric.Attributes{}, + }, + ), + } + return mm +} + +func (m *localMetricManager) GetMetricOptionForRequestDurationByMap(attrMap gmetric.AttributeMap) gmetric.Option { + return gmetric.Option{ + Attributes: attrMap.Pick( + metricAttrKeyServerAddress, + metricAttrKeyServerPort, + ), + } +} + +func (m *localMetricManager) GetMetricOptionForRequest(r *Request) gmetric.Option { + attrMap := m.GetMetricAttributeMap(r) + return m.GetMetricOptionForRequestByMap(attrMap) +} + +func (m *localMetricManager) GetMetricOptionForRequestByMap(attrMap gmetric.AttributeMap) gmetric.Option { + return gmetric.Option{ + Attributes: attrMap.Pick( + metricAttrKeyServerAddress, + metricAttrKeyServerPort, + metricAttrKeyHttpRoute, + metricAttrKeyUrlSchema, + metricAttrKeyHttpRequestMethod, + metricAttrKeyNetworkProtocolVersion, + ), + } +} + +func (m *localMetricManager) GetMetricOptionForResponseByMap(attrMap gmetric.AttributeMap) gmetric.Option { + return gmetric.Option{ + Attributes: attrMap.Pick( + metricAttrKeyServerAddress, + metricAttrKeyServerPort, + metricAttrKeyHttpRoute, + metricAttrKeyUrlSchema, + metricAttrKeyHttpRequestMethod, + metricAttrKeyNetworkProtocolVersion, + metricAttrKeyErrorCode, + metricAttrKeyHttpResponseStatusCode, + ), + } +} + +func (m *localMetricManager) GetMetricAttributeMap(r *Request) gmetric.AttributeMap { + var ( + serverAddress string + serverPort string + httpRoute string + protocolVersion string + handler = r.GetServeHandler() + localAddr = r.Context().Value(http.LocalAddrContextKey) + attrMap = make(gmetric.AttributeMap) + ) + serverAddress, serverPort = gstr.List2(r.Host, ":") + if localAddr != nil { + _, serverPort = gstr.List2(localAddr.(net.Addr).String(), ":") + } + if handler != nil && handler.Handler.Router != nil { + httpRoute = handler.Handler.Router.Uri + } else { + httpRoute = r.URL.Path + } + if array := gstr.Split(r.Proto, "/"); len(array) > 1 { + protocolVersion = array[1] + } + attrMap.Sets(gmetric.AttributeMap{ + metricAttrKeyServerAddress: serverAddress, + metricAttrKeyServerPort: serverPort, + metricAttrKeyHttpRoute: httpRoute, + metricAttrKeyUrlSchema: r.GetSchema(), + metricAttrKeyHttpRequestMethod: r.Method, + metricAttrKeyNetworkProtocolVersion: protocolVersion, + }) + if r.LeaveTime != nil { + var errCode int + if err := r.GetError(); err != nil { + errCode = gerror.Code(err).Code() + } + attrMap.Sets(gmetric.AttributeMap{ + metricAttrKeyErrorCode: errCode, + metricAttrKeyHttpResponseStatusCode: r.Response.Status, + }) + } + return attrMap +} + +func (s *Server) handleMetricsBeforeRequest(r *Request) { + if !gmetric.IsEnabled() { + return + } + var ( + ctx = r.Context() + attrMap = metricManager.GetMetricAttributeMap(r) + requestOption = metricManager.GetMetricOptionForRequestByMap(attrMap) + ) + metricManager.HttpServerRequestActive.Inc( + ctx, + requestOption, + ) + metricManager.HttpServerRequestBodySize.Add( + ctx, + float64(r.ContentLength), + requestOption, + ) +} + +func (s *Server) handleMetricsAfterRequestDone(r *Request) { + if !gmetric.IsEnabled() { + return + } + var ( + ctx = r.Context() + attrMap = metricManager.GetMetricAttributeMap(r) + durationMilli = float64(r.LeaveTime.Sub(r.EnterTime).Milliseconds()) + responseOption = metricManager.GetMetricOptionForResponseByMap(attrMap) + histogramOption = metricManager.GetMetricOptionForRequestDurationByMap(attrMap) + ) + metricManager.HttpServerRequestTotal.Inc(ctx, responseOption) + metricManager.HttpServerRequestActive.Dec( + ctx, + metricManager.GetMetricOptionForRequestByMap(attrMap), + ) + metricManager.HttpServerResponseBodySize.Add( + ctx, + float64(r.Response.BytesWritten()), + responseOption, + ) + metricManager.HttpServerRequestDurationTotal.Add( + ctx, + durationMilli, + responseOption, + ) + metricManager.HttpServerRequestDuration.Record( + durationMilli, + histogramOption, + ) +} diff --git a/net/ghttp/ghttp_server_websocket.go b/net/ghttp/ghttp_server_websocket.go index 96a56f359..c88b8711b 100644 --- a/net/ghttp/ghttp_server_websocket.go +++ b/net/ghttp/ghttp_server_websocket.go @@ -10,6 +10,8 @@ import "github.com/gorilla/websocket" // WebSocket wraps the underlying websocket connection // and provides convenient functions. +// +// Deprecated: will be removed in the future, please use third-party websocket library instead. type WebSocket struct { *websocket.Conn } diff --git a/net/ghttp/internal/response/response_buffer_writer.go b/net/ghttp/internal/response/response_buffer_writer.go new file mode 100644 index 000000000..f1f65c88e --- /dev/null +++ b/net/ghttp/internal/response/response_buffer_writer.go @@ -0,0 +1,95 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. +// + +package response + +import ( + "bytes" + "net/http" +) + +// BufferWriter is the custom writer for http response with buffer. +type BufferWriter struct { + *Writer // The underlying BufferWriter. + Status int // HTTP status. + buffer *bytes.Buffer // The output buffer. +} + +func NewBufferWriter(writer http.ResponseWriter) *BufferWriter { + return &BufferWriter{ + Writer: NewWriter(writer), + buffer: bytes.NewBuffer(nil), + } +} + +// RawWriter returns the underlying BufferWriter. +func (w *BufferWriter) RawWriter() http.ResponseWriter { + return w.Writer +} + +// Write implements the interface function of http.BufferWriter.Write. +func (w *BufferWriter) Write(data []byte) (int, error) { + return w.buffer.Write(data) +} + +// WriteString writes string content to internal buffer. +func (w *BufferWriter) WriteString(data string) (int, error) { + return w.buffer.WriteString(data) +} + +// Buffer returns the buffered content as []byte. +func (w *BufferWriter) Buffer() []byte { + return w.buffer.Bytes() +} + +// BufferString returns the buffered content as string. +func (w *BufferWriter) BufferString() string { + return w.buffer.String() +} + +// BufferLength returns the length of the buffered content. +func (w *BufferWriter) BufferLength() int { + return w.buffer.Len() +} + +// SetBuffer overwrites the buffer with `data`. +func (w *BufferWriter) SetBuffer(data []byte) { + w.buffer.Reset() + w.buffer.Write(data) +} + +// ClearBuffer clears the response buffer. +func (w *BufferWriter) ClearBuffer() { + w.buffer.Reset() +} + +// WriteHeader implements the interface of http.BufferWriter.WriteHeader. +func (w *BufferWriter) WriteHeader(status int) { + w.Status = status +} + +// Flush outputs the buffer to clients and clears the buffer. +func (w *BufferWriter) Flush() { + if w.Writer.IsHijacked() { + return + } + + if w.Status != 0 && !w.Writer.IsHeaderWrote() { + w.Writer.WriteHeader(w.Status) + } + // Default status text output. + if w.Status != http.StatusOK && w.buffer.Len() == 0 { + w.buffer.WriteString(http.StatusText(w.Status)) + } + if w.buffer.Len() > 0 { + _, _ = w.Writer.Write(w.buffer.Bytes()) + w.buffer.Reset() + if flusher, ok := w.RawWriter().(http.Flusher); ok { + flusher.Flush() + } + } +} diff --git a/net/ghttp/internal/response/response_writer.go b/net/ghttp/internal/response/response_writer.go index b974b6c34..fccd476f1 100644 --- a/net/ghttp/internal/response/response_writer.go +++ b/net/ghttp/internal/response/response_writer.go @@ -14,9 +14,10 @@ import ( // Writer wraps http.ResponseWriter for extra features. type Writer struct { - http.ResponseWriter // The underlying ResponseWriter. - hijacked bool // Mark this request is hijacked or not. - wroteHeader bool // Is header wrote or not, avoiding error: superfluous/multiple response.WriteHeader call. + http.ResponseWriter // The underlying ResponseWriter. + hijacked bool // Mark this request is hijacked or not. + wroteHeader bool // Is header wrote or not, avoiding error: superfluous/multiple response.WriteHeader call. + bytesWritten int64 // Bytes written to response. } // NewWriter creates and returns a new Writer. @@ -32,6 +33,18 @@ func (w *Writer) WriteHeader(status int) { w.wroteHeader = true } +// BytesWritten returns the length that was written to response. +func (w *Writer) BytesWritten() int64 { + return w.bytesWritten +} + +// Write implements the interface function of http.ResponseWriter.Write. +func (w *Writer) Write(data []byte) (int, error) { + n, err := w.ResponseWriter.Write(data) + w.bytesWritten += int64(n) + return n, err +} + // Hijack implements the interface function of http.Hijacker.Hijack. func (w *Writer) Hijack() (conn net.Conn, writer *bufio.ReadWriter, err error) { conn, writer, err = w.ResponseWriter.(http.Hijacker).Hijack() diff --git a/net/gtrace/gtrace.go b/net/gtrace/gtrace.go index 0eb96ebac..15f6e128e 100644 --- a/net/gtrace/gtrace.go +++ b/net/gtrace/gtrace.go @@ -15,7 +15,7 @@ import ( "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/propagation" - semconv "go.opentelemetry.io/otel/semconv/v1.4.0" + semconv "go.opentelemetry.io/otel/semconv/v1.18.0" "go.opentelemetry.io/otel/trace" "github.com/gogf/gf/v2/container/gmap" @@ -81,7 +81,7 @@ func CommonLabels() []attribute.KeyValue { return []attribute.KeyValue{ attribute.String(tracingCommonKeyIpHostname, hostname), attribute.String(tracingCommonKeyIpIntranet, intranetIpStr), - semconv.HostNameKey.String(hostname), + semconv.HostName(hostname), } } diff --git a/os/gmetric/gmetric.go b/os/gmetric/gmetric.go new file mode 100644 index 000000000..8cdc76d46 --- /dev/null +++ b/os/gmetric/gmetric.go @@ -0,0 +1,284 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +// Package gmetric provides interface definitions and simple api for metric feature. +package gmetric + +import ( + "context" +) + +// MetricType is the type of metric. +type MetricType string + +const ( + MetricTypeCounter MetricType = `Counter` // Counter. + MetricTypeUpDownCounter MetricType = `UpDownCounter` // UpDownCounter. + MetricTypeHistogram MetricType = `Histogram` // Histogram. + MetricTypeObservableCounter MetricType = `ObservableCounter` // ObservableCounter. + MetricTypeObservableUpDownCounter MetricType = `ObservableUpDownCounter` // ObservableUpDownCounter. + MetricTypeObservableGauge MetricType = `ObservableGauge` // ObservableGauge. +) + +const ( + // MetricNamePattern is the regular expression pattern for validating metric name. + MetricNamePattern = `[\w\.\-\/]` +) + +// Provider manages all Metric exporting. +// Be caution that the Histogram buckets could not be customized if the creation of the Histogram +// is before the creation of Provider. +type Provider interface { + // SetAsGlobal sets current provider as global meter provider for current process, + // which makes the following metrics creating on this Provider, especially the metrics created in runtime. + SetAsGlobal() + + // MeterPerformer creates and returns the MeterPerformer that can produce kinds of metric Performer. + MeterPerformer(config MeterOption) MeterPerformer + + // ForceFlush flushes all pending metrics. + // + // This method honors the deadline or cancellation of ctx. An appropriate + // error will be returned in these situations. There is no guaranteed that all + // metrics be flushed or all resources have been released in these situations. + ForceFlush(ctx context.Context) error + + // Shutdown shuts down the Provider flushing all pending metrics and + // releasing any held computational resources. + Shutdown(ctx context.Context) error +} + +// MeterPerformer manages all Metric performers creating. +type MeterPerformer interface { + // CounterPerformer creates and returns a CounterPerformer that performs + // the operations for Counter metric. + CounterPerformer(name string, option MetricOption) (CounterPerformer, error) + + // UpDownCounterPerformer creates and returns a UpDownCounterPerformer that performs + // the operations for UpDownCounter metric. + UpDownCounterPerformer(name string, option MetricOption) (UpDownCounterPerformer, error) + + // HistogramPerformer creates and returns a HistogramPerformer that performs + // the operations for Histogram metric. + HistogramPerformer(name string, option MetricOption) (HistogramPerformer, error) + + // ObservableCounterPerformer creates and returns an ObservableCounterPerformer that performs + // the operations for ObservableCounter metric. + ObservableCounterPerformer(name string, option MetricOption) (ObservableCounterPerformer, error) + + // ObservableUpDownCounterPerformer creates and returns an ObservableUpDownCounterPerformer that performs + // the operations for ObservableUpDownCounter metric. + ObservableUpDownCounterPerformer(name string, option MetricOption) (ObservableUpDownCounterPerformer, error) + + // ObservableGaugePerformer creates and returns an ObservableGaugePerformer that performs + // the operations for ObservableGauge metric. + ObservableGaugePerformer(name string, option MetricOption) (ObservableGaugePerformer, error) + + // RegisterCallback registers callback on certain metrics. + // A callback is bound to certain component and version, it is called when the associated metrics are read. + // Multiple callbacks on the same component and version will be called by their registered sequence. + RegisterCallback(callback Callback, canBeCallbackMetrics ...ObservableMetric) error +} + +// MetricOption holds the basic options for creating a metric. +type MetricOption struct { + // Help provides information about this Histogram. + // This is an optional configuration for a metric. + Help string + + // Unit is the unit for metric value. + // This is an optional configuration for a metric. + Unit string + + // Attributes holds the constant key-value pair description metadata for this metric. + // This is an optional configuration for a metric. + Attributes Attributes + + // Buckets defines the buckets into which observations are counted. + // For Histogram metric only. + // A histogram metric uses default buckets if no explicit buckets configured. + Buckets []float64 + + // Callback function for metric, which is called when metric value changes. + // For observable metric only. + // If an observable metric has either Callback attribute nor global callback configured, it does nothing. + Callback MetricCallback +} + +// Metric models a single sample value with its metadata being exported. +type Metric interface { + // Info returns the basic information of a Metric. + Info() MetricInfo +} + +// MetricInfo exports information of the Metric. +type MetricInfo interface { + Key() string // Key returns the unique string key of the metric. + Name() string // Name returns the name of the metric. + Help() string // Help returns the help description of the metric. + Unit() string // Unit returns the unit name of the metric. + Type() MetricType // Type returns the type of the metric. + Attributes() Attributes // Attributes returns the constant attribute slice of the metric. + Instrument() InstrumentInfo // InstrumentInfo returns the instrument info of the metric. +} + +// InstrumentInfo exports the instrument information of a metric. +type InstrumentInfo interface { + Name() string // Name returns the instrument name of the metric. + Version() string // Version returns the instrument version of the metric. +} + +// Counter is a Metric that represents a single numerical value that can ever +// goes up. +type Counter interface { + Metric + CounterPerformer +} + +// CounterPerformer performs operations for Counter metric. +type CounterPerformer interface { + // Inc increments the counter by 1. Use Add to increment it by arbitrary + // non-negative values. + Inc(ctx context.Context, option ...Option) + + // Add adds the given value to the counter. It panics if the value is < 0. + Add(ctx context.Context, increment float64, option ...Option) +} + +// UpDownCounter is a Metric that represents a single numerical value that can ever +// goes up or down. +type UpDownCounter interface { + Metric + UpDownCounterPerformer +} + +// UpDownCounterPerformer performs operations for UpDownCounter metric. +type UpDownCounterPerformer interface { + // Inc increments the counter by 1. Use Add to increment it by arbitrary + // non-negative values. + Inc(ctx context.Context, option ...Option) + + // Dec decrements the Gauge by 1. Use Sub to decrement it by arbitrary values. + Dec(ctx context.Context, option ...Option) + + // Add adds the given value to the counter. It panics if the value is < 0. + Add(ctx context.Context, increment float64, option ...Option) +} + +// Histogram counts individual observations from an event or sample stream in +// configurable static buckets (or in dynamic sparse buckets as part of the +// experimental Native Histograms, see below for more details). Similar to a +// Summary, it also provides a sum of observations and an observation count. +type Histogram interface { + Metric + HistogramPerformer + + // Buckets returns the bucket slice of the Histogram. + Buckets() []float64 +} + +// HistogramPerformer performs operations for Histogram metric. +type HistogramPerformer interface { + // Record adds a single value to the histogram. + // The value is usually positive or zero. + Record(increment float64, option ...Option) +} + +// ObservableCounter is an instrument used to asynchronously +// record float64 measurements once per collection cycle. Observations are only +// made within a callback for this instrument. The value observed is assumed +// the to be the cumulative sum of the count. +type ObservableCounter interface { + Metric + ObservableCounterPerformer +} + +// ObservableUpDownCounter is used to synchronously record float64 measurements during a computational +// operation. +type ObservableUpDownCounter interface { + Metric + ObservableUpDownCounterPerformer +} + +// ObservableGauge is an instrument used to asynchronously record +// instantaneous float64 measurements once per collection cycle. Observations +// are only made within a callback for this instrument. +type ObservableGauge interface { + Metric + ObservableGaugePerformer +} + +type ( + // ObservableCounterPerformer is performer for observable ObservableCounter. + ObservableCounterPerformer = ObservableMetric + + // ObservableUpDownCounterPerformer is performer for observable ObservableUpDownCounter. + ObservableUpDownCounterPerformer = ObservableMetric + + // ObservableGaugePerformer is performer for observable ObservableGauge. + ObservableGaugePerformer = ObservableMetric +) + +// ObservableMetric is an instrument used to asynchronously record +// instantaneous float64 measurements once per collection cycle. +type ObservableMetric interface { + observable() +} + +// MetricInitializer manages the initialization for Metric. +// It is called internally in metric interface implements. +type MetricInitializer interface { + // Init initializes the Metric in Provider creation. + // It sets the metric performer which really takes action. + Init(provider Provider) error +} + +// PerformerExporter exports internal Performer of Metric. +// It is called internally in metric interface implements. +type PerformerExporter interface { + // Performer exports internal Performer of Metric. + // This is usually used by metric implements. + Performer() any +} + +// MetricCallback is automatically called when metric reader starts reading the metric value. +type MetricCallback func(ctx context.Context, obs MetricObserver) error + +// Callback is a function registered with a Meter that makes observations for +// the set of instruments it is registered with. The Observer parameter is used +// to record measurement observations for these instruments. +type Callback func(ctx context.Context, obs Observer) error + +// Observer sets the value for certain initialized Metric. +type Observer interface { + // Observe observes the value for certain initialized Metric. + // It adds the value to total result if the observed Metrics is type of Counter. + // It sets the value as the result if the observed Metrics is type of Gauge. + Observe(m ObservableMetric, value float64, option ...Option) +} + +// MetricObserver sets the value for bound Metric. +type MetricObserver interface { + // Observe observes the value for certain initialized Metric. + // It adds the value to total result if the observed Metrics is type of Counter. + // It sets the value as the result if the observed Metrics is type of Gauge. + Observe(value float64, option ...Option) +} + +var ( + // metrics stores all created Metric by current package. + allMetrics = make([]Metric, 0) +) + +// IsEnabled returns whether the metrics feature is enabled. +func IsEnabled() bool { + return globalProvider != nil +} + +// GetAllMetrics returns all Metric that created by current package. +func GetAllMetrics() []Metric { + return allMetrics +} diff --git a/os/gmetric/gmetric_attribute.go b/os/gmetric/gmetric_attribute.go new file mode 100644 index 000000000..85d5159b7 --- /dev/null +++ b/os/gmetric/gmetric_attribute.go @@ -0,0 +1,109 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gmetric + +import ( + "bytes" + "fmt" + "os" + + "github.com/gogf/gf/v2/internal/json" + "github.com/gogf/gf/v2/os/gfile" +) + +// Attributes is a slice of Attribute. +type Attributes []Attribute + +// Attribute is the key-value pair item for Metric. +type Attribute interface { + Key() string // The key for this attribute. + Value() any // The value for this attribute. +} + +// AttributeKey is the attribute key. +type AttributeKey string + +// Option holds the option for perform a metric operation. +type Option struct { + // Attributes holds the dynamic key-value pair metadata. + Attributes Attributes +} + +// localAttribute implements interface Attribute. +type localAttribute struct { + key string + value any +} + +var ( + hostname string + processPath string +) + +func init() { + hostname, _ = os.Hostname() + processPath = gfile.SelfPath() +} + +// CommonAttributes returns the common used attributes for an instrument. +func CommonAttributes() Attributes { + return Attributes{ + NewAttribute(`os.host.name`, hostname), + NewAttribute(`process.path`, processPath), + } +} + +// NewAttribute creates and returns an Attribute by given `key` and `value`. +func NewAttribute(key string, value any) Attribute { + return &localAttribute{ + key: key, + value: value, + } +} + +// Key returns the key of the attribute. +func (l *localAttribute) Key() string { + return l.key +} + +// Value returns the value of the attribute. +func (l *localAttribute) Value() any { + return l.value +} + +// MarshalJSON implements the interface MarshalJSON for json.Marshal. +func (l *localAttribute) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`{"%s":%#v}`, l.key, l.value)), nil +} + +// MarshalJSON implements the interface MarshalJSON for json.Marshal. +func (attrs Attributes) String() string { + bs, _ := attrs.MarshalJSON() + return string(bs) +} + +// MarshalJSON implements the interface MarshalJSON for json.Marshal. +func (attrs Attributes) MarshalJSON() ([]byte, error) { + var ( + bs []byte + err error + buffer = bytes.NewBuffer(nil) + ) + buffer.WriteByte('[') + for _, attr := range attrs { + bs, err = json.Marshal(attr) + if err != nil { + return nil, err + } + if buffer.Len() > 1 { + buffer.WriteByte(',') + } + buffer.Write(bs) + } + buffer.WriteByte(']') + return buffer.Bytes(), nil +} diff --git a/os/gmetric/gmetric_attribute_map.go b/os/gmetric/gmetric_attribute_map.go new file mode 100644 index 000000000..28aa1b12e --- /dev/null +++ b/os/gmetric/gmetric_attribute_map.go @@ -0,0 +1,49 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gmetric + +// AttributeMap contains the attribute key and value as map for easy filtering. +type AttributeMap map[string]any + +// Sets adds given attribute map to current map. +func (m AttributeMap) Sets(attrMap map[string]any) { + for k, v := range attrMap { + m[k] = v + } +} + +// Pick picks and returns attributes by given attribute keys. +func (m AttributeMap) Pick(keys ...string) Attributes { + var attrs = make(Attributes, 0) + for _, key := range keys { + value, ok := m[key] + if !ok { + continue + } + attrs = append(attrs, NewAttribute(key, value)) + } + return attrs +} + +// PickEx picks and returns attributes of which the given attribute keys does not in given `keys`. +func (m AttributeMap) PickEx(keys ...string) Attributes { + var ( + exKeyMap = make(map[string]struct{}) + attrs = make(Attributes, 0) + ) + for _, key := range keys { + exKeyMap[key] = struct{}{} + } + for k, v := range m { + _, ok := exKeyMap[k] + if ok { + continue + } + attrs = append(attrs, NewAttribute(k, v)) + } + return attrs +} diff --git a/os/gmetric/gmetric_global_attributes.go b/os/gmetric/gmetric_global_attributes.go new file mode 100644 index 000000000..cc4c7e646 --- /dev/null +++ b/os/gmetric/gmetric_global_attributes.go @@ -0,0 +1,80 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gmetric + +import ( + "sync" + + "github.com/gogf/gf/v2/text/gregex" +) + +// SetGlobalAttributesOption binds the global attributes to certain instrument. +type SetGlobalAttributesOption struct { + Instrument string // Instrument specifies the instrument name. + InstrumentVersion string // Instrument specifies the instrument version. + InstrumentPattern string // InstrumentPattern specifies instrument by regular expression on Instrument name. +} + +// GetGlobalAttributesOption binds the global attributes to certain instrument. +type GetGlobalAttributesOption struct { + Instrument string // Instrument specifies the instrument name. + InstrumentVersion string // Instrument specifies the instrument version. +} + +type globalAttributeItem struct { + Attributes + SetGlobalAttributesOption +} + +var ( + globalAttributesMu sync.Mutex + // globalAttributes stores the global attributes to a map. + globalAttributes = make([]globalAttributeItem, 0) +) + +// SetGlobalAttributes appends global attributes according `SetGlobalAttributesOption`. +// It appends global attributes to all metrics if given `SetGlobalAttributesOption` is empty. +// It appends global attributes to certain instrument by given `SetGlobalAttributesOption`. +func SetGlobalAttributes(attrs Attributes, option ...SetGlobalAttributesOption) { + globalAttributesMu.Lock() + defer globalAttributesMu.Unlock() + var usedOption SetGlobalAttributesOption + if len(option) > 0 { + usedOption = option[0] + } + globalAttributes = append( + globalAttributes, globalAttributeItem{ + Attributes: attrs, + SetGlobalAttributesOption: usedOption, + }, + ) +} + +// GetGlobalAttributes retrieves and returns the global attributes by `GetGlobalAttributesOption`. +// It returns the global attributes if given `GetGlobalAttributesOption` is empty. +// It returns global attributes of certain instrument if `GetGlobalAttributesOption` is not empty. +func GetGlobalAttributes(option GetGlobalAttributesOption) Attributes { + globalAttributesMu.Lock() + defer globalAttributesMu.Unlock() + var attributes = make(Attributes, 0) + for _, attrItem := range globalAttributes { + if option.InstrumentVersion != "" && attrItem.InstrumentVersion != option.InstrumentVersion { + continue + } + if attrItem.InstrumentPattern == "" { + if attrItem.Instrument != option.Instrument { + continue + } + } else { + if !gregex.IsMatchString(attrItem.InstrumentPattern, option.Instrument) { + continue + } + } + attributes = append(attributes, attrItem.Attributes...) + } + return attributes +} diff --git a/os/gmetric/gmetric_meter.go b/os/gmetric/gmetric_meter.go new file mode 100644 index 000000000..c83c7a066 --- /dev/null +++ b/os/gmetric/gmetric_meter.go @@ -0,0 +1,42 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gmetric + +// localMeter for Meter implements. +type localMeter struct { + MeterOption +} + +// MeterOption holds the creation option for a Meter. +type MeterOption struct { + // Instrument is the instrumentation name to bind this Metric to a global MeterProvider. + // This is an optional configuration for a metric. + Instrument string + + // InstrumentVersion is the instrumentation version to bind this Metric to a global MeterProvider. + // This is an optional configuration for a metric. + InstrumentVersion string + + // Attributes holds the constant key-value pair description metadata for all metrics of Meter. + // This is an optional configuration for a meter. + Attributes Attributes +} + +// newMeter creates and returns a Meter implementer. +func newMeter(option MeterOption) Meter { + return &localMeter{ + MeterOption: option, + } +} + +// Performer creates and returns the Performer of the Meter. +func (meter *localMeter) Performer() MeterPerformer { + if globalProvider == nil { + return nil + } + return globalProvider.MeterPerformer(meter.MeterOption) +} diff --git a/os/gmetric/gmetric_meter_callback.go b/os/gmetric/gmetric_meter_callback.go new file mode 100644 index 000000000..e563e3e2e --- /dev/null +++ b/os/gmetric/gmetric_meter_callback.go @@ -0,0 +1,51 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gmetric + +// CallbackItem is the global callback item registered. +type CallbackItem struct { + Callback Callback // Global callback. + Metrics []ObservableMetric // Callback on certain metrics. + MeterOption MeterOption // MeterOption is the option that the meter holds. + Provider Provider // Provider is the Provider that the callback item is bound to. +} + +var ( + // Registered callbacks. + globalCallbackItems = make([]CallbackItem, 0) +) + +// RegisterCallback registers callback on certain metrics. +// A callback is bound to certain component and version, it is called when the associated metrics are read. +// Multiple callbacks on the same component and version will be called by their registered sequence. +func (meter *localMeter) RegisterCallback(callback Callback, observableMetrics ...ObservableMetric) error { + if len(observableMetrics) == 0 { + return nil + } + globalCallbackItems = append(globalCallbackItems, CallbackItem{ + Callback: callback, + Metrics: observableMetrics, + MeterOption: meter.MeterOption, + }) + return nil +} + +// MustRegisterCallback performs as RegisterCallback, but it panics if any error occurs. +func (meter *localMeter) MustRegisterCallback(callback Callback, observableMetrics ...ObservableMetric) { + err := meter.RegisterCallback(callback, observableMetrics...) + if err != nil { + panic(err) + } +} + +// GetRegisteredCallbacks retrieves and returns the registered global callbacks. +// It truncates the callback slice is the callbacks are returned. +func GetRegisteredCallbacks() []CallbackItem { + items := globalCallbackItems + globalCallbackItems = globalCallbackItems[:0] + return items +} diff --git a/os/gmetric/gmetric_meter_counter.go b/os/gmetric/gmetric_meter_counter.go new file mode 100644 index 000000000..464daf0d8 --- /dev/null +++ b/os/gmetric/gmetric_meter_counter.go @@ -0,0 +1,72 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gmetric + +// localCounter is the local implements for interface Counter. +type localCounter struct { + Metric + MeterOption + MetricOption + CounterPerformer +} + +var ( + // Check the implements for interface MetricInitializer. + _ MetricInitializer = (*localCounter)(nil) + // Check the implements for interface PerformerExporter. + _ PerformerExporter = (*localCounter)(nil) +) + +// Counter creates and returns a new Counter. +func (meter *localMeter) Counter(name string, option MetricOption) (Counter, error) { + m, err := meter.newMetric(MetricTypeCounter, name, option) + if err != nil { + return nil, err + } + counter := &localCounter{ + Metric: m, + MeterOption: meter.MeterOption, + MetricOption: option, + CounterPerformer: newNoopCounterPerformer(), + } + if globalProvider != nil { + if err = counter.Init(globalProvider); err != nil { + return nil, err + } + } + allMetrics = append(allMetrics, counter) + return counter, nil +} + +// MustCounter creates and returns a new Counter. +// It panics if any error occurs. +func (meter *localMeter) MustCounter(name string, option MetricOption) Counter { + m, err := meter.Counter(name, option) + if err != nil { + panic(err) + } + return m +} + +// Init initializes the Metric in Provider creation. +func (l *localCounter) Init(provider Provider) (err error) { + if _, ok := l.CounterPerformer.(noopCounterPerformer); !ok { + // already initialized. + return + } + l.CounterPerformer, err = provider.MeterPerformer(l.MeterOption).CounterPerformer( + l.Info().Name(), + l.MetricOption, + ) + return +} + +// Performer implements interface PerformerExporter, which exports internal Performer of Metric. +// This is usually used by metric implements. +func (l *localCounter) Performer() any { + return l.CounterPerformer +} diff --git a/os/gmetric/gmetric_meter_histogram.go b/os/gmetric/gmetric_meter_histogram.go new file mode 100644 index 000000000..0e42087f7 --- /dev/null +++ b/os/gmetric/gmetric_meter_histogram.go @@ -0,0 +1,77 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gmetric + +// localHistogram is the local implements for interface Histogram. +type localHistogram struct { + Metric + MeterOption + MetricOption + HistogramPerformer +} + +var ( + // Check the implements for interface MetricInitializer. + _ MetricInitializer = (*localHistogram)(nil) + // Check the implements for interface PerformerExporter. + _ PerformerExporter = (*localHistogram)(nil) +) + +// Histogram creates and returns a new Histogram. +func (meter *localMeter) Histogram(name string, option MetricOption) (Histogram, error) { + m, err := meter.newMetric(MetricTypeHistogram, name, option) + if err != nil { + return nil, err + } + histogram := &localHistogram{ + Metric: m, + MeterOption: meter.MeterOption, + MetricOption: option, + HistogramPerformer: newNoopHistogramPerformer(), + } + if globalProvider != nil { + if err = histogram.Init(globalProvider); err != nil { + return nil, err + } + } + allMetrics = append(allMetrics, histogram) + return histogram, nil +} + +// MustHistogram creates and returns a new Histogram. +// It panics if any error occurs. +func (meter *localMeter) MustHistogram(name string, option MetricOption) Histogram { + m, err := meter.Histogram(name, option) + if err != nil { + panic(err) + } + return m +} + +// Init initializes the Metric in Provider creation. +func (l *localHistogram) Init(provider Provider) (err error) { + if _, ok := l.HistogramPerformer.(noopHistogramPerformer); !ok { + // already initialized. + return + } + l.HistogramPerformer, err = provider.MeterPerformer(l.MeterOption).HistogramPerformer( + l.Info().Name(), + l.MetricOption, + ) + return err +} + +// Buckets returns the bucket slice of the Histogram. +func (l *localHistogram) Buckets() []float64 { + return l.MetricOption.Buckets +} + +// Performer implements interface PerformerExporter, which exports internal Performer of Metric. +// This is usually used by metric implements. +func (l *localHistogram) Performer() any { + return l.HistogramPerformer +} diff --git a/os/gmetric/gmetric_meter_metric_info.go b/os/gmetric/gmetric_meter_metric_info.go new file mode 100644 index 000000000..8ae2a7990 --- /dev/null +++ b/os/gmetric/gmetric_meter_metric_info.go @@ -0,0 +1,78 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gmetric + +import "fmt" + +// localMetricInfo implements interface MetricInfo. +type localMetricInfo struct { + MetricType + MetricOption + InstrumentInfo + MetricName string +} + +// newMetricInfo creates and returns a MetricInfo. +func (meter *localMeter) newMetricInfo( + metricType MetricType, metricName string, metricOption MetricOption, +) MetricInfo { + return &localMetricInfo{ + MetricName: metricName, + MetricType: metricType, + MetricOption: metricOption, + InstrumentInfo: meter.newInstrumentInfo(), + } +} + +// Name returns the name of the metric. +func (l *localMetricInfo) Name() string { + return l.MetricName +} + +// Help returns the help description of the metric. +func (l *localMetricInfo) Help() string { + return l.MetricOption.Help +} + +// Unit returns the unit name of the metric. +func (l *localMetricInfo) Unit() string { + return l.MetricOption.Unit +} + +// Type returns the type of the metric. +func (l *localMetricInfo) Type() MetricType { + return l.MetricType +} + +// Attributes returns the constant attribute slice of the metric. +func (l *localMetricInfo) Attributes() Attributes { + return l.MetricOption.Attributes +} + +// Instrument returns the instrument info of the metric. +func (l *localMetricInfo) Instrument() InstrumentInfo { + return l.InstrumentInfo +} + +func (l *localMetricInfo) Key() string { + if l.Instrument().Name() != "" && l.Instrument().Version() != "" { + return fmt.Sprintf( + `%s@%s:%s`, + l.Instrument().Name(), + l.Instrument().Version(), + l.Name(), + ) + } + if l.Instrument().Name() != "" && l.Instrument().Version() == "" { + return fmt.Sprintf( + `%s:%s`, + l.Instrument().Name(), + l.Name(), + ) + } + return l.Name() +} diff --git a/os/gmetric/gmetric_meter_metric_instrument.go b/os/gmetric/gmetric_meter_metric_instrument.go new file mode 100644 index 000000000..a93284bbe --- /dev/null +++ b/os/gmetric/gmetric_meter_metric_instrument.go @@ -0,0 +1,31 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gmetric + +// localMetricInstrument implements interface MetricInstrument. +type localInstrumentInfo struct { + name string + version string +} + +// newInstrumentInfo creates and returns a MetricInstrument. +func (meter *localMeter) newInstrumentInfo() InstrumentInfo { + return &localInstrumentInfo{ + name: meter.Instrument, + version: meter.InstrumentVersion, + } +} + +// Name returns the instrument name of the metric. +func (l *localInstrumentInfo) Name() string { + return l.name +} + +// Version returns the instrument version of the metric. +func (l *localInstrumentInfo) Version() string { + return l.version +} diff --git a/os/gmetric/gmetric_meter_observable_counter.go b/os/gmetric/gmetric_meter_observable_counter.go new file mode 100644 index 000000000..be83cd951 --- /dev/null +++ b/os/gmetric/gmetric_meter_observable_counter.go @@ -0,0 +1,72 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gmetric + +// localObservableCounter is the local implements for interface ObservableCounter. +type localObservableCounter struct { + Metric + MeterOption + MetricOption + ObservableCounterPerformer +} + +var ( + // Check the implements for interface MetricInitializer. + _ MetricInitializer = (*localObservableCounter)(nil) + // Check the implements for interface PerformerExporter. + _ PerformerExporter = (*localObservableCounter)(nil) +) + +// ObservableCounter creates and returns a new ObservableCounter. +func (meter *localMeter) ObservableCounter(name string, option MetricOption) (ObservableCounter, error) { + m, err := meter.newMetric(MetricTypeObservableCounter, name, option) + if err != nil { + return nil, err + } + observableCounter := &localObservableCounter{ + Metric: m, + MeterOption: meter.MeterOption, + MetricOption: option, + ObservableCounterPerformer: newNoopObservableCounterPerformer(), + } + if globalProvider != nil { + if err = observableCounter.Init(globalProvider); err != nil { + return nil, err + } + } + allMetrics = append(allMetrics, observableCounter) + return observableCounter, nil +} + +// MustObservableCounter creates and returns a new ObservableCounter. +// It panics if any error occurs. +func (meter *localMeter) MustObservableCounter(name string, option MetricOption) ObservableCounter { + m, err := meter.ObservableCounter(name, option) + if err != nil { + panic(err) + } + return m +} + +// Init initializes the Metric in Provider creation. +func (l *localObservableCounter) Init(provider Provider) (err error) { + if _, ok := l.ObservableCounterPerformer.(noopObservableCounterPerformer); !ok { + // already initialized. + return + } + l.ObservableCounterPerformer, err = provider.MeterPerformer(l.MeterOption).ObservableCounterPerformer( + l.Info().Name(), + l.MetricOption, + ) + return err +} + +// Performer implements interface PerformerExporter, which exports internal Performer of Metric. +// This is usually used by metric implements. +func (l *localObservableCounter) Performer() any { + return l.ObservableCounterPerformer +} diff --git a/os/gmetric/gmetric_meter_observable_gauge.go b/os/gmetric/gmetric_meter_observable_gauge.go new file mode 100644 index 000000000..d01651f1f --- /dev/null +++ b/os/gmetric/gmetric_meter_observable_gauge.go @@ -0,0 +1,72 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gmetric + +// localObservableGauge is the local implements for interface ObservableGauge. +type localObservableGauge struct { + Metric + MeterOption + MetricOption + ObservableGaugePerformer +} + +var ( + // Check the implements for interface MetricInitializer. + _ MetricInitializer = (*localObservableGauge)(nil) + // Check the implements for interface PerformerExporter. + _ PerformerExporter = (*localObservableGauge)(nil) +) + +// ObservableGauge creates and returns a new ObservableGauge. +func (meter *localMeter) ObservableGauge(name string, option MetricOption) (ObservableGauge, error) { + m, err := meter.newMetric(MetricTypeObservableGauge, name, option) + if err != nil { + return nil, err + } + observableGauge := &localObservableGauge{ + Metric: m, + MeterOption: meter.MeterOption, + MetricOption: option, + ObservableGaugePerformer: newNoopObservableGaugePerformer(), + } + if globalProvider != nil { + if err = observableGauge.Init(globalProvider); err != nil { + return nil, err + } + } + allMetrics = append(allMetrics, observableGauge) + return observableGauge, nil +} + +// MustObservableGauge creates and returns a new ObservableGauge. +// It panics if any error occurs. +func (meter *localMeter) MustObservableGauge(name string, option MetricOption) ObservableGauge { + m, err := meter.ObservableGauge(name, option) + if err != nil { + panic(err) + } + return m +} + +// Init initializes the Metric in Provider creation. +func (l *localObservableGauge) Init(provider Provider) (err error) { + if _, ok := l.ObservableGaugePerformer.(noopObservableGaugePerformer); !ok { + // already initialized. + return + } + l.ObservableGaugePerformer, err = provider.MeterPerformer(l.MeterOption).ObservableGaugePerformer( + l.Info().Name(), + l.MetricOption, + ) + return err +} + +// Performer implements interface PerformerExporter, which exports internal Performer of Metric. +// This is usually used by metric implements. +func (l *localObservableGauge) Performer() any { + return l.ObservableGaugePerformer +} diff --git a/os/gmetric/gmetric_meter_observable_updown_counter.go b/os/gmetric/gmetric_meter_observable_updown_counter.go new file mode 100644 index 000000000..afd89c832 --- /dev/null +++ b/os/gmetric/gmetric_meter_observable_updown_counter.go @@ -0,0 +1,72 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gmetric + +// localObservableUpDownCounter is the local implements for interface ObservableUpDownCounter. +type localObservableUpDownCounter struct { + Metric + MeterOption + MetricOption + ObservableUpDownCounterPerformer +} + +var ( + // Check the implements for interface MetricInitializer. + _ MetricInitializer = (*localObservableUpDownCounter)(nil) + // Check the implements for interface PerformerExporter. + _ PerformerExporter = (*localObservableUpDownCounter)(nil) +) + +// ObservableUpDownCounter creates and returns a new ObservableUpDownCounter. +func (meter *localMeter) ObservableUpDownCounter(name string, option MetricOption) (ObservableUpDownCounter, error) { + m, err := meter.newMetric(MetricTypeObservableUpDownCounter, name, option) + if err != nil { + return nil, err + } + observableUpDownCounter := &localObservableUpDownCounter{ + Metric: m, + MeterOption: meter.MeterOption, + MetricOption: option, + ObservableUpDownCounterPerformer: newNoopObservableUpDownCounterPerformer(), + } + if globalProvider != nil { + if err = observableUpDownCounter.Init(globalProvider); err != nil { + return nil, err + } + } + allMetrics = append(allMetrics, observableUpDownCounter) + return observableUpDownCounter, nil +} + +// MustObservableUpDownCounter creates and returns a new ObservableUpDownCounter. +// It panics if any error occurs. +func (meter *localMeter) MustObservableUpDownCounter(name string, option MetricOption) ObservableUpDownCounter { + m, err := meter.ObservableCounter(name, option) + if err != nil { + panic(err) + } + return m +} + +// Init initializes the Metric in Provider creation. +func (l *localObservableUpDownCounter) Init(provider Provider) (err error) { + if _, ok := l.ObservableUpDownCounterPerformer.(noopObservableUpDownCounterPerformer); !ok { + // already initialized. + return + } + l.ObservableUpDownCounterPerformer, err = provider.MeterPerformer(l.MeterOption).ObservableUpDownCounterPerformer( + l.Info().Name(), + l.MetricOption, + ) + return err +} + +// Performer implements interface PerformerExporter, which exports internal Performer of Metric. +// This is usually used by metric implements. +func (l *localObservableUpDownCounter) Performer() any { + return l.ObservableUpDownCounterPerformer +} diff --git a/os/gmetric/gmetric_meter_updown_counter.go b/os/gmetric/gmetric_meter_updown_counter.go new file mode 100644 index 000000000..1ad141b3d --- /dev/null +++ b/os/gmetric/gmetric_meter_updown_counter.go @@ -0,0 +1,72 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gmetric + +// localUpDownCounter is the local implements for interface UpDownCounter. +type localUpDownCounter struct { + Metric + MeterOption + MetricOption + UpDownCounterPerformer +} + +var ( + // Check the implements for interface MetricInitializer. + _ MetricInitializer = (*localUpDownCounter)(nil) + // Check the implements for interface PerformerExporter. + _ PerformerExporter = (*localUpDownCounter)(nil) +) + +// UpDownCounter creates and returns a new Counter. +func (meter *localMeter) UpDownCounter(name string, option MetricOption) (UpDownCounter, error) { + m, err := meter.newMetric(MetricTypeUpDownCounter, name, option) + if err != nil { + return nil, err + } + updownCounter := &localUpDownCounter{ + Metric: m, + MeterOption: meter.MeterOption, + MetricOption: option, + UpDownCounterPerformer: newNoopUpDownCounterPerformer(), + } + if globalProvider != nil { + if err = updownCounter.Init(globalProvider); err != nil { + return nil, err + } + } + allMetrics = append(allMetrics, updownCounter) + return updownCounter, nil +} + +// MustUpDownCounter creates and returns a new Counter. +// It panics if any error occurs. +func (meter *localMeter) MustUpDownCounter(name string, option MetricOption) UpDownCounter { + m, err := meter.UpDownCounter(name, option) + if err != nil { + panic(err) + } + return m +} + +// Init initializes the Metric in Provider creation. +func (l *localUpDownCounter) Init(provider Provider) (err error) { + if _, ok := l.UpDownCounterPerformer.(noopUpDownCounterPerformer); !ok { + // already initialized. + return + } + l.UpDownCounterPerformer, err = provider.MeterPerformer(l.MeterOption).UpDownCounterPerformer( + l.Info().Name(), + l.MetricOption, + ) + return +} + +// Performer implements interface PerformerExporter, which exports internal Performer of Metric. +// This is usually used by metric implements. +func (l *localUpDownCounter) Performer() any { + return l.UpDownCounterPerformer +} diff --git a/os/gmetric/gmetric_metric.go b/os/gmetric/gmetric_metric.go new file mode 100644 index 000000000..f4c9043a3 --- /dev/null +++ b/os/gmetric/gmetric_metric.go @@ -0,0 +1,47 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gmetric + +import ( + "github.com/gogf/gf/v2/encoding/gjson" + "github.com/gogf/gf/v2/errors/gcode" + "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/text/gregex" +) + +// localMetric implements interface Metric. +type localMetric struct { + MetricInfo +} + +// newMetric creates and returns an object that implements interface Metric. +func (meter *localMeter) newMetric( + metricType MetricType, metricName string, metricOption MetricOption, +) (Metric, error) { + if metricName == "" { + return nil, gerror.NewCodef( + gcode.CodeInvalidParameter, + `error creating %s metric while given name is empty, option: %s`, + metricType, gjson.MustEncodeString(metricOption), + ) + } + if !gregex.IsMatchString(MetricNamePattern, metricName) { + return nil, gerror.NewCodef( + gcode.CodeInvalidParameter, + `invalid metric name "%s", should match regular expression pattern "%s"`, + metricName, MetricNamePattern, + ) + } + return &localMetric{ + MetricInfo: meter.newMetricInfo(metricType, metricName, metricOption), + }, nil +} + +// Info returns the basic information of a Metric. +func (l *localMetric) Info() MetricInfo { + return l.MetricInfo +} diff --git a/os/gmetric/gmetric_noop_counter_performer.go b/os/gmetric/gmetric_noop_counter_performer.go new file mode 100644 index 000000000..42ea91b84 --- /dev/null +++ b/os/gmetric/gmetric_noop_counter_performer.go @@ -0,0 +1,23 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gmetric + +import "context" + +// noopCounterPerformer is an implementer for interface CounterPerformer with no truly operations. +type noopCounterPerformer struct{} + +// newNoopCounterPerformer creates and returns a CounterPerformer with no truly operations. +func newNoopCounterPerformer() CounterPerformer { + return noopCounterPerformer{} +} + +// Inc increments the counter by 1. +func (noopCounterPerformer) Inc(ctx context.Context, option ...Option) {} + +// Add adds the given value to the counter. It panics if the value is < 0. +func (noopCounterPerformer) Add(ctx context.Context, increment float64, option ...Option) {} diff --git a/os/gmetric/gmetric_noop_histogram_performer.go b/os/gmetric/gmetric_noop_histogram_performer.go new file mode 100644 index 000000000..8a71600b7 --- /dev/null +++ b/os/gmetric/gmetric_noop_histogram_performer.go @@ -0,0 +1,18 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gmetric + +// noopHistogramPerformer is an implementer for interface HistogramPerformer with no truly operations. +type noopHistogramPerformer struct{} + +// newNoopHistogramPerformer creates and returns a HistogramPerformer with no truly operations. +func newNoopHistogramPerformer() HistogramPerformer { + return noopHistogramPerformer{} +} + +// Record adds a single value to the histogram. The value is usually positive or zero. +func (noopHistogramPerformer) Record(increment float64, option ...Option) {} diff --git a/os/gmetric/gmetric_noop_observable_counter_performer.go b/os/gmetric/gmetric_noop_observable_counter_performer.go new file mode 100644 index 000000000..f12b6e5f6 --- /dev/null +++ b/os/gmetric/gmetric_noop_observable_counter_performer.go @@ -0,0 +1,17 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gmetric + +// noopObservableCounterPerformer is an implementer for interface ObservableCounterPerformer with no truly operations. +type noopObservableCounterPerformer struct{} + +// newNoopObservableCounterPerformer creates and returns a ObservableCounterPerformer with no truly operations. +func newNoopObservableCounterPerformer() ObservableCounterPerformer { + return noopObservableCounterPerformer{} +} + +func (noopObservableCounterPerformer) observable() {} diff --git a/os/gmetric/gmetric_noop_observable_gauge_performer.go b/os/gmetric/gmetric_noop_observable_gauge_performer.go new file mode 100644 index 000000000..aca1a6e00 --- /dev/null +++ b/os/gmetric/gmetric_noop_observable_gauge_performer.go @@ -0,0 +1,17 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gmetric + +// noopObservableGaugePerformer is an implementer for interface ObservableGaugePerformer with no truly operations. +type noopObservableGaugePerformer struct{} + +// newNoopObservableGaugePerformer creates and returns a ObservableGaugePerformer with no truly operations. +func newNoopObservableGaugePerformer() ObservableGaugePerformer { + return noopObservableGaugePerformer{} +} + +func (noopObservableGaugePerformer) observable() {} diff --git a/os/gmetric/gmetric_noop_observable_updown_counter_performer.go b/os/gmetric/gmetric_noop_observable_updown_counter_performer.go new file mode 100644 index 000000000..cad14009e --- /dev/null +++ b/os/gmetric/gmetric_noop_observable_updown_counter_performer.go @@ -0,0 +1,19 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gmetric + +// noopObservableUpDownCounterPerformer is an implementer for interface ObservableUpDownCounterPerformer +// with no truly operations. +type noopObservableUpDownCounterPerformer struct{} + +// newNoopObservableUpDownCounterPerformer creates and returns a ObservableUpDownCounterPerformer +// with no truly operations. +func newNoopObservableUpDownCounterPerformer() ObservableUpDownCounterPerformer { + return noopObservableUpDownCounterPerformer{} +} + +func (noopObservableUpDownCounterPerformer) observable() {} diff --git a/os/gmetric/gmetric_noop_updown_counter_performer.go b/os/gmetric/gmetric_noop_updown_counter_performer.go new file mode 100644 index 000000000..69b302768 --- /dev/null +++ b/os/gmetric/gmetric_noop_updown_counter_performer.go @@ -0,0 +1,26 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gmetric + +import "context" + +// noopUpDownCounterPerformer is an implementer for interface CounterPerformer with no truly operations. +type noopUpDownCounterPerformer struct{} + +// newNoopUpDownCounterPerformer creates and returns a CounterPerformer with no truly operations. +func newNoopUpDownCounterPerformer() UpDownCounterPerformer { + return noopUpDownCounterPerformer{} +} + +// Inc increments the counter by 1. +func (noopUpDownCounterPerformer) Inc(ctx context.Context, option ...Option) {} + +// Dec decrements the counter by 1. +func (noopUpDownCounterPerformer) Dec(ctx context.Context, option ...Option) {} + +// Add adds the given value to the counter. +func (noopUpDownCounterPerformer) Add(ctx context.Context, increment float64, option ...Option) {} diff --git a/os/gmetric/gmetric_provider.go b/os/gmetric/gmetric_provider.go new file mode 100644 index 000000000..736a8d51a --- /dev/null +++ b/os/gmetric/gmetric_provider.go @@ -0,0 +1,91 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gmetric + +// GlobalProvider hold the entry for creating Meter and Metric. +// The GlobalProvider has only one function for Meter creating, which is designed for convenient usage. +type GlobalProvider interface { + // Meter creates and returns the Meter by given MeterOption. + Meter(option MeterOption) Meter +} + +// Meter hold the functions for kinds of Metric creating. +type Meter interface { + // Counter creates and returns a new Counter. + Counter(name string, option MetricOption) (Counter, error) + + // UpDownCounter creates and returns a new UpDownCounter. + UpDownCounter(name string, option MetricOption) (UpDownCounter, error) + + // Histogram creates and returns a new Histogram. + Histogram(name string, option MetricOption) (Histogram, error) + + // ObservableCounter creates and returns a new ObservableCounter. + ObservableCounter(name string, option MetricOption) (ObservableCounter, error) + + // ObservableUpDownCounter creates and returns a new ObservableUpDownCounter. + ObservableUpDownCounter(name string, option MetricOption) (ObservableUpDownCounter, error) + + // ObservableGauge creates and returns a new ObservableGauge. + ObservableGauge(name string, option MetricOption) (ObservableGauge, error) + + // MustCounter creates and returns a new Counter. + // It panics if any error occurs. + MustCounter(name string, option MetricOption) Counter + + // MustUpDownCounter creates and returns a new UpDownCounter. + // It panics if any error occurs. + MustUpDownCounter(name string, option MetricOption) UpDownCounter + + // MustHistogram creates and returns a new Histogram. + // It panics if any error occurs. + MustHistogram(name string, option MetricOption) Histogram + + // MustObservableCounter creates and returns a new ObservableCounter. + // It panics if any error occurs. + MustObservableCounter(name string, option MetricOption) ObservableCounter + + // MustObservableUpDownCounter creates and returns a new ObservableUpDownCounter. + // It panics if any error occurs. + MustObservableUpDownCounter(name string, option MetricOption) ObservableUpDownCounter + + // MustObservableGauge creates and returns a new ObservableGauge. + // It panics if any error occurs. + MustObservableGauge(name string, option MetricOption) ObservableGauge + + // RegisterCallback registers callback on certain metrics. + // A callback is bound to certain component and version, it is called when the associated metrics are read. + // Multiple callbacks on the same component and version will be called by their registered sequence. + RegisterCallback(callback Callback, canBeCallbackMetrics ...ObservableMetric) error + + // MustRegisterCallback performs as RegisterCallback, but it panics if any error occurs. + MustRegisterCallback(callback Callback, canBeCallbackMetrics ...ObservableMetric) +} + +type localGlobalProvider struct { +} + +var ( + // globalProvider is the provider for global usage. + globalProvider Provider +) + +// GetGlobalProvider retrieves the GetGlobalProvider instance. +func GetGlobalProvider() GlobalProvider { + return &localGlobalProvider{} +} + +// SetGlobalProvider registers `provider` as the global Provider, +// which means the following metrics creating will be base on the global provider. +func SetGlobalProvider(provider Provider) { + globalProvider = provider +} + +// Meter creates and returns the Meter by given MeterOption. +func (l *localGlobalProvider) Meter(option MeterOption) Meter { + return newMeter(option) +} diff --git a/os/gmetric/gmetric_z_unit_internal_test.go b/os/gmetric/gmetric_z_unit_internal_test.go new file mode 100644 index 000000000..34aafff9d --- /dev/null +++ b/os/gmetric/gmetric_z_unit_internal_test.go @@ -0,0 +1,112 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gmetric + +import ( + "testing" + + "github.com/gogf/gf/v2/test/gtest" +) + +func truncateGlobalAttributes() { + globalAttributesMu.Lock() + defer globalAttributesMu.Unlock() + globalAttributes = make([]globalAttributeItem, 0) +} + +func Test_GlobalAttributes(t *testing.T) { + defer truncateGlobalAttributes() + gtest.C(t, func(t *gtest.T) { + SetGlobalAttributes(Attributes{ + NewAttribute("global", "gl"), + }, SetGlobalAttributesOption{ + Instrument: "", + InstrumentVersion: "", + InstrumentPattern: "", + }) + + SetGlobalAttributes(Attributes{ + NewAttribute("a", 1), + }, SetGlobalAttributesOption{ + Instrument: "ins_a", + InstrumentVersion: "v1.0", + InstrumentPattern: "", + }) + SetGlobalAttributes(Attributes{ + NewAttribute("b", 2), + }, SetGlobalAttributesOption{ + Instrument: "ins_bb", + InstrumentVersion: "v1.1", + InstrumentPattern: "", + }) + SetGlobalAttributes(Attributes{ + NewAttribute("c", 3), + }, SetGlobalAttributesOption{ + Instrument: "ins_bb", + InstrumentVersion: "v1.1", + InstrumentPattern: "", + }) + SetGlobalAttributes(Attributes{ + NewAttribute("d", 4), + }, SetGlobalAttributesOption{ + Instrument: "", + InstrumentVersion: "v1.0", + InstrumentPattern: "ins.+", + }) + SetGlobalAttributes(Attributes{ + NewAttribute("e", 5), + }, SetGlobalAttributesOption{ + Instrument: "", + InstrumentVersion: "v1.0", + InstrumentPattern: "ins_b.+", + }) + SetGlobalAttributes(Attributes{ + NewAttribute("f", 6), + }, SetGlobalAttributesOption{ + Instrument: "", + InstrumentVersion: "v1.1", + InstrumentPattern: "ins_b.+", + }) + + t.Assert(GetGlobalAttributes(GetGlobalAttributesOption{ + Instrument: "", + InstrumentVersion: "", + }), Attributes{ + NewAttribute("global", "gl"), + }) + t.Assert(GetGlobalAttributes(GetGlobalAttributesOption{ + Instrument: "ins_a", + InstrumentVersion: "", + }), Attributes{ + NewAttribute("a", 1), + NewAttribute("d", 4), + }) + t.Assert(GetGlobalAttributes(GetGlobalAttributesOption{ + Instrument: "ins_a", + InstrumentVersion: "1.1", + }), Attributes{}) + t.Assert(GetGlobalAttributes(GetGlobalAttributesOption{ + Instrument: "ins_bb", + InstrumentVersion: "v1.0", + }), Attributes{ + NewAttribute("d", 4), + NewAttribute("e", 5), + }) + t.Assert(GetGlobalAttributes(GetGlobalAttributesOption{ + Instrument: "ins_bb", + InstrumentVersion: "v1.1", + }), Attributes{ + NewAttribute("b", 2), + NewAttribute("c", 3), + NewAttribute("f", 6), + }) + t.Assert(GetGlobalAttributes(GetGlobalAttributesOption{ + Instrument: "ins_cc", + InstrumentVersion: "v1.1", + }), Attributes{}) + }) +} diff --git a/os/gmetric/gmetric_z_unit_test.go b/os/gmetric/gmetric_z_unit_test.go new file mode 100644 index 000000000..ca225eed6 --- /dev/null +++ b/os/gmetric/gmetric_z_unit_test.go @@ -0,0 +1,79 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gmetric_test + +import ( + "fmt" + "testing" + + "github.com/gogf/gf/v2/os/gmetric" + "github.com/gogf/gf/v2/test/gtest" +) + +func Test_Counter(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + var ( + meterOption = gmetric.MeterOption{ + Instrument: "github.com/gogf/gf/example/metric/basic", + InstrumentVersion: "v1.0", + } + metricName = "goframe.metric.demo.counter" + metricOption = gmetric.MetricOption{ + Help: "This is a simple demo for Counter usage", + Unit: "%", + Attributes: gmetric.Attributes{ + gmetric.NewAttribute("const_label_a", 1), + }, + } + meter = gmetric.GetGlobalProvider().Meter(meterOption) + counter = meter.MustCounter(metricName, metricOption) + ) + t.Assert(counter.Info().Name(), metricName) + t.Assert(counter.Info().Help(), metricOption.Help) + t.Assert(counter.Info().Unit(), metricOption.Unit) + t.Assert(counter.Info().Attributes(), metricOption.Attributes) + t.Assert(counter.Info().Instrument().Name(), meterOption.Instrument) + t.Assert(counter.Info().Instrument().Version(), meterOption.InstrumentVersion) + }) +} + +func Test_Histogram(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + var ( + meterOption = gmetric.MeterOption{ + Instrument: "github.com/gogf/gf/example/metric/basic", + InstrumentVersion: "v1.0", + } + metricName = "goframe.metric.demo.histogram" + metricOption = gmetric.MetricOption{ + Help: "This is a simple demo for Histogram usage", + Unit: "%", + Attributes: gmetric.Attributes{ + gmetric.NewAttribute("const_label_a", 1), + }, + Buckets: []float64{0, 10, 20, 50, 100, 500, 1000, 2000, 5000, 10000}, + } + meter = gmetric.GetGlobalProvider().Meter(meterOption) + histogram = meter.MustHistogram(metricName, metricOption) + ) + t.Assert(histogram.Info().Name(), metricName) + t.Assert(histogram.Info().Help(), metricOption.Help) + t.Assert(histogram.Info().Unit(), metricOption.Unit) + t.Assert(histogram.Info().Attributes(), metricOption.Attributes) + t.Assert(histogram.Info().Instrument().Name(), meterOption.Instrument) + t.Assert(histogram.Info().Instrument().Version(), meterOption.InstrumentVersion) + t.Assert(histogram.Buckets(), metricOption.Buckets) + }) +} + +func Test_CommonAttributes(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + commonAttributes := gmetric.CommonAttributes() + t.AssertGT(len(commonAttributes), 1) + fmt.Println(commonAttributes) + }) +}