diff --git a/agent/core/compile.go b/agent/core/compile.go index baefdb10..16ca286b 100644 --- a/agent/core/compile.go +++ b/agent/core/compile.go @@ -20,6 +20,7 @@ package core import ( //go:nolint _ "bytes" + _ "context" _ "encoding/base64" _ "fmt" _ "io" @@ -30,6 +31,7 @@ import ( _ "os" _ "path/filepath" _ "reflect" + _ "regexp" _ "runtime" _ "runtime/debug" _ "runtime/metrics" @@ -45,6 +47,7 @@ import ( //go:nolint _ "github.com/apache/skywalking-go/agent/core/metrics" _ "github.com/apache/skywalking-go/agent/core/operator" + _ "github.com/apache/skywalking-go/agent/core/profile" _ "github.com/apache/skywalking-go/agent/core/tracing" _ "github.com/apache/skywalking-go/agent/reporter" _ "github.com/apache/skywalking-go/log" diff --git a/agent/core/profile/compile.go b/agent/core/profile/compile.go new file mode 100644 index 00000000..97f8b110 --- /dev/null +++ b/agent/core/profile/compile.go @@ -0,0 +1,41 @@ +// Licensed to Apache Software Foundation (ASF) under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Apache Software Foundation (ASF) licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package profile + +import ( + //go:nolint + _ "bytes" + _ "context" + _ "fmt" + _ "runtime/pprof" + _ "strconv" + _ "strings" + _ "sync" + _ "sync/atomic" + _ "time" + _ "unsafe" + + //go:nolint + _ "github.com/pkg/errors" + + //go:nolint + _ "github.com/apache/skywalking-go/agent/core/operator" + + //go:nolint + _ "github.com/apache/skywalking-go/agent/reporter" +) diff --git a/agent/reporter/imports.go b/agent/reporter/imports.go index 90c7e8a3..34815e22 100644 --- a/agent/reporter/imports.go +++ b/agent/reporter/imports.go @@ -19,22 +19,26 @@ package reporter import ( // imports required packages for gRPC reporter + _ "bytes" _ "context" _ "crypto/tls" _ "crypto/x509" _ "fmt" _ "io" _ "os" + _ "runtime" + _ "runtime/pprof" _ "strconv" _ "strings" _ "sync" _ "time" - // imports the logs for reporter + // imports the logs and profiles for reporter _ "github.com/apache/skywalking-go/agent/core/operator" _ "github.com/apache/skywalking-go/log" // imports configuration and starter for gRPC + _ "github.com/pkg/errors" _ "google.golang.org/grpc" _ "google.golang.org/grpc/backoff" _ "google.golang.org/grpc/balancer" diff --git a/go.work b/go.work index 4afcbcaf..fc47bf56 100644 --- a/go.work +++ b/go.work @@ -26,6 +26,7 @@ use ( ./plugins/rocketmq ./plugins/amqp ./plugins/pulsar + ./plugins/pprof ./plugins/segmentio-kafka ./plugins/go-elasticsearchv8 ./plugins/goframe @@ -72,8 +73,10 @@ use ( ./test/plugins/scenarios/goframe ./test/plugins/scenarios/so11y ./test/plugins/scenarios/cross-goroutine + ./test/plugins/scenarios/pprof ./tools/go-agent ./toolkit ) + diff --git a/plugins/core/go.sum b/plugins/core/go.sum index 2b06a84f..f1c81677 100644 --- a/plugins/core/go.sum +++ b/plugins/core/go.sum @@ -1,58 +1,27 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/dave/dst v0.27.2 h1:4Y5VFTkhGLC1oddtNwuxxe36pnyLxMFXT51FOzH8Ekc= github.com/dave/dst v0.27.2/go.mod h1:jHh6EOibnHgcUW3WjKHisiooEkYwqpHLBSX1iOBhEyc= +github.com/dave/dst v0.27.3 h1:P1HPoMza3cMEquVf9kKy8yXsFirry4zEnWOdYPOoIzY= +github.com/dave/dst v0.27.3/go.mod h1:jHh6EOibnHgcUW3WjKHisiooEkYwqpHLBSX1iOBhEyc= github.com/dave/jennifer v1.5.0 h1:HmgPN93bVDpkQyYbqhCHj5QlgvUkvEOzMyEvKLgCRrg= +github.com/dave/jennifer v1.5.0/go.mod h1:4MnyiFIlZS3l5tSDn8VnzE6ffAhYBMB2SZntBsZGUok= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY= +github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= +github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw= +github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= @@ -61,144 +30,120 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0= +github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU= +github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/segmentio/kafka-go v0.4.47 h1:IqziR4pA3vrZq7YdRxaT3w1/5fvIH5qpCwstUanQQB0= +github.com/segmentio/kafka-go v0.4.47/go.mod h1:HjF6XbOKh0Pjlkr5GVZxt6CsjjwnmhVOfURM5KMd8qg= +github.com/segmentio/kafka-go v0.4.49 h1:GJiNX1d/g+kG6ljyJEoi9++PUMdXGAxb7JGPiDCuNmk= +github.com/segmentio/kafka-go v0.4.49/go.mod h1:Y1gn60kzLEEaW28YshXyk2+VCUKbJ3Qr6DrnT3i4+9E= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= +github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= +github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= +github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= +github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= +go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= +go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= +go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= +go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= +go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= +go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o= +go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w= +go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= +go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= +golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= +golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= +golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A= google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag= -google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.29.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250826171959-ef028d996bc1 h1:pmJpJEvT846VzausCQ5d7KreSROcDqmO388w5YbnltA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250826171959-ef028d996bc1/go.mod h1:GmFNa4BdJZ2a8G+wCe9Bg3wwThLrJun751XstdJt5Og= +google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok= +google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc= +google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= +google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= +google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -skywalking.apache.org/repo/goapi v0.0.0-20230314034821-0c5a44bb767a h1:m8DTnaSEOEnPXRWmA6g7isbdqw7WPZP6SnaEHz1Sx7s= -skywalking.apache.org/repo/goapi v0.0.0-20230314034821-0c5a44bb767a/go.mod h1:LcZMcxDjdJPn5yetydFnxe0l7rmiv8lvHEnzRbsey14= +skywalking.apache.org/repo/goapi v0.0.0-20250520033135-e237d585745f h1:of4GHiflH8hS8uabPidaHvuruM8uyY5u78G83IDI1Fc= +skywalking.apache.org/repo/goapi v0.0.0-20250520033135-e237d585745f/go.mod h1:rTNGn2QrS+p1i2OaIBxlwQ/VrDSDc7OwRk/iWV+mU0k= diff --git a/plugins/core/operator/common.go b/plugins/core/operator/common.go index e53f006f..0a4b60a7 100644 --- a/plugins/core/operator/common.go +++ b/plugins/core/operator/common.go @@ -25,6 +25,7 @@ var MetricsCollectAppender = func(func()) {} type Operator interface { Tracing() interface{} // to TracingOperator Logger() interface{} // to LogOperator + Profiler() interface{} // to ProfileOperator Tools() interface{} // to ToolsOperator DebugStack() []byte // Getting the stack of the current goroutine, for getting details when plugin broken. Entity() interface{} // Get the entity of the service diff --git a/plugins/core/operator/profiler.go b/plugins/core/operator/profiler.go new file mode 100644 index 00000000..97b57bb3 --- /dev/null +++ b/plugins/core/operator/profiler.go @@ -0,0 +1,23 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package operator + +type ProfileOperator interface { + // GetNowLabels get skywalking internal labels from goroutine,avoid covered by user + GetNowLabels() interface{} +} diff --git a/plugins/core/prof_labels.go b/plugins/core/prof_labels.go new file mode 100644 index 00000000..b4a62f90 --- /dev/null +++ b/plugins/core/prof_labels.go @@ -0,0 +1,221 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package core + +import ( + "context" + "regexp" + "runtime" + "runtime/pprof" + "sort" + "strconv" + "unsafe" +) + +type label struct { + key string + value string +} + +type LabelSet struct { + list []label +} + +type labelMap struct { + LabelSet +} + +type labelMap19 map[string]string + +type labelContextKey struct{} + +//go:linkname runtimeGetProfLabel runtime/pprof.runtime_getProfLabel +func runtimeGetProfLabel() unsafe.Pointer + +//go:linkname runtimeSetProfLabel runtime/pprof.runtime_setProfLabel +func runtimeSetProfLabel(label unsafe.Pointer) + +func setGoroutineLabelsInternal(ctx context.Context) { + if isGoVersionLMoreThan120(runtime.Version()) { + ctxLabels, _ := ctx.Value(labelContextKey{}).(*labelMap) + runtimeSetProfLabel(unsafe.Pointer(ctxLabels)) + return + } + ctxLabels, _ := ctx.Value(labelContextKey{}).(*labelMap19) + runtimeSetProfLabel(unsafe.Pointer(ctxLabels)) +} + +func labelValue(ctx context.Context) labelMap19 { + labels, _ := ctx.Value(labelContextKey{}).(*labelMap19) + if labels == nil { + return labelMap19(nil) + } + return *labels +} + +func WithLabels(ctx context.Context, s LabelSet) context.Context { + if isGoVersionLMoreThan120(runtime.Version()) { + ctx = context.WithValue(ctx, labelContextKey{}, &labelMap{s}) + return ctx + } + return withLabels19(ctx, s) +} + +func withLabels19(ctx context.Context, labels LabelSet) context.Context { + childLabels := make(labelMap19) + parentLabels := labelValue(ctx) + for k, v := range parentLabels { + childLabels[k] = v + } + for _, label := range labels.list { + childLabels[label.key] = label.value + } + return context.WithValue(ctx, labelContextKey{}, &childLabels) +} + +func GetNowLabelSet() LabelSet { + pl := LabelSet{ + list: make([]label, 0), + } + p := runtimeGetProfLabel() + if p != nil { + version := runtime.Version() + if !isGoVersionLMoreThan120(version) { + // Go1.19:map[string]string -> []label + m := *(*labelMap19)(p) + pl.list = make([]label, 0, len(m)) + for k, v := range m { + pl.list = append(pl.list, label{key: k, value: v}) + } + } else { + lm := (*labelMap)(p) + pl.list = lm.list + } + } + return pl +} + +// isGoVersionLMoreThan120 parses version strings like "go1.19.8" +func isGoVersionLMoreThan120(version string) bool { + re := regexp.MustCompile(`go(\d+)\.(\d+)`) + sub := re.FindStringSubmatch(version) + if len(sub) != 3 { + return false + } + major, err1 := strconv.Atoi(sub[1]) + minor, err2 := strconv.Atoi(sub[2]) + if err1 != nil || err2 != nil { + return false + } + if major < 1 { + return false + } + if major > 1 { + return true + } + return minor >= 20 +} + +func (m *ProfileManager) AddSkyLabels(traceID, segmentID string, spanID int32) interface{} { + pl := GetNowLabelSet() + re := UpdateTraceLabels(pl, TraceLabel, traceID, SegmentLabel, segmentID, SpanLabel, parseString(spanID)) + return &re +} + +func (m *ProfileManager) TurnToPprofLabel(l *LabelSet) pprof.LabelSet { + li := l.List() + if len(li) == 0 { + return pprof.LabelSet{} + } + re := pprof.Labels(li...) + return re +} + +func UpdateTraceLabels(s LabelSet, args ...string) LabelSet { + if len(args)%2 != 0 { + panic("uneven number of arguments to profile.UpdateTraceLabels") + } + + // add first + for i := 0; i < len(args); i += 2 { + s.list = append(s.list, label{key: args[i], value: args[i+1]}) + } + + // sort + sort.SliceStable(s.list, func(i, j int) bool { + return s.list[i].key < s.list[j].key + }) + + // remove duplicates + deduped := make([]label, 0, len(s.list)) + for i, lbl := range s.list { + if i == 0 || lbl.key != s.list[i-1].key { + deduped = append(deduped, lbl) + } else { + deduped[len(deduped)-1] = lbl + } + } + s.list = deduped + + return s +} + +func (s *LabelSet) List() []string { + var ret []string + for _, v := range s.list { + ret = append(ret, v.key, v.value) + } + return ret +} + +func SetGoroutineLabels(s *LabelSet) { + if s.IsEmpty() { + var c = context.Background() + setGoroutineLabelsInternal(c) + return + } + var c = context.Background() + l := *s + c = WithLabels(c, l) + setGoroutineLabelsInternal(c) +} + +func extractSkyInternalLabels(s LabelSet) LabelSet { + re := LabelSet{list: make([]label, 0)} + for _, l := range s.list { + if l.key == SpanLabel || l.key == SegmentLabel || l.key == TraceLabel || l.key == MinDurationLabel { + re.list = append(re.list, l) + } + } + return re +} + +// GetNowLabels Expose to operator +func (m *ProfileManager) GetNowLabels() interface{} { + row := GetNowLabelSet() + filter := extractSkyInternalLabels(row) + re := m.TurnToPprofLabel(&filter) + return re +} + +func (s *LabelSet) IsEmpty() bool { + if s == nil || s.list == nil { + return true + } + return len(s.list) == 0 +} diff --git a/plugins/core/prof_labels_test.go b/plugins/core/prof_labels_test.go new file mode 100644 index 00000000..a52f5813 --- /dev/null +++ b/plugins/core/prof_labels_test.go @@ -0,0 +1,49 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package core + +import ( + "sort" + "testing" + + "github.com/stretchr/testify/assert" +) + +func sortLabels(ls LabelSet) LabelSet { + sort.Slice(ls.list, func(i, j int) bool { + return ls.list[i].key < ls.list[j].key + }) + return ls +} + +func TestGetLabels(t *testing.T) { + p := NewProfileManager(nil) + p.currentTask = ¤tTask{ + serialNumber: "", + taskID: "", + minDurationThreshold: 0, + endpointName: "", + duration: 0, + } + ls := p.AddSkyLabels("test-TraceID", "test-segmentID", 0).(*LabelSet) + ts := LabelSet{list: []label{ + {key: "spanID", value: "0"}, + {key: "traceSegmentID", value: "test-segmentID"}, + {key: "traceID", value: "test-TraceID"}}} + assert.Equal(t, sortLabels(ts), sortLabels(*ls)) +} diff --git a/plugins/core/profile.go b/plugins/core/profile.go new file mode 100644 index 00000000..c8d8f68b --- /dev/null +++ b/plugins/core/profile.go @@ -0,0 +1,336 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package core + +import ( + "runtime/pprof" + "strconv" + "sync" + "time" + + "github.com/apache/skywalking-go/plugins/core/operator" + "github.com/apache/skywalking-go/plugins/core/reporter" + common "github.com/apache/skywalking-go/protocols/collect/common/v3" +) + +type profileLabels struct { + labels *LabelSet +} + +const ( + maxSendQueueSize int32 = 100 + timeOut time.Duration = 2 * time.Minute + ChunkSize = 1024 * 1024 + TraceLabel = "traceID" + SegmentLabel = "traceSegmentID" + MinDurationLabel = "minDurationThreshold" + SpanLabel = "spanID" +) + +type currentTask struct { + serialNumber string // uuid + taskID string + minDurationThreshold int64 + endpointName string + endTime time.Time + duration int +} + +type ProfileManager struct { + mu sync.Mutex + TraceProfileTask *reporter.TraceProfileTask + ProfileTaskQueue []*reporter.TraceProfileTask + rawCh chan profileRawData + FinalReportResults chan reporter.ProfileResult + profilingWriter *ProfilingWriter + profileEvents *TraceProfilingEventManager + currentTask *currentTask + Log operator.LogOperator +} + +func (m *ProfileManager) initReportChannel() { + // Original channel for receiving raw data chunks sent by the Writer + rawCh := make(chan profileRawData, maxSendQueueSize) + m.rawCh = rawCh + var d []byte + // Start a goroutine to supplement each data chunk with business information + go func() { + for rawResult := range rawCh { + d = append(d, rawResult.data...) + m.mu.Lock() + // Get business information from currentTask + if m.currentTask == nil { + m.Log.Info("no task") + m.mu.Unlock() + continue // Task has ended, ignore + } + task := m.currentTask + m.mu.Unlock() + + if rawResult.isLast { + m.FinalReportResults <- reporter.ProfileResult{ + TaskID: task.taskID, + Payload: rawResult.data, + IsLast: rawResult.isLast, + } + m.mu.Lock() + if m.TraceProfileTask == nil { + m.Log.Warn("no TraceProfileTask before finish profile") + } else { + m.TraceProfileTask.Status = reporter.Finished + } + m.currentTask = nil + m.profileEvents.BaseEventStatus[CurTaskExist] = false + m.mu.Unlock() + } else { + m.FinalReportResults <- reporter.ProfileResult{ + TaskID: task.taskID, + Payload: rawResult.data, + IsLast: rawResult.isLast, + } + } + } + }() +} + +func NewProfileManager(log operator.LogOperator) *ProfileManager { + pm := &ProfileManager{ + FinalReportResults: make(chan reporter.ProfileResult, maxSendQueueSize), + profileEvents: NewEventManager(), + ProfileTaskQueue: make([]*reporter.TraceProfileTask, 0), + } + pm.RegisterProfileEvents() + + if log == nil { + log = newDefaultLogger() + } + pm.Log = log + pm.initReportChannel() + pm.profilingWriter = NewProfilingWriter( + ChunkSize, + pm.rawCh, + ) + return pm +} + +func (m *ProfileManager) AddProfileTask(args []*common.KeyStringValuePair, t int64) int64 { + m.mu.Lock() + defer m.mu.Unlock() + var task reporter.TraceProfileTask + for _, arg := range args { + switch arg.Key { + case "TaskId": + task.TaskID = arg.Value + case "EndpointName": + task.EndpointName = arg.Value + case "Duration": + // Duration min + task.Duration = parseInt(arg.Value) + case "MinDurationThreshold": + task.MinDurationThreshold = parseInt64(arg.Value) + case "DumpPeriod": + task.DumpPeriod = parseInt(arg.Value) + case "MaxSamplingCount": + task.MaxSamplingCount = parseInt(arg.Value) + case "StartTime": + task.StartTime = time.UnixMilli(parseInt64(arg.Value)) + case "CreateTime": + temp := parseInt64(arg.Value) + task.CreateTime = time.UnixMilli(temp) + if temp > t { + t = temp + } + case "SerialNumber": + task.SerialNumber = arg.Value + } + } + m.Log.Info("adding profile task:", task) + endTime := task.StartTime.Add(time.Duration(task.Duration) * time.Minute) + task.EndTime = endTime + task.Status = reporter.Pending + m.addTask(&task) + return t +} + +func (m *ProfileManager) RemoveProfileTask() { + m.mu.Lock() + defer m.mu.Unlock() + if m.TraceProfileTask == nil { + return + } + if m.TraceProfileTask.Status == reporter.Reported || + time.Now().After(m.TraceProfileTask.EndTime.Add(timeOut)) { + m.TraceProfileTask = nil + } +} + +func (m *ProfileManager) addTask(task *reporter.TraceProfileTask) { + if task == nil { + return + } + for _, t := range m.ProfileTaskQueue { + if task.EndTime.After(t.StartTime) && task.StartTime.Before(t.EndTime) { + return + } + } + m.ProfileTaskQueue = append(m.ProfileTaskQueue, task) + + delay := time.Until(task.StartTime) + if delay < 0 { + delay = 0 + } + + time.AfterFunc(delay, func() { + m.mu.Lock() + defer m.mu.Unlock() + if m.TraceProfileTask != nil { + return + } + m.TraceProfileTask = task + m.trySetCurrentTaskAndStartProfile(task) + }) +} + +func (m *ProfileManager) tryStartCPUProfiling() { + ok, err := m.profileEvents.ExecuteComplexEvent(CouldProfile) + if err != nil { + m.Log.Errorf("profile event error:%v", err) + return + } + t := m.TraceProfileTask + if ok && t.Status == reporter.Pending { + err := pprof.StartCPUProfile(m.profilingWriter) + if err != nil { + m.Log.Info("failed to start cpu profiling", err) + return + } + err = m.profileEvents.UpdateBaseEventStatus(IfProfiling, true) + if err != nil { + m.Log.Errorf("update profile event error:%v", err) + } + t.Status = reporter.Running + go m.monitor() + } +} + +func (m *ProfileManager) CheckIfProfileTarget(endpoint string) bool { + m.mu.Lock() + defer m.mu.Unlock() + if m.currentTask == nil { + return false + } + return m.currentTask.endpointName == endpoint +} + +func (m *ProfileManager) IfProfiling() bool { + ok, err := m.profileEvents.GetBaseEventStatus(IfProfiling) + if err != nil { + m.Log.Errorf("get profile event error:%v", err) + return false + } + return ok +} + +func (m *ProfileManager) trySetCurrentTaskAndStartProfile(task *reporter.TraceProfileTask) { + if m.currentTask != nil && time.Now().Before(m.currentTask.endTime.Add(timeOut)) { + return + } + ok, err := m.profileEvents.ExecuteComplexEvent(CouldSetCurTask) + if err != nil { + m.Log.Errorf("profile event error:%v", err) + } + if ok { + m.generateCurrentTask(task) + m.tryStartCPUProfiling() + } +} + +func (m *ProfileManager) generateProfileLabels(traceSegmentID string, minDurationThreshold int64) profileLabels { + var l = LabelSet{} + l = UpdateTraceLabels(l, SegmentLabel, traceSegmentID, MinDurationLabel, strconv.FormatInt(minDurationThreshold, 10)) + return profileLabels{ + labels: &l, + } +} + +func (m *ProfileManager) generateCurrentTask(t *reporter.TraceProfileTask) { + var c = currentTask{ + serialNumber: t.SerialNumber, + taskID: t.TaskID, + minDurationThreshold: t.MinDurationThreshold, + duration: t.Duration, + endpointName: t.EndpointName, + endTime: t.EndTime, + } + m.currentTask = &c + err := m.profileEvents.UpdateBaseEventStatus(CurTaskExist, true) + if err != nil { + m.Log.Errorf("profile event error:%v", err) + } +} + +func (m *ProfileManager) TryToAddSegmentLabelSet(traceSegmentID string) { + if m.currentTask != nil { + c := m.generateProfileLabels(traceSegmentID, m.currentTask.minDurationThreshold) + SetGoroutineLabels(c.labels) + return + } +} + +func (m *ProfileManager) monitor() { + <-time.After(time.Duration(m.currentTask.duration) * time.Minute) + pprof.StopCPUProfile() + err := m.profileEvents.UpdateBaseEventStatus(IfProfiling, false) + if err != nil { + m.Log.Errorf("profile event error:%v", err) + } + if m.profilingWriter != nil { + m.profilingWriter.Flush() + } +} + +func (m *ProfileManager) AddSpanID(traceID, segmentID string, spanID int32) { + l := m.AddSkyLabels(traceID, segmentID, spanID).(*LabelSet) + SetGoroutineLabels(l) +} + +func (m *ProfileManager) GetProfileResults() chan reporter.ProfileResult { + m.mu.Lock() + defer m.mu.Unlock() + return m.FinalReportResults +} + +func (m *ProfileManager) ProfileFinish() { + m.mu.Lock() + defer m.mu.Unlock() + m.TraceProfileTask.Status = reporter.Reported +} + +func parseInt64(value string) int64 { + v, _ := strconv.ParseInt(value, 10, 64) + return v +} + +func parseInt(value string) int { + v, _ := strconv.Atoi(value) + return v +} +func parseString(value int32) string { + str := strconv.Itoa(int(value)) + return str +} diff --git a/plugins/core/profile/api.go b/plugins/core/profile/api.go new file mode 100644 index 00000000..5a2b576b --- /dev/null +++ b/plugins/core/profile/api.go @@ -0,0 +1,33 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package profile + +import "github.com/apache/skywalking-go/plugins/core/operator" + +func CatchNowProfileLabel() interface{} { + op := operator.GetOperator() + if op == nil { + return nil + } + profiler, ok := op.Profiler().(operator.ProfileOperator) + if !ok { + return nil + } + re := profiler.GetNowLabels() + return re +} diff --git a/plugins/core/profile_event_manager.go b/plugins/core/profile_event_manager.go new file mode 100644 index 00000000..ccd25470 --- /dev/null +++ b/plugins/core/profile_event_manager.go @@ -0,0 +1,58 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package core + +const ( + IfProfiling TraceProfilingBaseEvent = "IfProfiling" + CurTaskExist TraceProfilingBaseEvent = "CurTaskExist" + + CouldProfile TraceProfilingComplexEvent = "CouldProfile" + CouldSetCurTask TraceProfilingComplexEvent = "CouldSetCurTask" +) + +func (m *ProfileManager) RegisterProfileEvents() { + m.profileEvents.RegisterBaseEvent(IfProfiling, false) + m.profileEvents.RegisterBaseEvent(CurTaskExist, false) + var r1 = TraceProfilingRule{ + Event: IfProfiling, + Op: OpNothing, + IsNot: true, + } + var r2 = TraceProfilingRule{ + Event: CurTaskExist, + Op: OpAnd, + IsNot: false, + } + var r3 = TraceProfilingRule{ + Event: CurTaskExist, + Op: OpAnd, + IsNot: true, + } + m.profileEvents.RegisterComplexEvent(CouldProfile, &TraceProfilingExprNode{ + Rules: []TraceProfilingRule{ + r1, r2, + }, + Event: CouldProfile, + }) + m.profileEvents.RegisterComplexEvent(CouldSetCurTask, &TraceProfilingExprNode{ + Rules: []TraceProfilingRule{ + r1, r3, + }, + Event: CouldSetCurTask, + }) +} diff --git a/plugins/core/profile_event_manager_test.go b/plugins/core/profile_event_manager_test.go new file mode 100644 index 00000000..2f5dc8aa --- /dev/null +++ b/plugins/core/profile_event_manager_test.go @@ -0,0 +1,30 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package core + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestProfileEventController(t *testing.T) { + m := NewProfileManager(nil) + re, _ := m.profileEvents.ExecuteComplexEvent(CouldProfile) + assert.Equal(t, false, re) +} diff --git a/plugins/core/profile_writer.go b/plugins/core/profile_writer.go new file mode 100644 index 00000000..61d27dee --- /dev/null +++ b/plugins/core/profile_writer.go @@ -0,0 +1,83 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package core + +import ( + "sync" +) + +type ProfilingWriter struct { + mu sync.Mutex // Ensures concurrent safety + buf []byte // Temporary buffer for current chunk + chunkSize int // Threshold size for chunking (e.g., 1MB) + reportCh chan<- profileRawData // Channel for sending data chunks +} + +type profileRawData struct { + data []byte + isLast bool +} + +// NewProfilingWriter initializes a ProfilingWriter with specified chunk size and report channel +func NewProfilingWriter(chunkSize int, reportCh chan<- profileRawData) *ProfilingWriter { + return &ProfilingWriter{ + chunkSize: chunkSize, + reportCh: reportCh, + buf: make([]byte, 0, chunkSize), // Preallocate buffer for efficiency + } +} + +// Write implements io.Writer, handles data chunking and sending +func (w *ProfilingWriter) Write(p []byte) (n int, err error) { + w.mu.Lock() + defer w.mu.Unlock() + w.buf = append(w.buf, p...) + + // Send chunks when buffer reaches the threshold + for len(w.buf) >= w.chunkSize { + chunk := w.buf[:w.chunkSize] + w.buf = w.buf[w.chunkSize:] + + // Send raw chunk data (business info added externally) + w.reportCh <- profileRawData{ + data: chunk, + isLast: false, + } + } + + return len(p), nil +} + +// Flush sends remaining data in the buffer +func (w *ProfilingWriter) Flush() { + w.mu.Lock() + defer w.mu.Unlock() + + if len(w.buf) > 0 { + w.reportCh <- profileRawData{ + data: w.buf, + isLast: true, + } + } else { + w.reportCh <- profileRawData{ + data: nil, + isLast: true, + } + } + w.buf = nil +} diff --git a/plugins/core/reporter/api.go b/plugins/core/reporter/api.go index 1d35b757..5d75fa8e 100644 --- a/plugins/core/reporter/api.go +++ b/plugins/core/reporter/api.go @@ -108,11 +108,21 @@ var ( ConnectionStatusShutdown ConnectionStatus = 3 ) +type ProfileTaskStatus int + +const ( + Pending ProfileTaskStatus = iota + Running + Finished + Reported +) + type Reporter interface { Boot(entity *Entity, cdsWatchers []AgentConfigChangeWatcher) SendTracing(spans []ReportedSpan) SendMetrics(metrics []ReportedMeter) SendLog(log *logv3.LogData) ConnectionStatus() ConnectionStatus + AddProfileTaskManager(p ProfileTaskManager) Close() } diff --git a/plugins/core/reporter/discard_reporter.go b/plugins/core/reporter/discard_reporter.go index e1b5f387..7c0578dd 100644 --- a/plugins/core/reporter/discard_reporter.go +++ b/plugins/core/reporter/discard_reporter.go @@ -44,3 +44,6 @@ func (r *discardReporter) ConnectionStatus() ConnectionStatus { func (r *discardReporter) Close() { // do nothing } +func (r *discardReporter) AddProfileTaskManager(p ProfileTaskManager) { + // do nothing +} diff --git a/plugins/core/reporter/grpc/grpc.go b/plugins/core/reporter/grpc/grpc.go index 9d2ba5d3..7f21ad31 100644 --- a/plugins/core/reporter/grpc/grpc.go +++ b/plugins/core/reporter/grpc/grpc.go @@ -24,12 +24,13 @@ import ( "google.golang.org/grpc/metadata" + "github.com/apache/skywalking-go/plugins/core/operator" + "github.com/apache/skywalking-go/plugins/core/reporter" + common "github.com/apache/skywalking-go/protocols/collect/common/v3" agentv3 "github.com/apache/skywalking-go/protocols/collect/language/agent/v3" + profilev3 "github.com/apache/skywalking-go/protocols/collect/language/profile/v3" logv3 "github.com/apache/skywalking-go/protocols/collect/logging/v3" managementv3 "github.com/apache/skywalking-go/protocols/collect/management/v3" - - "github.com/apache/skywalking-go/plugins/core/operator" - "github.com/apache/skywalking-go/plugins/core/reporter" ) const ( @@ -40,26 +41,28 @@ const ( func NewGRPCReporter(logger operator.LogOperator, serverAddr string, checkInterval time.Duration, + profileFetchInterval time.Duration, connManager *reporter.ConnectionManager, cdsManager *reporter.CDSManager, pprofTaskManager *reporter.PprofTaskManager, opts ...ReporterOption, ) (reporter.Reporter, error) { r := &gRPCReporter{ - logger: logger, - serverAddr: serverAddr, - tracingSendCh: make(chan *agentv3.SegmentObject, maxSendQueueSize), - metricsSendCh: make(chan []*agentv3.MeterData, maxSendQueueSize), - logSendCh: make(chan *logv3.LogData, maxSendQueueSize), - checkInterval: checkInterval, - connManager: connManager, - cdsManager: cdsManager, - pprofTaskManager: pprofTaskManager, + logger: logger, + serverAddr: serverAddr, + tracingSendCh: make(chan *agentv3.SegmentObject, maxSendQueueSize), + metricsSendCh: make(chan []*agentv3.MeterData, maxSendQueueSize), + logSendCh: make(chan *logv3.LogData, maxSendQueueSize), + checkInterval: checkInterval, + profileFetchInterval: profileFetchInterval, + connManager: connManager, + cdsManager: cdsManager, + pprofTaskManager: pprofTaskManager, } for _, o := range opts { o(r) } - + r.lastProfileCommandTime = -1 conn, err := connManager.GetConnection(serverAddr) if err != nil { return nil, err @@ -68,22 +71,27 @@ func NewGRPCReporter(logger operator.LogOperator, r.metricsClient = agentv3.NewMeterReportServiceClient(conn) r.logClient = logv3.NewLogReportServiceClient(conn) r.managementClient = managementv3.NewManagementServiceClient(conn) + r.profileTaskClient = profilev3.NewProfileTaskClient(conn) return r, nil } type gRPCReporter struct { - entity *reporter.Entity - serverAddr string - logger operator.LogOperator - tracingSendCh chan *agentv3.SegmentObject - metricsSendCh chan []*agentv3.MeterData - logSendCh chan *logv3.LogData - traceClient agentv3.TraceSegmentReportServiceClient - metricsClient agentv3.MeterReportServiceClient - logClient logv3.LogReportServiceClient - managementClient managementv3.ManagementServiceClient - checkInterval time.Duration - + entity *reporter.Entity + serverAddr string + logger operator.LogOperator + tracingSendCh chan *agentv3.SegmentObject + metricsSendCh chan []*agentv3.MeterData + logSendCh chan *logv3.LogData + traceClient agentv3.TraceSegmentReportServiceClient + metricsClient agentv3.MeterReportServiceClient + logClient logv3.LogReportServiceClient + managementClient managementv3.ManagementServiceClient + profileTaskClient profilev3.ProfileTaskClient + profileTaskManager reporter.ProfileTaskManager + checkInterval time.Duration + profileFetchInterval time.Duration + // lastProfileCommandTime is the last timestamp we used to fetch profile commands. + lastProfileCommandTime int64 // bootFlag is set if Boot be executed bootFlag bool transform *reporter.Transform @@ -97,6 +105,7 @@ func (r *gRPCReporter) Boot(entity *reporter.Entity, cdsWatchers []reporter.Agen r.transform = reporter.NewTransform(entity) r.initSendPipeline() r.check() + r.fetchProfileTasks() r.cdsManager.InitCDS(entity, cdsWatchers) r.pprofTaskManager.InitPprofTask(entity) r.bootFlag = true @@ -283,6 +292,62 @@ func (r *gRPCReporter) initSendPipeline() { break } }() + go func() { + defer func() { + if err := recover(); err != nil { + r.logger.Errorf("gRPCReporter reportProfileResult panic err %v", err) + } + }() + + StreamLoop: + for { + switch r.connManager.GetConnectionStatus(r.serverAddr) { + case reporter.ConnectionStatusShutdown: + break + case reporter.ConnectionStatusDisconnect: + time.Sleep(5 * time.Second) + continue StreamLoop + } + + stream, err := r.profileTaskClient.GoProfileReport(metadata.NewOutgoingContext(context.Background(), r.connManager.GetMD())) + if err != nil { + r.logger.Errorf("open profile stream error %v", err) + time.Sleep(5 * time.Second) + continue StreamLoop + } + re := r.profileTaskManager.GetProfileResults() + + for task := range re { + profileData := &profilev3.GoProfileData{ + TaskId: task.TaskID, + Payload: task.Payload, + IsLast: task.IsLast, + } + r.logger.Infof("Sending profile task: TaskID='%s', PayloadSize=%d, IsLast=%v", + task.TaskID, len(task.Payload), task.IsLast) + err = stream.Send(profileData) + if err != nil { + r.logger.Errorf("send profile data error %v", err) + r.closeProfileStream(stream) + continue StreamLoop + } + if task.IsLast { + r.profileTaskManager.ProfileFinish() + var report = profilev3.ProfileTaskFinishReport{ + TaskId: task.TaskID, + Service: r.entity.ServiceName, + ServiceInstance: r.entity.ServiceInstanceName, + } + _, err = r.profileTaskClient.ReportTaskFinish(metadata.NewOutgoingContext(context.Background(), r.connManager.GetMD()), &report) + if err != nil { + r.logger.Errorf("report profile task finish error %v", err) + } + } + } + r.closeProfileStream(stream) + break + } + }() } func (r *gRPCReporter) closeTracingStream(stream agentv3.TraceSegmentReportService_CollectClient) { @@ -305,7 +370,12 @@ func (r *gRPCReporter) closeLogStream(stream logv3.LogReportService_CollectClien r.logger.Errorf("send closing error %v", err) } } - +func (r *gRPCReporter) closeProfileStream(stream profilev3.ProfileTask_GoProfileReportClient) { + _, err := stream.CloseAndRecv() + if err != nil && err != io.EOF { + r.logger.Errorf("send profile closing error %v", err) + } +} func (r *gRPCReporter) reportInstanceProperties() (err error) { _, err = r.managementClient.ReportInstanceProperties( metadata.NewOutgoingContext(context.Background(), r.connManager.GetMD()), @@ -361,3 +431,52 @@ func (r *gRPCReporter) check() { } }() } + +func (r *gRPCReporter) fetchProfileTasks() { + if r.profileFetchInterval < 0 { + r.logger.Errorf("profile init error:profileFetchInterval is %v", r.profileFetchInterval) + return + } + go func() { + for { + // Construct the request + req := &profilev3.ProfileTaskCommandQuery{ + Service: r.entity.ServiceName, + ServiceInstance: r.entity.ServiceInstanceName, + LastCommandTime: r.lastProfileCommandTime, + } + + // Pull tasks + resp, err := r.profileTaskClient.GetProfileTaskCommands(context.Background(), req) + if err != nil { + r.logger.Errorf("fetch profile task error: %v", err) + time.Sleep(r.profileFetchInterval) + continue + } + + // Handle all returned commands + for _, cmd := range resp.Commands { + nt := r.handleProfileTask(cmd, r.lastProfileCommandTime) + if nt > r.lastProfileCommandTime { + r.lastProfileCommandTime = nt + } + } + + // Remove completed tasks + r.profileTaskManager.RemoveProfileTask() + time.Sleep(r.profileFetchInterval) + } + }() +} + +func (r *gRPCReporter) AddProfileTaskManager(p reporter.ProfileTaskManager) { + r.profileTaskManager = p +} + +func (r *gRPCReporter) handleProfileTask(cmd *common.Command, t int64) int64 { + if cmd.Command != "ProfileTaskQuery" { + return t + } + nt := r.profileTaskManager.AddProfileTask(cmd.Args, t) + return nt +} diff --git a/plugins/core/reporter/kafka/kafka.go b/plugins/core/reporter/kafka/kafka.go index c0c1430b..012f9f2a 100644 --- a/plugins/core/reporter/kafka/kafka.go +++ b/plugins/core/reporter/kafka/kafka.go @@ -351,3 +351,5 @@ func (r *kafkaReporter) Close() { } } } + +func (r *kafkaReporter) AddProfileTaskManager(p reporter.ProfileTaskManager) {} diff --git a/plugins/core/reporter/profile.go b/plugins/core/reporter/profile.go new file mode 100644 index 00000000..63096ef3 --- /dev/null +++ b/plugins/core/reporter/profile.go @@ -0,0 +1,53 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package reporter + +import ( + "time" + + common "github.com/apache/skywalking-go/protocols/collect/common/v3" +) + +type ProfileTaskManager interface { + // AddProfileTask add new profile task + AddProfileTask(args []*common.KeyStringValuePair, t int64) int64 + GetProfileResults() chan ProfileResult + ProfileFinish() + RemoveProfileTask() +} + +type TraceProfileTask struct { + SerialNumber string // uuid + TaskID string + EndpointName string // endpoint + Duration int // monitoring duration (min) + MinDurationThreshold int64 // starting monitoring time (ms) + DumpPeriod int // monitoring interval (ms) + MaxSamplingCount int // maximum number of samples + StartTime time.Time + CreateTime time.Time + Status ProfileTaskStatus // task execution status + EndTime time.Time // task deadline +} + +type ProfileResult struct { + Payload []byte + TraceSegmentID string + TaskID string + IsLast bool +} diff --git a/plugins/core/span.go b/plugins/core/span.go index 89e2748e..219a7bca 100644 --- a/plugins/core/span.go +++ b/plugins/core/span.go @@ -50,4 +50,5 @@ type TracingSpan interface { IsExit() bool IsValid() bool ParentSpan() TracingSpan + IsProfileTarget() bool } diff --git a/plugins/core/span_default.go b/plugins/core/span_default.go index d55593b1..4c6a5aa9 100644 --- a/plugins/core/span_default.go +++ b/plugins/core/span_default.go @@ -158,6 +158,7 @@ func (ds *DefaultSpan) ErrorOccured() { func (ds *DefaultSpan) End(changeParent bool) { ds.EndTime = time.Now() + GetSo11y(ds.tracer).MeasureTracingContextCompletion(false) if changeParent { if ctx := getTracingContext(); ctx != nil { @@ -211,3 +212,11 @@ func (ds *DefaultSpan) GetEndPointName() string { func (ds *DefaultSpan) GetParentSpan() interface{} { return ds.Parent } + +func (ds *DefaultSpan) IsProfileTarget() bool { + endPoint := ds.GetEndPointName() + if ds.tracer.ProfileManager.IfProfiling() { + return ds.tracer.ProfileManager.CheckIfProfileTarget(endPoint) + } + return false +} diff --git a/plugins/core/span_noop.go b/plugins/core/span_noop.go index 5fb09abd..dc6e4769 100644 --- a/plugins/core/span_noop.go +++ b/plugins/core/span_noop.go @@ -35,6 +35,10 @@ func newSnapshotNoopSpan() *NoopSpan { } } +func (*NoopSpan) IsProfileTarget() bool { + return false +} + func newNoopSpan(tracer *Tracer) *NoopSpan { return &NoopSpan{ stackCount: 1, diff --git a/plugins/core/span_tracing.go b/plugins/core/span_tracing.go index fc17d105..8aaeab60 100644 --- a/plugins/core/span_tracing.go +++ b/plugins/core/span_tracing.go @@ -231,6 +231,10 @@ func (s *SegmentSpanImpl) createSegmentContext(ctx *TracingContext, parent Segme return } +func (s *SegmentSpanImpl) IsProfileTarget() bool { + return s.DefaultSpan.IsProfileTarget() +} + type RootSegmentSpan struct { *SegmentSpanImpl notify <-chan reporter.ReportedSpan @@ -275,6 +279,10 @@ func (rs *RootSegmentSpan) createRootSegmentContext(ctx *TracingContext, _ Segme return } +func (rs *RootSegmentSpan) IsProfileTarget() bool { + return rs.DefaultSpan.IsProfileTarget() +} + type SnapshotSpan struct { DefaultSpan SegmentContext @@ -413,3 +421,7 @@ func newSnapshotSpan(current TracingSpan) TracingSpan { return s } + +func (s *SnapshotSpan) IsProfileTarget() bool { + return s.DefaultSpan.IsProfileTarget() +} diff --git a/plugins/core/test_base.go b/plugins/core/test_base.go index 956955cd..8cd197c5 100644 --- a/plugins/core/test_base.go +++ b/plugins/core/test_base.go @@ -47,6 +47,9 @@ func ResetTracingContext() { SetGLS(nil) Tracing = &Tracer{initFlag: 1, Sampler: NewConstSampler(true), Reporter: &StoreReporter{}, ServiceEntity: NewEntity("test", "test-instance"), meterMap: &sync.Map{}} + // Initialize ProfileManager to avoid nil pointer dereference + Tracing.ProfileManager = NewProfileManager(nil) + Tracing.Reporter.AddProfileTaskManager(Tracing.ProfileManager) SetAsNewGoroutine() ReportConnectionStatus = reporter.ConnectionStatusConnected } @@ -96,3 +99,5 @@ func (r *StoreReporter) ConnectionStatus() reporter.ConnectionStatus { func (r *StoreReporter) Close() { } + +func (r *StoreReporter) AddProfileTaskManager(p reporter.ProfileTaskManager) {} diff --git a/plugins/core/trace_profiling_event_manager.go b/plugins/core/trace_profiling_event_manager.go new file mode 100644 index 00000000..16e647cf --- /dev/null +++ b/plugins/core/trace_profiling_event_manager.go @@ -0,0 +1,165 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package core + +import ( + "strconv" + "sync" + + "github.com/pkg/errors" +) + +type TraceProfilingBaseEvent string + +type TraceProfilingComplexEvent string + +type TraceProfilingLogicOp int + +const ( + OpAnd TraceProfilingLogicOp = iota // AND: all conditions must be true + OpOr // OR: at least one condition must be true + OpNothing // Do nothing, used for initial rules +) + +type TraceProfilingRule struct { + Event TraceProfilingBaseEvent + Op TraceProfilingLogicOp + IsNot bool +} + +// Expression node (used to build logical expression trees) +type TraceProfilingExprNode struct { + Rules []TraceProfilingRule + Event TraceProfilingComplexEvent +} + +// TraceProfilingEventManager manages event states and logical rules +// You can register basic events and states in the manager, +// and then define complex events by combining these basic events using logical operators (AND, OR, NOT). +// This makes it easier to manage whether complex events can be executed. +type TraceProfilingEventManager struct { + mu sync.RWMutex + BaseEventStatus map[TraceProfilingBaseEvent]bool // current status of base events (true=enabled, false=disabled) + ComplexEvents map[TraceProfilingComplexEvent]*TraceProfilingExprNode // logical expressions for complex events +} + +// Create a new TraceProfilingEventManager +func NewEventManager() *TraceProfilingEventManager { + return &TraceProfilingEventManager{ + BaseEventStatus: make(map[TraceProfilingBaseEvent]bool), + ComplexEvents: make(map[TraceProfilingComplexEvent]*TraceProfilingExprNode), + } +} + +// Register a base event with initial status +func (m *TraceProfilingEventManager) RegisterBaseEvent(event TraceProfilingBaseEvent, initialStatus bool) { + m.mu.Lock() + defer m.mu.Unlock() + m.BaseEventStatus[event] = initialStatus +} + +// Register a complex event with logical expression rules +func (m *TraceProfilingEventManager) RegisterComplexEvent(targetEvent TraceProfilingComplexEvent, expr *TraceProfilingExprNode) { + m.mu.Lock() + defer m.mu.Unlock() + m.ComplexEvents[targetEvent] = expr +} + +// Update the status of a base event +func (m *TraceProfilingEventManager) UpdateBaseEventStatus(event TraceProfilingBaseEvent, status bool) error { + m.mu.Lock() + defer m.mu.Unlock() + + if _, ok := m.BaseEventStatus[event]; !ok { + return errors.New("event not registered") + } + m.BaseEventStatus[event] = status + return nil +} + +// Get the status of a base event +func (m *TraceProfilingEventManager) GetBaseEventStatus(event TraceProfilingBaseEvent) (bool, error) { + m.mu.RLock() + defer m.mu.RUnlock() + status, ok := m.BaseEventStatus[event] + if !ok { + return false, errors.New("event not registered") + } + return status, nil +} + +// Execute a complex event by evaluating its logical expression +func (m *TraceProfilingEventManager) ExecuteComplexEvent(event TraceProfilingComplexEvent) (bool, error) { + m.mu.RLock() + defer m.mu.RUnlock() + expr, ok := m.ComplexEvents[event] + if !ok { + return false, errors.New("event not registered") + } + return m.evalExpr(expr) +} + +// Recursively evaluate the logical expression +func (m *TraceProfilingEventManager) evalExpr(node *TraceProfilingExprNode) (bool, error) { + if len(node.Rules) == 0 { + return false, errors.New("complex event has no rules") + } + + // 1. Evaluate the first rule directly (with optional NOT operation) + firstRule := node.Rules[0] + currentResult, err := m.getRuleValue(firstRule) + if err != nil { + return false, err + } + + // 2. From the second rule onward, combine results using logical operators + for i := 1; i < len(node.Rules); i++ { + rule := node.Rules[i] + // Get the value of the current rule (with optional NOT) + ruleValue, err := m.getRuleValue(rule) + if err != nil { + return false, err + } + switch rule.Op { + case OpAnd: + currentResult = currentResult && ruleValue + case OpOr: + currentResult = currentResult || ruleValue + default: + return false, errors.New("invalid logic op: " + strconv.Itoa(int(rule.Op))) + } + } + + return currentResult, nil +} + +// Get the value of a base event for a rule (with optional NOT) +func (m *TraceProfilingEventManager) getRuleValue(rule TraceProfilingRule) (bool, error) { + baseStatus, ok := m.BaseEventStatus[rule.Event] + if !ok { + return false, errors.New("base event not registered: " + string(rule.Event)) + } + + // Apply NOT operator if specified + if rule.IsNot { + return !baseStatus, nil + } + + // Otherwise return the base event status directly + return baseStatus, nil +} diff --git a/plugins/core/tracer.go b/plugins/core/tracer.go index 3090502c..5323ce63 100644 --- a/plugins/core/tracer.go +++ b/plugins/core/tracer.go @@ -38,8 +38,9 @@ type CorrelationConfig struct { } type Tracer struct { - ServiceEntity *reporter.Entity - Reporter reporter.Reporter + ServiceEntity *reporter.Entity + Reporter reporter.Reporter + ProfileManager *ProfileManager // 0 not init 1 init initFlag int32 Sampler Sampler @@ -65,6 +66,8 @@ func (t *Tracer) Init(entity *reporter.Entity, rep reporter.Reporter, samp Sampl if logger != nil && !reflect.ValueOf(logger).IsZero() { t.Log.ChangeLogger(logger) } + t.ProfileManager = NewProfileManager(t.Log) + t.Reporter.AddProfileTaskManager(t.ProfileManager) t.Reporter.Boot(entity, t.cdsWatchers) t.initFlag = 1 t.initMetricsCollect(meterCollectSecond) diff --git a/plugins/core/tracing.go b/plugins/core/tracing.go index 65f85615..b11d4e06 100644 --- a/plugins/core/tracing.go +++ b/plugins/core/tracing.go @@ -38,6 +38,10 @@ func (t *Tracer) Logger() interface{} { return t.Log } +func (t *Tracer) Profiler() interface{} { + return t.ProfileManager +} + func (t *Tracer) DebugStack() []byte { return debug.Stack() } @@ -64,6 +68,21 @@ func (t *Tracer) CreateEntrySpan(operationName string, extractor interface{}, op } span, _, err := t.createSpan0(ctx, tracingSpan, opts, withRef(ref), withSpanType(SpanTypeEntry), withOperationName(operationName)) + if err == nil { + sid := span.GetSegmentID() + tid := span.GetTraceID() + // check if is profile target + if t.ProfileManager.CheckIfProfileTarget(operationName) { + // check if is profiling + if t.ProfileManager.IfProfiling() { + if segmentSpan, ok := span.(SegmentSpan); ok { + c := segmentSpan.GetSegmentContext() + t.ProfileManager.TryToAddSegmentLabelSet(sid) + t.ProfileManager.AddSpanID(tid, sid, c.SpanID) + } + } + } + } return span, err } @@ -77,6 +96,19 @@ func (t *Tracer) CreateLocalSpan(operationName string, opts ...interface{}) (s i }() span, _, err := t.createSpan0(ctx, tracingSpan, opts, withSpanType(SpanTypeLocal), withOperationName(operationName)) + if err == nil { + sid := span.GetSegmentID() + tid := span.GetTraceID() + endpoint := span.GetOperationName() + if t.ProfileManager.CheckIfProfileTarget(endpoint) { + if segmentSpan, ok := span.(SegmentSpan); ok { + c := segmentSpan.GetSegmentContext() + if t.ProfileManager.IfProfiling() { + t.ProfileManager.AddSpanID(tid, sid, c.SpanID) + } + } + } + } return span, err } diff --git a/plugins/core/tracing/api.go b/plugins/core/tracing/api.go index 83e2a06e..530aef11 100644 --- a/plugins/core/tracing/api.go +++ b/plugins/core/tracing/api.go @@ -240,3 +240,4 @@ func (n *NoopSpan) PrepareAsync() { } func (n *NoopSpan) AsyncFinish() { } +func (n *NoopSpan) IsProfileTarget() bool { return false } diff --git a/plugins/core/tracing/bridge.go b/plugins/core/tracing/bridge.go index 8b40b730..2d7216e2 100644 --- a/plugins/core/tracing/bridge.go +++ b/plugins/core/tracing/bridge.go @@ -40,6 +40,7 @@ type AdaptSpan interface { Error(...string) ErrorOccured() End() + IsProfileTarget() bool } type SpanWrapper struct { @@ -105,3 +106,7 @@ func (s *SpanWrapper) PrepareAsync() { func (s *SpanWrapper) AsyncFinish() { s.Span.AsyncFinish() } + +func (s *SpanWrapper) IsProfileTarget() bool { + return s.Span.IsProfileTarget() +} diff --git a/plugins/core/tracing/span.go b/plugins/core/tracing/span.go index 641dcd75..e762f65e 100644 --- a/plugins/core/tracing/span.go +++ b/plugins/core/tracing/span.go @@ -141,4 +141,6 @@ type Span interface { ErrorOccured() // End end the Span End() + // check if is trace profile target + IsProfileTarget() bool } diff --git a/plugins/echov4/intercepter.go b/plugins/echov4/intercepter.go index 6741457e..c4e1005a 100644 --- a/plugins/echov4/intercepter.go +++ b/plugins/echov4/intercepter.go @@ -60,7 +60,6 @@ func middleware() echo.MiddlewareFunc { if err != nil { return err } - // serve the request to the next middleware if err = next(c); err != nil { span.Error(err.Error()) diff --git a/plugins/http/server_intercepter.go b/plugins/http/server_intercepter.go index a20dfac1..2b52ecc9 100644 --- a/plugins/http/server_intercepter.go +++ b/plugins/http/server_intercepter.go @@ -41,7 +41,6 @@ func (h *ServerInterceptor) BeforeInvoke(invocation operator.Invocation) error { if err != nil { return err } - if config.ServerCollectParameters && request.URL != nil { s.Tag(tracing.TagHTTPParams, request.URL.RawQuery) } diff --git a/plugins/pprof/go.mod b/plugins/pprof/go.mod new file mode 100644 index 00000000..d3ffce1e --- /dev/null +++ b/plugins/pprof/go.mod @@ -0,0 +1,3 @@ +module github.com/apache/skywalking-go/plugins/pprof + +go 1.19 diff --git a/plugins/pprof/instrument.go b/plugins/pprof/instrument.go new file mode 100644 index 00000000..156ac5e8 --- /dev/null +++ b/plugins/pprof/instrument.go @@ -0,0 +1,64 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package pprof + +import ( + "embed" + + "github.com/apache/skywalking-go/plugins/core/instrument" +) + +//go:embed * +var fs embed.FS + +//skywalking:nocopy +type Instrument struct{} + +func NewInstrument() *Instrument { + return &Instrument{} +} + +func (i *Instrument) Name() string { + return "profile" +} + +func (i *Instrument) BasePackage() string { + return "runtime/pprof" +} + +func (i *Instrument) VersionChecker(version string) bool { + return true +} + +func (i *Instrument) Points() []*instrument.Point { + return []*instrument.Point{ + { + PackagePath: "", + At: instrument.NewStaticMethodEnhance( + "SetGoroutineLabels", + instrument.WithArgsCount(1), + instrument.WithArgType(0, "context.Context"), + ), + Interceptor: "SetLabelsInterceptor", + }, + } +} + +func (i *Instrument) FS() *embed.FS { + return &fs +} diff --git a/plugins/pprof/intercepter.go b/plugins/pprof/intercepter.go new file mode 100644 index 00000000..f4a0a203 --- /dev/null +++ b/plugins/pprof/intercepter.go @@ -0,0 +1,52 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package pprof + +import ( + "context" + "errors" + "runtime/pprof" + + "github.com/apache/skywalking-go/plugins/core/operator" + "github.com/apache/skywalking-go/plugins/core/profile" + "github.com/apache/skywalking-go/plugins/core/tracing" +) + +type SetLabelsInterceptor struct{} + +func (h *SetLabelsInterceptor) BeforeInvoke(invocation operator.Invocation) error { + c := invocation.Args()[0].(context.Context) + if tracing.ActiveSpan() == nil { + return nil + } + if !tracing.ActiveSpan().IsProfileTarget() { + return nil + } + now := profile.CatchNowProfileLabel() + l, ok := now.(pprof.LabelSet) + if !ok { + return errors.New("profile label transform error") + } + c = pprof.WithLabels(c, l) + invocation.ChangeArg(0, c) + return nil +} + +func (h *SetLabelsInterceptor) AfterInvoke(invocation operator.Invocation, result ...interface{}) error { + return nil +} diff --git a/test/benchmark-codebase/consumer/main.go b/test/benchmark-codebase/consumer/main.go index 9f92bf4f..987041f4 100644 --- a/test/benchmark-codebase/consumer/main.go +++ b/test/benchmark-codebase/consumer/main.go @@ -110,7 +110,7 @@ func startPprof() { } go func() { if err := svr.ListenAndServe(); err != nil { - log.Printf("starting pprof server failure: %v", err) + log.Printf("starting profile server failure: %v", err) } }() } diff --git a/test/e2e/case/kafka/docker-compose.yml b/test/e2e/case/kafka/docker-compose.yml index 1679bf4a..c9e4c8d6 100644 --- a/test/e2e/case/kafka/docker-compose.yml +++ b/test/e2e/case/kafka/docker-compose.yml @@ -32,7 +32,7 @@ services: retries: 120 broker-a: - image: bitnami/kafka:2.4.1 + image: bitnamilegacy/kafka:2.4.1 hostname: broker-a expose: - 9092 @@ -53,7 +53,7 @@ services: retries: 120 broker-b: - image: bitnami/kafka:2.4.1 + image: bitnamilegacy/kafka:2.4.1 hostname: broker-b expose: - 9092 diff --git a/test/plugins/runner-helper/templates/docker-compose.tpl b/test/plugins/runner-helper/templates/docker-compose.tpl index 43ccc9bb..a7410aff 100644 --- a/test/plugins/runner-helper/templates/docker-compose.tpl +++ b/test/plugins/runner-helper/templates/docker-compose.tpl @@ -21,7 +21,7 @@ networks: services: oap: - image: ghcr.io/apache/skywalking-agent-test-tool/mock-collector:fa81b1b6d9caef484a65b5019efa28cac4e3d21d + image: ghcr.io/apache/skywalking-agent-test-tool/mock-collector:b22b7d8ba62dabdd8db1ecc52da6178b063edff7 expose: - 19876 - 12800 diff --git a/test/plugins/runner-helper/templates/windows-docker-compose.tpl b/test/plugins/runner-helper/templates/windows-docker-compose.tpl index befd6f67..2b42f508 100644 --- a/test/plugins/runner-helper/templates/windows-docker-compose.tpl +++ b/test/plugins/runner-helper/templates/windows-docker-compose.tpl @@ -17,7 +17,7 @@ version: '2.1' services: oap: - image: ghcr.io/apache/skywalking-agent-test-tool/mock-collector:fa81b1b6d9caef484a65b5019efa28cac4e3d21d + image: ghcr.io/apache/skywalking-agent-test-tool/mock-collector:b22b7d8ba62dabdd8db1ecc52da6178b063edff7 network_mode: host expose: - 19876 diff --git a/test/plugins/scenarios/logrus/config/excepted.yml b/test/plugins/scenarios/logrus/config/excepted.yml index 88bfca1a..59c9d4de 100644 --- a/test/plugins/scenarios/logrus/config/excepted.yml +++ b/test/plugins/scenarios/logrus/config/excepted.yml @@ -18,13 +18,14 @@ segmentItems: [] meterItems: [] logItems: - serviceName: logrus - logSize: ge 4 + logSize: ge 5 logs: - timestamp: nq 0 endpoint: '' body: type: TEXT - content: { text: not null } + content: { text: 'fetch dynamic configuration error rpc error: code = Unimplemented + desc = Method not found: skywalking.v3.ConfigurationDiscoveryService/fetchConfigurations' } traceContext: { traceId: N/A, traceSegmentId: N/A, spanId: -1 } tags: data: @@ -34,7 +35,19 @@ logItems: endpoint: '' body: type: TEXT - content: { text: not null } + content: { text: 'fetch pprof task commands error rpc error: code = Unimplemented + desc = Method not found: skywalking.v10.PprofTask/getPprofTaskCommands' } + traceContext: { traceId: N/A, traceSegmentId: N/A, spanId: -1 } + tags: + data: + - { key: LEVEL, value: error } + layer: GENERAL + - timestamp: nq 0 + endpoint: '' + body: + type: TEXT + content: { text: 'fetch profile task error: rpc error: code = Unimplemented + desc = Method not found: skywalking.v3.ProfileTask/getProfileTaskCommands' } traceContext: { traceId: N/A, traceSegmentId: N/A, spanId: -1 } tags: data: diff --git a/test/plugins/scenarios/pprof/bin/startup.sh b/test/plugins/scenarios/pprof/bin/startup.sh new file mode 100644 index 00000000..45c692f4 --- /dev/null +++ b/test/plugins/scenarios/pprof/bin/startup.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +home="$(cd "$(dirname $0)"; pwd)" +go build ${GO_BUILD_OPTS} -o pprof + +./pprof \ No newline at end of file diff --git a/test/plugins/scenarios/pprof/config/excepted.yml b/test/plugins/scenarios/pprof/config/excepted.yml new file mode 100644 index 00000000..dc1b7426 --- /dev/null +++ b/test/plugins/scenarios/pprof/config/excepted.yml @@ -0,0 +1,114 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +segmentItems: + - serviceName: pprof + segmentSize: ge 3 + segments: + - segmentId: not null + spans: + - operationName: GET:/provider + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: nq 0 + endTime: nq 0 + componentId: 5004 + isError: false + spanType: Entry + peer: '' + skipAnalysis: false + tags: + - {key: http.method, value: GET} + - {key: url, value: 'localhost:8080/provider'} + - {key: http.params, value: 'test=1'} + - {key: status_code, value: '200'} + refs: + - {parentEndpoint: 'GET:/consumer', networkAddress: 'localhost:8080', refType: CrossProcess, + parentSpanId: 1, parentTraceSegmentId: not null, parentServiceInstance: not null, + parentService: pprof, traceId: not null} + - segmentId: not null + spans: + - operationName: GET:/provider + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: nq 0 + endTime: nq 0 + componentId: 5004 + isError: false + spanType: Entry + peer: '' + skipAnalysis: false + tags: + - {key: http.method, value: GET} + - {key: url, value: 'localhost:8080/provider'} + - {key: http.params, value: 'test=2'} + - {key: status_code, value: '200'} + refs: + - {parentEndpoint: 'GET:/consumer', networkAddress: 'localhost:8080', refType: CrossProcess, + parentSpanId: 2, parentTraceSegmentId: not null, parentServiceInstance: not null, + parentService: pprof, traceId: not null} + - segmentId: not null + spans: + - operationName: GET:/provider + parentSpanId: 0 + spanId: 1 + spanLayer: Http + startTime: gt 0 + endTime: gt 0 + componentId: 5005 + isError: false + spanType: Exit + peer: localhost:8080 + skipAnalysis: false + tags: + - {key: http.method, value: GET} + - {key: url, value: 'localhost:8080/provider'} + - {key: status_code, value: '200'} + - operationName: GET:/provider + parentSpanId: 0 + spanId: 2 + spanLayer: Http + startTime: gt 0 + endTime: gt 0 + componentId: 5005 + isError: false + spanType: Exit + peer: localhost:8080 + skipAnalysis: false + tags: + - {key: http.method, value: GET} + - {key: url, value: 'localhost:8080/provider'} + - {key: status_code, value: '200'} + - operationName: GET:/consumer + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: gt 0 + endTime: gt 0 + componentId: 5004 + isError: false + spanType: Entry + peer: '' + skipAnalysis: false + tags: + - {key: http.method, value: GET} + - {key: url, value: 'service:8080/consumer'} + - {key: http.params, value: ''} + - {key: status_code, value: '200'} +meterItems: [] +logItems: [] \ No newline at end of file diff --git a/test/plugins/scenarios/pprof/go.mod b/test/plugins/scenarios/pprof/go.mod new file mode 100644 index 00000000..303901c1 --- /dev/null +++ b/test/plugins/scenarios/pprof/go.mod @@ -0,0 +1,3 @@ +module test/plugins/scenarios/pprof + +go 1.19 diff --git a/test/plugins/scenarios/pprof/main.go b/test/plugins/scenarios/pprof/main.go new file mode 100644 index 00000000..18916406 --- /dev/null +++ b/test/plugins/scenarios/pprof/main.go @@ -0,0 +1,96 @@ +// Licensed to Apache Software Foundation (ASF) under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Apache Software Foundation (ASF) licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package main + +import ( + "context" + "io" + "log" + "net/http" + "runtime/pprof" + "time" + + _ "github.com/apache/skywalking-go" +) + +func providerHandler(w http.ResponseWriter, r *http.Request) { + l := pprof.Labels("test-label", "test", "operation", "provider") + c := context.Background() + c = pprof.WithLabels(c, l) + + pprof.SetGoroutineLabels(c) + + doWork() + _, _ = w.Write([]byte("success")) +} + +func consumerHandler(w http.ResponseWriter, r *http.Request) { + l := pprof.Labels("test-label", "consumer", "operation", "consumer") + c := context.Background() + c = pprof.WithLabels(c, l) + pprof.SetGoroutineLabels(c) + + resp, err := http.Get("http://localhost:8080/provider?test=1") + if err != nil { + log.Printf("request provider error: %v", err) + w.WriteHeader(http.StatusInternalServerError) + return + } + defer resp.Body.Close() + body, err := io.ReadAll(resp.Body) + if err != nil { + log.Print(err) + w.WriteHeader(http.StatusInternalServerError) + return + } + + resp2, err := http.Get("http://localhost:8080/provider?test=2") + if err != nil { + log.Printf("request provider error: %v", err) + w.WriteHeader(http.StatusInternalServerError) + return + } + defer resp2.Body.Close() + body2, err := io.ReadAll(resp2.Body) + if err != nil { + log.Print(err) + w.WriteHeader(http.StatusInternalServerError) + return + } + + _, _ = w.Write(append(body, body2...)) +} + +func doWork() { + start := time.Now() + for time.Since(start) < 1*time.Second { + for i := 0; i < 1e6; i++ { + _ = i * i + } + } +} + +func main() { + http.HandleFunc("/provider", providerHandler) + http.HandleFunc("/consumer", consumerHandler) + + http.HandleFunc("/health", func(writer http.ResponseWriter, request *http.Request) { + writer.WriteHeader(http.StatusOK) + }) + _ = http.ListenAndServe("0.0.0.0:8080", nil) +} diff --git a/test/plugins/scenarios/pprof/plugin.yml b/test/plugins/scenarios/pprof/plugin.yml new file mode 100644 index 00000000..ada27b83 --- /dev/null +++ b/test/plugins/scenarios/pprof/plugin.yml @@ -0,0 +1,28 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +entry-service: http://${HTTP_HOST}:${HTTP_PORT}/consumer +health-checker: http://${HTTP_HOST}:${HTTP_PORT}/health +start-script: ./bin/startup.sh +framework: go +export-port: 8080 +support-version: + - go: 1.19 + - go: 1.20 + - go: 1.21 + - go: 1.22 + - go: 1.23 +toolkit: true \ No newline at end of file diff --git a/test/plugins/scenarios/segmentio-kafka/plugin.yml b/test/plugins/scenarios/segmentio-kafka/plugin.yml index 9b7f3b91..648e02c5 100644 --- a/test/plugins/scenarios/segmentio-kafka/plugin.yml +++ b/test/plugins/scenarios/segmentio-kafka/plugin.yml @@ -28,7 +28,7 @@ dependencies: image: zookeeper:3.9.2 hostname: zookeeper-server kafka-server: - image: bitnami/kafka:3.7.0 + image: bitnamilegacy/kafka:3.7.0 hostname: kafka-server ports: - 9092 diff --git a/test/plugins/scenarios/zap/config/excepted.yml b/test/plugins/scenarios/zap/config/excepted.yml index dd1b2d82..b505d051 100644 --- a/test/plugins/scenarios/zap/config/excepted.yml +++ b/test/plugins/scenarios/zap/config/excepted.yml @@ -18,13 +18,14 @@ segmentItems: [] meterItems: [] logItems: - serviceName: zap - logSize: ge 4 + logSize: ge 5 logs: - timestamp: nq 0 endpoint: '' body: type: TEXT - content: { text: not null } + content: { text: 'fetch dynamic configuration error rpc error: code = Unimplemented + desc = Method not found: skywalking.v3.ConfigurationDiscoveryService/fetchConfigurations' } traceContext: { traceId: N/A, traceSegmentId: N/A, spanId: -1 } tags: data: @@ -34,7 +35,19 @@ logItems: endpoint: '' body: type: TEXT - content: { text: not null } + content: { text: 'fetch pprof task commands error rpc error: code = Unimplemented + desc = Method not found: skywalking.v10.PprofTask/getPprofTaskCommands' } + traceContext: { traceId: N/A, traceSegmentId: N/A, spanId: -1 } + tags: + data: + - { key: LEVEL, value: error } + layer: GENERAL + - timestamp: nq 0 + endpoint: '' + body: + type: TEXT + content: { text: 'fetch profile task error: rpc error: code = Unimplemented + desc = Method not found: skywalking.v3.ProfileTask/getProfileTaskCommands' } traceContext: { traceId: N/A, traceSegmentId: N/A, spanId: -1 } tags: data: @@ -73,4 +86,4 @@ logItems: value: info - key: module value: test-service-consumer - layer: GENERAL + layer: GENERAL \ No newline at end of file diff --git a/tools/go-agent/config/agent.default.yaml b/tools/go-agent/config/agent.default.yaml index 02d351f4..8281612c 100644 --- a/tools/go-agent/config/agent.default.yaml +++ b/tools/go-agent/config/agent.default.yaml @@ -54,12 +54,13 @@ reporter: authentication: ${SW_AGENT_REPORTER_GRPC_AUTHENTICATION:} # The interval(s) of fetching dynamic configuration from backend. cds_fetch_interval: ${SW_AGENT_REPORTER_GRPC_CDS_FETCH_INTERVAL:20} + # The interval(s) of fetching profile task from backend. + profile_fetch_interval: ${SW_AGENT_REPORTER_GRPC_PROFILE_FETCH_INTERVAL:20} pprof: # The interval(s) of fetching pprof task from backend. pprof_fetch_interval: ${SW_AGENT_REPORTER_GRPC_PPROF_TASK_FETCH_INTERVAL:20} # The pprof file path generated when executing the profile task. pprof_file_path: ${SW_AGENT_REPORTER_GRPC_PROFILE_PPROF_FILE_PATH:} - tls: # Whether to enable TLS with backend. enable: ${SW_AGENT_REPORTER_GRPC_TLS_ENABLE:false} diff --git a/tools/go-agent/config/loader.go b/tools/go-agent/config/loader.go index 8d843df0..a792cd63 100644 --- a/tools/go-agent/config/loader.go +++ b/tools/go-agent/config/loader.go @@ -83,12 +83,13 @@ type Meter struct { } type GRPCReporter struct { - BackendService StringValue `yaml:"backend_service"` - MaxSendQueue StringValue `yaml:"max_send_queue"` - Authentication StringValue `yaml:"authentication"` - CDSFetchInterval StringValue `yaml:"cds_fetch_interval"` - TLS GRPCReporterTLS `yaml:"tls"` - Pprof GRPCReporterPprof `yaml:"pprof"` + BackendService StringValue `yaml:"backend_service"` + MaxSendQueue StringValue `yaml:"max_send_queue"` + Authentication StringValue `yaml:"authentication"` + CDSFetchInterval StringValue `yaml:"cds_fetch_interval"` + ProfileFetchInterval StringValue `yaml:"profile_fetch_interval"` + TLS GRPCReporterTLS `yaml:"tls"` + Pprof GRPCReporterPprof `yaml:"pprof"` } type GRPCReporterPprof struct { diff --git a/tools/go-agent/instrument/agentcore/instrument.go b/tools/go-agent/instrument/agentcore/instrument.go index 872b30cc..d637bf7e 100644 --- a/tools/go-agent/instrument/agentcore/instrument.go +++ b/tools/go-agent/instrument/agentcore/instrument.go @@ -44,7 +44,7 @@ var ( ReporterBasePackage = "agent/reporter" CopiedBasePackage = `skywalking-go(@[\d\w\.\-]+)?\/agent\/core` - CopiedSubPackages = []string{"", "tracing", "operator", "metrics"} + CopiedSubPackages = []string{"", "tracing", "operator", "metrics", "profile"} ) type Instrument struct { diff --git a/tools/go-agent/instrument/plugins/register.go b/tools/go-agent/instrument/plugins/register.go index 27e15a99..680dc4e4 100644 --- a/tools/go-agent/instrument/plugins/register.go +++ b/tools/go-agent/instrument/plugins/register.go @@ -40,6 +40,7 @@ import ( "github.com/apache/skywalking-go/plugins/microv4" "github.com/apache/skywalking-go/plugins/mongo" "github.com/apache/skywalking-go/plugins/mux" + "github.com/apache/skywalking-go/plugins/pprof" "github.com/apache/skywalking-go/plugins/pulsar" "github.com/apache/skywalking-go/plugins/rocketmq" runtime_metrics "github.com/apache/skywalking-go/plugins/runtimemetrics" @@ -68,6 +69,7 @@ func init() { registerFramework(fiber.NewInstrument()) registerFramework(rocketmq.NewInstrument()) registerFramework(amqp.NewInstrument()) + registerFramework(pprof.NewInstrument()) registerFramework(pulsar.NewInstrument()) registerFramework(segmentiokafka.NewInstrument()) registerFramework(goelasticsearchv8.NewInstrument()) diff --git a/tools/go-agent/instrument/plugins/rewrite/context.go b/tools/go-agent/instrument/plugins/rewrite/context.go index f7413344..7f8fb3f4 100644 --- a/tools/go-agent/instrument/plugins/rewrite/context.go +++ b/tools/go-agent/instrument/plugins/rewrite/context.go @@ -39,7 +39,7 @@ var GenerateCommonPrefix = "skywalking_" var GenerateMethodPrefix = GenerateCommonPrefix + "enhance_" var GenerateVarPrefix = GenerateCommonPrefix + "var_" -var OperatorDirs = []string{"operator", "log", "tracing", "tools", "metrics"} +var OperatorDirs = []string{"operator", "log", "tracing", "tools", "metrics", "profile"} var OperatePrefix = GenerateCommonPrefix + "operator" var TypePrefix = OperatePrefix + "Type" diff --git a/tools/go-agent/instrument/plugins/rewrite/func.go b/tools/go-agent/instrument/plugins/rewrite/func.go index eb1ddff0..8cc93dd6 100644 --- a/tools/go-agent/instrument/plugins/rewrite/func.go +++ b/tools/go-agent/instrument/plugins/rewrite/func.go @@ -77,8 +77,10 @@ func (c *Context) Func(funcDecl *dst.FuncDecl, cursor *dstutil.Cursor) { c.enhanceFuncParameter(funcDecl.Type.Results) // enhance the method body - for _, stmt := range funcDecl.Body.List { - c.enhanceFuncStmt(stmt) + if funcDecl.Body != nil { + for _, stmt := range funcDecl.Body.List { + c.enhanceFuncStmt(stmt) + } } } diff --git a/tools/go-agent/instrument/reporter/instrument.go b/tools/go-agent/instrument/reporter/instrument.go index 43bb06ae..7b77e7c8 100644 --- a/tools/go-agent/instrument/reporter/instrument.go +++ b/tools/go-agent/instrument/reporter/instrument.go @@ -203,7 +203,7 @@ func {{.InitFuncName}}(logger operator.LogOperator) (Reporter, error) { return NewDiscardReporter(), nil } checkIntervalVal := {{.Config.Reporter.CheckInterval.ToGoIntValue "the reporter check interval must be number"}} - checkInterval := time.Second * time.Duration(checkIntervalVal) + checkInterval := time.Second * time.Duration(checkIntervalVal) ` const initManagerFunc = ` @@ -233,7 +233,7 @@ func initManager(logger operator.LogOperator, checkInterval time.Duration) (*Con } cdsFetchIntervalVal := {{.Config.Reporter.GRPC.CDSFetchInterval.ToGoIntValue "the cds fetch interval must be number"}} - cdsFetchInterval := time.Second * time.Duration(cdsFetchIntervalVal) + cdsFetchInterval := time.Second * time.Duration(cdsFetchIntervalVal) cdsManager, err := NewCDSManager(logger, backendServiceVal, cdsFetchInterval, connManager) if err != nil { return nil, nil, nil, err @@ -261,9 +261,11 @@ func initGRPCReporter(logger operator.LogOperator, var opts []ReporterOption maxSendQueueVal := {{.Config.Reporter.GRPC.MaxSendQueue.ToGoIntValue "the GRPC reporter max queue size must be number"}} opts = append(opts, WithMaxSendQueueSize(maxSendQueueVal)) - + backendServiceVal := {{.Config.Reporter.GRPC.BackendService.ToGoStringValue}} - return NewGRPCReporter(logger, backendServiceVal, checkInterval, connManager, cdsManager, pprofTaskManager, opts...) + profileFetchIntervalVal := {{.Config.Reporter.GRPC.ProfileFetchInterval.ToGoIntValue "the profile fetch interval must be number"}} + profileFetchInterval := time.Second * time.Duration(profileFetchIntervalVal) + return NewGRPCReporter(logger, backendServiceVal, checkInterval,profileFetchInterval ,connManager, cdsManager, pprofTaskManager, opts...) } ` diff --git a/tools/go-agent/tools/dst.go b/tools/go-agent/tools/dst.go index fef5cfef..277cb28a 100644 --- a/tools/go-agent/tools/dst.go +++ b/tools/go-agent/tools/dst.go @@ -119,6 +119,8 @@ func RemovePackageRef(parent dst.Node, current *dst.SelectorExpr, inx int) { p.Type = dst.NewIdent(current.Sel.Name) case *dst.ArrayType: p.Elt = dst.NewIdent(current.Sel.Name) + case *dst.ChanType: + p.Value = dst.NewIdent(current.Sel.Name) case *dst.CallExpr: p.Fun = dst.NewIdent(current.Sel.Name) case *dst.KeyValueExpr: