├── auth ├── go.work ├── user │ ├── repository.go │ ├── entity.go │ ├── mocks │ │ ├── UseCase.go │ │ └── Repository.go │ ├── service_test.go │ ├── mysql │ │ └── mysql.go │ └── service.go ├── Dockerfile ├── ops │ └── db │ │ └── init.sql ├── security │ └── jwt.go ├── go.mod ├── main.go └── go.work.sum ├── votes ├── go.work ├── vote │ ├── repository.go │ ├── entity.go │ ├── service.go │ └── mysql │ │ └── mysql.go ├── ops │ └── db │ │ └── init.sql ├── Dockerfile ├── go.mod ├── main.go └── go.work.sum ├── feedbacks ├── go.work ├── feedback │ ├── repository.go │ ├── entity.go │ ├── service.go │ └── mysql │ │ └── mysql.go ├── ops │ └── db │ │ └── init.sql ├── Dockerfile ├── go.mod ├── main.go └── go.work.sum ├── .dockerignore ├── .gitignore ├── ops ├── otelcollector │ ├── otelcol-config-extras.yml │ └── otelcol-config.yml ├── grafana │ └── provisioning │ │ ├── datasources │ │ ├── jaeger.yaml │ │ └── default.yaml │ │ └── dashboards │ │ ├── general.yaml │ │ └── general │ │ ├── demo-dashboard.json │ │ ├── spanmetrics-dashboard.json │ │ └── opentelemetry-collector.json └── prometheus │ └── prometheus-config.yaml ├── internal ├── telemetry │ ├── telemetry.go │ ├── mocks │ │ ├── Telemetry.go │ │ └── Span.go │ ├── jaeger.go │ └── opentelemetry.go └── middleware │ └── is_authenticated.go ├── .env ├── go.mod ├── Makefile ├── README.md └── docker-compose.yml /auth/go.work: -------------------------------------------------------------------------------- 1 | go 1.21 2 | 3 | use . 4 | use ../ -------------------------------------------------------------------------------- /votes/go.work: -------------------------------------------------------------------------------- 1 | go 1.20 2 | 3 | use . 4 | use ../ -------------------------------------------------------------------------------- /feedbacks/go.work: -------------------------------------------------------------------------------- 1 | go 1.20 2 | 3 | use . 4 | use ../ -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | auth/go.work 2 | feedbacks/go.work 3 | votes/go.work -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | auth/bin 3 | feedbacks/bin 4 | votes/bin 5 | -------------------------------------------------------------------------------- /ops/otelcollector/otelcol-config-extras.yml: -------------------------------------------------------------------------------- 1 | # extra settings to be merged into OpenTelemetry Collector configuration 2 | # do not delete this file 3 | -------------------------------------------------------------------------------- /auth/user/repository.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import "context" 4 | 5 | type Repository interface { 6 | Get(ctx context.Context, email string) (*User, error) 7 | } 8 | -------------------------------------------------------------------------------- /feedbacks/feedback/repository.go: -------------------------------------------------------------------------------- 1 | package feedback 2 | 3 | import "context" 4 | 5 | type Repository interface { 6 | Store(ctx context.Context, f *Feedback) error 7 | } 8 | -------------------------------------------------------------------------------- /votes/vote/repository.go: -------------------------------------------------------------------------------- 1 | package vote 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | type Repository interface { 8 | Store(ctx context.Context, v *Vote) error 9 | } 10 | -------------------------------------------------------------------------------- /auth/user/entity.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import "github.com/google/uuid" 4 | 5 | type User struct { 6 | ID uuid.UUID 7 | Email string 8 | Password string 9 | FirstName string 10 | LastName string 11 | } 12 | -------------------------------------------------------------------------------- /feedbacks/feedback/entity.go: -------------------------------------------------------------------------------- 1 | package feedback 2 | 3 | import "github.com/google/uuid" 4 | 5 | type Feedback struct { 6 | ID uuid.UUID 7 | Email string 8 | Title string `json:"title"` 9 | Body string `json:"body"` 10 | } 11 | -------------------------------------------------------------------------------- /votes/vote/entity.go: -------------------------------------------------------------------------------- 1 | package vote 2 | 3 | import "github.com/google/uuid" 4 | 5 | type Vote struct { 6 | ID uuid.UUID 7 | Email string 8 | TalkName string `json:"talk_name"` 9 | Score int `json:"score,string"` 10 | } 11 | 12 | -------------------------------------------------------------------------------- /votes/ops/db/init.sql: -------------------------------------------------------------------------------- 1 | create database if not exists votes_db; 2 | use votes_db; 3 | create table if not exists vote (id varchar(50),email varchar(255),talk_name varchar(255), score int, created_at datetime, updated_at datetime, PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1; 4 | -------------------------------------------------------------------------------- /feedbacks/ops/db/init.sql: -------------------------------------------------------------------------------- 1 | create database if not exists feedbacks_db; 2 | use feedbacks_db; 3 | create table if not exists feedback (id varchar(50),email varchar(255),title varchar(255),body text, created_at datetime, updated_at datetime, PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1; 4 | 5 | -------------------------------------------------------------------------------- /auth/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.21-alpine AS builder 2 | 3 | WORKDIR /src 4 | 5 | # Restore dependencies 6 | COPY auth/ . 7 | 8 | RUN go mod tidy 9 | 10 | # Build executable 11 | RUN go build -o /src/auth ./ 12 | 13 | FROM scratch 14 | WORKDIR /src 15 | COPY --from=builder /src/auth ./ 16 | EXPOSE 8081 17 | CMD ["/src/auth"] 18 | -------------------------------------------------------------------------------- /votes/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.20-alpine AS builder 2 | 3 | WORKDIR /src 4 | 5 | # Restore dependencies 6 | COPY votes/ . 7 | 8 | RUN go mod tidy 9 | 10 | # Build executable 11 | RUN go build -o /src/votes ./ 12 | 13 | FROM scratch 14 | WORKDIR /src 15 | COPY --from=builder /src/votes ./ 16 | EXPOSE 8083 17 | CMD ["/src/votes"] 18 | -------------------------------------------------------------------------------- /feedbacks/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.20-alpine AS builder 2 | 3 | WORKDIR /src 4 | 5 | # Restore dependencies 6 | COPY feedbacks/ . 7 | 8 | RUN go mod tidy 9 | 10 | # Build executable 11 | RUN go build -o /src/feedbacks ./ 12 | 13 | 14 | FROM scratch 15 | WORKDIR /src 16 | COPY --from=builder /src/feedbacks ./ 17 | EXPOSE 8082 18 | CMD ["/src/feedbacks"] 19 | -------------------------------------------------------------------------------- /internal/telemetry/telemetry.go: -------------------------------------------------------------------------------- 1 | package telemetry 2 | 3 | import ( 4 | "context" 5 | "go.opentelemetry.io/otel/trace" 6 | ) 7 | 8 | type Telemetry interface { 9 | Start(ctx context.Context, name string, opts ...trace.SpanStartOption) (context.Context, Span) 10 | Shutdown(ctx context.Context) 11 | } 12 | 13 | type Span interface { 14 | trace.Span 15 | } 16 | -------------------------------------------------------------------------------- /auth/ops/db/init.sql: -------------------------------------------------------------------------------- 1 | create database if not exists auth_db; 2 | use auth_db; 3 | create table if not exists user (id varchar(50),email varchar(255),password varchar(255),first_name varchar(100), last_name varchar(100), created_at datetime, updated_at datetime, PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1; 4 | INSERT INTO user (id, email, password, first_name, last_name, created_at, updated_at) 5 | SELECT 'adb8101e-cfe6-4a71-8594-ebc80af3a86d','eminetto@email.com',SHA1('12345'), 'Elton', 'Minetto', now(), null FROM DUAL 6 | WHERE NOT EXISTS 7 | (SELECT email FROM user WHERE email='eminetto@email.com'); -------------------------------------------------------------------------------- /ops/grafana/provisioning/datasources/jaeger.yaml: -------------------------------------------------------------------------------- 1 | # Copyright The OpenTelemetry Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: 1 16 | 17 | datasources: 18 | - name: Jaeger 19 | uid: webstore-traces 20 | type: jaeger 21 | url: http://jaeger:16686/jaeger/ui 22 | editable: true 23 | isDefault: false 24 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | # OpenTelemetry Collector 2 | OTEL_COLLECTOR_HOST=otelcol 3 | OTEL_COLLECTOR_PORT=4317 4 | OTEL_EXPORTER_OTLP_ENDPOINT=${OTEL_COLLECTOR_HOST}:${OTEL_COLLECTOR_PORT} 5 | PUBLIC_OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://localhost:4318/v1/traces 6 | 7 | # OpenTelemetry Resource Definitions 8 | OTEL_RESOURCE_ATTRIBUTES="service.namespace=opentelemetry-demo" 9 | 10 | # Metrics Temporality 11 | OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE=cumulative 12 | 13 | # ******************** 14 | # Telemetry Components 15 | # ******************** 16 | # Grafana 17 | GRAFANA_SERVICE_PORT=3000 18 | GRAFANA_SERVICE_HOST=grafana 19 | 20 | # Jaeger 21 | JAEGER_SERVICE_PORT=16686 22 | JAEGER_SERVICE_HOST=jaeger 23 | JAEGER_TRACEPROVIDER=http://${JAEGER_SERVICE_HOST}:14268/api/traces 24 | 25 | # Prometheus 26 | PROMETHEUS_SERVICE_PORT=9090 27 | PROMETHEUS_SERVICE_HOST=prometheus 28 | PROMETHEUS_ADDR=${PROMETHEUS_SERVICE_HOST}:${PROMETHEUS_SERVICE_PORT} 29 | -------------------------------------------------------------------------------- /ops/grafana/provisioning/dashboards/general.yaml: -------------------------------------------------------------------------------- 1 | # Copyright The OpenTelemetry Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: 1 16 | providers: 17 | - name: 'OpenTelemetry Demo' 18 | orgId: 1 19 | folder: 'Demo' 20 | type: file 21 | disableDeletion: false 22 | editable: true 23 | options: 24 | path: /etc/grafana/provisioning/dashboards/general 25 | -------------------------------------------------------------------------------- /votes/vote/service.go: -------------------------------------------------------------------------------- 1 | package vote 2 | 3 | import ( 4 | "context" 5 | "github.com/eminetto/api-o11y/internal/telemetry" 6 | "github.com/google/uuid" 7 | "go.opentelemetry.io/otel/codes" 8 | ) 9 | 10 | type UseCase interface { 11 | Store(ctx context.Context, v *Vote) (uuid.UUID, error) 12 | } 13 | 14 | type Service struct { 15 | repo Repository 16 | telemetry telemetry.Telemetry 17 | } 18 | 19 | func NewService(repo Repository, telemetry telemetry.Telemetry) *Service { 20 | return &Service{ 21 | repo: repo, 22 | telemetry: telemetry, 23 | } 24 | } 25 | func (s *Service) Store(ctx context.Context, v *Vote) (uuid.UUID, error) { 26 | ctx, span := s.telemetry.Start(ctx, "service") 27 | defer span.End() 28 | v.ID = uuid.New() 29 | err := s.repo.Store(ctx, v) 30 | if err != nil { 31 | span.RecordError(err) 32 | span.SetStatus(codes.Error, err.Error()) 33 | return uuid.Nil, err 34 | } 35 | return v.ID, nil 36 | } 37 | -------------------------------------------------------------------------------- /feedbacks/feedback/service.go: -------------------------------------------------------------------------------- 1 | package feedback 2 | 3 | import ( 4 | "context" 5 | "github.com/eminetto/api-o11y/internal/telemetry" 6 | "github.com/google/uuid" 7 | "go.opentelemetry.io/otel/codes" 8 | ) 9 | 10 | type UseCase interface { 11 | Store(ctx context.Context, f *Feedback) (uuid.UUID, error) 12 | } 13 | 14 | type Service struct { 15 | repo Repository 16 | telemetry telemetry.Telemetry 17 | } 18 | 19 | func NewService(repo Repository, otel telemetry.Telemetry) *Service { 20 | return &Service{ 21 | repo: repo, 22 | telemetry: otel, 23 | } 24 | } 25 | func (s *Service) Store(ctx context.Context, f *Feedback) (uuid.UUID, error) { 26 | ctx, span := s.telemetry.Start(ctx, "service") 27 | defer span.End() 28 | f.ID = uuid.New() 29 | err := s.repo.Store(ctx, f) 30 | if err != nil { 31 | span.RecordError(err) 32 | span.SetStatus(codes.Error, err.Error()) 33 | return uuid.Nil, err 34 | } 35 | return f.ID, nil 36 | } 37 | -------------------------------------------------------------------------------- /ops/grafana/provisioning/datasources/default.yaml: -------------------------------------------------------------------------------- 1 | # Copyright The OpenTelemetry Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: 1 16 | 17 | datasources: 18 | - name: Prometheus 19 | uid: webstore-metrics 20 | type: prometheus 21 | url: http://prometheus:9090 22 | editable: true 23 | isDefault: true 24 | jsonData: 25 | exemplarTraceIdDestinations: 26 | - datasourceUid: webstore-traces 27 | name: trace_id 28 | 29 | - url: http://localhost:8080/jaeger/ui/trace/$${__value.raw} 30 | name: trace_id 31 | urlDisplayLabel: View in Jaeger UI 32 | -------------------------------------------------------------------------------- /auth/user/mocks/UseCase.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v2.18.0. DO NOT EDIT. 2 | 3 | package mocks 4 | 5 | import ( 6 | context "context" 7 | 8 | mock "github.com/stretchr/testify/mock" 9 | ) 10 | 11 | // UseCase is an autogenerated mock type for the UseCase type 12 | type UseCase struct { 13 | mock.Mock 14 | } 15 | 16 | // ValidateUser provides a mock function with given fields: ctx, email, password 17 | func (_m *UseCase) ValidateUser(ctx context.Context, email string, password string) error { 18 | ret := _m.Called(ctx, email, password) 19 | 20 | var r0 error 21 | if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok { 22 | r0 = rf(ctx, email, password) 23 | } else { 24 | r0 = ret.Error(0) 25 | } 26 | 27 | return r0 28 | } 29 | 30 | type mockConstructorTestingTNewUseCase interface { 31 | mock.TestingT 32 | Cleanup(func()) 33 | } 34 | 35 | // NewUseCase creates a new instance of UseCase. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. 36 | func NewUseCase(t mockConstructorTestingTNewUseCase) *UseCase { 37 | mock := &UseCase{} 38 | mock.Mock.Test(t) 39 | 40 | t.Cleanup(func() { mock.AssertExpectations(t) }) 41 | 42 | return mock 43 | } 44 | -------------------------------------------------------------------------------- /auth/user/service_test.go: -------------------------------------------------------------------------------- 1 | package user_test 2 | 3 | import ( 4 | "context" 5 | "github.com/eminetto/api-o11y/auth/user" 6 | "github.com/eminetto/api-o11y/auth/user/mocks" 7 | tmocks "github.com/eminetto/api-o11y/internal/telemetry/mocks" 8 | "github.com/stretchr/testify/assert" 9 | "github.com/stretchr/testify/mock" 10 | "testing" 11 | ) 12 | 13 | func TestValidatePassword(t *testing.T) { 14 | ctx := context.TODO() 15 | repo := mocks.NewRepository(t) 16 | otel := tmocks.NewTelemetry(t) 17 | span := tmocks.NewSpan(t) 18 | span.On("RecordError", mock.Anything).Return(nil) 19 | span.On("SetStatus", mock.Anything, mock.Anything).Return(nil) 20 | span.On("End").Return(nil) 21 | otel.On("Start", ctx, "validatePassword").Return(ctx, span) 22 | 23 | s := user.NewService(repo, otel) 24 | u := &user.User{ 25 | Email: "eminetto@email.com", 26 | Password: "8cb2237d0679ca88db6464eac60da96345513964", 27 | } 28 | t.Run("invalid password", func(t *testing.T) { 29 | err := s.ValidatePassword(ctx, u, "invalid") 30 | assert.NotNil(t, err) 31 | assert.Equal(t, "invalid password", err.Error()) 32 | }) 33 | t.Run("valid password", func(t *testing.T) { 34 | err := s.ValidatePassword(ctx, u, "12345") 35 | assert.Nil(t, err) 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /ops/prometheus/prometheus-config.yaml: -------------------------------------------------------------------------------- 1 | # Copyright The OpenTelemetry Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | global: 16 | evaluation_interval: 30s 17 | scrape_interval: 5s 18 | scrape_configs: 19 | - job_name: otel 20 | honor_labels: true 21 | static_configs: 22 | - targets: 23 | - 'otelcol:9464' 24 | - job_name: otel-collector 25 | static_configs: 26 | - targets: 27 | - 'otelcol:8888' 28 | - job_name: auth 29 | scrape_interval: 10s 30 | static_configs: 31 | - targets: 32 | - 'auth:8081' 33 | - job_name: feedbacks 34 | scrape_interval: 10s 35 | static_configs: 36 | - targets: 37 | - 'feedbacks:8082' 38 | - job_name: votes 39 | scrape_interval: 10s 40 | static_configs: 41 | - targets: 42 | - 'votes:8083' -------------------------------------------------------------------------------- /votes/vote/mysql/mysql.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | "github.com/eminetto/api-o11y/internal/telemetry" 7 | "github.com/eminetto/api-o11y/votes/vote" 8 | "go.opentelemetry.io/otel/codes" 9 | "time" 10 | ) 11 | 12 | // VoteMySQL mysql repo 13 | type VoteMySQL struct { 14 | db *sql.DB 15 | telemetry telemetry.Telemetry 16 | } 17 | 18 | // NewVoteMySQL create new repository 19 | func NewVoteMySQL(db *sql.DB, telemetry telemetry.Telemetry) *VoteMySQL { 20 | return &VoteMySQL{ 21 | db: db, 22 | telemetry: telemetry, 23 | } 24 | } 25 | 26 | // Store a feedback 27 | func (r *VoteMySQL) Store(ctx context.Context, v *vote.Vote) error { 28 | ctx, span := r.telemetry.Start(ctx, "mysql") 29 | defer span.End() 30 | stmt, err := r.db.Prepare(` 31 | insert into vote (id, email, talk_name, score, created_at) 32 | values(?,?,?,?,?)`) 33 | if err != nil { 34 | return err 35 | } 36 | _, err = stmt.Exec( 37 | v.ID, 38 | v.Email, 39 | v.TalkName, 40 | v.Score, 41 | time.Now().Format("2006-01-02 15:04:05"), 42 | ) 43 | if err != nil { 44 | span.RecordError(err) 45 | return err 46 | } 47 | err = stmt.Close() 48 | if err != nil { 49 | span.RecordError(err) 50 | span.SetStatus(codes.Error, err.Error()) 51 | return err 52 | } 53 | return nil 54 | } 55 | -------------------------------------------------------------------------------- /auth/user/mysql/mysql.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | "github.com/eminetto/api-o11y/auth/user" 7 | "github.com/eminetto/api-o11y/internal/telemetry" 8 | "go.opentelemetry.io/otel/codes" 9 | ) 10 | 11 | // UserMySQL mysql repo 12 | type UserMySQL struct { 13 | db *sql.DB 14 | telemetry telemetry.Telemetry 15 | } 16 | 17 | // NewUserMySQL create new repository 18 | func NewUserMySQL(db *sql.DB, telemetry telemetry.Telemetry) *UserMySQL { 19 | return &UserMySQL{ 20 | db: db, 21 | telemetry: telemetry, 22 | } 23 | } 24 | 25 | // Get an user 26 | func (r *UserMySQL) Get(ctx context.Context, email string) (*user.User, error) { 27 | ctx, span := r.telemetry.Start(ctx, "mysql") 28 | defer span.End() 29 | stmt, err := r.db.Prepare(`select id, email, password, first_name, last_name from user where email = ?`) 30 | if err != nil { 31 | return nil, err 32 | } 33 | var u user.User 34 | rows, err := stmt.Query(email) 35 | if err != nil { 36 | span.RecordError(err) 37 | span.SetStatus(codes.Error, err.Error()) 38 | return nil, err 39 | } 40 | for rows.Next() { 41 | err = rows.Scan(&u.ID, &u.Email, &u.Password, &u.FirstName, &u.LastName) 42 | if err != nil { 43 | span.RecordError(err) 44 | span.SetStatus(codes.Error, err.Error()) 45 | return nil, err 46 | } 47 | } 48 | return &u, nil 49 | } 50 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/eminetto/api-o11y 2 | 3 | go 1.20 4 | 5 | require ( 6 | github.com/stretchr/testify v1.8.2 7 | go.opentelemetry.io/otel v1.14.0 8 | go.opentelemetry.io/otel/exporters/jaeger v1.14.0 9 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.14.0 10 | go.opentelemetry.io/otel/sdk v1.14.0 11 | go.opentelemetry.io/otel/trace v1.14.0 12 | ) 13 | 14 | require ( 15 | github.com/cenkalti/backoff/v4 v4.2.0 // indirect 16 | github.com/davecgh/go-spew v1.1.1 // indirect 17 | github.com/go-logr/logr v1.2.3 // indirect 18 | github.com/go-logr/stdr v1.2.2 // indirect 19 | github.com/golang/protobuf v1.5.2 // indirect 20 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect 21 | github.com/pmezard/go-difflib v1.0.0 // indirect 22 | github.com/stretchr/objx v0.5.0 // indirect 23 | go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.14.0 // indirect 24 | go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.14.0 // indirect 25 | go.opentelemetry.io/proto/otlp v0.19.0 // indirect 26 | golang.org/x/net v0.7.0 // indirect 27 | golang.org/x/sys v0.5.0 // indirect 28 | golang.org/x/text v0.7.0 // indirect 29 | google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect 30 | google.golang.org/grpc v1.53.0 // indirect 31 | google.golang.org/protobuf v1.28.1 // indirect 32 | gopkg.in/yaml.v3 v3.0.1 // indirect 33 | ) 34 | -------------------------------------------------------------------------------- /auth/user/mocks/Repository.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v2.18.0. DO NOT EDIT. 2 | 3 | package mocks 4 | 5 | import ( 6 | context "context" 7 | 8 | user "github.com/eminetto/api-o11y/auth/user" 9 | mock "github.com/stretchr/testify/mock" 10 | ) 11 | 12 | // Repository is an autogenerated mock type for the Repository type 13 | type Repository struct { 14 | mock.Mock 15 | } 16 | 17 | // Get provides a mock function with given fields: ctx, email 18 | func (_m *Repository) Get(ctx context.Context, email string) (*user.User, error) { 19 | ret := _m.Called(ctx, email) 20 | 21 | var r0 *user.User 22 | if rf, ok := ret.Get(0).(func(context.Context, string) *user.User); ok { 23 | r0 = rf(ctx, email) 24 | } else { 25 | if ret.Get(0) != nil { 26 | r0 = ret.Get(0).(*user.User) 27 | } 28 | } 29 | 30 | var r1 error 31 | if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { 32 | r1 = rf(ctx, email) 33 | } else { 34 | r1 = ret.Error(1) 35 | } 36 | 37 | return r0, r1 38 | } 39 | 40 | type mockConstructorTestingTNewRepository interface { 41 | mock.TestingT 42 | Cleanup(func()) 43 | } 44 | 45 | // NewRepository creates a new instance of Repository. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. 46 | func NewRepository(t mockConstructorTestingTNewRepository) *Repository { 47 | mock := &Repository{} 48 | mock.Mock.Test(t) 49 | 50 | t.Cleanup(func() { mock.AssertExpectations(t) }) 51 | 52 | return mock 53 | } 54 | -------------------------------------------------------------------------------- /feedbacks/feedback/mysql/mysql.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | "github.com/eminetto/api-o11y/feedbacks/feedback" 7 | "github.com/eminetto/api-o11y/internal/telemetry" 8 | "go.opentelemetry.io/otel/codes" 9 | "time" 10 | ) 11 | 12 | // FeedbackMySQL mysql repo 13 | type FeedbackMySQL struct { 14 | db *sql.DB 15 | telemetry telemetry.Telemetry 16 | } 17 | 18 | // NewFeedbackMySQL create new repository 19 | func NewUserMySQL(db *sql.DB, telemetry telemetry.Telemetry) *FeedbackMySQL { 20 | return &FeedbackMySQL{ 21 | db: db, 22 | telemetry: telemetry, 23 | } 24 | } 25 | 26 | // Store a feedback 27 | func (r *FeedbackMySQL) Store(ctx context.Context, f *feedback.Feedback) error { 28 | ctx, span := r.telemetry.Start(ctx, "mysql") 29 | defer span.End() 30 | tx, err := r.db.BeginTx(ctx, nil) 31 | if err != nil { 32 | span.RecordError(err) 33 | span.SetStatus(codes.Error, err.Error()) 34 | return err 35 | } 36 | defer tx.Commit() 37 | stmt, err := tx.Prepare(` 38 | insert into feedback (id, email, title, body, created_at) 39 | values(?,?,?,?,?)`) 40 | if err != nil { 41 | return err 42 | } 43 | _, err = stmt.Exec( 44 | f.ID, 45 | f.Email, 46 | f.Title, 47 | f.Body, 48 | time.Now().Format("2006-01-02 15:04:05"), 49 | ) 50 | if err != nil { 51 | span.RecordError(err) 52 | span.SetStatus(codes.Error, err.Error()) 53 | tx.Rollback() 54 | return err 55 | } 56 | defer stmt.Close() 57 | 58 | return nil 59 | } 60 | -------------------------------------------------------------------------------- /auth/user/service.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "context" 5 | "crypto/sha1" 6 | "fmt" 7 | "github.com/eminetto/api-o11y/internal/telemetry" 8 | "go.opentelemetry.io/otel/codes" 9 | ) 10 | 11 | type UseCase interface { 12 | ValidateUser(ctx context.Context, email, password string) error 13 | } 14 | 15 | type Service struct { 16 | repo Repository 17 | telemetry telemetry.Telemetry 18 | } 19 | 20 | func NewService(repo Repository, telemetry telemetry.Telemetry) *Service { 21 | return &Service{ 22 | repo: repo, 23 | telemetry: telemetry, 24 | } 25 | } 26 | func (s *Service) ValidateUser(ctx context.Context, email, password string) error { 27 | ctx, span := s.telemetry.Start(ctx, "service") 28 | defer span.End() 29 | u, err := s.repo.Get(ctx, email) 30 | if err != nil { 31 | span.RecordError(err) 32 | span.SetStatus(codes.Error, err.Error()) 33 | return err 34 | } 35 | if u == nil { 36 | err := fmt.Errorf("invalid user") 37 | span.RecordError(err) 38 | span.SetStatus(codes.Error, err.Error()) 39 | return err 40 | } 41 | return s.ValidatePassword(ctx, u, password) 42 | } 43 | 44 | func (s *Service) ValidatePassword(ctx context.Context, u *User, password string) error { 45 | ctx, span := s.telemetry.Start(ctx, "validatePassword") 46 | defer span.End() 47 | h := sha1.New() 48 | h.Write([]byte(password)) 49 | p := fmt.Sprintf("%x", h.Sum(nil)) 50 | if p != u.Password { 51 | err := fmt.Errorf("invalid password") 52 | span.RecordError(err) 53 | span.SetStatus(codes.Error, err.Error()) 54 | return err 55 | } 56 | return nil 57 | } 58 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | all: build 3 | FORCE: ; 4 | 5 | .PHONY: build 6 | 7 | build: build-auth build-feedback build-vote 8 | 9 | build-auth: 10 | cd auth; go build -o bin/auth main.go 11 | 12 | build-feedback: 13 | cd feedbacks; go build -o bin/feedbacks main.go 14 | 15 | build-vote: 16 | cd votes; go build -o bin/votes main.go 17 | 18 | build-auth-linux: 19 | cd auth; CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -tags "netgo" -installsuffix netgo -o bin/auth main.go 20 | 21 | build-feedbacks-linux: 22 | cd feedbacks; CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -tags "netgo" -installsuffix netgo -o bin/feedbacks main.go 23 | 24 | build-votes-linux: 25 | cd votes; CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -tags "netgo" -installsuffix netgo -o bin/votes main.go 26 | 27 | build-linux: build-auth-linux build-feedbacks-linux build-votes-linux 28 | 29 | build-auth-docker: build-auth-linux 30 | cd auth; docker build -t auth -f Dockerfile . 31 | 32 | build-feedbacks-docker: build-feedbacks-linux 33 | cd feedbacks; docker build -t feedbacks -f Dockerfile . 34 | 35 | build-votes-docker: build-votes-linux 36 | cd votes; docker build -t votes -f Dockerfile . 37 | 38 | generate-mocks: 39 | @cd auth ; mockery --output user/mocks --dir user --all 40 | @cd internal ; mockery --output telemetry/mocks --dir telemetry --all 41 | 42 | clean: 43 | @rm -rf auth/user/mocks/* 44 | @rm -rf internal/telemetry/mocks/mocks/* 45 | 46 | test: generate-mocks 47 | go test ./... 48 | cd auth ; go test ./... 49 | cd feedbacks; go test ./... 50 | cd votes; go test ./... 51 | 52 | run-docker: build-auth-docker build-feedbacks-docker build-votes-docker 53 | docker run -d -p 8081:8081 auth 54 | docker run -d -p 8082:8082 feedbacks 55 | docker run -d -p 8083:8083 votes 56 | 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # API Observability 2 | 3 | ### Building 4 | 5 | ``` 6 | docker compose up -d --build 7 | ``` 8 | 9 | ## Using the services 10 | 11 | ### Auth 12 | 13 | ``` 14 | curl -X "POST" "http://localhost:8081/v1/auth" \ 15 | -H 'Accept: application/json' \ 16 | -H 'Content-Type: application/json' \ 17 | -d $'{ 18 | "email": "eminetto@email.com", 19 | "password": "12345" 20 | }' 21 | 22 | ``` 23 | 24 | The result should be a token, like: 25 | 26 | ``` 27 | { 28 | "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImVtaW5ldHRvQGVtYWlsLmNvbSIsImV4cCI6MTY4MTM0ODQ3MSwiaWF0IjoxNjgxMzQ0ODQxLCJuYmYiOjE2ODEzNDQ4NDF9.GdUiLYqrXeUZNIgHDhGDhGIV1NpN941UiFBqgvSoS-4" 29 | } 30 | ``` 31 | 32 | ### Feedback 33 | 34 | You need to use the token generated by the ```Auth``` service: 35 | 36 | ``` 37 | curl -X "POST" "http://localhost:8082/v1/feedback" \ 38 | -H 'Accept: application/json' \ 39 | -H 'Content-Type: application/json' \ 40 | -H 'Authorization:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImVtaW5ldHRvQGVtYWlsLmNvbSIsImV4cCI6MTY4MTM0ODQ3MSwiaWF0IjoxNjgxMzQ0ODQxLCJuYmYiOjE2ODEzNDQ4NDF9.GdUiLYqrXeUZNIgHDhGDhGIV1NpN941UiFBqgvSoS-4' \ 41 | -d $'{ 42 | "title": "Feedback test", 43 | "body": "Feedback body" 44 | }' 45 | ``` 46 | 47 | ### Vote 48 | 49 | You need to use the token generated by the ```Auth``` service: 50 | 51 | ``` 52 | curl -X "POST" "http://localhost:8083/v1/vote" \ 53 | -H 'Accept: application/json' \ 54 | -H 'Content-Type: application/json' \ 55 | -H 'Authorization:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImVtaW5ldHRvQGVtYWlsLmNvbSIsImV4cCI6MTY4MTM0ODQ3MSwiaWF0IjoxNjgxMzQ0ODQxLCJuYmYiOjE2ODEzNDQ4NDF9.GdUiLYqrXeUZNIgHDhGDhGIV1NpN941UiFBqgvSoS-4' \ 56 | -d $'{ 57 | "talk_name": "Go e Microserviços", 58 | "score": "10" 59 | }' 60 | ``` 61 | 62 | -------------------------------------------------------------------------------- /auth/security/jwt.go: -------------------------------------------------------------------------------- 1 | package security 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | jwt "github.com/dgrijalva/jwt-go" 8 | ) 9 | 10 | const ( 11 | JWT_SECRET = "d7830ad5791dsdsds" 12 | JWT_EXP_HOUR=1 13 | JWT_EXP_MIN = 0 14 | JWT_EXP_SEC = 30 15 | ) 16 | 17 | //NewToken create a new token 18 | func NewToken(email string) (string, error) { 19 | token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ 20 | "email": email, 21 | "nbf": time.Now().Unix(), 22 | "iat": time.Now().Unix(), 23 | "exp": time.Now().Local().Add(time.Hour*time.Duration(JWT_EXP_HOUR) + time.Minute*time.Duration(JWT_EXP_MIN) + time.Second*time.Duration(JWT_EXP_SEC)).Unix(), 24 | }) 25 | // Sign and get the complete encoded token as a string using the secret 26 | sToken, err := token.SignedString([]byte(JWT_SECRET)) 27 | if err != nil { 28 | return "", err 29 | } 30 | 31 | return sToken, nil 32 | } 33 | 34 | //ParseToken parse a token 35 | func ParseToken(tokenString string) (*jwt.Token, error) { 36 | var token *jwt.Token 37 | var err error 38 | token, err = parseHS256(tokenString, token) 39 | if err != nil && err.Error() != "Token is expired" { 40 | token, err = parseHS256(tokenString, token) 41 | } 42 | 43 | return token, err 44 | } 45 | 46 | func parseHS256(tokenString string, token *jwt.Token) (*jwt.Token, error) { 47 | token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { 48 | if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { 49 | return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"]) 50 | } 51 | return []byte(JWT_SECRET), nil 52 | }) 53 | return token, err 54 | } 55 | 56 | //GetClaims get claims information 57 | func GetClaims(token *jwt.Token) (jwt.MapClaims, error) { 58 | if !token.Valid { 59 | return nil, fmt.Errorf("Unauthorized") 60 | } 61 | err := token.Claims.(jwt.MapClaims).Valid() 62 | if err != nil { 63 | return nil, err 64 | } 65 | 66 | return token.Claims.(jwt.MapClaims), nil 67 | } -------------------------------------------------------------------------------- /ops/otelcollector/otelcol-config.yml: -------------------------------------------------------------------------------- 1 | # Copyright The OpenTelemetry Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | receivers: 16 | otlp: 17 | protocols: 18 | grpc: 19 | http: 20 | cors: 21 | allowed_origins: 22 | - "http://*" 23 | - "https://*" 24 | 25 | exporters: 26 | otlp: 27 | endpoint: "jaeger:4317" 28 | tls: 29 | insecure: true 30 | logging: 31 | prometheus: 32 | endpoint: "otelcol:9464" 33 | resource_to_telemetry_conversion: 34 | enabled: true 35 | enable_open_metrics: true 36 | processors: 37 | batch: 38 | spanmetrics: 39 | metrics_exporter: prometheus 40 | # temporary measure until description is fixed in .NET 41 | transform: 42 | metric_statements: 43 | - context: metric 44 | statements: 45 | - set(description, "Measures the duration of inbound HTTP requests") where name == "http.server.duration" 46 | filter: 47 | metrics: 48 | exclude: 49 | match_type: strict 50 | metric_names: 51 | - queueSize 52 | 53 | service: 54 | pipelines: 55 | traces: 56 | receivers: [otlp] 57 | processors: [spanmetrics, batch] 58 | exporters: [otlp, logging] 59 | metrics: 60 | receivers: [otlp] 61 | processors: [filter, transform, batch] 62 | exporters: [prometheus, logging] 63 | logs: 64 | receivers: [otlp] 65 | processors: [batch] 66 | exporters: [logging] 67 | -------------------------------------------------------------------------------- /internal/telemetry/mocks/Telemetry.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v2.18.0. DO NOT EDIT. 2 | 3 | package mocks 4 | 5 | import ( 6 | context "context" 7 | 8 | telemetry "github.com/eminetto/api-o11y/internal/telemetry" 9 | mock "github.com/stretchr/testify/mock" 10 | 11 | trace "go.opentelemetry.io/otel/trace" 12 | ) 13 | 14 | // Telemetry is an autogenerated mock type for the Telemetry type 15 | type Telemetry struct { 16 | mock.Mock 17 | } 18 | 19 | // Shutdown provides a mock function with given fields: ctx 20 | func (_m *Telemetry) Shutdown(ctx context.Context) { 21 | _m.Called(ctx) 22 | } 23 | 24 | // Start provides a mock function with given fields: ctx, name, opts 25 | func (_m *Telemetry) Start(ctx context.Context, name string, opts ...trace.SpanStartOption) (context.Context, telemetry.Span) { 26 | _va := make([]interface{}, len(opts)) 27 | for _i := range opts { 28 | _va[_i] = opts[_i] 29 | } 30 | var _ca []interface{} 31 | _ca = append(_ca, ctx, name) 32 | _ca = append(_ca, _va...) 33 | ret := _m.Called(_ca...) 34 | 35 | var r0 context.Context 36 | if rf, ok := ret.Get(0).(func(context.Context, string, ...trace.SpanStartOption) context.Context); ok { 37 | r0 = rf(ctx, name, opts...) 38 | } else { 39 | if ret.Get(0) != nil { 40 | r0 = ret.Get(0).(context.Context) 41 | } 42 | } 43 | 44 | var r1 telemetry.Span 45 | if rf, ok := ret.Get(1).(func(context.Context, string, ...trace.SpanStartOption) telemetry.Span); ok { 46 | r1 = rf(ctx, name, opts...) 47 | } else { 48 | if ret.Get(1) != nil { 49 | r1 = ret.Get(1).(telemetry.Span) 50 | } 51 | } 52 | 53 | return r0, r1 54 | } 55 | 56 | type mockConstructorTestingTNewTelemetry interface { 57 | mock.TestingT 58 | Cleanup(func()) 59 | } 60 | 61 | // NewTelemetry creates a new instance of Telemetry. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. 62 | func NewTelemetry(t mockConstructorTestingTNewTelemetry) *Telemetry { 63 | mock := &Telemetry{} 64 | mock.Mock.Test(t) 65 | 66 | t.Cleanup(func() { mock.AssertExpectations(t) }) 67 | 68 | return mock 69 | } 70 | -------------------------------------------------------------------------------- /votes/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/eminetto/api-o11y/votes 2 | 3 | go 1.20 4 | 5 | require ( 6 | github.com/eminetto/api-o11y v0.0.14 7 | github.com/go-chi/chi/v5 v5.0.8 8 | github.com/go-chi/httplog v0.3.0 9 | github.com/go-chi/telemetry v0.2.0 10 | github.com/go-sql-driver/mysql v1.7.0 11 | github.com/google/uuid v1.3.0 12 | go.opentelemetry.io/otel v1.14.0 13 | ) 14 | 15 | require ( 16 | github.com/beorn7/perks v1.0.1 // indirect 17 | github.com/cenkalti/backoff/v4 v4.2.0 // indirect 18 | github.com/go-logr/logr v1.2.3 // indirect 19 | github.com/go-logr/stdr v1.2.2 // indirect 20 | github.com/golang/protobuf v1.5.2 // indirect 21 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect 22 | github.com/m3db/prometheus_client_golang v0.8.1 // indirect 23 | github.com/m3db/prometheus_client_model v0.1.0 // indirect 24 | github.com/m3db/prometheus_common v0.1.0 // indirect 25 | github.com/m3db/prometheus_procfs v0.8.1 // indirect 26 | github.com/mattn/go-colorable v0.1.12 // indirect 27 | github.com/mattn/go-isatty v0.0.14 // indirect 28 | github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect 29 | github.com/pkg/errors v0.9.1 // indirect 30 | github.com/rs/zerolog v1.27.0 // indirect 31 | github.com/twmb/murmur3 v1.1.6 // indirect 32 | github.com/uber-go/tally v3.4.3+incompatible // indirect 33 | go.opentelemetry.io/otel/exporters/jaeger v1.14.0 // indirect 34 | go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.14.0 // indirect 35 | go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.14.0 // indirect 36 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.14.0 // indirect 37 | go.opentelemetry.io/otel/sdk v1.14.0 // indirect 38 | go.opentelemetry.io/otel/trace v1.14.0 // indirect 39 | go.opentelemetry.io/proto/otlp v0.19.0 // indirect 40 | go.uber.org/atomic v1.9.0 // indirect 41 | golang.org/x/net v0.7.0 // indirect 42 | golang.org/x/sys v0.5.0 // indirect 43 | golang.org/x/text v0.7.0 // indirect 44 | google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect 45 | google.golang.org/grpc v1.53.0 // indirect 46 | google.golang.org/protobuf v1.28.1 // indirect 47 | ) 48 | -------------------------------------------------------------------------------- /feedbacks/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/eminetto/api-o11y/feedbacks 2 | 3 | go 1.20 4 | 5 | require ( 6 | github.com/eminetto/api-o11y v0.0.14 7 | github.com/go-chi/chi/v5 v5.0.8 8 | github.com/go-chi/httplog v0.3.0 9 | github.com/go-chi/telemetry v0.2.0 10 | github.com/go-sql-driver/mysql v1.7.0 11 | github.com/google/uuid v1.3.0 12 | go.opentelemetry.io/otel v1.14.0 13 | ) 14 | 15 | require ( 16 | github.com/beorn7/perks v1.0.1 // indirect 17 | github.com/cenkalti/backoff/v4 v4.2.0 // indirect 18 | github.com/go-logr/logr v1.2.3 // indirect 19 | github.com/go-logr/stdr v1.2.2 // indirect 20 | github.com/golang/protobuf v1.5.2 // indirect 21 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect 22 | github.com/m3db/prometheus_client_golang v0.8.1 // indirect 23 | github.com/m3db/prometheus_client_model v0.1.0 // indirect 24 | github.com/m3db/prometheus_common v0.1.0 // indirect 25 | github.com/m3db/prometheus_procfs v0.8.1 // indirect 26 | github.com/mattn/go-colorable v0.1.12 // indirect 27 | github.com/mattn/go-isatty v0.0.14 // indirect 28 | github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect 29 | github.com/pkg/errors v0.9.1 // indirect 30 | github.com/rs/zerolog v1.27.0 // indirect 31 | github.com/twmb/murmur3 v1.1.6 // indirect 32 | github.com/uber-go/tally v3.4.3+incompatible // indirect 33 | go.opentelemetry.io/otel/exporters/jaeger v1.14.0 // indirect 34 | go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.14.0 // indirect 35 | go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.14.0 // indirect 36 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.14.0 // indirect 37 | go.opentelemetry.io/otel/sdk v1.14.0 // indirect 38 | go.opentelemetry.io/otel/trace v1.14.0 // indirect 39 | go.opentelemetry.io/proto/otlp v0.19.0 // indirect 40 | go.uber.org/atomic v1.9.0 // indirect 41 | golang.org/x/net v0.7.0 // indirect 42 | golang.org/x/sys v0.5.0 // indirect 43 | golang.org/x/text v0.7.0 // indirect 44 | google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect 45 | google.golang.org/grpc v1.53.0 // indirect 46 | google.golang.org/protobuf v1.28.1 // indirect 47 | 48 | ) 49 | -------------------------------------------------------------------------------- /internal/telemetry/jaeger.go: -------------------------------------------------------------------------------- 1 | package telemetry 2 | 3 | import ( 4 | "context" 5 | "go.opentelemetry.io/otel" 6 | "go.opentelemetry.io/otel/exporters/jaeger" 7 | "go.opentelemetry.io/otel/propagation" 8 | "go.opentelemetry.io/otel/sdk/resource" 9 | sdktrace "go.opentelemetry.io/otel/sdk/trace" 10 | semconv "go.opentelemetry.io/otel/semconv/v1.10.0" 11 | "go.opentelemetry.io/otel/trace" 12 | "os" 13 | ) 14 | 15 | type Jaeger struct { 16 | provider *sdktrace.TracerProvider 17 | tracer trace.Tracer 18 | } 19 | 20 | func NewJaeger(ctx context.Context, serviceName string) (*Jaeger, error) { 21 | var tp *sdktrace.TracerProvider 22 | var err error 23 | tp, err = createJaegerTraceProvider(ctx, serviceName) 24 | if err != nil { 25 | return nil, err 26 | } 27 | otel.SetTracerProvider(tp) 28 | otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{})) 29 | tracer := tp.Tracer(serviceName) 30 | 31 | return &Jaeger{ 32 | provider: tp, 33 | tracer: tracer, 34 | }, nil 35 | } 36 | 37 | func (ot *Jaeger) Start(ctx context.Context, name string, opts ...trace.SpanStartOption) (context.Context, Span) { 38 | if len(opts) == 0 { 39 | return ot.tracer.Start(ctx, name) 40 | } 41 | return ot.tracer.Start(ctx, name, opts[0]) 42 | } 43 | 44 | func (ot *Jaeger) Shutdown(ctx context.Context) { 45 | ot.provider.Shutdown(ctx) 46 | } 47 | 48 | func createJaegerTraceProvider(ctx context.Context, serviceName string) (*sdktrace.TracerProvider, error) { 49 | res, err := resource.New(ctx, 50 | resource.WithAttributes( 51 | semconv.ServiceNameKey.String(serviceName), 52 | ), 53 | ) 54 | if err != nil { 55 | return nil, err 56 | } 57 | 58 | exp, err := jaeger.New( 59 | jaeger.WithCollectorEndpoint( 60 | jaeger.WithEndpoint(os.Getenv("JAEGER_TRACEPROVIDER")), 61 | ), 62 | ) 63 | if err != nil { 64 | return nil, err 65 | } 66 | tp := sdktrace.NewTracerProvider( 67 | sdktrace.WithBatcher(exp), 68 | sdktrace.WithResource(res), 69 | sdktrace.WithResource(resource.NewWithAttributes( 70 | semconv.SchemaURL, 71 | semconv.ServiceNameKey.String(serviceName), 72 | semconv.DeploymentEnvironmentKey.String("prod"), //@todo get from env 73 | )), 74 | ) 75 | return tp, nil 76 | } 77 | -------------------------------------------------------------------------------- /internal/telemetry/opentelemetry.go: -------------------------------------------------------------------------------- 1 | package telemetry 2 | 3 | import ( 4 | "context" 5 | "go.opentelemetry.io/otel" 6 | "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" 7 | "go.opentelemetry.io/otel/propagation" 8 | "go.opentelemetry.io/otel/sdk/resource" 9 | sdktrace "go.opentelemetry.io/otel/sdk/trace" 10 | semconv "go.opentelemetry.io/otel/semconv/v1.10.0" 11 | "go.opentelemetry.io/otel/trace" 12 | "os" 13 | ) 14 | 15 | type OTel struct { 16 | provider *sdktrace.TracerProvider 17 | tracer trace.Tracer 18 | } 19 | 20 | func New(ctx context.Context, serviceName string) (*OTel, error) { 21 | var tp *sdktrace.TracerProvider 22 | var err error 23 | tp, err = createOtelTraceProvider(ctx, serviceName) 24 | if err != nil { 25 | return nil, err 26 | } 27 | otel.SetTracerProvider(tp) 28 | otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{})) 29 | tracer := tp.Tracer(serviceName) 30 | 31 | return &OTel{ 32 | provider: tp, 33 | tracer: tracer, 34 | }, nil 35 | } 36 | 37 | func (ot *OTel) Start(ctx context.Context, name string, opts ...trace.SpanStartOption) (context.Context, Span) { 38 | if len(opts) == 0 { 39 | return ot.tracer.Start(ctx, name) 40 | } 41 | return ot.tracer.Start(ctx, name, opts[0]) 42 | } 43 | 44 | func (ot *OTel) Shutdown(ctx context.Context) { 45 | ot.provider.Shutdown(ctx) 46 | } 47 | 48 | func createOtelTraceProvider(ctx context.Context, serviceName string) (*sdktrace.TracerProvider, error) { 49 | res, err := resource.New(ctx, 50 | resource.WithAttributes( 51 | semconv.ServiceNameKey.String(serviceName), 52 | ), 53 | ) 54 | if err != nil { 55 | return nil, err 56 | } 57 | 58 | exp, err := 59 | otlptracegrpc.New(ctx, 60 | otlptracegrpc.WithInsecure(), 61 | otlptracegrpc.WithEndpoint(os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT")), 62 | ) 63 | if err != nil { 64 | return nil, err 65 | } 66 | tp := sdktrace.NewTracerProvider( 67 | sdktrace.WithBatcher(exp), 68 | sdktrace.WithResource(res), 69 | sdktrace.WithResource(resource.NewWithAttributes( 70 | semconv.SchemaURL, 71 | semconv.ServiceNameKey.String(serviceName), 72 | semconv.DeploymentEnvironmentKey.String("prod"), //@todo get from env 73 | )), 74 | ) 75 | return tp, nil 76 | } 77 | -------------------------------------------------------------------------------- /auth/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/eminetto/api-o11y/auth 2 | 3 | go 1.21 4 | 5 | require ( 6 | github.com/dgrijalva/jwt-go v3.2.0+incompatible 7 | github.com/eminetto/api-o11y v0.0.14 8 | github.com/go-chi/chi/v5 v5.0.8 9 | github.com/go-chi/httplog v0.3.0 10 | github.com/go-chi/telemetry v0.2.0 11 | github.com/go-sql-driver/mysql v1.7.0 12 | github.com/google/uuid v1.3.0 13 | github.com/stretchr/testify v1.8.2 14 | go.opentelemetry.io/otel v1.14.0 15 | ) 16 | 17 | require ( 18 | github.com/beorn7/perks v1.0.1 // indirect 19 | github.com/cenkalti/backoff/v4 v4.2.0 // indirect 20 | github.com/davecgh/go-spew v1.1.1 // indirect 21 | github.com/go-logr/logr v1.2.3 // indirect 22 | github.com/go-logr/stdr v1.2.2 // indirect 23 | github.com/golang/protobuf v1.5.2 // indirect 24 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect 25 | github.com/m3db/prometheus_client_golang v0.8.1 // indirect 26 | github.com/m3db/prometheus_client_model v0.1.0 // indirect 27 | github.com/m3db/prometheus_common v0.1.0 // indirect 28 | github.com/m3db/prometheus_procfs v0.8.1 // indirect 29 | github.com/mattn/go-colorable v0.1.12 // indirect 30 | github.com/mattn/go-isatty v0.0.14 // indirect 31 | github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect 32 | github.com/pkg/errors v0.9.1 // indirect 33 | github.com/pmezard/go-difflib v1.0.0 // indirect 34 | github.com/rs/zerolog v1.27.0 // indirect 35 | github.com/stretchr/objx v0.5.0 // indirect 36 | github.com/twmb/murmur3 v1.1.6 // indirect 37 | github.com/uber-go/tally v3.4.3+incompatible // indirect 38 | go.opentelemetry.io/otel/exporters/jaeger v1.14.0 // indirect 39 | go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.14.0 // indirect 40 | go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.14.0 // indirect 41 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.14.0 // indirect 42 | go.opentelemetry.io/otel/sdk v1.14.0 // indirect 43 | go.opentelemetry.io/otel/trace v1.14.0 // indirect 44 | go.opentelemetry.io/proto/otlp v0.19.0 // indirect 45 | go.uber.org/atomic v1.9.0 // indirect 46 | golang.org/x/net v0.7.0 // indirect 47 | golang.org/x/sys v0.5.0 // indirect 48 | golang.org/x/text v0.7.0 // indirect 49 | google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect 50 | google.golang.org/grpc v1.53.0 // indirect 51 | google.golang.org/protobuf v1.28.1 // indirect 52 | gopkg.in/yaml.v3 v3.0.1 // indirect 53 | ) 54 | -------------------------------------------------------------------------------- /internal/middleware/is_authenticated.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "errors" 7 | "github.com/eminetto/api-o11y/internal/telemetry" 8 | "go.opentelemetry.io/otel/codes" 9 | "net/http" 10 | "os" 11 | "strconv" 12 | "strings" 13 | ) 14 | 15 | func IsAuthenticated(ctx context.Context, telemetry telemetry.Telemetry) func(next http.Handler) http.Handler { 16 | return Handler(ctx, telemetry) 17 | } 18 | 19 | func Handler(ctx context.Context, telemetry telemetry.Telemetry) func(next http.Handler) http.Handler { 20 | return func(next http.Handler) http.Handler { 21 | fn := func(rw http.ResponseWriter, r *http.Request) { 22 | _, span := telemetry.Start(ctx, "IsAuthenticated") 23 | defer span.End() 24 | errorMessage := "Erro na autenticação" 25 | tokenString := r.Header.Get("Authorization") 26 | if tokenString == "" { 27 | err := errors.New("Unauthorized") 28 | span.RecordError(err) 29 | span.SetStatus(codes.Error, err.Error()) 30 | respondWithError(rw, http.StatusUnauthorized, err.Error(), errorMessage) 31 | return 32 | } 33 | payload := `{ 34 | "token": "` + tokenString + `" 35 | }` 36 | 37 | req, err := http.Post(os.Getenv("AUTH_URL")+"/v1/validate-token", "text/plain", strings.NewReader(payload)) 38 | if err != nil { 39 | span.RecordError(err) 40 | span.SetStatus(codes.Error, err.Error()) 41 | respondWithError(rw, http.StatusUnauthorized, err.Error(), errorMessage) 42 | return 43 | } 44 | defer req.Body.Close() 45 | type result struct { 46 | Email string `json:"email"` 47 | } 48 | var res result 49 | err = json.NewDecoder(req.Body).Decode(&res) 50 | if err != nil { 51 | span.RecordError(err) 52 | span.SetStatus(codes.Error, err.Error()) 53 | respondWithError(rw, http.StatusUnauthorized, err.Error(), errorMessage) 54 | return 55 | } 56 | newCTX := context.WithValue(r.Context(), "email", res.Email) 57 | next.ServeHTTP(rw, r.WithContext(newCTX)) 58 | } 59 | return http.HandlerFunc(fn) 60 | } 61 | } 62 | 63 | // RespondWithError return a http error 64 | func respondWithError(w http.ResponseWriter, code int, e string, message string) { 65 | respondWithJSON(w, code, map[string]string{"code": strconv.Itoa(code), "error": e, "message": message}) 66 | } 67 | 68 | func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) { 69 | response, _ := json.Marshal(payload) 70 | 71 | w.Header().Set("Content-Type", "application/json") 72 | w.WriteHeader(code) 73 | w.Write(response) 74 | } 75 | -------------------------------------------------------------------------------- /feedbacks/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | "encoding/json" 7 | "fmt" 8 | "github.com/eminetto/api-o11y/feedbacks/feedback" 9 | "github.com/eminetto/api-o11y/feedbacks/feedback/mysql" 10 | "github.com/eminetto/api-o11y/internal/middleware" 11 | "github.com/eminetto/api-o11y/internal/telemetry" 12 | "github.com/go-chi/chi/v5" 13 | "github.com/go-chi/httplog" 14 | telemetrymiddleware "github.com/go-chi/telemetry" 15 | _ "github.com/go-sql-driver/mysql" 16 | "github.com/google/uuid" 17 | "go.opentelemetry.io/otel/codes" 18 | "net/http" 19 | "os" 20 | "time" 21 | ) 22 | 23 | func main() { 24 | // Logger 25 | logger := httplog.NewLogger("feedbacks", httplog.Options{ 26 | JSON: true, 27 | }) 28 | dataSourceName := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?parseTime=true", os.Getenv("DB_USER"), os.Getenv("DB_PASSWORD"), os.Getenv("DB_HOST"), os.Getenv("DB_PORT"), os.Getenv("DB_DATABASE")) 29 | db, err := sql.Open("mysql", dataSourceName) 30 | if err != nil { 31 | logger.Panic().Msg(err.Error()) 32 | } 33 | defer db.Close() 34 | ctx := context.Background() 35 | otel, err := telemetry.NewJaeger(ctx, "feedbacks") 36 | if err != nil { 37 | logger.Panic().Msg(err.Error()) 38 | } 39 | defer otel.Shutdown(ctx) 40 | 41 | repo := mysql.NewUserMySQL(db, otel) 42 | fService := feedback.NewService(repo, otel) 43 | 44 | r := chi.NewRouter() 45 | r.Use(httplog.RequestLogger(logger)) 46 | r.Use(telemetrymiddleware.Collector(telemetrymiddleware.Config{ 47 | AllowAny: true, 48 | }, []string{"/v1"})) // path prefix filters basically records generic http request metrics 49 | r.Use(middleware.IsAuthenticated(ctx, otel)) 50 | r.Post("/v1/feedback", storeFeedback(ctx, fService, otel)) 51 | 52 | http.Handle("/", r) 53 | srv := &http.Server{ 54 | ReadTimeout: 30 * time.Second, 55 | WriteTimeout: 30 * time.Second, 56 | Addr: ":" + os.Getenv("PORT"), 57 | Handler: http.DefaultServeMux, 58 | } 59 | err = srv.ListenAndServe() 60 | if err != nil { 61 | logger.Panic().Msg(err.Error()) 62 | } 63 | } 64 | 65 | func storeFeedback(ctx context.Context, fService feedback.UseCase, otel telemetry.Telemetry) http.HandlerFunc { 66 | return func(w http.ResponseWriter, r *http.Request) { 67 | oplog := httplog.LogEntry(r.Context()) 68 | ctx, span := otel.Start(ctx, "store") 69 | defer span.End() 70 | var f feedback.Feedback 71 | err := json.NewDecoder(r.Body).Decode(&f) 72 | if err != nil { 73 | w.WriteHeader(http.StatusBadGateway) 74 | oplog.Error().Msg(err.Error()) 75 | return 76 | } 77 | f.Email = r.Context().Value("email").(string) 78 | var result struct { 79 | ID uuid.UUID `json:"id"` 80 | } 81 | result.ID, err = fService.Store(ctx, &f) 82 | if err != nil { 83 | w.WriteHeader(http.StatusInternalServerError) 84 | oplog.Error().Msg(err.Error()) 85 | span.RecordError(err) 86 | span.SetStatus(codes.Error, err.Error()) 87 | return 88 | } 89 | if err := json.NewEncoder(w).Encode(result); err != nil { 90 | w.WriteHeader(http.StatusBadGateway) 91 | oplog.Error().Msg(err.Error()) 92 | span.RecordError(err) 93 | span.SetStatus(codes.Error, err.Error()) 94 | return 95 | } 96 | return 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /votes/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | "encoding/json" 7 | "fmt" 8 | "github.com/eminetto/api-o11y/internal/middleware" 9 | "github.com/eminetto/api-o11y/internal/telemetry" 10 | "github.com/eminetto/api-o11y/votes/vote" 11 | "github.com/eminetto/api-o11y/votes/vote/mysql" 12 | "github.com/go-chi/chi/v5" 13 | "github.com/go-chi/httplog" 14 | telemetrymiddleware "github.com/go-chi/telemetry" 15 | _ "github.com/go-sql-driver/mysql" 16 | "github.com/google/uuid" 17 | "go.opentelemetry.io/otel/codes" 18 | "net/http" 19 | "os" 20 | "time" 21 | ) 22 | 23 | func main() { 24 | // Logger 25 | logger := httplog.NewLogger("votes", httplog.Options{ 26 | JSON: true, 27 | }) 28 | dataSourceName := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?parseTime=true", os.Getenv("DB_USER"), os.Getenv("DB_PASSWORD"), os.Getenv("DB_HOST"), os.Getenv("DB_PORT"), os.Getenv("DB_DATABASE")) 29 | db, err := sql.Open("mysql", dataSourceName) 30 | if err != nil { 31 | logger.Panic().Msg(err.Error()) 32 | } 33 | defer db.Close() 34 | 35 | ctx := context.Background() 36 | otel, err := telemetry.NewJaeger(ctx, "votes") 37 | if err != nil { 38 | logger.Panic().Msg(err.Error()) 39 | } 40 | defer otel.Shutdown(ctx) 41 | 42 | repo := mysql.NewVoteMySQL(db, otel) 43 | 44 | vService := vote.NewService(repo, otel) 45 | 46 | r := chi.NewRouter() 47 | r.Use(httplog.RequestLogger(logger)) 48 | r.Use(telemetrymiddleware.Collector(telemetrymiddleware.Config{ 49 | AllowAny: true, 50 | }, []string{"/v1"})) // path prefix filters basically records generic http request metrics 51 | r.Use(middleware.IsAuthenticated(ctx, otel)) 52 | r.Post("/v1/vote", storeVote(ctx, vService, otel)) 53 | 54 | http.Handle("/", r) 55 | srv := &http.Server{ 56 | ReadTimeout: 30 * time.Second, 57 | WriteTimeout: 30 * time.Second, 58 | Addr: ":" + os.Getenv("PORT"), 59 | Handler: http.DefaultServeMux, 60 | } 61 | err = srv.ListenAndServe() 62 | if err != nil { 63 | logger.Panic().Msg(err.Error()) 64 | } 65 | } 66 | 67 | func storeVote(ctx context.Context, vService vote.UseCase, otel telemetry.Telemetry) http.HandlerFunc { 68 | return func(w http.ResponseWriter, r *http.Request) { 69 | oplog := httplog.LogEntry(r.Context()) 70 | ctx, span := otel.Start(ctx, "store") 71 | defer span.End() 72 | var v vote.Vote 73 | err := json.NewDecoder(r.Body).Decode(&v) 74 | if err != nil { 75 | w.WriteHeader(http.StatusBadGateway) 76 | oplog.Error().Msg(err.Error()) 77 | span.RecordError(err) 78 | span.SetStatus(codes.Error, err.Error()) 79 | return 80 | } 81 | v.Email = r.Context().Value("email").(string) 82 | var result struct { 83 | ID uuid.UUID `json:"id"` 84 | } 85 | result.ID, err = vService.Store(ctx, &v) 86 | if err != nil { 87 | w.WriteHeader(http.StatusInternalServerError) 88 | oplog.Error().Msg(err.Error()) 89 | span.RecordError(err) 90 | span.SetStatus(codes.Error, err.Error()) 91 | return 92 | } 93 | if err := json.NewEncoder(w).Encode(result); err != nil { 94 | w.WriteHeader(http.StatusBadGateway) 95 | oplog.Error().Msg(err.Error()) 96 | span.RecordError(err) 97 | span.SetStatus(codes.Error, err.Error()) 98 | return 99 | } 100 | return 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /internal/telemetry/mocks/Span.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v2.18.0. DO NOT EDIT. 2 | 3 | package mocks 4 | 5 | import ( 6 | attribute "go.opentelemetry.io/otel/attribute" 7 | codes "go.opentelemetry.io/otel/codes" 8 | 9 | mock "github.com/stretchr/testify/mock" 10 | 11 | trace "go.opentelemetry.io/otel/trace" 12 | ) 13 | 14 | // Span is an autogenerated mock type for the Span type 15 | type Span struct { 16 | mock.Mock 17 | } 18 | 19 | // AddEvent provides a mock function with given fields: name, options 20 | func (_m *Span) AddEvent(name string, options ...trace.EventOption) { 21 | _va := make([]interface{}, len(options)) 22 | for _i := range options { 23 | _va[_i] = options[_i] 24 | } 25 | var _ca []interface{} 26 | _ca = append(_ca, name) 27 | _ca = append(_ca, _va...) 28 | _m.Called(_ca...) 29 | } 30 | 31 | // End provides a mock function with given fields: options 32 | func (_m *Span) End(options ...trace.SpanEndOption) { 33 | _va := make([]interface{}, len(options)) 34 | for _i := range options { 35 | _va[_i] = options[_i] 36 | } 37 | var _ca []interface{} 38 | _ca = append(_ca, _va...) 39 | _m.Called(_ca...) 40 | } 41 | 42 | // IsRecording provides a mock function with given fields: 43 | func (_m *Span) IsRecording() bool { 44 | ret := _m.Called() 45 | 46 | var r0 bool 47 | if rf, ok := ret.Get(0).(func() bool); ok { 48 | r0 = rf() 49 | } else { 50 | r0 = ret.Get(0).(bool) 51 | } 52 | 53 | return r0 54 | } 55 | 56 | // RecordError provides a mock function with given fields: err, options 57 | func (_m *Span) RecordError(err error, options ...trace.EventOption) { 58 | _va := make([]interface{}, len(options)) 59 | for _i := range options { 60 | _va[_i] = options[_i] 61 | } 62 | var _ca []interface{} 63 | _ca = append(_ca, err) 64 | _ca = append(_ca, _va...) 65 | _m.Called(_ca...) 66 | } 67 | 68 | // SetAttributes provides a mock function with given fields: kv 69 | func (_m *Span) SetAttributes(kv ...attribute.KeyValue) { 70 | _va := make([]interface{}, len(kv)) 71 | for _i := range kv { 72 | _va[_i] = kv[_i] 73 | } 74 | var _ca []interface{} 75 | _ca = append(_ca, _va...) 76 | _m.Called(_ca...) 77 | } 78 | 79 | // SetName provides a mock function with given fields: name 80 | func (_m *Span) SetName(name string) { 81 | _m.Called(name) 82 | } 83 | 84 | // SetStatus provides a mock function with given fields: code, description 85 | func (_m *Span) SetStatus(code codes.Code, description string) { 86 | _m.Called(code, description) 87 | } 88 | 89 | // SpanContext provides a mock function with given fields: 90 | func (_m *Span) SpanContext() trace.SpanContext { 91 | ret := _m.Called() 92 | 93 | var r0 trace.SpanContext 94 | if rf, ok := ret.Get(0).(func() trace.SpanContext); ok { 95 | r0 = rf() 96 | } else { 97 | r0 = ret.Get(0).(trace.SpanContext) 98 | } 99 | 100 | return r0 101 | } 102 | 103 | // TracerProvider provides a mock function with given fields: 104 | func (_m *Span) TracerProvider() trace.TracerProvider { 105 | ret := _m.Called() 106 | 107 | var r0 trace.TracerProvider 108 | if rf, ok := ret.Get(0).(func() trace.TracerProvider); ok { 109 | r0 = rf() 110 | } else { 111 | if ret.Get(0) != nil { 112 | r0 = ret.Get(0).(trace.TracerProvider) 113 | } 114 | } 115 | 116 | return r0 117 | } 118 | 119 | type mockConstructorTestingTNewSpan interface { 120 | mock.TestingT 121 | Cleanup(func()) 122 | } 123 | 124 | // NewSpan creates a new instance of Span. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. 125 | func NewSpan(t mockConstructorTestingTNewSpan) *Span { 126 | mock := &Span{} 127 | mock.Mock.Test(t) 128 | 129 | t.Cleanup(func() { mock.AssertExpectations(t) }) 130 | 131 | return mock 132 | } 133 | -------------------------------------------------------------------------------- /auth/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "database/sql" 5 | "encoding/json" 6 | "fmt" 7 | "github.com/eminetto/api-o11y/auth/security" 8 | "github.com/eminetto/api-o11y/auth/user" 9 | "github.com/eminetto/api-o11y/auth/user/mysql" 10 | "github.com/eminetto/api-o11y/internal/telemetry" 11 | "github.com/go-chi/httplog" 12 | "go.opentelemetry.io/otel/codes" 13 | "net/http" 14 | "os" 15 | "time" 16 | 17 | "context" 18 | "github.com/go-chi/chi/v5" 19 | telemetrymiddleware "github.com/go-chi/telemetry" 20 | _ "github.com/go-sql-driver/mysql" 21 | ) 22 | 23 | func main() { 24 | // Logger 25 | logger := httplog.NewLogger("auth", httplog.Options{ 26 | JSON: true, 27 | }) 28 | 29 | dataSourceName := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?parseTime=true", os.Getenv("DB_USER"), os.Getenv("DB_PASSWORD"), os.Getenv("DB_HOST"), os.Getenv("DB_PORT"), os.Getenv("DB_DATABASE")) 30 | db, err := sql.Open("mysql", dataSourceName) 31 | if err != nil { 32 | logger.Panic().Msg(err.Error()) 33 | } 34 | defer db.Close() 35 | 36 | ctx := context.Background() 37 | otel, err := telemetry.NewJaeger(ctx, "auth") 38 | if err != nil { 39 | logger.Panic().Msg(err.Error()) 40 | } 41 | defer otel.Shutdown(ctx) 42 | 43 | repo := mysql.NewUserMySQL(db, otel) 44 | uService := user.NewService(repo, otel) 45 | 46 | r := chi.NewRouter() 47 | r.Use(httplog.RequestLogger(logger)) 48 | r.Use(telemetrymiddleware.Collector(telemetrymiddleware.Config{ 49 | AllowAny: true, 50 | }, []string{"/v1"})) // path prefix filters basically records generic http request metrics 51 | r.Post("/v1/auth", userAuth(ctx, uService, otel)) 52 | r.Post("/v1/validate-token", validateToken(ctx, otel)) 53 | 54 | http.Handle("/", r) 55 | srv := &http.Server{ 56 | ReadTimeout: 30 * time.Second, 57 | WriteTimeout: 30 * time.Second, 58 | Addr: ":" + os.Getenv("PORT"), 59 | Handler: http.DefaultServeMux, 60 | } 61 | err = srv.ListenAndServe() 62 | if err != nil { 63 | logger.Panic().Msg(err.Error()) 64 | } 65 | } 66 | 67 | func userAuth(ctx context.Context, uService user.UseCase, otel telemetry.Telemetry) http.HandlerFunc { 68 | return func(w http.ResponseWriter, r *http.Request) { 69 | oplog := httplog.LogEntry(r.Context()) 70 | ctx, span := otel.Start(ctx, "userAuth") 71 | defer span.End() 72 | var param struct { 73 | Email string `json:"email"` 74 | Password string `json:"password"` 75 | } 76 | err := json.NewDecoder(r.Body).Decode(¶m) 77 | if err != nil { 78 | w.WriteHeader(http.StatusBadGateway) 79 | span.RecordError(err) 80 | span.SetStatus(codes.Error, err.Error()) 81 | oplog.Error().Msg(err.Error()) 82 | return 83 | } 84 | err = uService.ValidateUser(ctx, param.Email, param.Password) 85 | if err != nil { 86 | w.WriteHeader(http.StatusForbidden) 87 | span.RecordError(err) 88 | span.SetStatus(codes.Error, err.Error()) 89 | oplog.Error().Msg(err.Error()) 90 | return 91 | } 92 | var result struct { 93 | Token string `json:"token"` 94 | } 95 | result.Token, err = security.NewToken(param.Email) 96 | if err != nil { 97 | w.WriteHeader(http.StatusInternalServerError) 98 | oplog.Error().Msg(err.Error()) 99 | span.SetStatus(codes.Error, err.Error()) 100 | span.RecordError(err) 101 | return 102 | } 103 | 104 | if err := json.NewEncoder(w).Encode(result); err != nil { 105 | w.WriteHeader(http.StatusBadGateway) 106 | oplog.Error().Msg(err.Error()) 107 | span.SetStatus(codes.Error, err.Error()) 108 | span.RecordError(err) 109 | return 110 | } 111 | return 112 | } 113 | } 114 | 115 | func validateToken(ctx context.Context, otel telemetry.Telemetry) http.HandlerFunc { 116 | return func(w http.ResponseWriter, r *http.Request) { 117 | oplog := httplog.LogEntry(r.Context()) 118 | _, span := otel.Start(ctx, "validateToken") 119 | defer span.End() 120 | var param struct { 121 | Token string `json:"token"` 122 | } 123 | err := json.NewDecoder(r.Body).Decode(¶m) 124 | if err != nil { 125 | w.WriteHeader(http.StatusBadGateway) 126 | span.RecordError(err) 127 | span.SetStatus(codes.Error, err.Error()) 128 | oplog.Error().Msg(err.Error()) 129 | return 130 | } 131 | 132 | t, err := security.ParseToken(param.Token) 133 | if err != nil { 134 | w.WriteHeader(http.StatusInternalServerError) 135 | span.RecordError(err) 136 | span.SetStatus(codes.Error, err.Error()) 137 | oplog.Error().Msg(err.Error()) 138 | return 139 | } 140 | tData, err := security.GetClaims(t) 141 | if err != nil { 142 | w.WriteHeader(http.StatusInternalServerError) 143 | span.RecordError(err) 144 | span.SetStatus(codes.Error, err.Error()) 145 | oplog.Error().Msg(err.Error()) 146 | return 147 | } 148 | var result struct { 149 | Email string `json:"email"` 150 | } 151 | result.Email = tData["email"].(string) 152 | 153 | if err := json.NewEncoder(w).Encode(result); err != nil { 154 | w.WriteHeader(http.StatusBadGateway) 155 | span.RecordError(err) 156 | span.SetStatus(codes.Error, err.Error()) 157 | oplog.Error().Msg(err.Error()) 158 | return 159 | } 160 | return 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | x-default-logging: &logging 3 | driver: "json-file" 4 | options: 5 | max-size: "5m" 6 | max-file: "2" 7 | networks: 8 | default: 9 | name: api-o11y 10 | driver: bridge 11 | services: 12 | mysql-auth: 13 | image: mariadb:10.5.8 14 | command: --default-authentication-plugin=mysql_native_password --init-file /data/application/init.sql 15 | volumes: 16 | - ./auth/ops/db/init.sql:/data/application/init.sql 17 | environment: 18 | MYSQL_ROOT_PASSWORD: auth_pwd 19 | MYSQL_DATABASE: auth_db 20 | MYSQL_USER: auth_user 21 | MYSQL_PASSWORD: auth_pwd 22 | ports: 23 | - "3306:3306" 24 | container_name: auth_mysql 25 | mysql-feedbacks: 26 | image: mariadb:10.5.8 27 | command: --default-authentication-plugin=mysql_native_password --init-file /data/application/init.sql 28 | volumes: 29 | - ./feedbacks/ops/db/init.sql:/data/application/init.sql 30 | environment: 31 | MYSQL_ROOT_PASSWORD: feedbacks_pwd 32 | MYSQL_DATABASE: feedbacks_db 33 | MYSQL_USER: feedbacks_user 34 | MYSQL_PASSWORD: feedbacks_pwd 35 | ports: 36 | - "3307:3306" 37 | container_name: feedbacks_mysql 38 | mysql-votes: 39 | image: mariadb:10.5.8 40 | command: --default-authentication-plugin=mysql_native_password --init-file /data/application/init.sql 41 | volumes: 42 | - ./votes/ops/db/init.sql:/data/application/init.sql 43 | environment: 44 | MYSQL_ROOT_PASSWORD: votes_pwd 45 | MYSQL_DATABASE: votes_db 46 | MYSQL_USER: votes_user 47 | MYSQL_PASSWORD: votes_pwd 48 | ports: 49 | - "3308:3306" 50 | container_name: votes_mysql 51 | auth: 52 | image: auth 53 | build: 54 | context: ./ 55 | dockerfile: ./auth/Dockerfile 56 | environment: 57 | DB_USER: "auth_user" 58 | DB_PASSWORD: "auth_pwd" 59 | DB_HOST: "auth_mysql" 60 | DB_DATABASE: "auth_db" 61 | DB_PORT: "3306" 62 | PORT: "8081" 63 | OTEL_EXPORTER_OTLP_ENDPOINT: "${OTEL_EXPORTER_OTLP_ENDPOINT}" 64 | JAEGER_TRACEPROVIDER: "${JAEGER_TRACEPROVIDER}" 65 | ports: 66 | - "8081:8081" 67 | container_name: auth 68 | depends_on: 69 | - mysql-auth 70 | - otelcol 71 | feedbacks: 72 | image: feedbacks 73 | build: 74 | context: ./ 75 | dockerfile: ./feedbacks/Dockerfile 76 | environment: 77 | DB_USER: "feedbacks_user" 78 | DB_PASSWORD: "feedbacks_pwd" 79 | DB_HOST: "feedbacks_mysql" 80 | DB_DATABASE: "feedbacks_db" 81 | DB_PORT: "3306" 82 | PORT: "8082" 83 | AUTH_URL: "http://auth:8081" 84 | OTEL_EXPORTER_OTLP_ENDPOINT: "${OTEL_EXPORTER_OTLP_ENDPOINT}" 85 | JAEGER_TRACEPROVIDER: "${JAEGER_TRACEPROVIDER}" 86 | ports: 87 | - "8082:8082" 88 | container_name: feedbacks 89 | depends_on: 90 | - mysql-feedbacks 91 | - otelcol 92 | votes: 93 | image: votes 94 | build: 95 | context: ./ 96 | dockerfile: ./votes/Dockerfile 97 | environment: 98 | DB_USER: "votes_user" 99 | DB_PASSWORD: "votes_pwd" 100 | DB_HOST: "votes_mysql" 101 | DB_DATABASE: "votes_db" 102 | DB_PORT: "3306" 103 | PORT: "8083" 104 | AUTH_URL: "http://auth:8081" 105 | OTEL_EXPORTER_OTLP_ENDPOINT: "${OTEL_EXPORTER_OTLP_ENDPOINT}" 106 | JAEGER_TRACEPROVIDER: "${JAEGER_TRACEPROVIDER}" 107 | ports: 108 | - "8083:8083" 109 | container_name: votes 110 | depends_on: 111 | - mysql-votes 112 | - otelcol 113 | 114 | # ******************** 115 | # Telemetry Components 116 | # ******************** 117 | # Jaeger 118 | jaeger: 119 | image: jaegertracing/all-in-one 120 | container_name: jaeger 121 | command: 122 | - "--log-level=debug" 123 | - "--memory.max-traces" 124 | - "10000" 125 | - "--query.base-path" 126 | - "/jaeger/ui" 127 | - "--prometheus.server-url" 128 | - "http://${PROMETHEUS_ADDR}" 129 | deploy: 130 | resources: 131 | limits: 132 | memory: 300M 133 | restart: unless-stopped 134 | ports: 135 | - "${JAEGER_SERVICE_PORT}:${JAEGER_SERVICE_PORT}" # Jaeger UI 136 | - "14250:14250" 137 | - "14268:14268" # Jaeger collector 138 | - "4317" # OTLP gRPC default port 139 | environment: 140 | - COLLECTOR_OTLP_ENABLED=true 141 | - METRICS_STORAGE_TYPE=prometheus 142 | logging: *logging 143 | 144 | # Grafana 145 | grafana: 146 | image: grafana/grafana:9.1.0 147 | container_name: grafana 148 | ports: 149 | - "${GRAFANA_SERVICE_PORT}:${GRAFANA_SERVICE_PORT}" 150 | depends_on: 151 | - prometheus 152 | logging: *logging 153 | 154 | # OpenTelemetry Collector 155 | otelcol: 156 | image: otel/opentelemetry-collector-contrib:0.74.0 157 | container_name: otel-col 158 | deploy: 159 | resources: 160 | limits: 161 | memory: 125M 162 | restart: unless-stopped 163 | command: [ "--config=/etc/otelcol-config.yml", "--config=/etc/otelcol-config-extras.yml" ] 164 | volumes: 165 | - ./ops/otelcollector/otelcol-config.yml:/etc/otelcol-config.yml 166 | - ./ops/otelcollector/otelcol-config-extras.yml:/etc/otelcol-config-extras.yml 167 | ports: 168 | - "4317" # OTLP over gRPC receiver 169 | - "4318:4318" # OTLP over HTTP receiver 170 | - "9464" # Prometheus exporter 171 | - "8888" # metrics endpoint 172 | depends_on: 173 | - jaeger 174 | logging: *logging 175 | 176 | # Prometheus 177 | prometheus: 178 | image: quay.io/prometheus/prometheus:v2.34.0 179 | container_name: prometheus 180 | command: 181 | - --web.console.templates=/etc/prometheus/consoles 182 | - --web.console.libraries=/etc/prometheus/console_libraries 183 | - --storage.tsdb.retention.time=1h 184 | - --config.file=/etc/prometheus/prometheus-config.yaml 185 | - --storage.tsdb.path=/prometheus 186 | - --web.enable-lifecycle 187 | - --web.route-prefix=/ 188 | - --enable-feature=exemplar-storage 189 | volumes: 190 | - ./ops/prometheus/prometheus-config.yaml:/etc/prometheus/prometheus-config.yaml 191 | deploy: 192 | resources: 193 | limits: 194 | memory: 300M 195 | ports: 196 | - "${PROMETHEUS_SERVICE_PORT}:${PROMETHEUS_SERVICE_PORT}" 197 | logging: *logging 198 | 199 | -------------------------------------------------------------------------------- /votes/go.work.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.105.0 h1:DNtEKRBAAzeS4KyIory52wWHuClNaXJ5x1F7xa4q+5Y= 2 | cloud.google.com/go/accessapproval v1.5.0 h1:/nTivgnV/n1CaAeo+ekGexTYUsKEU9jUVkoY5359+3Q= 3 | cloud.google.com/go/accesscontextmanager v1.4.0 h1:CFhNhU7pcD11cuDkQdrE6PQJgv0EXNKNv06jIzbLlCU= 4 | cloud.google.com/go/aiplatform v1.27.0 h1:DBi3Jk9XjCJ4pkkLM4NqKgj3ozUL1wq4l+d3/jTGXAI= 5 | cloud.google.com/go/analytics v0.12.0 h1:NKw6PpQi6V1O+KsjuTd+bhip9d0REYu4NevC45vtGp8= 6 | cloud.google.com/go/apigateway v1.4.0 h1:IIoXKR7FKrEAQhMTz5hK2wiDz2WNFHS7eVr/L1lE/rM= 7 | cloud.google.com/go/apigeeconnect v1.4.0 h1:AONoTYJviyv1vS4IkvWzq69gEVdvHx35wKXc+e6wjZQ= 8 | cloud.google.com/go/appengine v1.5.0 h1:lmG+O5oaR9xNwaRBwE2XoMhwQHsHql5IoiGr1ptdDwU= 9 | cloud.google.com/go/area120 v0.6.0 h1:TCMhwWEWhCn8d44/Zs7UCICTWje9j3HuV6nVGMjdpYw= 10 | cloud.google.com/go/artifactregistry v1.9.0 h1:3d0LRAU1K6vfqCahhl9fx2oGHcq+s5gftdix4v8Ibrc= 11 | cloud.google.com/go/asset v1.10.0 h1:aCrlaLGJWTODJX4G56ZYzJefITKEWNfbjjtHSzWpxW0= 12 | cloud.google.com/go/assuredworkloads v1.9.0 h1:hhIdCOowsT1GG5eMCIA0OwK6USRuYTou/1ZeNxCSRtA= 13 | cloud.google.com/go/automl v1.8.0 h1:BMioyXSbg7d7xLibn47cs0elW6RT780IUWr42W8rp2Q= 14 | cloud.google.com/go/baremetalsolution v0.4.0 h1:g9KO6SkakcYPcc/XjAzeuUrEOXlYPnMpuiaywYaGrmQ= 15 | cloud.google.com/go/batch v0.4.0 h1:1jvEBY55OH4Sd2FxEXQfxGExFWov1A/IaRe+Z5Z71Fw= 16 | cloud.google.com/go/beyondcorp v0.3.0 h1:w+4kThysgl0JiKshi2MKDCg2NZgOyqOI0wq2eBZyrzA= 17 | cloud.google.com/go/bigquery v1.44.0 h1:Wi4dITi+cf9VYp4VH2T9O41w0kCW0uQTELq2Z6tukN0= 18 | cloud.google.com/go/billing v1.7.0 h1:Xkii76HWELHwBtkQVZvqmSo9GTr0O+tIbRNnMcGdlg4= 19 | cloud.google.com/go/binaryauthorization v1.4.0 h1:pL70vXWn9TitQYXBWTK2abHl2JHLwkFRjYw6VflRqEA= 20 | cloud.google.com/go/certificatemanager v1.4.0 h1:tzbR4UHBbgsewMWUD93JHi8EBi/gHBoSAcY1/sThFGk= 21 | cloud.google.com/go/channel v1.9.0 h1:pNuUlZx0Jb0Ts9P312bmNMuH5IiFWIR4RUtLb70Ke5s= 22 | cloud.google.com/go/cloudbuild v1.4.0 h1:TAAmCmAlOJ4uNBu6zwAjwhyl/7fLHHxIEazVhr3QBbQ= 23 | cloud.google.com/go/clouddms v1.4.0 h1:UhzHIlgFfMr6luVYVNydw/pl9/U5kgtjCMJHnSvoVws= 24 | cloud.google.com/go/cloudtasks v1.8.0 h1:faUiUgXjW8yVZ7XMnKHKm1WE4OldPBUWWfIRN/3z1dc= 25 | cloud.google.com/go/compute v1.15.1 h1:7UGq3QknM33pw5xATlpzeoomNxsacIVvTqTTvbfajmE= 26 | cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= 27 | cloud.google.com/go/contactcenterinsights v1.4.0 h1:tTQLI/ZvguUf9Hv+36BkG2+/PeC8Ol1q4pBW+tgCx0A= 28 | cloud.google.com/go/container v1.7.0 h1:nbEK/59GyDRKKlo1SqpohY1TK8LmJ2XNcvS9Gyom2A0= 29 | cloud.google.com/go/containeranalysis v0.6.0 h1:2824iym832ljKdVpCBnpqm5K94YT/uHTVhNF+dRTXPI= 30 | cloud.google.com/go/datacatalog v1.8.0 h1:6kZ4RIOW/uT7QWC5SfPfq/G8sYzr/v+UOmOAxy4Z1TE= 31 | cloud.google.com/go/dataflow v0.7.0 h1:CW3541Fm7KPTyZjJdnX6NtaGXYFn5XbFC5UcjgALKvU= 32 | cloud.google.com/go/dataform v0.5.0 h1:vLwowLF2ZB5J5gqiZCzv076lDI/Rd7zYQQFu5XO1PSg= 33 | cloud.google.com/go/datafusion v1.5.0 h1:j5m2hjWovTZDTQak4MJeXAR9yN7O+zMfULnjGw/OOLg= 34 | cloud.google.com/go/datalabeling v0.6.0 h1:dp8jOF21n/7jwgo/uuA0RN8hvLcKO4q6s/yvwevs2ZM= 35 | cloud.google.com/go/dataplex v1.4.0 h1:cNxeA2DiWliQGi21kPRqnVeQ5xFhNoEjPRt1400Pm8Y= 36 | cloud.google.com/go/dataproc v1.8.0 h1:gVOqNmElfa6n/ccG/QDlfurMWwrK3ezvy2b2eDoCmS0= 37 | cloud.google.com/go/dataqna v0.6.0 h1:gx9jr41ytcA3dXkbbd409euEaWtofCVXYBvJz3iYm18= 38 | cloud.google.com/go/datastore v1.10.0 h1:4siQRf4zTiAVt/oeH4GureGkApgb2vtPQAtOmhpqQwE= 39 | cloud.google.com/go/datastream v1.5.0 h1:PgIgbhedBtYBU6POGXFMn2uSl9vpqubc3ewTNdcU8Mk= 40 | cloud.google.com/go/deploy v1.5.0 h1:kI6dxt8Ml0is/x7YZjLveTvR7YPzXAUD/8wQZ2nH5zA= 41 | cloud.google.com/go/dialogflow v1.19.0 h1:HYHVOkoxQ9bSfNIelSZYNAtUi4CeSrCnROyOsbOqPq8= 42 | cloud.google.com/go/dlp v1.7.0 h1:9I4BYeJSVKoSKgjr70fLdRDumqcUeVmHV4fd5f9LR6Y= 43 | cloud.google.com/go/documentai v1.10.0 h1:jfq09Fdjtnpnmt/MLyf6A3DM3ynb8B2na0K+vSXvpFM= 44 | cloud.google.com/go/domains v0.7.0 h1:pu3JIgC1rswIqi5romW0JgNO6CTUydLYX8zyjiAvO1c= 45 | cloud.google.com/go/edgecontainer v0.2.0 h1:hd6J2n5dBBRuAqnNUEsKWrp6XNPKsaxwwIyzOPZTokk= 46 | cloud.google.com/go/errorreporting v0.3.0 h1:kj1XEWMu8P0qlLhm3FwcaFsUvXChV/OraZwA70trRR0= 47 | cloud.google.com/go/essentialcontacts v1.4.0 h1:b6csrQXCHKQmfo9h3dG/pHyoEh+fQG1Yg78a53LAviY= 48 | cloud.google.com/go/eventarc v1.8.0 h1:AgCqrmMMIcel5WWKkzz5EkCUKC3Rl5LNMMYsS+LvsI0= 49 | cloud.google.com/go/filestore v1.4.0 h1:yjKOpzvqtDmL5AXbKttLc8j0hL20kuC1qPdy5HPcxp0= 50 | cloud.google.com/go/firestore v1.9.0 h1:IBlRyxgGySXu5VuW0RgGFlTtLukSnNkpDiEOMkQkmpA= 51 | cloud.google.com/go/functions v1.9.0 h1:35tgv1fQOtvKqH/uxJMzX3w6usneJ0zXpsFr9KAVhNE= 52 | cloud.google.com/go/gaming v1.8.0 h1:97OAEQtDazAJD7yh/kvQdSCQuTKdR0O+qWAJBZJ4xiA= 53 | cloud.google.com/go/gkebackup v0.3.0 h1:4K+jiv4ocqt1niN8q5Imd8imRoXBHTrdnJVt/uFFxF4= 54 | cloud.google.com/go/gkeconnect v0.6.0 h1:zAcvDa04tTnGdu6TEZewaLN2tdMtUOJJ7fEceULjguA= 55 | cloud.google.com/go/gkehub v0.10.0 h1:JTcTaYQRGsVm+qkah7WzHb6e9sf1C0laYdRPn9aN+vg= 56 | cloud.google.com/go/gkemulticloud v0.4.0 h1:8F1NhJj8ucNj7lK51UZMtAjSWTgP1zO18XF6vkfiPPU= 57 | cloud.google.com/go/gsuiteaddons v1.4.0 h1:TGT2oGmO5q3VH6SjcrlgPUWI0njhYv4kywLm6jag0to= 58 | cloud.google.com/go/iam v0.8.0 h1:E2osAkZzxI/+8pZcxVLcDtAQx/u+hZXVryUaYQ5O0Kk= 59 | cloud.google.com/go/iap v1.5.0 h1:BGEXovwejOCt1zDk8hXq0bOhhRu9haXKWXXXp2B4wBM= 60 | cloud.google.com/go/ids v1.2.0 h1:LncHK4HHucb5Du310X8XH9/ICtMwZ2PCfK0ScjWiJoY= 61 | cloud.google.com/go/iot v1.4.0 h1:Y9+oZT9jD4GUZzORXTU45XsnQrhxmDT+TFbPil6pRVQ= 62 | cloud.google.com/go/kms v1.6.0 h1:OWRZzrPmOZUzurjI2FBGtgY2mB1WaJkqhw6oIwSj0Yg= 63 | cloud.google.com/go/language v1.8.0 h1:3Wa+IUMamL4JH3Zd3cDZUHpwyqplTACt6UZKRD2eCL4= 64 | cloud.google.com/go/lifesciences v0.6.0 h1:tIqhivE2LMVYkX0BLgG7xL64oNpDaFFI7teunglt1tI= 65 | cloud.google.com/go/logging v1.6.1 h1:ZBsZK+JG+oCDT+vaxwqF2egKNRjz8soXiS6Xv79benI= 66 | cloud.google.com/go/longrunning v0.3.0 h1:NjljC+FYPV3uh5/OwWT6pVU+doBqMg2x/rZlE+CamDs= 67 | cloud.google.com/go/managedidentities v1.4.0 h1:3Kdajn6X25yWQFhFCErmKSYTSvkEd3chJROny//F1A0= 68 | cloud.google.com/go/maps v0.1.0 h1:kLReRbclTgJefw2fcCbdLPLhPj0U6UUWN10ldG8sdOU= 69 | cloud.google.com/go/mediatranslation v0.6.0 h1:qAJzpxmEX+SeND10Y/4868L5wfZpo4Y3BIEnIieP4dk= 70 | cloud.google.com/go/memcache v1.7.0 h1:yLxUzJkZVSH2kPaHut7k+7sbIBFpvSh1LW9qjM2JDjA= 71 | cloud.google.com/go/metastore v1.8.0 h1:3KcShzqWdqxrDEXIBWpYJpOOrgpDj+HlBi07Grot49Y= 72 | cloud.google.com/go/monitoring v1.8.0 h1:c9riaGSPQ4dUKWB+M1Fl0N+iLxstMbCktdEwYSPGDvA= 73 | cloud.google.com/go/networkconnectivity v1.7.0 h1:BVdIKaI68bihnXGdCVL89Jsg9kq2kg+II30fjVqo62E= 74 | cloud.google.com/go/networkmanagement v1.5.0 h1:mDHA3CDW00imTvC5RW6aMGsD1bH+FtKwZm/52BxaiMg= 75 | cloud.google.com/go/networksecurity v0.6.0 h1:qDEX/3sipg9dS5JYsAY+YvgTjPR63cozzAWop8oZS94= 76 | cloud.google.com/go/notebooks v1.5.0 h1:AC8RPjNvel3ExgXjO1YOAz+teg9+j+89TNxa7pIZfww= 77 | cloud.google.com/go/optimization v1.2.0 h1:7PxOq9VTT7TMib/6dMoWpMvWS2E4dJEvtYzjvBreaec= 78 | cloud.google.com/go/orchestration v1.4.0 h1:39d6tqvNjd/wsSub1Bn4cEmrYcet5Ur6xpaN+SxOxtY= 79 | cloud.google.com/go/orgpolicy v1.5.0 h1:erF5PHqDZb6FeFrUHiYj2JK2BMhsk8CyAg4V4amJ3rE= 80 | cloud.google.com/go/osconfig v1.10.0 h1:NO0RouqCOM7M2S85Eal6urMSSipWwHU8evzwS+siqUI= 81 | cloud.google.com/go/oslogin v1.7.0 h1:pKGDPfeZHDybtw48WsnVLjoIPMi9Kw62kUE5TXCLCN4= 82 | cloud.google.com/go/phishingprotection v0.6.0 h1:OrwHLSRSZyaiOt3tnY33dsKSedxbMzsXvqB21okItNQ= 83 | cloud.google.com/go/policytroubleshooter v1.4.0 h1:NQklJuOUoz1BPP+Epjw81COx7IISWslkZubz/1i0UN8= 84 | cloud.google.com/go/privatecatalog v0.6.0 h1:Vz86uiHCtNGm1DeC32HeG2VXmOq5JRYA3VRPf8ZEcSg= 85 | cloud.google.com/go/pubsub v1.27.1 h1:q+J/Nfr6Qx4RQeu3rJcnN48SNC0qzlYzSeqkPq93VHs= 86 | cloud.google.com/go/pubsublite v1.5.0 h1:iqrD8vp3giTb7hI1q4TQQGj77cj8zzgmMPsTZtLnprM= 87 | cloud.google.com/go/recaptchaenterprise/v2 v2.5.0 h1:UqzFfb/WvhwXGDF1eQtdHLrmni+iByZXY4h3w9Kdyv8= 88 | cloud.google.com/go/recommendationengine v0.6.0 h1:6w+WxPf2LmUEqX0YyvfCoYb8aBYOcbIV25Vg6R0FLGw= 89 | cloud.google.com/go/recommender v1.8.0 h1:9kMZQGeYfcOD/RtZfcNKGKtoex3DdoB4zRgYU/WaIwE= 90 | cloud.google.com/go/redis v1.10.0 h1:/zTwwBKIAD2DEWTrXZp8WD9yD/gntReF/HkPssVYd0U= 91 | cloud.google.com/go/resourcemanager v1.4.0 h1:NDao6CHMwEZIaNsdWy+tuvHaavNeGP06o1tgrR0kLvU= 92 | cloud.google.com/go/resourcesettings v1.4.0 h1:eTzOwB13WrfF0kuzG2ZXCfB3TLunSHBur4s+HFU6uSM= 93 | cloud.google.com/go/retail v1.11.0 h1:N9fa//ecFUOEPsW/6mJHfcapPV0wBSwIUwpVZB7MQ3o= 94 | cloud.google.com/go/run v0.3.0 h1:AWPuzU7Xtaj3Jf+QarDWIs6AJ5hM1VFQ+F6Q+VZ6OT4= 95 | cloud.google.com/go/scheduler v1.7.0 h1:K/mxOewgHGeKuATUJNGylT75Mhtjmx1TOkKukATqMT8= 96 | cloud.google.com/go/secretmanager v1.9.0 h1:xE6uXljAC1kCR8iadt9+/blg1fvSbmenlsDN4fT9gqw= 97 | cloud.google.com/go/security v1.10.0 h1:KSKzzJMyUoMRQzcz7azIgqAUqxo7rmQ5rYvimMhikqg= 98 | cloud.google.com/go/securitycenter v1.16.0 h1:QTVtk/Reqnx2bVIZtJKm1+mpfmwRwymmNvlaFez7fQY= 99 | cloud.google.com/go/servicecontrol v1.5.0 h1:ImIzbOu6y4jL6ob65I++QzvqgFaoAKgHOG+RU9/c4y8= 100 | cloud.google.com/go/servicedirectory v1.7.0 h1:f7M8IMcVzO3T425AqlZbP3yLzeipsBHtRza8vVFYMhQ= 101 | cloud.google.com/go/servicemanagement v1.5.0 h1:TpkCO5M7dhKSy1bKUD9o/sSEW/U1Gtx7opA1fsiMx0c= 102 | cloud.google.com/go/serviceusage v1.4.0 h1:b0EwJxPJLpavSljMQh0RcdHsUrr5DQ+Nelt/3BAs5ro= 103 | cloud.google.com/go/shell v1.4.0 h1:b1LFhFBgKsG252inyhtmsUUZwchqSz3WTvAIf3JFo4g= 104 | cloud.google.com/go/spanner v1.41.0 h1:NvdTpRwf7DTegbfFdPjAWyD7bOVu0VeMqcvR9aCQCAc= 105 | cloud.google.com/go/speech v1.9.0 h1:yK0ocnFH4Wsf0cMdUyndJQ/hPv02oTJOxzi6AgpBy4s= 106 | cloud.google.com/go/storage v1.10.0 h1:STgFzyU5/8miMl0//zKh2aQeTyeaUH3WN9bSUiJ09bA= 107 | cloud.google.com/go/storagetransfer v1.6.0 h1:fUe3OydbbvHcAYp07xY+2UpH4AermGbmnm7qdEj3tGE= 108 | cloud.google.com/go/talent v1.4.0 h1:MrekAGxLqAeAol4Sc0allOVqUGO8j+Iim8NMvpiD7tM= 109 | cloud.google.com/go/texttospeech v1.5.0 h1:ccPiHgTewxgyAeCWgQWvZvrLmbfQSFABTMAfrSPLPyY= 110 | cloud.google.com/go/tpu v1.4.0 h1:ztIdKoma1Xob2qm6QwNh4Xi9/e7N3IfvtwG5AcNsj1g= 111 | cloud.google.com/go/trace v1.4.0 h1:qO9eLn2esajC9sxpqp1YKX37nXC3L4BfGnPS0Cx9dYo= 112 | cloud.google.com/go/translate v1.4.0 h1:AOYOH3MspzJ/bH1YXzB+xTE8fMpn3mwhLjugwGXvMPI= 113 | cloud.google.com/go/video v1.9.0 h1:ttlvO4J5c1VGq6FkHqWPD/aH6PfdxujHt+muTJlW1Zk= 114 | cloud.google.com/go/videointelligence v1.9.0 h1:RPFgVVXbI2b5vnrciZjtsUgpNKVtHO/WIyXUhEfuMhA= 115 | cloud.google.com/go/vision/v2 v2.5.0 h1:TQHxRqvLMi19azwm3qYuDbEzZWmiKJNTpGbkNsfRCik= 116 | cloud.google.com/go/vmmigration v1.3.0 h1:A2Tl2ZmwMRpvEmhV2ibISY85fmQR+Y5w9a0PlRz5P3s= 117 | cloud.google.com/go/vmwareengine v0.1.0 h1:JMPZaOT/gIUxVlTqSl/QQ32Y2k+r0stNeM1NSqhVP9o= 118 | cloud.google.com/go/vpcaccess v1.5.0 h1:woHXXtnW8b9gLFdWO9HLPalAddBQ9V4LT+1vjKwR3W8= 119 | cloud.google.com/go/webrisk v1.7.0 h1:ypSnpGlJnZSXbN9a13PDmAYvVekBLnGKxQ3Q9SMwnYY= 120 | cloud.google.com/go/websecurityscanner v1.4.0 h1:y7yIFg/h/mO+5Y5aCOtVAnpGUOgqCH5rXQ2Oc8Oq2+g= 121 | cloud.google.com/go/workflows v1.9.0 h1:7Chpin9p50NTU8Tb7qk+I11U/IwVXmDhEoSsdccvInE= 122 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9 h1:VpgP7xuJadIUuKccphEpTJnWhS2jkQyMt6Y7pJCD7fY= 123 | github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= 124 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc= 125 | github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= 126 | github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg= 127 | github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g= 128 | github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= 129 | github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= 130 | github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= 131 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= 132 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= 133 | github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI= 134 | github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe h1:QQ3GSy+MqSHxm/d8nCtnAiZdYFd45cYZPs8vOOIYKfk= 135 | github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b h1:ACGZRIr7HsgBKHsueQ1yM4WaVaXh21ynwqsF8M8tXhA= 136 | github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534 h1:rtAn27wIbmOGUs7RIbVgPEjb31ehTVniDwPGXyMxm5U= 137 | github.com/envoyproxy/go-control-plane v0.10.3 h1:xdCVXxEe0Y3FQith+0cj2irwZudqGYvecuLB1HtdexY= 138 | github.com/envoyproxy/protoc-gen-validate v0.9.1 h1:PS7VIOgmSVhWUEeZwTe7z7zouA22Cr590PzXKbZHOVY= 139 | github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= 140 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0= 141 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4 h1:WtGNWLvXpe6ZudgnXrq0barxBImvnnJoMEhXAzcbM0I= 142 | github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA= 143 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= 144 | github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc= 145 | github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= 146 | github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= 147 | github.com/google/martian/v3 v3.0.0 h1:pMen7vLs8nvgEYhywH3KDWJIJTeEr2ULsVWHWYHQyBs= 148 | github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99 h1:Ak8CrdlwwXwAZxzS66vgPt4U8yUZX7JwLvVR58FN5jM= 149 | github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA= 150 | github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= 151 | github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= 152 | github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= 153 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6 h1:UDMh68UUwekSh5iP2OMhRRZJiiBccgV7axzUG8vi56c= 154 | github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o= 155 | github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= 156 | github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw= 157 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM= 158 | github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s= 159 | github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk= 160 | github.com/rs/xid v1.3.0 h1:6NjYksEUlhurdVehpc7S7dk6DAmcKv8V9gG0FsVN2U4= 161 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ= 162 | github.com/yuin/goldmark v1.1.32 h1:5tjfNdR2ki3yYQ842+eX2sQHeiwpKJ0RnHO4IYOc4V8= 163 | go.opencensus.io v0.22.4 h1:LYy1Hy3MJdrCdMwwzxA/dRok4ejH+RwNGbuoD9fCjto= 164 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= 165 | golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6 h1:QE6XYQK6naiK1EPAe1g/ILLxN5RBoH5xkJk3CqlMI/Y= 166 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4= 167 | golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k= 168 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs= 169 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= 170 | golang.org/x/oauth2 v0.4.0 h1:NF0gk8LVPg1Ml7SSbGyySuoxdsXitj7TvgvuRxIMc/M= 171 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA= 172 | golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= 173 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= 174 | golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= 175 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= 176 | google.golang.org/api v0.30.0 h1:yfrXXP61wVuLb0vBcG6qaOoIoqYEzOQS8jum51jkv2w= 177 | google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= 178 | gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8= 179 | gopkg.in/yaml.v2 v2.2.3 h1:fvjTMHxHEw/mxHbtzPi3JCcKXQRAnQTBRo6YCJSVHKI= 180 | honnef.co/go/tools v0.0.1-2020.1.4 h1:UoveltGrhghAA7ePc+e+QYDHXrBps2PqFZiHkGR/xK8= 181 | rsc.io/binaryregexp v0.2.0 h1:HfqmD5MEmC0zvwBuF187nq9mdnXjXsSivRiXN7SmRkE= 182 | rsc.io/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY= 183 | rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4= 184 | -------------------------------------------------------------------------------- /feedbacks/go.work.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.105.0 h1:DNtEKRBAAzeS4KyIory52wWHuClNaXJ5x1F7xa4q+5Y= 2 | cloud.google.com/go/accessapproval v1.5.0 h1:/nTivgnV/n1CaAeo+ekGexTYUsKEU9jUVkoY5359+3Q= 3 | cloud.google.com/go/accesscontextmanager v1.4.0 h1:CFhNhU7pcD11cuDkQdrE6PQJgv0EXNKNv06jIzbLlCU= 4 | cloud.google.com/go/aiplatform v1.27.0 h1:DBi3Jk9XjCJ4pkkLM4NqKgj3ozUL1wq4l+d3/jTGXAI= 5 | cloud.google.com/go/analytics v0.12.0 h1:NKw6PpQi6V1O+KsjuTd+bhip9d0REYu4NevC45vtGp8= 6 | cloud.google.com/go/apigateway v1.4.0 h1:IIoXKR7FKrEAQhMTz5hK2wiDz2WNFHS7eVr/L1lE/rM= 7 | cloud.google.com/go/apigeeconnect v1.4.0 h1:AONoTYJviyv1vS4IkvWzq69gEVdvHx35wKXc+e6wjZQ= 8 | cloud.google.com/go/appengine v1.5.0 h1:lmG+O5oaR9xNwaRBwE2XoMhwQHsHql5IoiGr1ptdDwU= 9 | cloud.google.com/go/area120 v0.6.0 h1:TCMhwWEWhCn8d44/Zs7UCICTWje9j3HuV6nVGMjdpYw= 10 | cloud.google.com/go/artifactregistry v1.9.0 h1:3d0LRAU1K6vfqCahhl9fx2oGHcq+s5gftdix4v8Ibrc= 11 | cloud.google.com/go/asset v1.10.0 h1:aCrlaLGJWTODJX4G56ZYzJefITKEWNfbjjtHSzWpxW0= 12 | cloud.google.com/go/assuredworkloads v1.9.0 h1:hhIdCOowsT1GG5eMCIA0OwK6USRuYTou/1ZeNxCSRtA= 13 | cloud.google.com/go/automl v1.8.0 h1:BMioyXSbg7d7xLibn47cs0elW6RT780IUWr42W8rp2Q= 14 | cloud.google.com/go/baremetalsolution v0.4.0 h1:g9KO6SkakcYPcc/XjAzeuUrEOXlYPnMpuiaywYaGrmQ= 15 | cloud.google.com/go/batch v0.4.0 h1:1jvEBY55OH4Sd2FxEXQfxGExFWov1A/IaRe+Z5Z71Fw= 16 | cloud.google.com/go/beyondcorp v0.3.0 h1:w+4kThysgl0JiKshi2MKDCg2NZgOyqOI0wq2eBZyrzA= 17 | cloud.google.com/go/bigquery v1.44.0 h1:Wi4dITi+cf9VYp4VH2T9O41w0kCW0uQTELq2Z6tukN0= 18 | cloud.google.com/go/billing v1.7.0 h1:Xkii76HWELHwBtkQVZvqmSo9GTr0O+tIbRNnMcGdlg4= 19 | cloud.google.com/go/binaryauthorization v1.4.0 h1:pL70vXWn9TitQYXBWTK2abHl2JHLwkFRjYw6VflRqEA= 20 | cloud.google.com/go/certificatemanager v1.4.0 h1:tzbR4UHBbgsewMWUD93JHi8EBi/gHBoSAcY1/sThFGk= 21 | cloud.google.com/go/channel v1.9.0 h1:pNuUlZx0Jb0Ts9P312bmNMuH5IiFWIR4RUtLb70Ke5s= 22 | cloud.google.com/go/cloudbuild v1.4.0 h1:TAAmCmAlOJ4uNBu6zwAjwhyl/7fLHHxIEazVhr3QBbQ= 23 | cloud.google.com/go/clouddms v1.4.0 h1:UhzHIlgFfMr6luVYVNydw/pl9/U5kgtjCMJHnSvoVws= 24 | cloud.google.com/go/cloudtasks v1.8.0 h1:faUiUgXjW8yVZ7XMnKHKm1WE4OldPBUWWfIRN/3z1dc= 25 | cloud.google.com/go/compute v1.15.1 h1:7UGq3QknM33pw5xATlpzeoomNxsacIVvTqTTvbfajmE= 26 | cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= 27 | cloud.google.com/go/contactcenterinsights v1.4.0 h1:tTQLI/ZvguUf9Hv+36BkG2+/PeC8Ol1q4pBW+tgCx0A= 28 | cloud.google.com/go/container v1.7.0 h1:nbEK/59GyDRKKlo1SqpohY1TK8LmJ2XNcvS9Gyom2A0= 29 | cloud.google.com/go/containeranalysis v0.6.0 h1:2824iym832ljKdVpCBnpqm5K94YT/uHTVhNF+dRTXPI= 30 | cloud.google.com/go/datacatalog v1.8.0 h1:6kZ4RIOW/uT7QWC5SfPfq/G8sYzr/v+UOmOAxy4Z1TE= 31 | cloud.google.com/go/dataflow v0.7.0 h1:CW3541Fm7KPTyZjJdnX6NtaGXYFn5XbFC5UcjgALKvU= 32 | cloud.google.com/go/dataform v0.5.0 h1:vLwowLF2ZB5J5gqiZCzv076lDI/Rd7zYQQFu5XO1PSg= 33 | cloud.google.com/go/datafusion v1.5.0 h1:j5m2hjWovTZDTQak4MJeXAR9yN7O+zMfULnjGw/OOLg= 34 | cloud.google.com/go/datalabeling v0.6.0 h1:dp8jOF21n/7jwgo/uuA0RN8hvLcKO4q6s/yvwevs2ZM= 35 | cloud.google.com/go/dataplex v1.4.0 h1:cNxeA2DiWliQGi21kPRqnVeQ5xFhNoEjPRt1400Pm8Y= 36 | cloud.google.com/go/dataproc v1.8.0 h1:gVOqNmElfa6n/ccG/QDlfurMWwrK3ezvy2b2eDoCmS0= 37 | cloud.google.com/go/dataqna v0.6.0 h1:gx9jr41ytcA3dXkbbd409euEaWtofCVXYBvJz3iYm18= 38 | cloud.google.com/go/datastore v1.10.0 h1:4siQRf4zTiAVt/oeH4GureGkApgb2vtPQAtOmhpqQwE= 39 | cloud.google.com/go/datastream v1.5.0 h1:PgIgbhedBtYBU6POGXFMn2uSl9vpqubc3ewTNdcU8Mk= 40 | cloud.google.com/go/deploy v1.5.0 h1:kI6dxt8Ml0is/x7YZjLveTvR7YPzXAUD/8wQZ2nH5zA= 41 | cloud.google.com/go/dialogflow v1.19.0 h1:HYHVOkoxQ9bSfNIelSZYNAtUi4CeSrCnROyOsbOqPq8= 42 | cloud.google.com/go/dlp v1.7.0 h1:9I4BYeJSVKoSKgjr70fLdRDumqcUeVmHV4fd5f9LR6Y= 43 | cloud.google.com/go/documentai v1.10.0 h1:jfq09Fdjtnpnmt/MLyf6A3DM3ynb8B2na0K+vSXvpFM= 44 | cloud.google.com/go/domains v0.7.0 h1:pu3JIgC1rswIqi5romW0JgNO6CTUydLYX8zyjiAvO1c= 45 | cloud.google.com/go/edgecontainer v0.2.0 h1:hd6J2n5dBBRuAqnNUEsKWrp6XNPKsaxwwIyzOPZTokk= 46 | cloud.google.com/go/errorreporting v0.3.0 h1:kj1XEWMu8P0qlLhm3FwcaFsUvXChV/OraZwA70trRR0= 47 | cloud.google.com/go/essentialcontacts v1.4.0 h1:b6csrQXCHKQmfo9h3dG/pHyoEh+fQG1Yg78a53LAviY= 48 | cloud.google.com/go/eventarc v1.8.0 h1:AgCqrmMMIcel5WWKkzz5EkCUKC3Rl5LNMMYsS+LvsI0= 49 | cloud.google.com/go/filestore v1.4.0 h1:yjKOpzvqtDmL5AXbKttLc8j0hL20kuC1qPdy5HPcxp0= 50 | cloud.google.com/go/firestore v1.9.0 h1:IBlRyxgGySXu5VuW0RgGFlTtLukSnNkpDiEOMkQkmpA= 51 | cloud.google.com/go/functions v1.9.0 h1:35tgv1fQOtvKqH/uxJMzX3w6usneJ0zXpsFr9KAVhNE= 52 | cloud.google.com/go/gaming v1.8.0 h1:97OAEQtDazAJD7yh/kvQdSCQuTKdR0O+qWAJBZJ4xiA= 53 | cloud.google.com/go/gkebackup v0.3.0 h1:4K+jiv4ocqt1niN8q5Imd8imRoXBHTrdnJVt/uFFxF4= 54 | cloud.google.com/go/gkeconnect v0.6.0 h1:zAcvDa04tTnGdu6TEZewaLN2tdMtUOJJ7fEceULjguA= 55 | cloud.google.com/go/gkehub v0.10.0 h1:JTcTaYQRGsVm+qkah7WzHb6e9sf1C0laYdRPn9aN+vg= 56 | cloud.google.com/go/gkemulticloud v0.4.0 h1:8F1NhJj8ucNj7lK51UZMtAjSWTgP1zO18XF6vkfiPPU= 57 | cloud.google.com/go/gsuiteaddons v1.4.0 h1:TGT2oGmO5q3VH6SjcrlgPUWI0njhYv4kywLm6jag0to= 58 | cloud.google.com/go/iam v0.8.0 h1:E2osAkZzxI/+8pZcxVLcDtAQx/u+hZXVryUaYQ5O0Kk= 59 | cloud.google.com/go/iap v1.5.0 h1:BGEXovwejOCt1zDk8hXq0bOhhRu9haXKWXXXp2B4wBM= 60 | cloud.google.com/go/ids v1.2.0 h1:LncHK4HHucb5Du310X8XH9/ICtMwZ2PCfK0ScjWiJoY= 61 | cloud.google.com/go/iot v1.4.0 h1:Y9+oZT9jD4GUZzORXTU45XsnQrhxmDT+TFbPil6pRVQ= 62 | cloud.google.com/go/kms v1.6.0 h1:OWRZzrPmOZUzurjI2FBGtgY2mB1WaJkqhw6oIwSj0Yg= 63 | cloud.google.com/go/language v1.8.0 h1:3Wa+IUMamL4JH3Zd3cDZUHpwyqplTACt6UZKRD2eCL4= 64 | cloud.google.com/go/lifesciences v0.6.0 h1:tIqhivE2LMVYkX0BLgG7xL64oNpDaFFI7teunglt1tI= 65 | cloud.google.com/go/logging v1.6.1 h1:ZBsZK+JG+oCDT+vaxwqF2egKNRjz8soXiS6Xv79benI= 66 | cloud.google.com/go/longrunning v0.3.0 h1:NjljC+FYPV3uh5/OwWT6pVU+doBqMg2x/rZlE+CamDs= 67 | cloud.google.com/go/managedidentities v1.4.0 h1:3Kdajn6X25yWQFhFCErmKSYTSvkEd3chJROny//F1A0= 68 | cloud.google.com/go/maps v0.1.0 h1:kLReRbclTgJefw2fcCbdLPLhPj0U6UUWN10ldG8sdOU= 69 | cloud.google.com/go/mediatranslation v0.6.0 h1:qAJzpxmEX+SeND10Y/4868L5wfZpo4Y3BIEnIieP4dk= 70 | cloud.google.com/go/memcache v1.7.0 h1:yLxUzJkZVSH2kPaHut7k+7sbIBFpvSh1LW9qjM2JDjA= 71 | cloud.google.com/go/metastore v1.8.0 h1:3KcShzqWdqxrDEXIBWpYJpOOrgpDj+HlBi07Grot49Y= 72 | cloud.google.com/go/monitoring v1.8.0 h1:c9riaGSPQ4dUKWB+M1Fl0N+iLxstMbCktdEwYSPGDvA= 73 | cloud.google.com/go/networkconnectivity v1.7.0 h1:BVdIKaI68bihnXGdCVL89Jsg9kq2kg+II30fjVqo62E= 74 | cloud.google.com/go/networkmanagement v1.5.0 h1:mDHA3CDW00imTvC5RW6aMGsD1bH+FtKwZm/52BxaiMg= 75 | cloud.google.com/go/networksecurity v0.6.0 h1:qDEX/3sipg9dS5JYsAY+YvgTjPR63cozzAWop8oZS94= 76 | cloud.google.com/go/notebooks v1.5.0 h1:AC8RPjNvel3ExgXjO1YOAz+teg9+j+89TNxa7pIZfww= 77 | cloud.google.com/go/optimization v1.2.0 h1:7PxOq9VTT7TMib/6dMoWpMvWS2E4dJEvtYzjvBreaec= 78 | cloud.google.com/go/orchestration v1.4.0 h1:39d6tqvNjd/wsSub1Bn4cEmrYcet5Ur6xpaN+SxOxtY= 79 | cloud.google.com/go/orgpolicy v1.5.0 h1:erF5PHqDZb6FeFrUHiYj2JK2BMhsk8CyAg4V4amJ3rE= 80 | cloud.google.com/go/osconfig v1.10.0 h1:NO0RouqCOM7M2S85Eal6urMSSipWwHU8evzwS+siqUI= 81 | cloud.google.com/go/oslogin v1.7.0 h1:pKGDPfeZHDybtw48WsnVLjoIPMi9Kw62kUE5TXCLCN4= 82 | cloud.google.com/go/phishingprotection v0.6.0 h1:OrwHLSRSZyaiOt3tnY33dsKSedxbMzsXvqB21okItNQ= 83 | cloud.google.com/go/policytroubleshooter v1.4.0 h1:NQklJuOUoz1BPP+Epjw81COx7IISWslkZubz/1i0UN8= 84 | cloud.google.com/go/privatecatalog v0.6.0 h1:Vz86uiHCtNGm1DeC32HeG2VXmOq5JRYA3VRPf8ZEcSg= 85 | cloud.google.com/go/pubsub v1.27.1 h1:q+J/Nfr6Qx4RQeu3rJcnN48SNC0qzlYzSeqkPq93VHs= 86 | cloud.google.com/go/pubsublite v1.5.0 h1:iqrD8vp3giTb7hI1q4TQQGj77cj8zzgmMPsTZtLnprM= 87 | cloud.google.com/go/recaptchaenterprise/v2 v2.5.0 h1:UqzFfb/WvhwXGDF1eQtdHLrmni+iByZXY4h3w9Kdyv8= 88 | cloud.google.com/go/recommendationengine v0.6.0 h1:6w+WxPf2LmUEqX0YyvfCoYb8aBYOcbIV25Vg6R0FLGw= 89 | cloud.google.com/go/recommender v1.8.0 h1:9kMZQGeYfcOD/RtZfcNKGKtoex3DdoB4zRgYU/WaIwE= 90 | cloud.google.com/go/redis v1.10.0 h1:/zTwwBKIAD2DEWTrXZp8WD9yD/gntReF/HkPssVYd0U= 91 | cloud.google.com/go/resourcemanager v1.4.0 h1:NDao6CHMwEZIaNsdWy+tuvHaavNeGP06o1tgrR0kLvU= 92 | cloud.google.com/go/resourcesettings v1.4.0 h1:eTzOwB13WrfF0kuzG2ZXCfB3TLunSHBur4s+HFU6uSM= 93 | cloud.google.com/go/retail v1.11.0 h1:N9fa//ecFUOEPsW/6mJHfcapPV0wBSwIUwpVZB7MQ3o= 94 | cloud.google.com/go/run v0.3.0 h1:AWPuzU7Xtaj3Jf+QarDWIs6AJ5hM1VFQ+F6Q+VZ6OT4= 95 | cloud.google.com/go/scheduler v1.7.0 h1:K/mxOewgHGeKuATUJNGylT75Mhtjmx1TOkKukATqMT8= 96 | cloud.google.com/go/secretmanager v1.9.0 h1:xE6uXljAC1kCR8iadt9+/blg1fvSbmenlsDN4fT9gqw= 97 | cloud.google.com/go/security v1.10.0 h1:KSKzzJMyUoMRQzcz7azIgqAUqxo7rmQ5rYvimMhikqg= 98 | cloud.google.com/go/securitycenter v1.16.0 h1:QTVtk/Reqnx2bVIZtJKm1+mpfmwRwymmNvlaFez7fQY= 99 | cloud.google.com/go/servicecontrol v1.5.0 h1:ImIzbOu6y4jL6ob65I++QzvqgFaoAKgHOG+RU9/c4y8= 100 | cloud.google.com/go/servicedirectory v1.7.0 h1:f7M8IMcVzO3T425AqlZbP3yLzeipsBHtRza8vVFYMhQ= 101 | cloud.google.com/go/servicemanagement v1.5.0 h1:TpkCO5M7dhKSy1bKUD9o/sSEW/U1Gtx7opA1fsiMx0c= 102 | cloud.google.com/go/serviceusage v1.4.0 h1:b0EwJxPJLpavSljMQh0RcdHsUrr5DQ+Nelt/3BAs5ro= 103 | cloud.google.com/go/shell v1.4.0 h1:b1LFhFBgKsG252inyhtmsUUZwchqSz3WTvAIf3JFo4g= 104 | cloud.google.com/go/spanner v1.41.0 h1:NvdTpRwf7DTegbfFdPjAWyD7bOVu0VeMqcvR9aCQCAc= 105 | cloud.google.com/go/speech v1.9.0 h1:yK0ocnFH4Wsf0cMdUyndJQ/hPv02oTJOxzi6AgpBy4s= 106 | cloud.google.com/go/storage v1.10.0 h1:STgFzyU5/8miMl0//zKh2aQeTyeaUH3WN9bSUiJ09bA= 107 | cloud.google.com/go/storagetransfer v1.6.0 h1:fUe3OydbbvHcAYp07xY+2UpH4AermGbmnm7qdEj3tGE= 108 | cloud.google.com/go/talent v1.4.0 h1:MrekAGxLqAeAol4Sc0allOVqUGO8j+Iim8NMvpiD7tM= 109 | cloud.google.com/go/texttospeech v1.5.0 h1:ccPiHgTewxgyAeCWgQWvZvrLmbfQSFABTMAfrSPLPyY= 110 | cloud.google.com/go/tpu v1.4.0 h1:ztIdKoma1Xob2qm6QwNh4Xi9/e7N3IfvtwG5AcNsj1g= 111 | cloud.google.com/go/trace v1.4.0 h1:qO9eLn2esajC9sxpqp1YKX37nXC3L4BfGnPS0Cx9dYo= 112 | cloud.google.com/go/translate v1.4.0 h1:AOYOH3MspzJ/bH1YXzB+xTE8fMpn3mwhLjugwGXvMPI= 113 | cloud.google.com/go/video v1.9.0 h1:ttlvO4J5c1VGq6FkHqWPD/aH6PfdxujHt+muTJlW1Zk= 114 | cloud.google.com/go/videointelligence v1.9.0 h1:RPFgVVXbI2b5vnrciZjtsUgpNKVtHO/WIyXUhEfuMhA= 115 | cloud.google.com/go/vision/v2 v2.5.0 h1:TQHxRqvLMi19azwm3qYuDbEzZWmiKJNTpGbkNsfRCik= 116 | cloud.google.com/go/vmmigration v1.3.0 h1:A2Tl2ZmwMRpvEmhV2ibISY85fmQR+Y5w9a0PlRz5P3s= 117 | cloud.google.com/go/vmwareengine v0.1.0 h1:JMPZaOT/gIUxVlTqSl/QQ32Y2k+r0stNeM1NSqhVP9o= 118 | cloud.google.com/go/vpcaccess v1.5.0 h1:woHXXtnW8b9gLFdWO9HLPalAddBQ9V4LT+1vjKwR3W8= 119 | cloud.google.com/go/webrisk v1.7.0 h1:ypSnpGlJnZSXbN9a13PDmAYvVekBLnGKxQ3Q9SMwnYY= 120 | cloud.google.com/go/websecurityscanner v1.4.0 h1:y7yIFg/h/mO+5Y5aCOtVAnpGUOgqCH5rXQ2Oc8Oq2+g= 121 | cloud.google.com/go/workflows v1.9.0 h1:7Chpin9p50NTU8Tb7qk+I11U/IwVXmDhEoSsdccvInE= 122 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9 h1:VpgP7xuJadIUuKccphEpTJnWhS2jkQyMt6Y7pJCD7fY= 123 | github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= 124 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc= 125 | github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= 126 | github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg= 127 | github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g= 128 | github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= 129 | github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= 130 | github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= 131 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= 132 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= 133 | github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI= 134 | github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe h1:QQ3GSy+MqSHxm/d8nCtnAiZdYFd45cYZPs8vOOIYKfk= 135 | github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b h1:ACGZRIr7HsgBKHsueQ1yM4WaVaXh21ynwqsF8M8tXhA= 136 | github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534 h1:rtAn27wIbmOGUs7RIbVgPEjb31ehTVniDwPGXyMxm5U= 137 | github.com/envoyproxy/go-control-plane v0.10.3 h1:xdCVXxEe0Y3FQith+0cj2irwZudqGYvecuLB1HtdexY= 138 | github.com/envoyproxy/protoc-gen-validate v0.9.1 h1:PS7VIOgmSVhWUEeZwTe7z7zouA22Cr590PzXKbZHOVY= 139 | github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= 140 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0= 141 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4 h1:WtGNWLvXpe6ZudgnXrq0barxBImvnnJoMEhXAzcbM0I= 142 | github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA= 143 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= 144 | github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc= 145 | github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= 146 | github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= 147 | github.com/google/martian/v3 v3.0.0 h1:pMen7vLs8nvgEYhywH3KDWJIJTeEr2ULsVWHWYHQyBs= 148 | github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99 h1:Ak8CrdlwwXwAZxzS66vgPt4U8yUZX7JwLvVR58FN5jM= 149 | github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA= 150 | github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= 151 | github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= 152 | github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= 153 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6 h1:UDMh68UUwekSh5iP2OMhRRZJiiBccgV7axzUG8vi56c= 154 | github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o= 155 | github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= 156 | github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw= 157 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM= 158 | github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s= 159 | github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk= 160 | github.com/rs/xid v1.3.0 h1:6NjYksEUlhurdVehpc7S7dk6DAmcKv8V9gG0FsVN2U4= 161 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ= 162 | github.com/yuin/goldmark v1.1.32 h1:5tjfNdR2ki3yYQ842+eX2sQHeiwpKJ0RnHO4IYOc4V8= 163 | go.opencensus.io v0.22.4 h1:LYy1Hy3MJdrCdMwwzxA/dRok4ejH+RwNGbuoD9fCjto= 164 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= 165 | golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6 h1:QE6XYQK6naiK1EPAe1g/ILLxN5RBoH5xkJk3CqlMI/Y= 166 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4= 167 | golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k= 168 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs= 169 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= 170 | golang.org/x/oauth2 v0.4.0 h1:NF0gk8LVPg1Ml7SSbGyySuoxdsXitj7TvgvuRxIMc/M= 171 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA= 172 | golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= 173 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= 174 | golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= 175 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= 176 | google.golang.org/api v0.30.0 h1:yfrXXP61wVuLb0vBcG6qaOoIoqYEzOQS8jum51jkv2w= 177 | google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= 178 | gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8= 179 | gopkg.in/yaml.v2 v2.2.3 h1:fvjTMHxHEw/mxHbtzPi3JCcKXQRAnQTBRo6YCJSVHKI= 180 | honnef.co/go/tools v0.0.1-2020.1.4 h1:UoveltGrhghAA7ePc+e+QYDHXrBps2PqFZiHkGR/xK8= 181 | rsc.io/binaryregexp v0.2.0 h1:HfqmD5MEmC0zvwBuF187nq9mdnXjXsSivRiXN7SmRkE= 182 | rsc.io/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY= 183 | rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4= 184 | -------------------------------------------------------------------------------- /ops/grafana/provisioning/dashboards/general/demo-dashboard.json: -------------------------------------------------------------------------------- 1 | { 2 | "annotations": { 3 | "list": [ 4 | { 5 | "builtIn": 1, 6 | "datasource": { 7 | "type": "grafana", 8 | "uid": "-- Grafana --" 9 | }, 10 | "enable": true, 11 | "hide": true, 12 | "iconColor": "rgba(0, 211, 255, 1)", 13 | "name": "Annotations & Alerts", 14 | "target": { 15 | "limit": 100, 16 | "matchAny": false, 17 | "tags": [], 18 | "type": "dashboard" 19 | }, 20 | "type": "dashboard" 21 | } 22 | ] 23 | }, 24 | "editable": true, 25 | "fiscalYearStartMonth": 0, 26 | "graphTooltip": 0, 27 | "links": [], 28 | "liveNow": false, 29 | "panels": [ 30 | { 31 | "collapsed": false, 32 | "gridPos": { 33 | "h": 1, 34 | "w": 24, 35 | "x": 0, 36 | "y": 0 37 | }, 38 | "id": 14, 39 | "panels": [], 40 | "title": "Metrics", 41 | "type": "row" 42 | }, 43 | { 44 | "datasource": { 45 | "type": "prometheus", 46 | "uid": "webstore-metrics" 47 | }, 48 | "fieldConfig": { 49 | "defaults": { 50 | "color": { 51 | "mode": "palette-classic" 52 | }, 53 | "custom": { 54 | "axisCenteredZero": false, 55 | "axisColorMode": "text", 56 | "axisLabel": "", 57 | "axisPlacement": "auto", 58 | "barAlignment": 0, 59 | "drawStyle": "line", 60 | "fillOpacity": 0, 61 | "gradientMode": "none", 62 | "hideFrom": { 63 | "legend": false, 64 | "tooltip": false, 65 | "viz": false 66 | }, 67 | "lineInterpolation": "linear", 68 | "lineWidth": 1, 69 | "pointSize": 5, 70 | "scaleDistribution": { 71 | "type": "linear" 72 | }, 73 | "showPoints": "auto", 74 | "spanNulls": false, 75 | "stacking": { 76 | "group": "A", 77 | "mode": "none" 78 | }, 79 | "thresholdsStyle": { 80 | "mode": "off" 81 | } 82 | }, 83 | "mappings": [], 84 | "thresholds": { 85 | "mode": "absolute", 86 | "steps": [ 87 | { 88 | "color": "green", 89 | "value": null 90 | }, 91 | { 92 | "color": "red", 93 | "value": 80 94 | } 95 | ] 96 | }, 97 | "unit": "percent" 98 | }, 99 | "overrides": [] 100 | }, 101 | "gridPos": { 102 | "h": 8, 103 | "w": 12, 104 | "x": 0, 105 | "y": 1 106 | }, 107 | "id": 6, 108 | "options": { 109 | "legend": { 110 | "calcs": [], 111 | "displayMode": "list", 112 | "placement": "bottom", 113 | "showLegend": true 114 | }, 115 | "tooltip": { 116 | "mode": "single", 117 | "sort": "none" 118 | } 119 | }, 120 | "targets": [ 121 | { 122 | "datasource": { 123 | "type": "prometheus", 124 | "uid": "webstore-metrics" 125 | }, 126 | "editorMode": "code", 127 | "expr": "rate(runtime_cpython_cpu_time{type=~\"system\"}[$__interval])*100", 128 | "legendFormat": "__auto", 129 | "range": true, 130 | "refId": "A" 131 | }, 132 | { 133 | "datasource": { 134 | "type": "prometheus", 135 | "uid": "webstore-metrics" 136 | }, 137 | "editorMode": "code", 138 | "expr": "rate(runtime_cpython_cpu_time{type=~\"user\"}[$__interval])*100", 139 | "hide": false, 140 | "legendFormat": "__auto", 141 | "range": true, 142 | "refId": "B" 143 | } 144 | ], 145 | "title": "Recommendation Service (CPU%)", 146 | "type": "timeseries" 147 | }, 148 | { 149 | "datasource": { 150 | "type": "prometheus", 151 | "uid": "webstore-metrics" 152 | }, 153 | "fieldConfig": { 154 | "defaults": { 155 | "color": { 156 | "mode": "palette-classic" 157 | }, 158 | "custom": { 159 | "axisCenteredZero": false, 160 | "axisColorMode": "text", 161 | "axisLabel": "", 162 | "axisPlacement": "auto", 163 | "barAlignment": 0, 164 | "drawStyle": "line", 165 | "fillOpacity": 0, 166 | "gradientMode": "none", 167 | "hideFrom": { 168 | "legend": false, 169 | "tooltip": false, 170 | "viz": false 171 | }, 172 | "lineInterpolation": "linear", 173 | "lineWidth": 1, 174 | "pointSize": 5, 175 | "scaleDistribution": { 176 | "type": "linear" 177 | }, 178 | "showPoints": "auto", 179 | "spanNulls": false, 180 | "stacking": { 181 | "group": "A", 182 | "mode": "none" 183 | }, 184 | "thresholdsStyle": { 185 | "mode": "off" 186 | } 187 | }, 188 | "mappings": [], 189 | "thresholds": { 190 | "mode": "absolute", 191 | "steps": [ 192 | { 193 | "color": "green", 194 | "value": null 195 | }, 196 | { 197 | "color": "red", 198 | "value": 80 199 | } 200 | ] 201 | }, 202 | "unit": "decmbytes" 203 | }, 204 | "overrides": [] 205 | }, 206 | "gridPos": { 207 | "h": 8, 208 | "w": 12, 209 | "x": 12, 210 | "y": 1 211 | }, 212 | "id": 8, 213 | "options": { 214 | "legend": { 215 | "calcs": [], 216 | "displayMode": "list", 217 | "placement": "bottom", 218 | "showLegend": true 219 | }, 220 | "tooltip": { 221 | "mode": "single", 222 | "sort": "none" 223 | } 224 | }, 225 | "targets": [ 226 | { 227 | "datasource": { 228 | "type": "prometheus", 229 | "uid": "webstore-metrics" 230 | }, 231 | "editorMode": "code", 232 | "expr": "rate(runtime_cpython_memory{type=~\"rss|vms\"}[$__interval])/1024/1024", 233 | "legendFormat": "__auto", 234 | "range": true, 235 | "refId": "A" 236 | } 237 | ], 238 | "title": "Recommendation Service (Memory)", 239 | "type": "timeseries" 240 | }, 241 | { 242 | "datasource": { 243 | "type": "prometheus", 244 | "uid": "webstore-metrics" 245 | }, 246 | "fieldConfig": { 247 | "defaults": { 248 | "color": { 249 | "mode": "palette-classic" 250 | }, 251 | "custom": { 252 | "axisCenteredZero": false, 253 | "axisColorMode": "text", 254 | "axisLabel": "", 255 | "axisPlacement": "auto", 256 | "barAlignment": 0, 257 | "drawStyle": "bars", 258 | "fillOpacity": 0, 259 | "gradientMode": "none", 260 | "hideFrom": { 261 | "legend": false, 262 | "tooltip": false, 263 | "viz": false 264 | }, 265 | "lineInterpolation": "linear", 266 | "lineWidth": 1, 267 | "pointSize": 5, 268 | "scaleDistribution": { 269 | "type": "linear" 270 | }, 271 | "showPoints": "auto", 272 | "spanNulls": false, 273 | "stacking": { 274 | "group": "A", 275 | "mode": "none" 276 | }, 277 | "thresholdsStyle": { 278 | "mode": "off" 279 | } 280 | }, 281 | "mappings": [], 282 | "thresholds": { 283 | "mode": "absolute", 284 | "steps": [ 285 | { 286 | "color": "green", 287 | "value": null 288 | }, 289 | { 290 | "color": "red", 291 | "value": 80 292 | } 293 | ] 294 | } 295 | }, 296 | "overrides": [] 297 | }, 298 | "gridPos": { 299 | "h": 8, 300 | "w": 12, 301 | "x": 0, 302 | "y": 9 303 | }, 304 | "id": 4, 305 | "options": { 306 | "legend": { 307 | "calcs": [], 308 | "displayMode": "list", 309 | "placement": "bottom", 310 | "showLegend": true 311 | }, 312 | "tooltip": { 313 | "mode": "single", 314 | "sort": "none" 315 | } 316 | }, 317 | "targets": [ 318 | { 319 | "datasource": { 320 | "type": "prometheus", 321 | "uid": "webstore-metrics" 322 | }, 323 | "editorMode": "code", 324 | "expr": "rate(app_recommendations_counter{recommendation_type=\"catalog\"}[$__interval])", 325 | "legendFormat": "__auto", 326 | "range": true, 327 | "refId": "A" 328 | } 329 | ], 330 | "title": "Recommendations Count", 331 | "type": "timeseries" 332 | }, 333 | { 334 | "datasource": { 335 | "type": "prometheus", 336 | "uid": "webstore-metrics" 337 | }, 338 | "fieldConfig": { 339 | "defaults": { 340 | "color": { 341 | "mode": "palette-classic" 342 | }, 343 | "custom": { 344 | "axisCenteredZero": false, 345 | "axisColorMode": "text", 346 | "axisLabel": "", 347 | "axisPlacement": "auto", 348 | "barAlignment": 0, 349 | "drawStyle": "line", 350 | "fillOpacity": 0, 351 | "gradientMode": "none", 352 | "hideFrom": { 353 | "legend": false, 354 | "tooltip": false, 355 | "viz": false 356 | }, 357 | "lineInterpolation": "linear", 358 | "lineWidth": 1, 359 | "pointSize": 5, 360 | "scaleDistribution": { 361 | "type": "linear" 362 | }, 363 | "showPoints": "auto", 364 | "spanNulls": false, 365 | "stacking": { 366 | "group": "A", 367 | "mode": "none" 368 | }, 369 | "thresholdsStyle": { 370 | "mode": "off" 371 | } 372 | }, 373 | "mappings": [], 374 | "thresholds": { 375 | "mode": "absolute", 376 | "steps": [ 377 | { 378 | "color": "green", 379 | "value": null 380 | }, 381 | { 382 | "color": "red", 383 | "value": 80 384 | } 385 | ] 386 | } 387 | }, 388 | "overrides": [] 389 | }, 390 | "gridPos": { 391 | "h": 8, 392 | "w": 12, 393 | "x": 12, 394 | "y": 9 395 | }, 396 | "id": 10, 397 | "options": { 398 | "legend": { 399 | "calcs": [], 400 | "displayMode": "list", 401 | "placement": "bottom", 402 | "showLegend": true 403 | }, 404 | "tooltip": { 405 | "mode": "single", 406 | "sort": "none" 407 | } 408 | }, 409 | "targets": [ 410 | { 411 | "datasource": { 412 | "type": "prometheus", 413 | "uid": "webstore-metrics" 414 | }, 415 | "editorMode": "code", 416 | "expr": "rate(calls_total{status_code=\"STATUS_CODE_ERROR\"}[$__interval])", 417 | "legendFormat": "__auto", 418 | "range": true, 419 | "refId": "A" 420 | } 421 | ], 422 | "title": "Error Rate", 423 | "type": "timeseries" 424 | }, 425 | { 426 | "datasource": { 427 | "type": "prometheus", 428 | "uid": "webstore-metrics" 429 | }, 430 | "fieldConfig": { 431 | "defaults": { 432 | "color": { 433 | "mode": "palette-classic" 434 | }, 435 | "custom": { 436 | "axisCenteredZero": false, 437 | "axisColorMode": "text", 438 | "axisLabel": "", 439 | "axisPlacement": "auto", 440 | "barAlignment": 0, 441 | "drawStyle": "line", 442 | "fillOpacity": 0, 443 | "gradientMode": "none", 444 | "hideFrom": { 445 | "legend": false, 446 | "tooltip": false, 447 | "viz": false 448 | }, 449 | "lineInterpolation": "linear", 450 | "lineWidth": 1, 451 | "pointSize": 5, 452 | "scaleDistribution": { 453 | "type": "linear" 454 | }, 455 | "showPoints": "auto", 456 | "spanNulls": false, 457 | "stacking": { 458 | "group": "A", 459 | "mode": "none" 460 | }, 461 | "thresholdsStyle": { 462 | "mode": "off" 463 | } 464 | }, 465 | "mappings": [], 466 | "thresholds": { 467 | "mode": "absolute", 468 | "steps": [ 469 | { 470 | "color": "green", 471 | "value": null 472 | }, 473 | { 474 | "color": "red", 475 | "value": 80 476 | } 477 | ] 478 | }, 479 | "unit": "dtdurationms" 480 | }, 481 | "overrides": [] 482 | }, 483 | "gridPos": { 484 | "h": 8, 485 | "w": 12, 486 | "x": 0, 487 | "y": 17 488 | }, 489 | "id": 2, 490 | "options": { 491 | "legend": { 492 | "calcs": [], 493 | "displayMode": "list", 494 | "placement": "bottom", 495 | "showLegend": true 496 | }, 497 | "tooltip": { 498 | "mode": "single", 499 | "sort": "none" 500 | } 501 | }, 502 | "targets": [ 503 | { 504 | "datasource": { 505 | "type": "prometheus", 506 | "uid": "webstore-metrics" 507 | }, 508 | "editorMode": "code", 509 | "exemplar": true, 510 | "expr": "histogram_quantile(0.50, sum(rate(latency_bucket{service_name=\"${service}\"}[$__rate_interval])) by (le))", 511 | "legendFormat": "__auto", 512 | "range": true, 513 | "refId": "A" 514 | }, 515 | { 516 | "datasource": { 517 | "type": "prometheus", 518 | "uid": "webstore-metrics" 519 | }, 520 | "editorMode": "code", 521 | "exemplar": false, 522 | "expr": "histogram_quantile(0.95, sum(rate(latency_bucket{service_name=\"${service}\"}[$__rate_interval])) by (le))", 523 | "hide": false, 524 | "legendFormat": "__auto", 525 | "range": true, 526 | "refId": "B" 527 | }, 528 | { 529 | "datasource": { 530 | "type": "prometheus", 531 | "uid": "webstore-metrics" 532 | }, 533 | "editorMode": "code", 534 | "exemplar": false, 535 | "expr": "histogram_quantile(0.99, sum(rate(latency_bucket{service_name=\"${service}\"}[$__rate_interval])) by (le))", 536 | "hide": false, 537 | "legendFormat": "__auto", 538 | "range": true, 539 | "refId": "C" 540 | }, 541 | { 542 | "datasource": { 543 | "type": "prometheus", 544 | "uid": "webstore-metrics" 545 | }, 546 | "editorMode": "code", 547 | "exemplar": false, 548 | "expr": "histogram_quantile(0.999, sum(rate(latency_bucket{service_name=\"${service}\"}[$__rate_interval])) by (le))", 549 | "hide": false, 550 | "legendFormat": "__auto", 551 | "range": true, 552 | "refId": "D" 553 | } 554 | ], 555 | "title": "Service Latency (from SpanMetrics)", 556 | "type": "timeseries" 557 | }, 558 | { 559 | "datasource": { 560 | "type": "prometheus", 561 | "uid": "webstore-metrics" 562 | }, 563 | "fieldConfig": { 564 | "defaults": { 565 | "color": { 566 | "mode": "palette-classic" 567 | }, 568 | "custom": { 569 | "axisCenteredZero": false, 570 | "axisColorMode": "text", 571 | "axisLabel": "", 572 | "axisPlacement": "auto", 573 | "barAlignment": 0, 574 | "drawStyle": "line", 575 | "fillOpacity": 0, 576 | "gradientMode": "none", 577 | "hideFrom": { 578 | "legend": false, 579 | "tooltip": false, 580 | "viz": false 581 | }, 582 | "lineInterpolation": "linear", 583 | "lineWidth": 1, 584 | "pointSize": 5, 585 | "scaleDistribution": { 586 | "type": "linear" 587 | }, 588 | "showPoints": "auto", 589 | "spanNulls": false, 590 | "stacking": { 591 | "group": "A", 592 | "mode": "none" 593 | }, 594 | "thresholdsStyle": { 595 | "mode": "off" 596 | } 597 | }, 598 | "mappings": [], 599 | "thresholds": { 600 | "mode": "absolute", 601 | "steps": [ 602 | { 603 | "color": "green", 604 | "value": null 605 | }, 606 | { 607 | "color": "red", 608 | "value": 80 609 | } 610 | ] 611 | }, 612 | "unit": "reqps" 613 | }, 614 | "overrides": [] 615 | }, 616 | "gridPos": { 617 | "h": 8, 618 | "w": 12, 619 | "x": 12, 620 | "y": 17 621 | }, 622 | "id": 12, 623 | "options": { 624 | "legend": { 625 | "calcs": [], 626 | "displayMode": "list", 627 | "placement": "bottom", 628 | "showLegend": true 629 | }, 630 | "tooltip": { 631 | "mode": "single", 632 | "sort": "none" 633 | } 634 | }, 635 | "targets": [ 636 | { 637 | "datasource": { 638 | "type": "prometheus", 639 | "uid": "webstore-metrics" 640 | }, 641 | "editorMode": "code", 642 | "expr": "rate(latency_count{service_name=\"${service}\"}[$__rate_interval])", 643 | "legendFormat": "__auto", 644 | "range": true, 645 | "refId": "A" 646 | } 647 | ], 648 | "title": "Endpoint Rate by Service", 649 | "type": "timeseries" 650 | } 651 | ], 652 | "schemaVersion": 37, 653 | "style": "dark", 654 | "tags": [], 655 | "templating": { 656 | "list": [ 657 | { 658 | "allValue": "", 659 | "current": { 660 | "selected": false, 661 | "text": "featureflagservice", 662 | "value": "featureflagservice" 663 | }, 664 | "datasource": { 665 | "type": "prometheus", 666 | "uid": "webstore-metrics" 667 | }, 668 | "definition": "latency_bucket", 669 | "hide": 0, 670 | "includeAll": false, 671 | "multi": false, 672 | "name": "service", 673 | "options": [], 674 | "query": { 675 | "query": "latency_bucket", 676 | "refId": "StandardVariableQuery" 677 | }, 678 | "refresh": 1, 679 | "regex": "/.*service_name=\\\"([^\\\"]+)\\\".*/", 680 | "skipUrlSync": false, 681 | "sort": 1, 682 | "type": "query" 683 | } 684 | ] 685 | }, 686 | "time": { 687 | "from": "now-15m", 688 | "to": "now" 689 | }, 690 | "timepicker": {}, 691 | "timezone": "", 692 | "title": "Demo Dashboard", 693 | "uid": "W2gX2zHVk", 694 | "version": 1, 695 | "weekStart": "" 696 | } 697 | -------------------------------------------------------------------------------- /auth/go.work.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.105.0 h1:DNtEKRBAAzeS4KyIory52wWHuClNaXJ5x1F7xa4q+5Y= 2 | cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM= 3 | cloud.google.com/go/accessapproval v1.5.0 h1:/nTivgnV/n1CaAeo+ekGexTYUsKEU9jUVkoY5359+3Q= 4 | cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw= 5 | cloud.google.com/go/accesscontextmanager v1.4.0 h1:CFhNhU7pcD11cuDkQdrE6PQJgv0EXNKNv06jIzbLlCU= 6 | cloud.google.com/go/accesscontextmanager v1.4.0/go.mod h1:/Kjh7BBu/Gh83sv+K60vN9QE5NJcd80sU33vIe2IFPE= 7 | cloud.google.com/go/aiplatform v1.27.0 h1:DBi3Jk9XjCJ4pkkLM4NqKgj3ozUL1wq4l+d3/jTGXAI= 8 | cloud.google.com/go/aiplatform v1.27.0/go.mod h1:Bvxqtl40l0WImSb04d0hXFU7gDOiq9jQmorivIiWcKg= 9 | cloud.google.com/go/analytics v0.12.0 h1:NKw6PpQi6V1O+KsjuTd+bhip9d0REYu4NevC45vtGp8= 10 | cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4= 11 | cloud.google.com/go/apigateway v1.4.0 h1:IIoXKR7FKrEAQhMTz5hK2wiDz2WNFHS7eVr/L1lE/rM= 12 | cloud.google.com/go/apigateway v1.4.0/go.mod h1:pHVY9MKGaH9PQ3pJ4YLzoj6U5FUDeDFBllIz7WmzJoc= 13 | cloud.google.com/go/apigeeconnect v1.4.0 h1:AONoTYJviyv1vS4IkvWzq69gEVdvHx35wKXc+e6wjZQ= 14 | cloud.google.com/go/apigeeconnect v1.4.0/go.mod h1:kV4NwOKqjvt2JYR0AoIWo2QGfoRtn/pkS3QlHp0Ni04= 15 | cloud.google.com/go/appengine v1.5.0 h1:lmG+O5oaR9xNwaRBwE2XoMhwQHsHql5IoiGr1ptdDwU= 16 | cloud.google.com/go/appengine v1.5.0/go.mod h1:TfasSozdkFI0zeoxW3PTBLiNqRmzraodCWatWI9Dmak= 17 | cloud.google.com/go/area120 v0.6.0 h1:TCMhwWEWhCn8d44/Zs7UCICTWje9j3HuV6nVGMjdpYw= 18 | cloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0= 19 | cloud.google.com/go/artifactregistry v1.9.0 h1:3d0LRAU1K6vfqCahhl9fx2oGHcq+s5gftdix4v8Ibrc= 20 | cloud.google.com/go/artifactregistry v1.9.0/go.mod h1:2K2RqvA2CYvAeARHRkLDhMDJ3OXy26h3XW+3/Jh2uYc= 21 | cloud.google.com/go/asset v1.10.0 h1:aCrlaLGJWTODJX4G56ZYzJefITKEWNfbjjtHSzWpxW0= 22 | cloud.google.com/go/asset v1.10.0/go.mod h1:pLz7uokL80qKhzKr4xXGvBQXnzHn5evJAEAtZiIb0wY= 23 | cloud.google.com/go/assuredworkloads v1.9.0 h1:hhIdCOowsT1GG5eMCIA0OwK6USRuYTou/1ZeNxCSRtA= 24 | cloud.google.com/go/assuredworkloads v1.9.0/go.mod h1:kFuI1P78bplYtT77Tb1hi0FMxM0vVpRC7VVoJC3ZoT0= 25 | cloud.google.com/go/automl v1.8.0 h1:BMioyXSbg7d7xLibn47cs0elW6RT780IUWr42W8rp2Q= 26 | cloud.google.com/go/automl v1.8.0/go.mod h1:xWx7G/aPEe/NP+qzYXktoBSDfjO+vnKMGgsApGJJquM= 27 | cloud.google.com/go/baremetalsolution v0.4.0 h1:g9KO6SkakcYPcc/XjAzeuUrEOXlYPnMpuiaywYaGrmQ= 28 | cloud.google.com/go/baremetalsolution v0.4.0/go.mod h1:BymplhAadOO/eBa7KewQ0Ppg4A4Wplbn+PsFKRLo0uI= 29 | cloud.google.com/go/batch v0.4.0 h1:1jvEBY55OH4Sd2FxEXQfxGExFWov1A/IaRe+Z5Z71Fw= 30 | cloud.google.com/go/batch v0.4.0/go.mod h1:WZkHnP43R/QCGQsZ+0JyG4i79ranE2u8xvjq/9+STPE= 31 | cloud.google.com/go/beyondcorp v0.3.0 h1:w+4kThysgl0JiKshi2MKDCg2NZgOyqOI0wq2eBZyrzA= 32 | cloud.google.com/go/beyondcorp v0.3.0/go.mod h1:E5U5lcrcXMsCuoDNyGrpyTm/hn7ne941Jz2vmksAxW8= 33 | cloud.google.com/go/bigquery v1.44.0 h1:Wi4dITi+cf9VYp4VH2T9O41w0kCW0uQTELq2Z6tukN0= 34 | cloud.google.com/go/bigquery v1.44.0/go.mod h1:0Y33VqXTEsbamHJvJHdFmtqHvMIY28aK1+dFsvaChGc= 35 | cloud.google.com/go/billing v1.7.0 h1:Xkii76HWELHwBtkQVZvqmSo9GTr0O+tIbRNnMcGdlg4= 36 | cloud.google.com/go/billing v1.7.0/go.mod h1:q457N3Hbj9lYwwRbnlD7vUpyjq6u5U1RAOArInEiD5Y= 37 | cloud.google.com/go/binaryauthorization v1.4.0 h1:pL70vXWn9TitQYXBWTK2abHl2JHLwkFRjYw6VflRqEA= 38 | cloud.google.com/go/binaryauthorization v1.4.0/go.mod h1:tsSPQrBd77VLplV70GUhBf/Zm3FsKmgSqgm4UmiDItk= 39 | cloud.google.com/go/certificatemanager v1.4.0 h1:tzbR4UHBbgsewMWUD93JHi8EBi/gHBoSAcY1/sThFGk= 40 | cloud.google.com/go/certificatemanager v1.4.0/go.mod h1:vowpercVFyqs8ABSmrdV+GiFf2H/ch3KyudYQEMM590= 41 | cloud.google.com/go/channel v1.9.0 h1:pNuUlZx0Jb0Ts9P312bmNMuH5IiFWIR4RUtLb70Ke5s= 42 | cloud.google.com/go/channel v1.9.0/go.mod h1:jcu05W0my9Vx4mt3/rEHpfxc9eKi9XwsdDL8yBMbKUk= 43 | cloud.google.com/go/cloudbuild v1.4.0 h1:TAAmCmAlOJ4uNBu6zwAjwhyl/7fLHHxIEazVhr3QBbQ= 44 | cloud.google.com/go/cloudbuild v1.4.0/go.mod h1:5Qwa40LHiOXmz3386FrjrYM93rM/hdRr7b53sySrTqA= 45 | cloud.google.com/go/clouddms v1.4.0 h1:UhzHIlgFfMr6luVYVNydw/pl9/U5kgtjCMJHnSvoVws= 46 | cloud.google.com/go/clouddms v1.4.0/go.mod h1:Eh7sUGCC+aKry14O1NRljhjyrr0NFC0G2cjwX0cByRk= 47 | cloud.google.com/go/cloudtasks v1.8.0 h1:faUiUgXjW8yVZ7XMnKHKm1WE4OldPBUWWfIRN/3z1dc= 48 | cloud.google.com/go/cloudtasks v1.8.0/go.mod h1:gQXUIwCSOI4yPVK7DgTVFiiP0ZW/eQkydWzwVMdHxrI= 49 | cloud.google.com/go/compute v1.15.1 h1:7UGq3QknM33pw5xATlpzeoomNxsacIVvTqTTvbfajmE= 50 | cloud.google.com/go/compute v1.15.1/go.mod h1:bjjoF/NtFUrkD/urWfdHaKuOPDR5nWIs63rR+SXhcpA= 51 | cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= 52 | cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= 53 | cloud.google.com/go/contactcenterinsights v1.4.0 h1:tTQLI/ZvguUf9Hv+36BkG2+/PeC8Ol1q4pBW+tgCx0A= 54 | cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck= 55 | cloud.google.com/go/container v1.7.0 h1:nbEK/59GyDRKKlo1SqpohY1TK8LmJ2XNcvS9Gyom2A0= 56 | cloud.google.com/go/container v1.7.0/go.mod h1:Dp5AHtmothHGX3DwwIHPgq45Y8KmNsgN3amoYfxVkLo= 57 | cloud.google.com/go/containeranalysis v0.6.0 h1:2824iym832ljKdVpCBnpqm5K94YT/uHTVhNF+dRTXPI= 58 | cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4= 59 | cloud.google.com/go/datacatalog v1.8.0 h1:6kZ4RIOW/uT7QWC5SfPfq/G8sYzr/v+UOmOAxy4Z1TE= 60 | cloud.google.com/go/datacatalog v1.8.0/go.mod h1:KYuoVOv9BM8EYz/4eMFxrr4DUKhGIOXxZoKYF5wdISM= 61 | cloud.google.com/go/dataflow v0.7.0 h1:CW3541Fm7KPTyZjJdnX6NtaGXYFn5XbFC5UcjgALKvU= 62 | cloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ= 63 | cloud.google.com/go/dataform v0.5.0 h1:vLwowLF2ZB5J5gqiZCzv076lDI/Rd7zYQQFu5XO1PSg= 64 | cloud.google.com/go/dataform v0.5.0/go.mod h1:GFUYRe8IBa2hcomWplodVmUx/iTL0FrsauObOM3Ipr0= 65 | cloud.google.com/go/datafusion v1.5.0 h1:j5m2hjWovTZDTQak4MJeXAR9yN7O+zMfULnjGw/OOLg= 66 | cloud.google.com/go/datafusion v1.5.0/go.mod h1:Kz+l1FGHB0J+4XF2fud96WMmRiq/wj8N9u007vyXZ2w= 67 | cloud.google.com/go/datalabeling v0.6.0 h1:dp8jOF21n/7jwgo/uuA0RN8hvLcKO4q6s/yvwevs2ZM= 68 | cloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ= 69 | cloud.google.com/go/dataplex v1.4.0 h1:cNxeA2DiWliQGi21kPRqnVeQ5xFhNoEjPRt1400Pm8Y= 70 | cloud.google.com/go/dataplex v1.4.0/go.mod h1:X51GfLXEMVJ6UN47ESVqvlsRplbLhcsAt0kZCCKsU0A= 71 | cloud.google.com/go/dataproc v1.8.0 h1:gVOqNmElfa6n/ccG/QDlfurMWwrK3ezvy2b2eDoCmS0= 72 | cloud.google.com/go/dataproc v1.8.0/go.mod h1:5OW+zNAH0pMpw14JVrPONsxMQYMBqJuzORhIBfBn9uI= 73 | cloud.google.com/go/dataqna v0.6.0 h1:gx9jr41ytcA3dXkbbd409euEaWtofCVXYBvJz3iYm18= 74 | cloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA= 75 | cloud.google.com/go/datastore v1.10.0 h1:4siQRf4zTiAVt/oeH4GureGkApgb2vtPQAtOmhpqQwE= 76 | cloud.google.com/go/datastore v1.10.0/go.mod h1:PC5UzAmDEkAmkfaknstTYbNpgE49HAgW2J1gcgUfmdM= 77 | cloud.google.com/go/datastream v1.5.0 h1:PgIgbhedBtYBU6POGXFMn2uSl9vpqubc3ewTNdcU8Mk= 78 | cloud.google.com/go/datastream v1.5.0/go.mod h1:6TZMMNPwjUqZHBKPQ1wwXpb0d5VDVPl2/XoS5yi88q4= 79 | cloud.google.com/go/deploy v1.5.0 h1:kI6dxt8Ml0is/x7YZjLveTvR7YPzXAUD/8wQZ2nH5zA= 80 | cloud.google.com/go/deploy v1.5.0/go.mod h1:ffgdD0B89tToyW/U/D2eL0jN2+IEV/3EMuXHA0l4r+s= 81 | cloud.google.com/go/dialogflow v1.19.0 h1:HYHVOkoxQ9bSfNIelSZYNAtUi4CeSrCnROyOsbOqPq8= 82 | cloud.google.com/go/dialogflow v1.19.0/go.mod h1:JVmlG1TwykZDtxtTXujec4tQ+D8SBFMoosgy+6Gn0s0= 83 | cloud.google.com/go/dlp v1.7.0 h1:9I4BYeJSVKoSKgjr70fLdRDumqcUeVmHV4fd5f9LR6Y= 84 | cloud.google.com/go/dlp v1.7.0/go.mod h1:68ak9vCiMBjbasxeVD17hVPxDEck+ExiHavX8kiHG+Q= 85 | cloud.google.com/go/documentai v1.10.0 h1:jfq09Fdjtnpnmt/MLyf6A3DM3ynb8B2na0K+vSXvpFM= 86 | cloud.google.com/go/documentai v1.10.0/go.mod h1:vod47hKQIPeCfN2QS/jULIvQTugbmdc0ZvxxfQY1bg4= 87 | cloud.google.com/go/domains v0.7.0 h1:pu3JIgC1rswIqi5romW0JgNO6CTUydLYX8zyjiAvO1c= 88 | cloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg= 89 | cloud.google.com/go/edgecontainer v0.2.0 h1:hd6J2n5dBBRuAqnNUEsKWrp6XNPKsaxwwIyzOPZTokk= 90 | cloud.google.com/go/edgecontainer v0.2.0/go.mod h1:RTmLijy+lGpQ7BXuTDa4C4ssxyXT34NIuHIgKuP4s5w= 91 | cloud.google.com/go/errorreporting v0.3.0 h1:kj1XEWMu8P0qlLhm3FwcaFsUvXChV/OraZwA70trRR0= 92 | cloud.google.com/go/errorreporting v0.3.0/go.mod h1:xsP2yaAp+OAW4OIm60An2bbLpqIhKXdWR/tawvl7QzU= 93 | cloud.google.com/go/essentialcontacts v1.4.0 h1:b6csrQXCHKQmfo9h3dG/pHyoEh+fQG1Yg78a53LAviY= 94 | cloud.google.com/go/essentialcontacts v1.4.0/go.mod h1:8tRldvHYsmnBCHdFpvU+GL75oWiBKl80BiqlFh9tp+8= 95 | cloud.google.com/go/eventarc v1.8.0 h1:AgCqrmMMIcel5WWKkzz5EkCUKC3Rl5LNMMYsS+LvsI0= 96 | cloud.google.com/go/eventarc v1.8.0/go.mod h1:imbzxkyAU4ubfsaKYdQg04WS1NvncblHEup4kvF+4gw= 97 | cloud.google.com/go/filestore v1.4.0 h1:yjKOpzvqtDmL5AXbKttLc8j0hL20kuC1qPdy5HPcxp0= 98 | cloud.google.com/go/filestore v1.4.0/go.mod h1:PaG5oDfo9r224f8OYXURtAsY+Fbyq/bLYoINEK8XQAI= 99 | cloud.google.com/go/firestore v1.9.0 h1:IBlRyxgGySXu5VuW0RgGFlTtLukSnNkpDiEOMkQkmpA= 100 | cloud.google.com/go/firestore v1.9.0/go.mod h1:HMkjKHNTtRyZNiMzu7YAsLr9K3X2udY2AMwDaMEQiiE= 101 | cloud.google.com/go/functions v1.9.0 h1:35tgv1fQOtvKqH/uxJMzX3w6usneJ0zXpsFr9KAVhNE= 102 | cloud.google.com/go/functions v1.9.0/go.mod h1:Y+Dz8yGguzO3PpIjhLTbnqV1CWmgQ5UwtlpzoyquQ08= 103 | cloud.google.com/go/gaming v1.8.0 h1:97OAEQtDazAJD7yh/kvQdSCQuTKdR0O+qWAJBZJ4xiA= 104 | cloud.google.com/go/gaming v1.8.0/go.mod h1:xAqjS8b7jAVW0KFYeRUxngo9My3f33kFmua++Pi+ggM= 105 | cloud.google.com/go/gkebackup v0.3.0 h1:4K+jiv4ocqt1niN8q5Imd8imRoXBHTrdnJVt/uFFxF4= 106 | cloud.google.com/go/gkebackup v0.3.0/go.mod h1:n/E671i1aOQvUxT541aTkCwExO/bTer2HDlj4TsBRAo= 107 | cloud.google.com/go/gkeconnect v0.6.0 h1:zAcvDa04tTnGdu6TEZewaLN2tdMtUOJJ7fEceULjguA= 108 | cloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A= 109 | cloud.google.com/go/gkehub v0.10.0 h1:JTcTaYQRGsVm+qkah7WzHb6e9sf1C0laYdRPn9aN+vg= 110 | cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0= 111 | cloud.google.com/go/gkemulticloud v0.4.0 h1:8F1NhJj8ucNj7lK51UZMtAjSWTgP1zO18XF6vkfiPPU= 112 | cloud.google.com/go/gkemulticloud v0.4.0/go.mod h1:E9gxVBnseLWCk24ch+P9+B2CoDFJZTyIgLKSalC7tuI= 113 | cloud.google.com/go/gsuiteaddons v1.4.0 h1:TGT2oGmO5q3VH6SjcrlgPUWI0njhYv4kywLm6jag0to= 114 | cloud.google.com/go/gsuiteaddons v1.4.0/go.mod h1:rZK5I8hht7u7HxFQcFei0+AtfS9uSushomRlg+3ua1o= 115 | cloud.google.com/go/iam v0.8.0 h1:E2osAkZzxI/+8pZcxVLcDtAQx/u+hZXVryUaYQ5O0Kk= 116 | cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE= 117 | cloud.google.com/go/iap v1.5.0 h1:BGEXovwejOCt1zDk8hXq0bOhhRu9haXKWXXXp2B4wBM= 118 | cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A= 119 | cloud.google.com/go/ids v1.2.0 h1:LncHK4HHucb5Du310X8XH9/ICtMwZ2PCfK0ScjWiJoY= 120 | cloud.google.com/go/ids v1.2.0/go.mod h1:5WXvp4n25S0rA/mQWAg1YEEBBq6/s+7ml1RDCW1IrcY= 121 | cloud.google.com/go/iot v1.4.0 h1:Y9+oZT9jD4GUZzORXTU45XsnQrhxmDT+TFbPil6pRVQ= 122 | cloud.google.com/go/iot v1.4.0/go.mod h1:dIDxPOn0UvNDUMD8Ger7FIaTuvMkj+aGk94RPP0iV+g= 123 | cloud.google.com/go/kms v1.6.0 h1:OWRZzrPmOZUzurjI2FBGtgY2mB1WaJkqhw6oIwSj0Yg= 124 | cloud.google.com/go/kms v1.6.0/go.mod h1:Jjy850yySiasBUDi6KFUwUv2n1+o7QZFyuUJg6OgjA0= 125 | cloud.google.com/go/language v1.8.0 h1:3Wa+IUMamL4JH3Zd3cDZUHpwyqplTACt6UZKRD2eCL4= 126 | cloud.google.com/go/language v1.8.0/go.mod h1:qYPVHf7SPoNNiCL2Dr0FfEFNil1qi3pQEyygwpgVKB8= 127 | cloud.google.com/go/lifesciences v0.6.0 h1:tIqhivE2LMVYkX0BLgG7xL64oNpDaFFI7teunglt1tI= 128 | cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08= 129 | cloud.google.com/go/logging v1.6.1 h1:ZBsZK+JG+oCDT+vaxwqF2egKNRjz8soXiS6Xv79benI= 130 | cloud.google.com/go/logging v1.6.1/go.mod h1:5ZO0mHHbvm8gEmeEUHrmDlTDSu5imF6MUP9OfilNXBw= 131 | cloud.google.com/go/longrunning v0.3.0 h1:NjljC+FYPV3uh5/OwWT6pVU+doBqMg2x/rZlE+CamDs= 132 | cloud.google.com/go/longrunning v0.3.0/go.mod h1:qth9Y41RRSUE69rDcOn6DdK3HfQfsUI0YSmW3iIlLJc= 133 | cloud.google.com/go/managedidentities v1.4.0 h1:3Kdajn6X25yWQFhFCErmKSYTSvkEd3chJROny//F1A0= 134 | cloud.google.com/go/managedidentities v1.4.0/go.mod h1:NWSBYbEMgqmbZsLIyKvxrYbtqOsxY1ZrGM+9RgDqInM= 135 | cloud.google.com/go/maps v0.1.0 h1:kLReRbclTgJefw2fcCbdLPLhPj0U6UUWN10ldG8sdOU= 136 | cloud.google.com/go/maps v0.1.0/go.mod h1:BQM97WGyfw9FWEmQMpZ5T6cpovXXSd1cGmFma94eubI= 137 | cloud.google.com/go/mediatranslation v0.6.0 h1:qAJzpxmEX+SeND10Y/4868L5wfZpo4Y3BIEnIieP4dk= 138 | cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w= 139 | cloud.google.com/go/memcache v1.7.0 h1:yLxUzJkZVSH2kPaHut7k+7sbIBFpvSh1LW9qjM2JDjA= 140 | cloud.google.com/go/memcache v1.7.0/go.mod h1:ywMKfjWhNtkQTxrWxCkCFkoPjLHPW6A7WOTVI8xy3LY= 141 | cloud.google.com/go/metastore v1.8.0 h1:3KcShzqWdqxrDEXIBWpYJpOOrgpDj+HlBi07Grot49Y= 142 | cloud.google.com/go/metastore v1.8.0/go.mod h1:zHiMc4ZUpBiM7twCIFQmJ9JMEkDSyZS9U12uf7wHqSI= 143 | cloud.google.com/go/monitoring v1.8.0 h1:c9riaGSPQ4dUKWB+M1Fl0N+iLxstMbCktdEwYSPGDvA= 144 | cloud.google.com/go/monitoring v1.8.0/go.mod h1:E7PtoMJ1kQXWxPjB6mv2fhC5/15jInuulFdYYtlcvT4= 145 | cloud.google.com/go/networkconnectivity v1.7.0 h1:BVdIKaI68bihnXGdCVL89Jsg9kq2kg+II30fjVqo62E= 146 | cloud.google.com/go/networkconnectivity v1.7.0/go.mod h1:RMuSbkdbPwNMQjB5HBWD5MpTBnNm39iAVpC3TmsExt8= 147 | cloud.google.com/go/networkmanagement v1.5.0 h1:mDHA3CDW00imTvC5RW6aMGsD1bH+FtKwZm/52BxaiMg= 148 | cloud.google.com/go/networkmanagement v1.5.0/go.mod h1:ZnOeZ/evzUdUsnvRt792H0uYEnHQEMaz+REhhzJRcf4= 149 | cloud.google.com/go/networksecurity v0.6.0 h1:qDEX/3sipg9dS5JYsAY+YvgTjPR63cozzAWop8oZS94= 150 | cloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU= 151 | cloud.google.com/go/notebooks v1.5.0 h1:AC8RPjNvel3ExgXjO1YOAz+teg9+j+89TNxa7pIZfww= 152 | cloud.google.com/go/notebooks v1.5.0/go.mod h1:q8mwhnP9aR8Hpfnrc5iN5IBhrXUy8S2vuYs+kBJ/gu0= 153 | cloud.google.com/go/optimization v1.2.0 h1:7PxOq9VTT7TMib/6dMoWpMvWS2E4dJEvtYzjvBreaec= 154 | cloud.google.com/go/optimization v1.2.0/go.mod h1:Lr7SOHdRDENsh+WXVmQhQTrzdu9ybg0NecjHidBq6xs= 155 | cloud.google.com/go/orchestration v1.4.0 h1:39d6tqvNjd/wsSub1Bn4cEmrYcet5Ur6xpaN+SxOxtY= 156 | cloud.google.com/go/orchestration v1.4.0/go.mod h1:6W5NLFWs2TlniBphAViZEVhrXRSMgUGDfW7vrWKvsBk= 157 | cloud.google.com/go/orgpolicy v1.5.0 h1:erF5PHqDZb6FeFrUHiYj2JK2BMhsk8CyAg4V4amJ3rE= 158 | cloud.google.com/go/orgpolicy v1.5.0/go.mod h1:hZEc5q3wzwXJaKrsx5+Ewg0u1LxJ51nNFlext7Tanwc= 159 | cloud.google.com/go/osconfig v1.10.0 h1:NO0RouqCOM7M2S85Eal6urMSSipWwHU8evzwS+siqUI= 160 | cloud.google.com/go/osconfig v1.10.0/go.mod h1:uMhCzqC5I8zfD9zDEAfvgVhDS8oIjySWh+l4WK6GnWw= 161 | cloud.google.com/go/oslogin v1.7.0 h1:pKGDPfeZHDybtw48WsnVLjoIPMi9Kw62kUE5TXCLCN4= 162 | cloud.google.com/go/oslogin v1.7.0/go.mod h1:e04SN0xO1UNJ1M5GP0vzVBFicIe4O53FOfcixIqTyXo= 163 | cloud.google.com/go/phishingprotection v0.6.0 h1:OrwHLSRSZyaiOt3tnY33dsKSedxbMzsXvqB21okItNQ= 164 | cloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA= 165 | cloud.google.com/go/policytroubleshooter v1.4.0 h1:NQklJuOUoz1BPP+Epjw81COx7IISWslkZubz/1i0UN8= 166 | cloud.google.com/go/policytroubleshooter v1.4.0/go.mod h1:DZT4BcRw3QoO8ota9xw/LKtPa8lKeCByYeKTIf/vxdE= 167 | cloud.google.com/go/privatecatalog v0.6.0 h1:Vz86uiHCtNGm1DeC32HeG2VXmOq5JRYA3VRPf8ZEcSg= 168 | cloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI= 169 | cloud.google.com/go/pubsub v1.27.1 h1:q+J/Nfr6Qx4RQeu3rJcnN48SNC0qzlYzSeqkPq93VHs= 170 | cloud.google.com/go/pubsub v1.27.1/go.mod h1:hQN39ymbV9geqBnfQq6Xf63yNhUAhv9CZhzp5O6qsW0= 171 | cloud.google.com/go/pubsublite v1.5.0 h1:iqrD8vp3giTb7hI1q4TQQGj77cj8zzgmMPsTZtLnprM= 172 | cloud.google.com/go/pubsublite v1.5.0/go.mod h1:xapqNQ1CuLfGi23Yda/9l4bBCKz/wC3KIJ5gKcxveZg= 173 | cloud.google.com/go/recaptchaenterprise/v2 v2.5.0 h1:UqzFfb/WvhwXGDF1eQtdHLrmni+iByZXY4h3w9Kdyv8= 174 | cloud.google.com/go/recaptchaenterprise/v2 v2.5.0/go.mod h1:O8LzcHXN3rz0j+LBC91jrwI3R+1ZSZEWrfL7XHgNo9U= 175 | cloud.google.com/go/recommendationengine v0.6.0 h1:6w+WxPf2LmUEqX0YyvfCoYb8aBYOcbIV25Vg6R0FLGw= 176 | cloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4= 177 | cloud.google.com/go/recommender v1.8.0 h1:9kMZQGeYfcOD/RtZfcNKGKtoex3DdoB4zRgYU/WaIwE= 178 | cloud.google.com/go/recommender v1.8.0/go.mod h1:PkjXrTT05BFKwxaUxQmtIlrtj0kph108r02ZZQ5FE70= 179 | cloud.google.com/go/redis v1.10.0 h1:/zTwwBKIAD2DEWTrXZp8WD9yD/gntReF/HkPssVYd0U= 180 | cloud.google.com/go/redis v1.10.0/go.mod h1:ThJf3mMBQtW18JzGgh41/Wld6vnDDc/F/F35UolRZPM= 181 | cloud.google.com/go/resourcemanager v1.4.0 h1:NDao6CHMwEZIaNsdWy+tuvHaavNeGP06o1tgrR0kLvU= 182 | cloud.google.com/go/resourcemanager v1.4.0/go.mod h1:MwxuzkumyTX7/a3n37gmsT3py7LIXwrShilPh3P1tR0= 183 | cloud.google.com/go/resourcesettings v1.4.0 h1:eTzOwB13WrfF0kuzG2ZXCfB3TLunSHBur4s+HFU6uSM= 184 | cloud.google.com/go/resourcesettings v1.4.0/go.mod h1:ldiH9IJpcrlC3VSuCGvjR5of/ezRrOxFtpJoJo5SmXg= 185 | cloud.google.com/go/retail v1.11.0 h1:N9fa//ecFUOEPsW/6mJHfcapPV0wBSwIUwpVZB7MQ3o= 186 | cloud.google.com/go/retail v1.11.0/go.mod h1:MBLk1NaWPmh6iVFSz9MeKG/Psyd7TAgm6y/9L2B4x9Y= 187 | cloud.google.com/go/run v0.3.0 h1:AWPuzU7Xtaj3Jf+QarDWIs6AJ5hM1VFQ+F6Q+VZ6OT4= 188 | cloud.google.com/go/run v0.3.0/go.mod h1:TuyY1+taHxTjrD0ZFk2iAR+xyOXEA0ztb7U3UNA0zBo= 189 | cloud.google.com/go/scheduler v1.7.0 h1:K/mxOewgHGeKuATUJNGylT75Mhtjmx1TOkKukATqMT8= 190 | cloud.google.com/go/scheduler v1.7.0/go.mod h1:jyCiBqWW956uBjjPMMuX09n3x37mtyPJegEWKxRsn44= 191 | cloud.google.com/go/secretmanager v1.9.0 h1:xE6uXljAC1kCR8iadt9+/blg1fvSbmenlsDN4fT9gqw= 192 | cloud.google.com/go/secretmanager v1.9.0/go.mod h1:b71qH2l1yHmWQHt9LC80akm86mX8AL6X1MA01dW8ht4= 193 | cloud.google.com/go/security v1.10.0 h1:KSKzzJMyUoMRQzcz7azIgqAUqxo7rmQ5rYvimMhikqg= 194 | cloud.google.com/go/security v1.10.0/go.mod h1:QtOMZByJVlibUT2h9afNDWRZ1G96gVywH8T5GUSb9IA= 195 | cloud.google.com/go/securitycenter v1.16.0 h1:QTVtk/Reqnx2bVIZtJKm1+mpfmwRwymmNvlaFez7fQY= 196 | cloud.google.com/go/securitycenter v1.16.0/go.mod h1:Q9GMaLQFUD+5ZTabrbujNWLtSLZIZF7SAR0wWECrjdk= 197 | cloud.google.com/go/servicecontrol v1.5.0 h1:ImIzbOu6y4jL6ob65I++QzvqgFaoAKgHOG+RU9/c4y8= 198 | cloud.google.com/go/servicecontrol v1.5.0/go.mod h1:qM0CnXHhyqKVuiZnGKrIurvVImCs8gmqWsDoqe9sU1s= 199 | cloud.google.com/go/servicedirectory v1.7.0 h1:f7M8IMcVzO3T425AqlZbP3yLzeipsBHtRza8vVFYMhQ= 200 | cloud.google.com/go/servicedirectory v1.7.0/go.mod h1:5p/U5oyvgYGYejufvxhgwjL8UVXjkuw7q5XcG10wx1U= 201 | cloud.google.com/go/servicemanagement v1.5.0 h1:TpkCO5M7dhKSy1bKUD9o/sSEW/U1Gtx7opA1fsiMx0c= 202 | cloud.google.com/go/servicemanagement v1.5.0/go.mod h1:XGaCRe57kfqu4+lRxaFEAuqmjzF0r+gWHjWqKqBvKFo= 203 | cloud.google.com/go/serviceusage v1.4.0 h1:b0EwJxPJLpavSljMQh0RcdHsUrr5DQ+Nelt/3BAs5ro= 204 | cloud.google.com/go/serviceusage v1.4.0/go.mod h1:SB4yxXSaYVuUBYUml6qklyONXNLt83U0Rb+CXyhjEeU= 205 | cloud.google.com/go/shell v1.4.0 h1:b1LFhFBgKsG252inyhtmsUUZwchqSz3WTvAIf3JFo4g= 206 | cloud.google.com/go/shell v1.4.0/go.mod h1:HDxPzZf3GkDdhExzD/gs8Grqk+dmYcEjGShZgYa9URw= 207 | cloud.google.com/go/spanner v1.41.0 h1:NvdTpRwf7DTegbfFdPjAWyD7bOVu0VeMqcvR9aCQCAc= 208 | cloud.google.com/go/spanner v1.41.0/go.mod h1:MLYDBJR/dY4Wt7ZaMIQ7rXOTLjYrmxLE/5ve9vFfWos= 209 | cloud.google.com/go/speech v1.9.0 h1:yK0ocnFH4Wsf0cMdUyndJQ/hPv02oTJOxzi6AgpBy4s= 210 | cloud.google.com/go/speech v1.9.0/go.mod h1:xQ0jTcmnRFFM2RfX/U+rk6FQNUF6DQlydUSyoooSpco= 211 | cloud.google.com/go/storage v1.10.0 h1:STgFzyU5/8miMl0//zKh2aQeTyeaUH3WN9bSUiJ09bA= 212 | cloud.google.com/go/storagetransfer v1.6.0 h1:fUe3OydbbvHcAYp07xY+2UpH4AermGbmnm7qdEj3tGE= 213 | cloud.google.com/go/storagetransfer v1.6.0/go.mod h1:y77xm4CQV/ZhFZH75PLEXY0ROiS7Gh6pSKrM8dJyg6I= 214 | cloud.google.com/go/talent v1.4.0 h1:MrekAGxLqAeAol4Sc0allOVqUGO8j+Iim8NMvpiD7tM= 215 | cloud.google.com/go/talent v1.4.0/go.mod h1:ezFtAgVuRf8jRsvyE6EwmbTK5LKciD4KVnHuDEFmOOA= 216 | cloud.google.com/go/texttospeech v1.5.0 h1:ccPiHgTewxgyAeCWgQWvZvrLmbfQSFABTMAfrSPLPyY= 217 | cloud.google.com/go/texttospeech v1.5.0/go.mod h1:oKPLhR4n4ZdQqWKURdwxMy0uiTS1xU161C8W57Wkea4= 218 | cloud.google.com/go/tpu v1.4.0 h1:ztIdKoma1Xob2qm6QwNh4Xi9/e7N3IfvtwG5AcNsj1g= 219 | cloud.google.com/go/tpu v1.4.0/go.mod h1:mjZaX8p0VBgllCzF6wcU2ovUXN9TONFLd7iz227X2Xg= 220 | cloud.google.com/go/trace v1.4.0 h1:qO9eLn2esajC9sxpqp1YKX37nXC3L4BfGnPS0Cx9dYo= 221 | cloud.google.com/go/trace v1.4.0/go.mod h1:UG0v8UBqzusp+z63o7FK74SdFE+AXpCLdFb1rshXG+Y= 222 | cloud.google.com/go/translate v1.4.0 h1:AOYOH3MspzJ/bH1YXzB+xTE8fMpn3mwhLjugwGXvMPI= 223 | cloud.google.com/go/translate v1.4.0/go.mod h1:06Dn/ppvLD6WvA5Rhdp029IX2Mi3Mn7fpMRLPvXT5Wg= 224 | cloud.google.com/go/video v1.9.0 h1:ttlvO4J5c1VGq6FkHqWPD/aH6PfdxujHt+muTJlW1Zk= 225 | cloud.google.com/go/video v1.9.0/go.mod h1:0RhNKFRF5v92f8dQt0yhaHrEuH95m068JYOvLZYnJSw= 226 | cloud.google.com/go/videointelligence v1.9.0 h1:RPFgVVXbI2b5vnrciZjtsUgpNKVtHO/WIyXUhEfuMhA= 227 | cloud.google.com/go/videointelligence v1.9.0/go.mod h1:29lVRMPDYHikk3v8EdPSaL8Ku+eMzDljjuvRs105XoU= 228 | cloud.google.com/go/vision/v2 v2.5.0 h1:TQHxRqvLMi19azwm3qYuDbEzZWmiKJNTpGbkNsfRCik= 229 | cloud.google.com/go/vision/v2 v2.5.0/go.mod h1:MmaezXOOE+IWa+cS7OhRRLK2cNv1ZL98zhqFFZaaH2E= 230 | cloud.google.com/go/vmmigration v1.3.0 h1:A2Tl2ZmwMRpvEmhV2ibISY85fmQR+Y5w9a0PlRz5P3s= 231 | cloud.google.com/go/vmmigration v1.3.0/go.mod h1:oGJ6ZgGPQOFdjHuocGcLqX4lc98YQ7Ygq8YQwHh9A7g= 232 | cloud.google.com/go/vmwareengine v0.1.0 h1:JMPZaOT/gIUxVlTqSl/QQ32Y2k+r0stNeM1NSqhVP9o= 233 | cloud.google.com/go/vmwareengine v0.1.0/go.mod h1:RsdNEf/8UDvKllXhMz5J40XxDrNJNN4sagiox+OI208= 234 | cloud.google.com/go/vpcaccess v1.5.0 h1:woHXXtnW8b9gLFdWO9HLPalAddBQ9V4LT+1vjKwR3W8= 235 | cloud.google.com/go/vpcaccess v1.5.0/go.mod h1:drmg4HLk9NkZpGfCmZ3Tz0Bwnm2+DKqViEpeEpOq0m8= 236 | cloud.google.com/go/webrisk v1.7.0 h1:ypSnpGlJnZSXbN9a13PDmAYvVekBLnGKxQ3Q9SMwnYY= 237 | cloud.google.com/go/webrisk v1.7.0/go.mod h1:mVMHgEYH0r337nmt1JyLthzMr6YxwN1aAIEc2fTcq7A= 238 | cloud.google.com/go/websecurityscanner v1.4.0 h1:y7yIFg/h/mO+5Y5aCOtVAnpGUOgqCH5rXQ2Oc8Oq2+g= 239 | cloud.google.com/go/websecurityscanner v1.4.0/go.mod h1:ebit/Fp0a+FWu5j4JOmJEV8S8CzdTkAS77oDsiSqYWQ= 240 | cloud.google.com/go/workflows v1.9.0 h1:7Chpin9p50NTU8Tb7qk+I11U/IwVXmDhEoSsdccvInE= 241 | cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA= 242 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9 h1:VpgP7xuJadIUuKccphEpTJnWhS2jkQyMt6Y7pJCD7fY= 243 | github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= 244 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc= 245 | github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= 246 | github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg= 247 | github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g= 248 | github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= 249 | github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= 250 | github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= 251 | github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 252 | github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= 253 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= 254 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= 255 | github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI= 256 | github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe h1:QQ3GSy+MqSHxm/d8nCtnAiZdYFd45cYZPs8vOOIYKfk= 257 | github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= 258 | github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b h1:ACGZRIr7HsgBKHsueQ1yM4WaVaXh21ynwqsF8M8tXhA= 259 | github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 260 | github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534 h1:rtAn27wIbmOGUs7RIbVgPEjb31ehTVniDwPGXyMxm5U= 261 | github.com/envoyproxy/go-control-plane v0.10.3 h1:xdCVXxEe0Y3FQith+0cj2irwZudqGYvecuLB1HtdexY= 262 | github.com/envoyproxy/go-control-plane v0.10.3/go.mod h1:fJJn/j26vwOu972OllsvAgJJM//w9BV6Fxbg2LuVd34= 263 | github.com/envoyproxy/protoc-gen-validate v0.9.1 h1:PS7VIOgmSVhWUEeZwTe7z7zouA22Cr590PzXKbZHOVY= 264 | github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= 265 | github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= 266 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0= 267 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4 h1:WtGNWLvXpe6ZudgnXrq0barxBImvnnJoMEhXAzcbM0I= 268 | github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA= 269 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= 270 | github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc= 271 | github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= 272 | github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= 273 | github.com/google/martian/v3 v3.0.0 h1:pMen7vLs8nvgEYhywH3KDWJIJTeEr2ULsVWHWYHQyBs= 274 | github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99 h1:Ak8CrdlwwXwAZxzS66vgPt4U8yUZX7JwLvVR58FN5jM= 275 | github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA= 276 | github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= 277 | github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= 278 | github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= 279 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6 h1:UDMh68UUwekSh5iP2OMhRRZJiiBccgV7axzUG8vi56c= 280 | github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o= 281 | github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= 282 | github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw= 283 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM= 284 | github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s= 285 | github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk= 286 | github.com/rs/xid v1.3.0 h1:6NjYksEUlhurdVehpc7S7dk6DAmcKv8V9gG0FsVN2U4= 287 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ= 288 | github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= 289 | github.com/yuin/goldmark v1.1.32 h1:5tjfNdR2ki3yYQ842+eX2sQHeiwpKJ0RnHO4IYOc4V8= 290 | go.opencensus.io v0.22.4 h1:LYy1Hy3MJdrCdMwwzxA/dRok4ejH+RwNGbuoD9fCjto= 291 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= 292 | golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6 h1:QE6XYQK6naiK1EPAe1g/ILLxN5RBoH5xkJk3CqlMI/Y= 293 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4= 294 | golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k= 295 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs= 296 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= 297 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 298 | golang.org/x/oauth2 v0.4.0 h1:NF0gk8LVPg1Ml7SSbGyySuoxdsXitj7TvgvuRxIMc/M= 299 | golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec= 300 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA= 301 | golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= 302 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= 303 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= 304 | golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= 305 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 306 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= 307 | google.golang.org/api v0.30.0 h1:yfrXXP61wVuLb0vBcG6qaOoIoqYEzOQS8jum51jkv2w= 308 | google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= 309 | google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 310 | gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8= 311 | gopkg.in/yaml.v2 v2.2.3 h1:fvjTMHxHEw/mxHbtzPi3JCcKXQRAnQTBRo6YCJSVHKI= 312 | honnef.co/go/tools v0.0.1-2020.1.4 h1:UoveltGrhghAA7ePc+e+QYDHXrBps2PqFZiHkGR/xK8= 313 | rsc.io/binaryregexp v0.2.0 h1:HfqmD5MEmC0zvwBuF187nq9mdnXjXsSivRiXN7SmRkE= 314 | rsc.io/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY= 315 | rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4= 316 | -------------------------------------------------------------------------------- /ops/grafana/provisioning/dashboards/general/spanmetrics-dashboard.json: -------------------------------------------------------------------------------- 1 | { 2 | "annotations": { 3 | "list": [ 4 | { 5 | "builtIn": 1, 6 | "datasource": { 7 | "type": "grafana", 8 | "uid": "-- Grafana --" 9 | }, 10 | "enable": true, 11 | "hide": true, 12 | "iconColor": "rgba(0, 211, 255, 1)", 13 | "name": "Annotations & Alerts", 14 | "target": { 15 | "limit": 100, 16 | "matchAny": false, 17 | "tags": [], 18 | "type": "dashboard" 19 | }, 20 | "type": "dashboard" 21 | } 22 | ] 23 | }, 24 | "description": "Spanmetrics way of demo application view.", 25 | "author": { 26 | "name": "devrimdemiroz" 27 | }, 28 | "editable": true, 29 | "fiscalYearStartMonth": 0, 30 | "graphTooltip": 0, 31 | "links": [], 32 | "liveNow": false, 33 | "panels": [ 34 | { 35 | "collapsed": false, 36 | "gridPos": { 37 | "h": 1, 38 | "w": 24, 39 | "x": 0, 40 | "y": 0 41 | }, 42 | "id": 24, 43 | "panels": [], 44 | "title": "Service Level - Throughput and Latencies", 45 | "type": "row" 46 | }, 47 | { 48 | "datasource": { 49 | "type": "prometheus", 50 | "uid": "webstore-metrics" 51 | }, 52 | "fieldConfig": { 53 | "defaults": { 54 | "color": { 55 | "mode": "continuous-BlYlRd" 56 | }, 57 | "mappings": [], 58 | "thresholds": { 59 | "mode": "absolute", 60 | "steps": [ 61 | { 62 | "color": "blue", 63 | "value": null 64 | }, 65 | { 66 | "color": "green", 67 | "value": 2 68 | }, 69 | { 70 | "color": "#EAB839", 71 | "value": 64 72 | }, 73 | { 74 | "color": "orange", 75 | "value": 128 76 | }, 77 | { 78 | "color": "red", 79 | "value": 256 80 | } 81 | ] 82 | }, 83 | "unit": "ms" 84 | }, 85 | "overrides": [] 86 | }, 87 | "gridPos": { 88 | "h": 20, 89 | "w": 12, 90 | "x": 0, 91 | "y": 1 92 | }, 93 | "id": 2, 94 | "interval": "5m", 95 | "options": { 96 | "orientation": "auto", 97 | "reduceOptions": { 98 | "calcs": [ 99 | "lastNotNull" 100 | ], 101 | "fields": "", 102 | "values": false 103 | }, 104 | "showThresholdLabels": false, 105 | "showThresholdMarkers": true 106 | }, 107 | "pluginVersion": "9.1.0", 108 | "targets": [ 109 | { 110 | "datasource": { 111 | "type": "prometheus", 112 | "uid": "webstore-metrics" 113 | }, 114 | "editorMode": "code", 115 | "exemplar": false, 116 | "expr": "topk(7,histogram_quantile(0.50, sum(rate(latency_bucket{service_name=~\"$service\", operation=~\"$operation\"}[$__rate_interval])) by (le,service_name)))", 117 | "format": "time_series", 118 | "hide": true, 119 | "instant": false, 120 | "interval": "", 121 | "legendFormat": "{{service_name}}-quantile_0.50", 122 | "range": true, 123 | "refId": "A" 124 | }, 125 | { 126 | "datasource": { 127 | "type": "prometheus", 128 | "uid": "webstore-metrics" 129 | }, 130 | "editorMode": "code", 131 | "exemplar": false, 132 | "expr": "topk(7,histogram_quantile(0.95, sum(rate(latency_bucket{service_name=~\"$service\", operation=~\"$operation\"}[$__range])) by (le,service_name)))", 133 | "hide": false, 134 | "instant": true, 135 | "interval": "", 136 | "legendFormat": "{{le}} - {{service_name}}", 137 | "range": false, 138 | "refId": "B" 139 | }, 140 | { 141 | "datasource": { 142 | "type": "prometheus", 143 | "uid": "webstore-metrics" 144 | }, 145 | "editorMode": "code", 146 | "exemplar": false, 147 | "expr": "histogram_quantile(0.99, sum(rate(latency_bucket{service_name=~\"$service\", operation=~\"$operation\"}[$__rate_interval])) by (le,service_name))", 148 | "hide": true, 149 | "interval": "", 150 | "legendFormat": "quantile99", 151 | "range": true, 152 | "refId": "C" 153 | }, 154 | { 155 | "datasource": { 156 | "type": "prometheus", 157 | "uid": "webstore-metrics" 158 | }, 159 | "editorMode": "code", 160 | "exemplar": false, 161 | "expr": "histogram_quantile(0.999, sum(rate(latency_bucket{service_name=~\"$service\", operation=~\"$operation\"}[$__rate_interval])) by (le,service_name))", 162 | "hide": true, 163 | "interval": "", 164 | "legendFormat": "quantile999", 165 | "range": true, 166 | "refId": "D" 167 | } 168 | ], 169 | "title": "Top 3x3 - Service Latency - quantile95", 170 | "type": "gauge" 171 | }, 172 | { 173 | "datasource": { 174 | "type": "prometheus", 175 | "uid": "webstore-metrics" 176 | }, 177 | "fieldConfig": { 178 | "defaults": { 179 | "color": { 180 | "mode": "continuous-BlYlRd" 181 | }, 182 | "decimals": 2, 183 | "mappings": [], 184 | "thresholds": { 185 | "mode": "absolute", 186 | "steps": [ 187 | { 188 | "color": "green", 189 | "value": null 190 | }, 191 | { 192 | "color": "super-light-blue", 193 | "value": 1 194 | }, 195 | { 196 | "color": "#EAB839", 197 | "value": 2 198 | }, 199 | { 200 | "color": "red", 201 | "value": 10 202 | } 203 | ] 204 | }, 205 | "unit": "reqps" 206 | }, 207 | "overrides": [] 208 | }, 209 | "gridPos": { 210 | "h": 13, 211 | "w": 12, 212 | "x": 12, 213 | "y": 1 214 | }, 215 | "id": 4, 216 | "interval": "5m", 217 | "options": { 218 | "displayMode": "lcd", 219 | "minVizHeight": 10, 220 | "minVizWidth": 0, 221 | "orientation": "horizontal", 222 | "reduceOptions": { 223 | "calcs": [ 224 | "mean" 225 | ], 226 | "fields": "", 227 | "values": false 228 | }, 229 | "showUnfilled": true, 230 | "text": {} 231 | }, 232 | "pluginVersion": "9.1.0", 233 | "targets": [ 234 | { 235 | "datasource": { 236 | "type": "prometheus", 237 | "uid": "webstore-metrics" 238 | }, 239 | "editorMode": "code", 240 | "exemplar": false, 241 | "expr": "topk(7,sum by (service_name) (rate( calls_total{service_name=~\"$service\", operation=~\"$operation\"}[$__range])))", 242 | "format": "time_series", 243 | "instant": true, 244 | "interval": "", 245 | "legendFormat": "{{service_name}}", 246 | "range": false, 247 | "refId": "A" 248 | } 249 | ], 250 | "title": "Top 7 Services Mean Rate over Range", 251 | "transformations": [], 252 | "type": "bargauge" 253 | }, 254 | { 255 | "datasource": { 256 | "type": "prometheus", 257 | "uid": "webstore-metrics" 258 | }, 259 | "fieldConfig": { 260 | "defaults": { 261 | "color": { 262 | "mode": "continuous-reds" 263 | }, 264 | "decimals": 4, 265 | "mappings": [], 266 | "thresholds": { 267 | "mode": "absolute", 268 | "steps": [ 269 | { 270 | "color": "green", 271 | "value": null 272 | }, 273 | { 274 | "color": "#EAB839", 275 | "value": 1 276 | }, 277 | { 278 | "color": "red", 279 | "value": 15 280 | } 281 | ] 282 | }, 283 | "unit": "reqps" 284 | }, 285 | "overrides": [] 286 | }, 287 | "gridPos": { 288 | "h": 7, 289 | "w": 12, 290 | "x": 12, 291 | "y": 14 292 | }, 293 | "id": 15, 294 | "interval": "5m", 295 | "options": { 296 | "displayMode": "lcd", 297 | "minVizHeight": 10, 298 | "minVizWidth": 0, 299 | "orientation": "vertical", 300 | "reduceOptions": { 301 | "calcs": [ 302 | "mean" 303 | ], 304 | "fields": "", 305 | "values": false 306 | }, 307 | "showUnfilled": true, 308 | "text": {} 309 | }, 310 | "pluginVersion": "9.1.0", 311 | "targets": [ 312 | { 313 | "datasource": { 314 | "type": "prometheus", 315 | "uid": "webstore-metrics" 316 | }, 317 | "editorMode": "code", 318 | "exemplar": false, 319 | "expr": "topk(7,sum(rate( calls_total{status_code=\"STATUS_CODE_ERROR\",service_name=~\"$service\", operation=~\"$operation\"}[$__range])) by (service_name))", 320 | "instant": true, 321 | "interval": "", 322 | "legendFormat": "{{service_name}}", 323 | "range": false, 324 | "refId": "A" 325 | } 326 | ], 327 | "title": "Top 7 Services Mean ERROR Rate over Range", 328 | "transformations": [], 329 | "type": "bargauge" 330 | }, 331 | { 332 | "collapsed": false, 333 | "datasource": { 334 | "type": "prometheus", 335 | "uid": "webstore-metrics" 336 | }, 337 | "gridPos": { 338 | "h": 1, 339 | "w": 24, 340 | "x": 0, 341 | "y": 21 342 | }, 343 | "id": 14, 344 | "panels": [], 345 | "targets": [ 346 | { 347 | "datasource": { 348 | "type": "prometheus", 349 | "uid": "webstore-metrics" 350 | }, 351 | "refId": "A" 352 | } 353 | ], 354 | "title": "Operations Level - Throughput", 355 | "type": "row" 356 | }, 357 | { 358 | "datasource": { 359 | "type": "prometheus", 360 | "uid": "webstore-metrics" 361 | }, 362 | "description": "", 363 | "fieldConfig": { 364 | "defaults": { 365 | "color": { 366 | "mode": "thresholds" 367 | }, 368 | "custom": { 369 | "align": "auto", 370 | "displayMode": "auto", 371 | "inspect": false 372 | }, 373 | "decimals": 2, 374 | "mappings": [], 375 | "thresholds": { 376 | "mode": "absolute", 377 | "steps": [ 378 | { 379 | "color": "green", 380 | "value": null 381 | }, 382 | { 383 | "color": "red", 384 | "value": 80 385 | } 386 | ] 387 | }, 388 | "unit": "reqps" 389 | }, 390 | "overrides": [ 391 | { 392 | "matcher": { 393 | "id": "byName", 394 | "options": "bRate" 395 | }, 396 | "properties": [ 397 | { 398 | "id": "custom.displayMode", 399 | "value": "lcd-gauge" 400 | }, 401 | { 402 | "id": "color", 403 | "value": { 404 | "mode": "continuous-BlYlRd" 405 | } 406 | } 407 | ] 408 | }, 409 | { 410 | "matcher": { 411 | "id": "byName", 412 | "options": "eRate" 413 | }, 414 | "properties": [ 415 | { 416 | "id": "custom.displayMode", 417 | "value": "lcd-gauge" 418 | }, 419 | { 420 | "id": "color", 421 | "value": { 422 | "mode": "continuous-RdYlGr" 423 | } 424 | } 425 | ] 426 | }, 427 | { 428 | "matcher": { 429 | "id": "byName", 430 | "options": "Error Rate" 431 | }, 432 | "properties": [ 433 | { 434 | "id": "custom.width", 435 | "value": 663 436 | } 437 | ] 438 | }, 439 | { 440 | "matcher": { 441 | "id": "byName", 442 | "options": "Rate" 443 | }, 444 | "properties": [ 445 | { 446 | "id": "custom.width", 447 | "value": 667 448 | } 449 | ] 450 | }, 451 | { 452 | "matcher": { 453 | "id": "byName", 454 | "options": "Service" 455 | }, 456 | "properties": [ 457 | { 458 | "id": "custom.width", 459 | "value": null 460 | } 461 | ] 462 | } 463 | ] 464 | }, 465 | "gridPos": { 466 | "h": 11, 467 | "w": 24, 468 | "x": 0, 469 | "y": 22 470 | }, 471 | "id": 22, 472 | "interval": "5m", 473 | "options": { 474 | "footer": { 475 | "fields": "", 476 | "reducer": [ 477 | "sum" 478 | ], 479 | "show": false 480 | }, 481 | "showHeader": true, 482 | "sortBy": [] 483 | }, 484 | "pluginVersion": "9.1.0", 485 | "targets": [ 486 | { 487 | "datasource": { 488 | "type": "prometheus", 489 | "uid": "webstore-metrics" 490 | }, 491 | "exemplar": false, 492 | "expr": "topk(7, sum(rate(calls_total{service_name=~\"$service\", operation=~\"$operation\"}[$__range])) by (operation,service_name)) ", 493 | "format": "table", 494 | "instant": true, 495 | "interval": "", 496 | "legendFormat": "", 497 | "refId": "Rate" 498 | }, 499 | { 500 | "datasource": { 501 | "type": "prometheus", 502 | "uid": "webstore-metrics" 503 | }, 504 | "exemplar": false, 505 | "expr": "topk(7, sum(rate(calls_total{status_code=\"STATUS_CODE_ERROR\",service_name=~\"$service\", operation=~\"$operation\"}[$__range])) by (operation,service_name))", 506 | "format": "table", 507 | "hide": false, 508 | "instant": true, 509 | "interval": "", 510 | "legendFormat": "", 511 | "refId": "Error Rate" 512 | } 513 | ], 514 | "title": "Top 7 Operations and Errors (APM Table)", 515 | "transformations": [ 516 | { 517 | "id": "seriesToColumns", 518 | "options": { 519 | "byField": "operation" 520 | } 521 | }, 522 | { 523 | "id": "organize", 524 | "options": { 525 | "excludeByName": { 526 | "Time 1": true, 527 | "Time 2": true 528 | }, 529 | "indexByName": {}, 530 | "renameByName": { 531 | "Value #Error Rate": "Error Rate", 532 | "Value #Rate": "Rate", 533 | "service_name 1": "Rate in Service", 534 | "service_name 2": "Error Rate in Service" 535 | } 536 | } 537 | }, 538 | { 539 | "id": "calculateField", 540 | "options": { 541 | "alias": "bRate", 542 | "mode": "reduceRow", 543 | "reduce": { 544 | "include": [ 545 | "Rate" 546 | ], 547 | "reducer": "sum" 548 | } 549 | } 550 | }, 551 | { 552 | "id": "calculateField", 553 | "options": { 554 | "alias": "eRate", 555 | "mode": "reduceRow", 556 | "reduce": { 557 | "include": [ 558 | "Error Rate" 559 | ], 560 | "reducer": "sum" 561 | } 562 | } 563 | }, 564 | { 565 | "id": "organize", 566 | "options": { 567 | "excludeByName": { 568 | "Error Rate": true, 569 | "Rate": true, 570 | "bRate": false 571 | }, 572 | "indexByName": { 573 | "Error Rate": 4, 574 | "Error Rate in Service": 6, 575 | "Rate": 1, 576 | "Rate in Service": 5, 577 | "bRate": 2, 578 | "eRate": 3, 579 | "operation": 0 580 | }, 581 | "renameByName": { 582 | "Rate in Service": "Service", 583 | "bRate": "Rate", 584 | "eRate": "Error Rate", 585 | "operation": "Operation Name" 586 | } 587 | } 588 | }, 589 | { 590 | "id": "sortBy", 591 | "options": { 592 | "fields": {}, 593 | "sort": [ 594 | { 595 | "desc": true, 596 | "field": "Rate" 597 | } 598 | ] 599 | } 600 | } 601 | ], 602 | "type": "table" 603 | }, 604 | { 605 | "collapsed": false, 606 | "datasource": { 607 | "type": "prometheus", 608 | "uid": "webstore-metrics" 609 | }, 610 | "gridPos": { 611 | "h": 1, 612 | "w": 24, 613 | "x": 0, 614 | "y": 33 615 | }, 616 | "id": 20, 617 | "panels": [], 618 | "targets": [ 619 | { 620 | "datasource": { 621 | "type": "prometheus", 622 | "uid": "webstore-metrics" 623 | }, 624 | "refId": "A" 625 | } 626 | ], 627 | "title": "Operation Level - Latencies", 628 | "type": "row" 629 | }, 630 | { 631 | "datasource": { 632 | "type": "prometheus", 633 | "uid": "webstore-metrics" 634 | }, 635 | "fieldConfig": { 636 | "defaults": { 637 | "color": { 638 | "mode": "continuous-BlYlRd" 639 | }, 640 | "mappings": [], 641 | "thresholds": { 642 | "mode": "absolute", 643 | "steps": [ 644 | { 645 | "color": "blue", 646 | "value": null 647 | }, 648 | { 649 | "color": "green", 650 | "value": 2 651 | }, 652 | { 653 | "color": "#EAB839", 654 | "value": 64 655 | }, 656 | { 657 | "color": "orange", 658 | "value": 128 659 | }, 660 | { 661 | "color": "red", 662 | "value": 256 663 | } 664 | ] 665 | }, 666 | "unit": "ms" 667 | }, 668 | "overrides": [] 669 | }, 670 | "gridPos": { 671 | "h": 13, 672 | "w": 12, 673 | "x": 0, 674 | "y": 34 675 | }, 676 | "id": 25, 677 | "interval": "5m", 678 | "options": { 679 | "orientation": "auto", 680 | "reduceOptions": { 681 | "calcs": [ 682 | "lastNotNull" 683 | ], 684 | "fields": "", 685 | "values": false 686 | }, 687 | "showThresholdLabels": false, 688 | "showThresholdMarkers": true 689 | }, 690 | "pluginVersion": "9.1.0", 691 | "targets": [ 692 | { 693 | "datasource": { 694 | "type": "prometheus", 695 | "uid": "webstore-metrics" 696 | }, 697 | "editorMode": "code", 698 | "exemplar": false, 699 | "expr": "topk(7,histogram_quantile(0.50, sum(rate(latency_bucket{service_name=~\"$service\", operation=~\"$operation\"}[$__rate_interval])) by (le,service_name)))", 700 | "format": "time_series", 701 | "hide": true, 702 | "instant": false, 703 | "interval": "", 704 | "legendFormat": "{{service_name}}-quantile_0.50", 705 | "range": true, 706 | "refId": "A" 707 | }, 708 | { 709 | "datasource": { 710 | "type": "prometheus", 711 | "uid": "webstore-metrics" 712 | }, 713 | "editorMode": "code", 714 | "exemplar": false, 715 | "expr": "topk(7,histogram_quantile(0.95, sum(rate(latency_bucket{service_name=~\"$service\", operation=~\"$operation\"}[$__range])) by (le,operation)))", 716 | "hide": false, 717 | "instant": true, 718 | "interval": "", 719 | "legendFormat": "{{operation}}", 720 | "range": false, 721 | "refId": "B" 722 | }, 723 | { 724 | "datasource": { 725 | "type": "prometheus", 726 | "uid": "webstore-metrics" 727 | }, 728 | "editorMode": "code", 729 | "exemplar": false, 730 | "expr": "histogram_quantile(0.99, sum(rate(latency_bucket{service_name=~\"$service\", operation=~\"$operation\"}[$__rate_interval])) by (le,service_name))", 731 | "hide": true, 732 | "interval": "", 733 | "legendFormat": "quantile99", 734 | "range": true, 735 | "refId": "C" 736 | }, 737 | { 738 | "datasource": { 739 | "type": "prometheus", 740 | "uid": "webstore-metrics" 741 | }, 742 | "editorMode": "code", 743 | "exemplar": false, 744 | "expr": "histogram_quantile(0.999, sum(rate(latency_bucket{service_name=~\"$service\", operation=~\"$operation\"}[$__rate_interval])) by (le,service_name))", 745 | "hide": true, 746 | "interval": "", 747 | "legendFormat": "quantile999", 748 | "range": true, 749 | "refId": "D" 750 | } 751 | ], 752 | "title": "Top 3x3 - Operation Latency - quantile95", 753 | "type": "gauge" 754 | }, 755 | { 756 | "datasource": { 757 | "type": "prometheus", 758 | "uid": "webstore-metrics" 759 | }, 760 | "fieldConfig": { 761 | "defaults": { 762 | "color": { 763 | "mode": "continuous-BlYlRd" 764 | }, 765 | "decimals": 2, 766 | "mappings": [], 767 | "thresholds": { 768 | "mode": "absolute", 769 | "steps": [ 770 | { 771 | "color": "green", 772 | "value": null 773 | }, 774 | { 775 | "color": "red", 776 | "value": 80 777 | } 778 | ] 779 | }, 780 | "unit": "ms" 781 | }, 782 | "overrides": [] 783 | }, 784 | "gridPos": { 785 | "h": 13, 786 | "w": 12, 787 | "x": 12, 788 | "y": 34 789 | }, 790 | "id": 10, 791 | "interval": "5m", 792 | "options": { 793 | "displayMode": "lcd", 794 | "minVizHeight": 10, 795 | "minVizWidth": 0, 796 | "orientation": "horizontal", 797 | "reduceOptions": { 798 | "calcs": [ 799 | "mean" 800 | ], 801 | "fields": "", 802 | "values": false 803 | }, 804 | "showUnfilled": true 805 | }, 806 | "pluginVersion": "9.1.0", 807 | "targets": [ 808 | { 809 | "datasource": { 810 | "type": "prometheus", 811 | "uid": "webstore-metrics" 812 | }, 813 | "editorMode": "code", 814 | "exemplar": false, 815 | "expr": "topk(7, sum by (operation,service_name)(increase(latency_sum{service_name=~\"${service}\", operation=~\"$operation\"}[5m]) / increase(latency_count{service_name=~\"${service}\",operation=~\"$operation\"}[5m\n])))", 816 | "instant": true, 817 | "interval": "", 818 | "legendFormat": "{{operation}} [{{service_name}}]", 819 | "range": false, 820 | "refId": "A" 821 | } 822 | ], 823 | "title": "Top 7 Highest Endpoint Latencies Mean Over Range ", 824 | "transformations": [], 825 | "type": "bargauge" 826 | }, 827 | { 828 | "datasource": { 829 | "type": "prometheus", 830 | "uid": "webstore-metrics" 831 | }, 832 | "fieldConfig": { 833 | "defaults": { 834 | "color": { 835 | "mode": "palette-classic" 836 | }, 837 | "custom": { 838 | "axisCenteredZero": false, 839 | "axisColorMode": "text", 840 | "axisLabel": "", 841 | "axisPlacement": "auto", 842 | "barAlignment": 0, 843 | "drawStyle": "line", 844 | "fillOpacity": 15, 845 | "gradientMode": "none", 846 | "hideFrom": { 847 | "legend": false, 848 | "tooltip": false, 849 | "viz": false 850 | }, 851 | "lineInterpolation": "smooth", 852 | "lineWidth": 1, 853 | "pointSize": 5, 854 | "scaleDistribution": { 855 | "type": "linear" 856 | }, 857 | "showPoints": "auto", 858 | "spanNulls": false, 859 | "stacking": { 860 | "group": "A", 861 | "mode": "none" 862 | }, 863 | "thresholdsStyle": { 864 | "mode": "off" 865 | } 866 | }, 867 | "mappings": [], 868 | "thresholds": { 869 | "mode": "absolute", 870 | "steps": [ 871 | { 872 | "color": "green", 873 | "value": null 874 | }, 875 | { 876 | "color": "red", 877 | "value": 80 878 | } 879 | ] 880 | }, 881 | "unit": "ms" 882 | }, 883 | "overrides": [] 884 | }, 885 | "gridPos": { 886 | "h": 12, 887 | "w": 24, 888 | "x": 0, 889 | "y": 47 890 | }, 891 | "id": 16, 892 | "interval": "5m", 893 | "options": { 894 | "legend": { 895 | "calcs": [ 896 | "mean", 897 | "logmin", 898 | "max", 899 | "delta" 900 | ], 901 | "displayMode": "table", 902 | "placement": "bottom", 903 | "showLegend": true 904 | }, 905 | "tooltip": { 906 | "mode": "single", 907 | "sort": "none" 908 | } 909 | }, 910 | "pluginVersion": "8.4.7", 911 | "targets": [ 912 | { 913 | "datasource": { 914 | "type": "prometheus", 915 | "uid": "webstore-metrics" 916 | }, 917 | "editorMode": "code", 918 | "exemplar": true, 919 | "expr": "topk(7,sum by (operation,service_name)(increase(latency_sum{service_name=~\"$service\", operation=~\"$operation\"}[$__rate_interval]) / increase(latency_count{service_name=~\"$service\", operation=~\"$operation\"}[$__rate_interval])))", 920 | "instant": false, 921 | "interval": "", 922 | "legendFormat": "[{{service_name}}] {{operation}}", 923 | "range": true, 924 | "refId": "A" 925 | } 926 | ], 927 | "title": "Top 7 Latencies Over Range ", 928 | "type": "timeseries" 929 | } 930 | ], 931 | "refresh": "5m", 932 | "schemaVersion": 37, 933 | "style": "dark", 934 | "tags": [], 935 | "templating": { 936 | "list": [ 937 | { 938 | "allValue": ".*", 939 | "current": { 940 | "selected": false, 941 | "text": "All", 942 | "value": "$__all" 943 | }, 944 | "datasource": { 945 | "type": "prometheus", 946 | "uid": "webstore-metrics" 947 | }, 948 | "definition": "query_result(count by (service_name)(count_over_time(calls_total[$__range])))", 949 | "hide": 0, 950 | "includeAll": true, 951 | "multi": true, 952 | "name": "service", 953 | "options": [], 954 | "query": { 955 | "query": "query_result(count by (service_name)(count_over_time(calls_total[$__range])))", 956 | "refId": "StandardVariableQuery" 957 | }, 958 | "refresh": 2, 959 | "regex": "/.*service_name=\"(.*)\".*/", 960 | "skipUrlSync": false, 961 | "sort": 1, 962 | "type": "query" 963 | }, 964 | { 965 | "allValue": ".*", 966 | "current": { 967 | "selected": false, 968 | "text": "All", 969 | "value": "$__all" 970 | }, 971 | "datasource": { 972 | "type": "prometheus", 973 | "uid": "webstore-metrics" 974 | }, 975 | "definition": "query_result(sum ({__name__=~\".*calls_total\",service_name=~\"$service\"}) by (operation))", 976 | "hide": 0, 977 | "includeAll": true, 978 | "multi": true, 979 | "name": "operation", 980 | "options": [], 981 | "query": { 982 | "query": "query_result(sum ({__name__=~\".*calls_total\",service_name=~\"$service\"}) by (operation))", 983 | "refId": "StandardVariableQuery" 984 | }, 985 | "refresh": 2, 986 | "regex": "/.*operation=\"(.*)\".*/", 987 | "skipUrlSync": false, 988 | "sort": 0, 989 | "type": "query" 990 | } 991 | ] 992 | }, 993 | "time": { 994 | "from": "now-1h", 995 | "to": "now" 996 | }, 997 | "timepicker": {}, 998 | "timezone": "", 999 | "title": "Spanmetrics Demo Dashboard", 1000 | "uid": "W2gX2zHVk48", 1001 | "version": 1, 1002 | "weekStart": "" 1003 | } 1004 | -------------------------------------------------------------------------------- /ops/grafana/provisioning/dashboards/general/opentelemetry-collector.json: -------------------------------------------------------------------------------- 1 | { 2 | "__inputs": [ 3 | { 4 | "name": "webstore", 5 | "label": "Prometheus", 6 | "description": "", 7 | "type": "datasource", 8 | "pluginId": "prometheus", 9 | "pluginName": "Prometheus" 10 | } 11 | ], 12 | "__requires": [ 13 | { 14 | "type": "grafana", 15 | "id": "grafana", 16 | "name": "Grafana", 17 | "version": "7.0.3" 18 | }, 19 | { 20 | "type": "panel", 21 | "id": "graph", 22 | "name": "Graph", 23 | "version": "" 24 | }, 25 | { 26 | "type": "datasource", 27 | "id": "prometheus", 28 | "name": "Prometheus", 29 | "version": "1.0.0" 30 | }, 31 | { 32 | "type": "panel", 33 | "id": "stat", 34 | "name": "Stat", 35 | "version": "" 36 | } 37 | ], 38 | "annotations": { 39 | "list": [ 40 | { 41 | "builtIn": 1, 42 | "datasource": "-- Grafana --", 43 | "enable": true, 44 | "hide": true, 45 | "iconColor": "rgba(0, 211, 255, 1)", 46 | "name": "Annotations & Alerts", 47 | "type": "dashboard" 48 | } 49 | ] 50 | }, 51 | "description": "Provides information about the status of the OpenTelemetry Collector", 52 | "editable": true, 53 | "gnetId": 12553, 54 | "graphTooltip": 0, 55 | "id": 22, 56 | "iteration": 1593502896956, 57 | "links": [], 58 | "panels": [ 59 | { 60 | "collapsed": false, 61 | "datasource": "Prometheus", 62 | "gridPos": { 63 | "h": 1, 64 | "w": 24, 65 | "x": 0, 66 | "y": 0 67 | }, 68 | "id": 23, 69 | "panels": [], 70 | "title": "Receivers", 71 | "type": "row" 72 | }, 73 | { 74 | "aliasColors": {}, 75 | "bars": false, 76 | "dashLength": 10, 77 | "dashes": false, 78 | "datasource": "Prometheus", 79 | "description": "Rate of spans successfully accepted vs refused per second", 80 | "fieldConfig": { 81 | "defaults": { 82 | "custom": {} 83 | }, 84 | "overrides": [] 85 | }, 86 | "fill": 1, 87 | "fillGradient": 0, 88 | "gridPos": { 89 | "h": 9, 90 | "w": 12, 91 | "x": 0, 92 | "y": 1 93 | }, 94 | "hiddenSeries": false, 95 | "id": 28, 96 | "interval": "", 97 | "legend": { 98 | "avg": false, 99 | "current": false, 100 | "max": false, 101 | "min": false, 102 | "show": true, 103 | "total": false, 104 | "values": false 105 | }, 106 | "lines": true, 107 | "linewidth": 1, 108 | "links": [], 109 | "nullPointMode": "null", 110 | "options": { 111 | "dataLinks": [] 112 | }, 113 | "percentage": false, 114 | "pointradius": 5, 115 | "points": false, 116 | "renderer": "flot", 117 | "seriesOverrides": [], 118 | "spaceLength": 10, 119 | "stack": false, 120 | "steppedLine": false, 121 | "targets": [ 122 | { 123 | "expr": "sum(rate(otelcol_receiver_accepted_spans{receiver=~\"$receiver\"}[1m])) by (receiver)", 124 | "format": "time_series", 125 | "interval": "", 126 | "intervalFactor": 1, 127 | "legendFormat": "{{receiver}} spans accepted / sec", 128 | "refId": "A" 129 | }, 130 | { 131 | "expr": "sum(rate(otelcol_receiver_refused_spans{receiver=~\"$receiver\"}[1m])) by (receiver)", 132 | "interval": "", 133 | "legendFormat": "{{ receiver }} spans refused /sec", 134 | "refId": "B" 135 | } 136 | ], 137 | "thresholds": [], 138 | "timeFrom": null, 139 | "timeRegions": [], 140 | "timeShift": null, 141 | "title": "Spans Accepted vs Refused /Second", 142 | "tooltip": { 143 | "shared": true, 144 | "sort": 0, 145 | "value_type": "individual" 146 | }, 147 | "type": "graph", 148 | "xaxis": { 149 | "buckets": null, 150 | "mode": "time", 151 | "name": null, 152 | "show": true, 153 | "values": [] 154 | }, 155 | "yaxes": [ 156 | { 157 | "format": "short", 158 | "label": null, 159 | "logBase": 1, 160 | "max": null, 161 | "min": "0", 162 | "show": true 163 | }, 164 | { 165 | "format": "short", 166 | "label": null, 167 | "logBase": 1, 168 | "max": null, 169 | "min": null, 170 | "show": true 171 | } 172 | ], 173 | "yaxis": { 174 | "align": false, 175 | "alignLevel": null 176 | } 177 | }, 178 | { 179 | "aliasColors": {}, 180 | "bars": false, 181 | "dashLength": 10, 182 | "dashes": false, 183 | "datasource": "Prometheus", 184 | "description": "Rate of metrics successfully accepted vs refused per second", 185 | "fieldConfig": { 186 | "defaults": { 187 | "custom": {} 188 | }, 189 | "overrides": [] 190 | }, 191 | "fill": 1, 192 | "fillGradient": 0, 193 | "gridPos": { 194 | "h": 9, 195 | "w": 12, 196 | "x": 12, 197 | "y": 1 198 | }, 199 | "hiddenSeries": false, 200 | "id": 19, 201 | "interval": "", 202 | "legend": { 203 | "avg": false, 204 | "current": false, 205 | "max": false, 206 | "min": false, 207 | "show": true, 208 | "total": false, 209 | "values": false 210 | }, 211 | "lines": true, 212 | "linewidth": 1, 213 | "links": [], 214 | "nullPointMode": "null", 215 | "options": { 216 | "dataLinks": [] 217 | }, 218 | "percentage": false, 219 | "pointradius": 5, 220 | "points": false, 221 | "renderer": "flot", 222 | "seriesOverrides": [], 223 | "spaceLength": 10, 224 | "stack": false, 225 | "steppedLine": false, 226 | "targets": [ 227 | { 228 | "expr": "sum(rate(otelcol_receiver_accepted_metric_points{receiver=~\"$receiver\"}[1m])) by (receiver)", 229 | "format": "time_series", 230 | "interval": "", 231 | "intervalFactor": 1, 232 | "legendFormat": "{{receiver}} accepted /sec", 233 | "refId": "A" 234 | }, 235 | { 236 | "expr": "sum(rate(otelcol_receiver_refused_metric_points{receiver=~\"$receiver\"}[1m])) by (receiver)", 237 | "interval": "", 238 | "legendFormat": "{{ receiver }} refused / sec", 239 | "refId": "B" 240 | } 241 | ], 242 | "thresholds": [], 243 | "timeFrom": null, 244 | "timeRegions": [], 245 | "timeShift": null, 246 | "title": "Metrics Accepted vs Refused Received/Second", 247 | "tooltip": { 248 | "shared": true, 249 | "sort": 0, 250 | "value_type": "individual" 251 | }, 252 | "type": "graph", 253 | "xaxis": { 254 | "buckets": null, 255 | "mode": "time", 256 | "name": null, 257 | "show": true, 258 | "values": [] 259 | }, 260 | "yaxes": [ 261 | { 262 | "format": "short", 263 | "label": null, 264 | "logBase": 1, 265 | "max": null, 266 | "min": "0", 267 | "show": true 268 | }, 269 | { 270 | "format": "short", 271 | "label": null, 272 | "logBase": 1, 273 | "max": null, 274 | "min": null, 275 | "show": true 276 | } 277 | ], 278 | "yaxis": { 279 | "align": false, 280 | "alignLevel": null 281 | } 282 | }, 283 | { 284 | "collapsed": false, 285 | "datasource": "Prometheus", 286 | "gridPos": { 287 | "h": 1, 288 | "w": 24, 289 | "x": 0, 290 | "y": 10 291 | }, 292 | "id": 25, 293 | "panels": [], 294 | "title": "Exporters", 295 | "type": "row" 296 | }, 297 | { 298 | "aliasColors": {}, 299 | "bars": false, 300 | "dashLength": 10, 301 | "dashes": false, 302 | "datasource": "Prometheus", 303 | "description": "Rate of spans successfully exported vs Failed per second", 304 | "fieldConfig": { 305 | "defaults": { 306 | "custom": {} 307 | }, 308 | "overrides": [] 309 | }, 310 | "fill": 0, 311 | "fillGradient": 0, 312 | "gridPos": { 313 | "h": 9, 314 | "w": 12, 315 | "x": 0, 316 | "y": 11 317 | }, 318 | "hiddenSeries": false, 319 | "id": 30, 320 | "interval": "", 321 | "legend": { 322 | "avg": false, 323 | "current": false, 324 | "max": false, 325 | "min": false, 326 | "show": true, 327 | "total": false, 328 | "values": false 329 | }, 330 | "lines": true, 331 | "linewidth": 1, 332 | "links": [], 333 | "nullPointMode": "null", 334 | "options": { 335 | "dataLinks": [] 336 | }, 337 | "percentage": false, 338 | "pointradius": 5, 339 | "points": false, 340 | "renderer": "flot", 341 | "seriesOverrides": [], 342 | "spaceLength": 10, 343 | "stack": false, 344 | "steppedLine": false, 345 | "targets": [ 346 | { 347 | "expr": "sum(rate(otelcol_exporter_sent_spans{exporter=~\"$exporter\"}[1m])) by (exporter)", 348 | "format": "time_series", 349 | "interval": "", 350 | "intervalFactor": 1, 351 | "legendFormat": "{{exporter}} sent / sec", 352 | "refId": "A" 353 | }, 354 | { 355 | "expr": "sum(rate(otelcol_exporter_send_failed_spans{exporter=~\"$exporter\"}[1m])) by (exporter)", 356 | "interval": "", 357 | "legendFormat": "{{ exporter }} failed /sec", 358 | "refId": "B" 359 | } 360 | ], 361 | "thresholds": [], 362 | "timeFrom": null, 363 | "timeRegions": [], 364 | "timeShift": null, 365 | "title": "Spans Exported vs Failed / Second", 366 | "tooltip": { 367 | "shared": true, 368 | "sort": 0, 369 | "value_type": "individual" 370 | }, 371 | "type": "graph", 372 | "xaxis": { 373 | "buckets": null, 374 | "mode": "time", 375 | "name": null, 376 | "show": true, 377 | "values": [] 378 | }, 379 | "yaxes": [ 380 | { 381 | "format": "short", 382 | "label": null, 383 | "logBase": 1, 384 | "max": null, 385 | "min": "0", 386 | "show": true 387 | }, 388 | { 389 | "format": "short", 390 | "label": null, 391 | "logBase": 1, 392 | "max": null, 393 | "min": null, 394 | "show": true 395 | } 396 | ], 397 | "yaxis": { 398 | "align": false, 399 | "alignLevel": null 400 | } 401 | }, 402 | { 403 | "aliasColors": {}, 404 | "bars": false, 405 | "dashLength": 10, 406 | "dashes": false, 407 | "datasource": "Prometheus", 408 | "description": "Rate of timeseries successfully exported per second", 409 | "fieldConfig": { 410 | "defaults": { 411 | "custom": {} 412 | }, 413 | "overrides": [] 414 | }, 415 | "fill": 1, 416 | "fillGradient": 0, 417 | "gridPos": { 418 | "h": 9, 419 | "w": 12, 420 | "x": 12, 421 | "y": 11 422 | }, 423 | "hiddenSeries": false, 424 | "id": 31, 425 | "interval": "", 426 | "legend": { 427 | "avg": false, 428 | "current": false, 429 | "max": false, 430 | "min": false, 431 | "show": true, 432 | "total": false, 433 | "values": false 434 | }, 435 | "lines": true, 436 | "linewidth": 1, 437 | "links": [], 438 | "nullPointMode": "null", 439 | "options": { 440 | "dataLinks": [] 441 | }, 442 | "percentage": false, 443 | "pointradius": 5, 444 | "points": false, 445 | "renderer": "flot", 446 | "seriesOverrides": [], 447 | "spaceLength": 10, 448 | "stack": false, 449 | "steppedLine": false, 450 | "targets": [ 451 | { 452 | "expr": "sum(rate(otelcol_exporter_sent_metric_points{exporter=~\"$exporter\"}[1m])) by (exporter)", 453 | "format": "time_series", 454 | "interval": "", 455 | "intervalFactor": 1, 456 | "legendFormat": "{{exporter}} sent /sec", 457 | "refId": "A" 458 | }, 459 | { 460 | "expr": "sum(rate(otelcol_exporter_send_failed_metric_points{exporter=~\"$exporter\"}[1m])) by (exporter)", 461 | "interval": "", 462 | "legendFormat": "{{ exporter }} failed / sec", 463 | "refId": "B" 464 | } 465 | ], 466 | "thresholds": [], 467 | "timeFrom": null, 468 | "timeRegions": [], 469 | "timeShift": null, 470 | "title": "Metrics Exported vs failed /Second", 471 | "tooltip": { 472 | "shared": true, 473 | "sort": 0, 474 | "value_type": "individual" 475 | }, 476 | "type": "graph", 477 | "xaxis": { 478 | "buckets": null, 479 | "mode": "time", 480 | "name": null, 481 | "show": true, 482 | "values": [] 483 | }, 484 | "yaxes": [ 485 | { 486 | "format": "short", 487 | "label": null, 488 | "logBase": 1, 489 | "max": null, 490 | "min": "0", 491 | "show": true 492 | }, 493 | { 494 | "format": "short", 495 | "label": null, 496 | "logBase": 1, 497 | "max": null, 498 | "min": null, 499 | "show": true 500 | } 501 | ], 502 | "yaxis": { 503 | "align": false, 504 | "alignLevel": null 505 | } 506 | }, 507 | { 508 | "collapsed": false, 509 | "datasource": "Prometheus", 510 | "gridPos": { 511 | "h": 1, 512 | "w": 24, 513 | "x": 0, 514 | "y": 20 515 | }, 516 | "id": 21, 517 | "panels": [], 518 | "repeat": null, 519 | "title": "Processors", 520 | "type": "row" 521 | }, 522 | { 523 | "aliasColors": {}, 524 | "bars": false, 525 | "dashLength": 10, 526 | "dashes": false, 527 | "datasource": "Prometheus", 528 | "fieldConfig": { 529 | "defaults": { 530 | "custom": {} 531 | }, 532 | "overrides": [] 533 | }, 534 | "fill": 1, 535 | "fillGradient": 0, 536 | "gridPos": { 537 | "h": 9, 538 | "w": 8, 539 | "x": 0, 540 | "y": 21 541 | }, 542 | "hiddenSeries": false, 543 | "id": 10, 544 | "legend": { 545 | "avg": false, 546 | "current": false, 547 | "max": false, 548 | "min": false, 549 | "show": true, 550 | "total": false, 551 | "values": false 552 | }, 553 | "lines": true, 554 | "linewidth": 1, 555 | "links": [], 556 | "nullPointMode": "null", 557 | "options": { 558 | "dataLinks": [] 559 | }, 560 | "percentage": false, 561 | "pointradius": 5, 562 | "points": false, 563 | "renderer": "flot", 564 | "seriesOverrides": [], 565 | "spaceLength": 10, 566 | "stack": false, 567 | "steppedLine": false, 568 | "targets": [ 569 | { 570 | "expr": "max(otelcol_processor_queued_retry_queue_length)", 571 | "format": "time_series", 572 | "interval": "", 573 | "intervalFactor": 1, 574 | "legendFormat": "Queue Length", 575 | "refId": "A" 576 | } 577 | ], 578 | "thresholds": [], 579 | "timeFrom": null, 580 | "timeRegions": [], 581 | "timeShift": null, 582 | "title": "Queued Retry Max Queue Length", 583 | "tooltip": { 584 | "shared": true, 585 | "sort": 0, 586 | "value_type": "individual" 587 | }, 588 | "type": "graph", 589 | "xaxis": { 590 | "buckets": null, 591 | "mode": "time", 592 | "name": null, 593 | "show": true, 594 | "values": [] 595 | }, 596 | "yaxes": [ 597 | { 598 | "format": "short", 599 | "label": null, 600 | "logBase": 1, 601 | "max": null, 602 | "min": null, 603 | "show": true 604 | }, 605 | { 606 | "format": "short", 607 | "label": null, 608 | "logBase": 1, 609 | "max": null, 610 | "min": null, 611 | "show": true 612 | } 613 | ], 614 | "yaxis": { 615 | "align": false, 616 | "alignLevel": null 617 | } 618 | }, 619 | { 620 | "aliasColors": {}, 621 | "bars": false, 622 | "dashLength": 10, 623 | "dashes": false, 624 | "datasource": "Prometheus", 625 | "fieldConfig": { 626 | "defaults": { 627 | "custom": {} 628 | }, 629 | "overrides": [] 630 | }, 631 | "fill": 1, 632 | "fillGradient": 0, 633 | "gridPos": { 634 | "h": 9, 635 | "w": 8, 636 | "x": 8, 637 | "y": 21 638 | }, 639 | "hiddenSeries": false, 640 | "id": 11, 641 | "legend": { 642 | "avg": false, 643 | "current": false, 644 | "max": false, 645 | "min": false, 646 | "show": true, 647 | "total": false, 648 | "values": false 649 | }, 650 | "lines": true, 651 | "linewidth": 1, 652 | "links": [], 653 | "nullPointMode": "null", 654 | "options": { 655 | "dataLinks": [] 656 | }, 657 | "percentage": false, 658 | "pointradius": 5, 659 | "points": false, 660 | "renderer": "flot", 661 | "seriesOverrides": [], 662 | "spaceLength": 10, 663 | "stack": false, 664 | "steppedLine": false, 665 | "targets": [ 666 | { 667 | "expr": "sum(otelcol_processor_queued_retry_queue_latency_sum/ otelcol_processor_queued_retry_queue_latency_count)", 668 | "format": "time_series", 669 | "interval": "", 670 | "intervalFactor": 1, 671 | "legendFormat": "ms", 672 | "refId": "A" 673 | } 674 | ], 675 | "thresholds": [], 676 | "timeFrom": null, 677 | "timeRegions": [], 678 | "timeShift": null, 679 | "title": "Queued Retry Processor In-queue Latency (ms)", 680 | "tooltip": { 681 | "shared": true, 682 | "sort": 0, 683 | "value_type": "individual" 684 | }, 685 | "type": "graph", 686 | "xaxis": { 687 | "buckets": null, 688 | "mode": "time", 689 | "name": null, 690 | "show": true, 691 | "values": [] 692 | }, 693 | "yaxes": [ 694 | { 695 | "format": "short", 696 | "label": null, 697 | "logBase": 1, 698 | "max": null, 699 | "min": null, 700 | "show": true 701 | }, 702 | { 703 | "format": "short", 704 | "label": null, 705 | "logBase": 1, 706 | "max": null, 707 | "min": null, 708 | "show": true 709 | } 710 | ], 711 | "yaxis": { 712 | "align": false, 713 | "alignLevel": null 714 | } 715 | }, 716 | { 717 | "aliasColors": {}, 718 | "bars": false, 719 | "dashLength": 10, 720 | "dashes": false, 721 | "datasource": "Prometheus", 722 | "fieldConfig": { 723 | "defaults": { 724 | "custom": {} 725 | }, 726 | "overrides": [] 727 | }, 728 | "fill": 1, 729 | "fillGradient": 0, 730 | "gridPos": { 731 | "h": 9, 732 | "w": 8, 733 | "x": 16, 734 | "y": 21 735 | }, 736 | "hiddenSeries": false, 737 | "id": 32, 738 | "legend": { 739 | "avg": false, 740 | "current": false, 741 | "max": false, 742 | "min": false, 743 | "show": true, 744 | "total": false, 745 | "values": false 746 | }, 747 | "lines": true, 748 | "linewidth": 1, 749 | "links": [], 750 | "nullPointMode": "null", 751 | "options": { 752 | "dataLinks": [] 753 | }, 754 | "percentage": false, 755 | "pointradius": 5, 756 | "points": false, 757 | "renderer": "flot", 758 | "seriesOverrides": [], 759 | "spaceLength": 10, 760 | "stack": false, 761 | "steppedLine": false, 762 | "targets": [ 763 | { 764 | "expr": "sum(otelcol_processor_queued_retry_send_latency_sum/ otelcol_processor_queued_retry_send_latency_count)", 765 | "format": "time_series", 766 | "interval": "", 767 | "intervalFactor": 1, 768 | "legendFormat": "ms", 769 | "refId": "A" 770 | } 771 | ], 772 | "thresholds": [], 773 | "timeFrom": null, 774 | "timeRegions": [], 775 | "timeShift": null, 776 | "title": "Queued Retry Processor Send Latency (ms)", 777 | "tooltip": { 778 | "shared": true, 779 | "sort": 0, 780 | "value_type": "individual" 781 | }, 782 | "type": "graph", 783 | "xaxis": { 784 | "buckets": null, 785 | "mode": "time", 786 | "name": null, 787 | "show": true, 788 | "values": [] 789 | }, 790 | "yaxes": [ 791 | { 792 | "format": "short", 793 | "label": null, 794 | "logBase": 1, 795 | "max": null, 796 | "min": null, 797 | "show": true 798 | }, 799 | { 800 | "format": "short", 801 | "label": null, 802 | "logBase": 1, 803 | "max": null, 804 | "min": null, 805 | "show": true 806 | } 807 | ], 808 | "yaxis": { 809 | "align": false, 810 | "alignLevel": null 811 | } 812 | }, 813 | { 814 | "aliasColors": {}, 815 | "bars": false, 816 | "dashLength": 10, 817 | "dashes": false, 818 | "datasource": "Prometheus", 819 | "fieldConfig": { 820 | "defaults": { 821 | "custom": {} 822 | }, 823 | "overrides": [] 824 | }, 825 | "fill": 1, 826 | "fillGradient": 0, 827 | "gridPos": { 828 | "h": 9, 829 | "w": 12, 830 | "x": 0, 831 | "y": 30 832 | }, 833 | "hiddenSeries": false, 834 | "id": 5, 835 | "legend": { 836 | "avg": false, 837 | "current": false, 838 | "max": false, 839 | "min": false, 840 | "show": true, 841 | "total": false, 842 | "values": false 843 | }, 844 | "lines": true, 845 | "linewidth": 1, 846 | "links": [], 847 | "nullPointMode": "null", 848 | "options": { 849 | "dataLinks": [] 850 | }, 851 | "percentage": false, 852 | "pointradius": 5, 853 | "points": false, 854 | "renderer": "flot", 855 | "seriesOverrides": [], 856 | "spaceLength": 10, 857 | "stack": false, 858 | "steppedLine": false, 859 | "targets": [ 860 | { 861 | "expr": "sum(rate(otelcol_processor_spans_received{processor=~\"$processor\"}[1m])) by (processor)", 862 | "format": "time_series", 863 | "interval": "", 864 | "intervalFactor": 1, 865 | "legendFormat": "{{processor}} | received", 866 | "refId": "A" 867 | }, 868 | { 869 | "expr": "sum(rate(otelcol_processor_spans_dropped{processor=~\"$processor\"}[1m])) by (processor)", 870 | "interval": "", 871 | "legendFormat": "{{processor}} | dropped", 872 | "refId": "B" 873 | }, 874 | { 875 | "expr": "sum(rate(otelcol_processor_accepted_spans{processor=~\"$processor\"}[1m])) by (processor)", 876 | "interval": "", 877 | "legendFormat": "{{processor}} | accepted", 878 | "refId": "C" 879 | }, 880 | { 881 | "expr": "sum(rate(otelcol_processor_refused_spans{processor=~\"$processor\"}[1m])) by (processor)", 882 | "interval": "", 883 | "legendFormat": "{{processor}} | refused", 884 | "refId": "E" 885 | } 886 | ], 887 | "thresholds": [], 888 | "timeFrom": null, 889 | "timeRegions": [], 890 | "timeShift": null, 891 | "title": "Processor Spans Received, Dropped, Accepted, Refused/Second", 892 | "tooltip": { 893 | "shared": true, 894 | "sort": 0, 895 | "value_type": "individual" 896 | }, 897 | "type": "graph", 898 | "xaxis": { 899 | "buckets": null, 900 | "mode": "time", 901 | "name": null, 902 | "show": true, 903 | "values": [] 904 | }, 905 | "yaxes": [ 906 | { 907 | "decimals": null, 908 | "format": "short", 909 | "label": null, 910 | "logBase": 1, 911 | "max": null, 912 | "min": "0", 913 | "show": true 914 | }, 915 | { 916 | "format": "short", 917 | "label": null, 918 | "logBase": 1, 919 | "max": null, 920 | "min": null, 921 | "show": true 922 | } 923 | ], 924 | "yaxis": { 925 | "align": false, 926 | "alignLevel": null 927 | } 928 | }, 929 | { 930 | "aliasColors": {}, 931 | "bars": false, 932 | "dashLength": 10, 933 | "dashes": false, 934 | "datasource": "Prometheus", 935 | "fieldConfig": { 936 | "defaults": { 937 | "custom": {} 938 | }, 939 | "overrides": [] 940 | }, 941 | "fill": 1, 942 | "fillGradient": 0, 943 | "gridPos": { 944 | "h": 9, 945 | "w": 12, 946 | "x": 12, 947 | "y": 30 948 | }, 949 | "hiddenSeries": false, 950 | "id": 14, 951 | "legend": { 952 | "avg": false, 953 | "current": false, 954 | "max": false, 955 | "min": false, 956 | "show": true, 957 | "total": false, 958 | "values": false 959 | }, 960 | "lines": true, 961 | "linewidth": 1, 962 | "links": [], 963 | "nullPointMode": "null", 964 | "options": { 965 | "dataLinks": [] 966 | }, 967 | "percentage": false, 968 | "pointradius": 5, 969 | "points": false, 970 | "renderer": "flot", 971 | "seriesOverrides": [], 972 | "spaceLength": 10, 973 | "stack": false, 974 | "steppedLine": false, 975 | "targets": [ 976 | { 977 | "expr": "sum(rate(otelcol_processor_queued_retry_success_send{processor=~\"$processor\"}[1m])) by (processor)", 978 | "format": "time_series", 979 | "hide": false, 980 | "interval": "", 981 | "intervalFactor": 1, 982 | "legendFormat": "{{processor}}", 983 | "refId": "A" 984 | }, 985 | { 986 | "expr": "sum(rate(otelcol_processor_queued_retry_fail_send{processor=~\"$processor\"}[1m])) by (processor)", 987 | "interval": "", 988 | "legendFormat": "{{processor}}", 989 | "refId": "B" 990 | } 991 | ], 992 | "thresholds": [], 993 | "timeFrom": null, 994 | "timeRegions": [], 995 | "timeShift": null, 996 | "title": "QueuedRetry Successful vs Failed Sent/Second", 997 | "tooltip": { 998 | "shared": true, 999 | "sort": 0, 1000 | "value_type": "individual" 1001 | }, 1002 | "type": "graph", 1003 | "xaxis": { 1004 | "buckets": null, 1005 | "mode": "time", 1006 | "name": null, 1007 | "show": true, 1008 | "values": [] 1009 | }, 1010 | "yaxes": [ 1011 | { 1012 | "format": "short", 1013 | "label": null, 1014 | "logBase": 1, 1015 | "max": null, 1016 | "min": null, 1017 | "show": true 1018 | }, 1019 | { 1020 | "format": "short", 1021 | "label": null, 1022 | "logBase": 1, 1023 | "max": null, 1024 | "min": null, 1025 | "show": true 1026 | } 1027 | ], 1028 | "yaxis": { 1029 | "align": false, 1030 | "alignLevel": null 1031 | } 1032 | }, 1033 | { 1034 | "aliasColors": {}, 1035 | "bars": false, 1036 | "dashLength": 10, 1037 | "dashes": false, 1038 | "datasource": "Prometheus", 1039 | "fieldConfig": { 1040 | "defaults": { 1041 | "custom": {} 1042 | }, 1043 | "overrides": [] 1044 | }, 1045 | "fill": 1, 1046 | "fillGradient": 0, 1047 | "gridPos": { 1048 | "h": 9, 1049 | "w": 12, 1050 | "x": 0, 1051 | "y": 39 1052 | }, 1053 | "hiddenSeries": false, 1054 | "id": 2, 1055 | "interval": "", 1056 | "legend": { 1057 | "avg": false, 1058 | "current": false, 1059 | "max": false, 1060 | "min": false, 1061 | "show": true, 1062 | "total": false, 1063 | "values": false 1064 | }, 1065 | "lines": true, 1066 | "linewidth": 1, 1067 | "links": [], 1068 | "nullPointMode": "null", 1069 | "options": { 1070 | "dataLinks": [] 1071 | }, 1072 | "percentage": false, 1073 | "pointradius": 5, 1074 | "points": false, 1075 | "renderer": "flot", 1076 | "seriesOverrides": [], 1077 | "spaceLength": 10, 1078 | "stack": false, 1079 | "steppedLine": false, 1080 | "targets": [ 1081 | { 1082 | "expr": "sum(rate(otelcol_processor_spans_received{processor=~\"$processor\"}[1m]) / rate(otelcol_processor_batches_received{processor=~\"$processor\"}[1m])) by (processor)", 1083 | "format": "time_series", 1084 | "hide": false, 1085 | "interval": "", 1086 | "intervalFactor": 1, 1087 | "legendFormat": "{{processor}}", 1088 | "refId": "A" 1089 | } 1090 | ], 1091 | "thresholds": [], 1092 | "timeFrom": null, 1093 | "timeRegions": [], 1094 | "timeShift": null, 1095 | "title": "Spans per Batch (avg from rates)", 1096 | "tooltip": { 1097 | "shared": true, 1098 | "sort": 0, 1099 | "value_type": "individual" 1100 | }, 1101 | "type": "graph", 1102 | "xaxis": { 1103 | "buckets": null, 1104 | "mode": "time", 1105 | "name": null, 1106 | "show": true, 1107 | "values": [] 1108 | }, 1109 | "yaxes": [ 1110 | { 1111 | "format": "short", 1112 | "label": null, 1113 | "logBase": 1, 1114 | "max": null, 1115 | "min": null, 1116 | "show": true 1117 | }, 1118 | { 1119 | "format": "short", 1120 | "label": null, 1121 | "logBase": 1, 1122 | "max": null, 1123 | "min": null, 1124 | "show": true 1125 | } 1126 | ], 1127 | "yaxis": { 1128 | "align": false, 1129 | "alignLevel": null 1130 | } 1131 | }, 1132 | { 1133 | "aliasColors": {}, 1134 | "bars": false, 1135 | "dashLength": 10, 1136 | "dashes": false, 1137 | "datasource": "Prometheus", 1138 | "fieldConfig": { 1139 | "defaults": { 1140 | "custom": {} 1141 | }, 1142 | "overrides": [] 1143 | }, 1144 | "fill": 1, 1145 | "fillGradient": 0, 1146 | "gridPos": { 1147 | "h": 9, 1148 | "w": 12, 1149 | "x": 12, 1150 | "y": 39 1151 | }, 1152 | "hiddenSeries": false, 1153 | "id": 12, 1154 | "interval": "", 1155 | "legend": { 1156 | "avg": false, 1157 | "current": false, 1158 | "max": false, 1159 | "min": false, 1160 | "show": true, 1161 | "total": false, 1162 | "values": false 1163 | }, 1164 | "lines": true, 1165 | "linewidth": 1, 1166 | "links": [], 1167 | "nullPointMode": "null", 1168 | "options": { 1169 | "dataLinks": [] 1170 | }, 1171 | "percentage": false, 1172 | "pointradius": 5, 1173 | "points": false, 1174 | "renderer": "flot", 1175 | "seriesOverrides": [], 1176 | "spaceLength": 10, 1177 | "stack": false, 1178 | "steppedLine": false, 1179 | "targets": [ 1180 | { 1181 | "expr": "sum(rate(otelcol_processor_batches_received{processor=~\"$processor\"}[1m])) by (processor)", 1182 | "format": "time_series", 1183 | "interval": "", 1184 | "intervalFactor": 1, 1185 | "legendFormat": "{{processor}} | received", 1186 | "refId": "A" 1187 | }, 1188 | { 1189 | "expr": "sum(rate(otelcol_processor_trace_batches_dropped{processor=~\"$processor\"}[1m])) by (processor)", 1190 | "interval": "", 1191 | "legendFormat": "{{processor}} | trace dropped", 1192 | "refId": "B" 1193 | } 1194 | ], 1195 | "thresholds": [], 1196 | "timeFrom": null, 1197 | "timeRegions": [], 1198 | "timeShift": null, 1199 | "title": "Batches Received vs Dropped /Second", 1200 | "tooltip": { 1201 | "shared": true, 1202 | "sort": 0, 1203 | "value_type": "individual" 1204 | }, 1205 | "type": "graph", 1206 | "xaxis": { 1207 | "buckets": null, 1208 | "mode": "time", 1209 | "name": null, 1210 | "show": true, 1211 | "values": [] 1212 | }, 1213 | "yaxes": [ 1214 | { 1215 | "format": "short", 1216 | "label": null, 1217 | "logBase": 1, 1218 | "max": null, 1219 | "min": null, 1220 | "show": true 1221 | }, 1222 | { 1223 | "format": "short", 1224 | "label": null, 1225 | "logBase": 1, 1226 | "max": null, 1227 | "min": null, 1228 | "show": true 1229 | } 1230 | ], 1231 | "yaxis": { 1232 | "align": false, 1233 | "alignLevel": null 1234 | } 1235 | }, 1236 | { 1237 | "aliasColors": {}, 1238 | "bars": false, 1239 | "dashLength": 10, 1240 | "dashes": false, 1241 | "datasource": "Prometheus", 1242 | "fieldConfig": { 1243 | "defaults": { 1244 | "custom": {} 1245 | }, 1246 | "overrides": [] 1247 | }, 1248 | "fill": 1, 1249 | "fillGradient": 0, 1250 | "gridPos": { 1251 | "h": 9, 1252 | "w": 12, 1253 | "x": 0, 1254 | "y": 48 1255 | }, 1256 | "hiddenSeries": false, 1257 | "id": 6, 1258 | "interval": "", 1259 | "legend": { 1260 | "avg": false, 1261 | "current": false, 1262 | "max": false, 1263 | "min": false, 1264 | "show": true, 1265 | "total": false, 1266 | "values": false 1267 | }, 1268 | "lines": true, 1269 | "linewidth": 1, 1270 | "links": [], 1271 | "nullPointMode": "null", 1272 | "options": { 1273 | "dataLinks": [] 1274 | }, 1275 | "percentage": false, 1276 | "pointradius": 5, 1277 | "points": false, 1278 | "renderer": "flot", 1279 | "seriesOverrides": [], 1280 | "spaceLength": 10, 1281 | "stack": false, 1282 | "steppedLine": false, 1283 | "targets": [ 1284 | { 1285 | "expr": "sum(otelcol_processor_queued_retry_success_send{processor=~\"$processor\"}) by (processor)", 1286 | "format": "time_series", 1287 | "hide": false, 1288 | "interval": "", 1289 | "intervalFactor": 1, 1290 | "legendFormat": "{{processor}}", 1291 | "refId": "A" 1292 | } 1293 | ], 1294 | "thresholds": [], 1295 | "timeFrom": null, 1296 | "timeRegions": [], 1297 | "timeShift": null, 1298 | "title": "Successful Batches Sent Cumulative", 1299 | "tooltip": { 1300 | "shared": true, 1301 | "sort": 0, 1302 | "value_type": "individual" 1303 | }, 1304 | "type": "graph", 1305 | "xaxis": { 1306 | "buckets": null, 1307 | "mode": "time", 1308 | "name": null, 1309 | "show": true, 1310 | "values": [] 1311 | }, 1312 | "yaxes": [ 1313 | { 1314 | "format": "short", 1315 | "label": null, 1316 | "logBase": 1, 1317 | "max": null, 1318 | "min": "0", 1319 | "show": true 1320 | }, 1321 | { 1322 | "format": "short", 1323 | "label": null, 1324 | "logBase": 1, 1325 | "max": null, 1326 | "min": null, 1327 | "show": true 1328 | } 1329 | ], 1330 | "yaxis": { 1331 | "align": false, 1332 | "alignLevel": null 1333 | } 1334 | }, 1335 | { 1336 | "aliasColors": {}, 1337 | "bars": false, 1338 | "dashLength": 10, 1339 | "dashes": false, 1340 | "datasource": "Prometheus", 1341 | "fieldConfig": { 1342 | "defaults": { 1343 | "custom": {} 1344 | }, 1345 | "overrides": [] 1346 | }, 1347 | "fill": 1, 1348 | "fillGradient": 0, 1349 | "gridPos": { 1350 | "h": 9, 1351 | "w": 12, 1352 | "x": 12, 1353 | "y": 48 1354 | }, 1355 | "hiddenSeries": false, 1356 | "id": 3, 1357 | "legend": { 1358 | "avg": false, 1359 | "current": false, 1360 | "max": false, 1361 | "min": false, 1362 | "show": true, 1363 | "total": false, 1364 | "values": false 1365 | }, 1366 | "lines": true, 1367 | "linewidth": 1, 1368 | "links": [], 1369 | "nullPointMode": "null", 1370 | "options": { 1371 | "dataLinks": [] 1372 | }, 1373 | "percentage": false, 1374 | "pointradius": 5, 1375 | "points": false, 1376 | "renderer": "flot", 1377 | "seriesOverrides": [], 1378 | "spaceLength": 10, 1379 | "stack": false, 1380 | "steppedLine": false, 1381 | "targets": [ 1382 | { 1383 | "expr": "sum(otelcol_processor_spans_received{exporter=~\"$exporter\"}) by (exporter)", 1384 | "format": "time_series", 1385 | "hide": false, 1386 | "interval": "", 1387 | "intervalFactor": 1, 1388 | "legendFormat": "{{exporter}}", 1389 | "refId": "A" 1390 | } 1391 | ], 1392 | "thresholds": [], 1393 | "timeFrom": null, 1394 | "timeRegions": [], 1395 | "timeShift": null, 1396 | "title": "Enqueued Spans Cumulative", 1397 | "tooltip": { 1398 | "shared": true, 1399 | "sort": 0, 1400 | "value_type": "individual" 1401 | }, 1402 | "type": "graph", 1403 | "xaxis": { 1404 | "buckets": null, 1405 | "mode": "time", 1406 | "name": null, 1407 | "show": true, 1408 | "values": [] 1409 | }, 1410 | "yaxes": [ 1411 | { 1412 | "format": "short", 1413 | "label": null, 1414 | "logBase": 1, 1415 | "max": null, 1416 | "min": null, 1417 | "show": true 1418 | }, 1419 | { 1420 | "format": "short", 1421 | "label": null, 1422 | "logBase": 1, 1423 | "max": null, 1424 | "min": null, 1425 | "show": true 1426 | } 1427 | ], 1428 | "yaxis": { 1429 | "align": false, 1430 | "alignLevel": null 1431 | } 1432 | } 1433 | ], 1434 | "refresh": false, 1435 | "schemaVersion": 25, 1436 | "style": "dark", 1437 | "tags": [ 1438 | "opentelemetry" 1439 | ], 1440 | "templating": { 1441 | "list": [ 1442 | { 1443 | "allValue": ".*", 1444 | "current": { 1445 | "selected": false, 1446 | "text": "All", 1447 | "value": "$__all" 1448 | }, 1449 | "datasource": "Prometheus", 1450 | "definition": "label_values(otelsvc_receiver)", 1451 | "hide": 0, 1452 | "includeAll": true, 1453 | "label": "receiver", 1454 | "multi": true, 1455 | "name": "receiver", 1456 | "options": [], 1457 | "query": "label_values(otelsvc_receiver)", 1458 | "refresh": 2, 1459 | "regex": "", 1460 | "skipUrlSync": false, 1461 | "sort": 0, 1462 | "tagValuesQuery": "", 1463 | "tags": [], 1464 | "tagsQuery": "", 1465 | "type": "query", 1466 | "useTags": false 1467 | }, 1468 | { 1469 | "allValue": ".*", 1470 | "current": { 1471 | "selected": false, 1472 | "text": "All", 1473 | "value": "$__all" 1474 | }, 1475 | "datasource": "Prometheus", 1476 | "definition": "label_values(otelsvc_exporter)", 1477 | "hide": 0, 1478 | "includeAll": true, 1479 | "label": "exporter", 1480 | "multi": true, 1481 | "name": "exporter", 1482 | "options": [], 1483 | "query": "label_values(otelsvc_exporter)", 1484 | "refresh": 2, 1485 | "regex": "", 1486 | "skipUrlSync": false, 1487 | "sort": 0, 1488 | "tagValuesQuery": "", 1489 | "tags": [], 1490 | "tagsQuery": "", 1491 | "type": "query", 1492 | "useTags": false 1493 | }, 1494 | { 1495 | "allValue": null, 1496 | "current": { 1497 | "selected": false, 1498 | "text": "All", 1499 | "value": "$__all" 1500 | }, 1501 | "datasource": "Prometheus", 1502 | "definition": "label_values(processor)", 1503 | "hide": 0, 1504 | "includeAll": true, 1505 | "label": "processor", 1506 | "multi": true, 1507 | "name": "processor", 1508 | "options": [], 1509 | "query": "label_values(processor)", 1510 | "refresh": 2, 1511 | "regex": "", 1512 | "skipUrlSync": false, 1513 | "sort": 1, 1514 | "tagValuesQuery": "", 1515 | "tags": [], 1516 | "tagsQuery": "", 1517 | "type": "query", 1518 | "useTags": false 1519 | } 1520 | ] 1521 | }, 1522 | "time": { 1523 | "from": "now-15m", 1524 | "to": "now" 1525 | }, 1526 | "timepicker": { 1527 | "refresh_intervals": [ 1528 | "10s", 1529 | "30s", 1530 | "1m", 1531 | "5m", 1532 | "15m", 1533 | "30m", 1534 | "1h", 1535 | "2h", 1536 | "1d" 1537 | ], 1538 | "time_options": [ 1539 | "5m", 1540 | "15m", 1541 | "1h", 1542 | "6h", 1543 | "12h", 1544 | "24h", 1545 | "2d", 1546 | "7d", 1547 | "30d" 1548 | ] 1549 | }, 1550 | "timezone": "", 1551 | "title": "OpenTelemetry Collector", 1552 | "uid": "BKf2sowmj", 1553 | "version": 7 1554 | } 1555 | --------------------------------------------------------------------------------