mirror of
https://github.com/grafana/grafana.git
synced 2025-02-10 23:55:47 -06:00
Stackdriver: Project selector (#22447)
* clean PR #17366 * udpate vendor * [WIP] Implement projects management for stackdriver * [WIP] Implement projects management for stackdriver * [WIP] Implement projects management for stackdriver * Implement projects management for stackdriver * [WIP][Tests] Fix errors * clean anonymous struct * remove await * don't store project list * Add default project on query editor * gofmt * Fix tests * Move test data source to backend * Use segment instead of dropdown. remove ensure default project since it's not being used anymore. * Fix broken annotation editor * Load gceDefaultAccount only once when in the config page * Reset error message on auth type change * Add metric find query for projects * Remove debug code * Fix broken tests * Fix typings * Fix lint error * Slightly different approach - now having a distiction between config page default project, and project that is selectable from the dropdown in the query editor. * Fix broken tests * Attempt to fix strict ts errors * Prevent state from being set multiple times * Remove noOptionsMessage since it seems to be obosolete in react select * One more attempt to solve ts strict error * Interpolate project template variable. Make sure its loaded correctly when opening variable query editor first time * Implicit any fix * fix: typescript strict null check fixes * Return empty array in case project endpoint fails * Rename project to projectName to prevent clashing with legacy query prop * Fix broken test * fix: Stackdriver - template replace on filter label should have a regex format as that escapes the dots in the label name which is not valid. Co-authored-by: Labesse Kévin <kevin@labesse.me> Co-authored-by: Elias Cédric Laouiti <elias@abtasty.com> Co-authored-by: Daniel Lee <dan.limerick@gmail.com>
This commit is contained in:
parent
75fe3c839b
commit
934a8f08ae
3
go.mod
3
go.mod
@ -71,10 +71,11 @@ require (
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f // indirect
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478
|
||||
golang.org/x/oauth2 v0.0.0-20190319182350-c85d3e98c914
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e
|
||||
golang.org/x/tools v0.0.0-20191213221258-04c2e8eff935 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898
|
||||
google.golang.org/api v0.14.0
|
||||
google.golang.org/grpc v1.23.1
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect
|
||||
|
48
go.sum
48
go.sum
@ -1,6 +1,8 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0 h1:eOI3/cP2VTU6uZLDYAoic+eyzzB9YyGmJ7eIjl8rOPg=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/VividCortex/mysqlerr v0.0.0-20170204212430-6c6b55f8796f h1:HR5nRmUQgXrwqZOwZ2DAc/aCi3Bu3xENpspW935vxu0=
|
||||
@ -102,6 +104,7 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
@ -110,13 +113,20 @@ github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/flatbuffers v1.11.0 h1:O7CEyB8Cb3/DmtxODGtLHcEvpr81Jm5qLg/hsHnxA2A=
|
||||
github.com/google/flatbuffers v1.11.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
|
||||
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
|
||||
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 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
@ -139,6 +149,9 @@ github.com/hashicorp/go-plugin v1.0.1 h1:4OtAfUGbnKC6yS48p0CtMX2oFYtzFZVv6rok3cR
|
||||
github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY=
|
||||
github.com/hashicorp/go-version v1.1.0 h1:bPIoEKD27tNdebFGGxxYwcL4nepeY4j1QP23PFRGzg0=
|
||||
github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M=
|
||||
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
||||
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ=
|
||||
@ -158,6 +171,7 @@ github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.8 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok=
|
||||
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
@ -313,6 +327,8 @@ github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZ
|
||||
github.com/zenazn/goji v0.9.1-0.20160507202103-64eb34159fe5/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
||||
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
|
||||
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
|
||||
go.opencensus.io v0.21.0 h1:mU6zScU4U1YAFPHEHYk+3JC4SY7JxgkqS10ZOSyksNg=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.uber.org/atomic v1.5.1 h1:rsqfU5vBkVknbhUGbAUwQKR2H4ItV8tjJ+6kJX4cxHM=
|
||||
go.uber.org/atomic v1.5.1/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
@ -325,8 +341,13 @@ golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392 h1:ACG4HJsFiNMf47Y4PeRoeb
|
||||
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/image v0.0.0-20190507092727-e4e5bf290fec/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
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-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f h1:J5lckAjkw6qYlOZNj90mLYNTEKDvWeuc1yieZ8qUzUE=
|
||||
@ -337,8 +358,10 @@ golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73r
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/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/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
@ -347,15 +370,20 @@ golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7MvYXnkV9/iQNo1lX6g=
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190319182350-c85d3e98c914 h1:jIOcLT9BZzyJ9ce+IwwZ+aF9yeCqzrR+NrD68a/SHKw=
|
||||
golang.org/x/oauth2 v0.0.0-20190319182350-c85d3e98c914/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/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/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@ -365,6 +393,7 @@ golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpbl
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/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-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa h1:KIDDMLT1O0Nr7TSxp8xM5tJcdn8tgyAONntO829og1M=
|
||||
golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -376,11 +405,17 @@ golang.org/x/sys v0.0.0-20191220142924-d4481acd189f h1:68K/z8GLUxV76xGSqwTWw2gyk
|
||||
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
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-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190802220118-1d1727260058/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
@ -393,15 +428,25 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+y
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.14.0 h1:uMf5uLi4eQMRrMKhCplNik4U4H8Z6C1br3zOtAa/aDE=
|
||||
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873 h1:nfPFGzJkUDX6uBmpN/pSw7MbOAWegH5QDQuoXFHedLg=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/grpc v1.14.0 h1:ArxJuB1NWfPY6r9Gp9gqwplT0Ge7nqv9msgu03lHLmo=
|
||||
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.23.1 h1:q4XQuHFC6I28BKZpo6IYyb3mNO+l7lSOxRuYTCiDfXk=
|
||||
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
@ -440,4 +485,7 @@ gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
@ -37,7 +37,6 @@ export function SegmentSelect<T>({
|
||||
width: ${width > 120 ? width : 120}px;
|
||||
`
|
||||
)}
|
||||
noOptionsMessage={() => noOptionsMessage}
|
||||
placeholder=""
|
||||
autoFocus={true}
|
||||
isOpen={true}
|
||||
|
@ -1,24 +0,0 @@
|
||||
package stackdriver
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/tsdb"
|
||||
)
|
||||
|
||||
func (e *StackdriverExecutor) ensureDefaultProject(ctx context.Context, tsdbQuery *tsdb.TsdbQuery) (*tsdb.Response, error) {
|
||||
queryResult := &tsdb.QueryResult{Meta: simplejson.New(), RefId: tsdbQuery.Queries[0].RefId}
|
||||
result := &tsdb.Response{
|
||||
Results: make(map[string]*tsdb.QueryResult),
|
||||
}
|
||||
defaultProject, err := e.getDefaultProject(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
e.dsInfo.JsonData.Set("defaultProject", defaultProject)
|
||||
queryResult.Meta.Set("defaultProject", defaultProject)
|
||||
result.Results[tsdbQuery.Queries[0].RefId] = queryResult
|
||||
return result, nil
|
||||
}
|
@ -15,9 +15,6 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context/ctxhttp"
|
||||
"golang.org/x/oauth2/google"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/pluginproxy"
|
||||
"github.com/grafana/grafana/pkg/components/null"
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
@ -27,6 +24,8 @@ import (
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/tsdb"
|
||||
"github.com/opentracing/opentracing-go"
|
||||
"golang.org/x/net/context/ctxhttp"
|
||||
"golang.org/x/oauth2/google"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -81,8 +80,10 @@ func (e *StackdriverExecutor) Query(ctx context.Context, dsInfo *models.DataSour
|
||||
switch queryType {
|
||||
case "annotationQuery":
|
||||
result, err = e.executeAnnotationQuery(ctx, tsdbQuery)
|
||||
case "ensureDefaultProjectQuery":
|
||||
result, err = e.ensureDefaultProject(ctx, tsdbQuery)
|
||||
case "getProjectsListQuery":
|
||||
result, err = e.getProjectList(ctx, tsdbQuery)
|
||||
case "getGCEDefaultProject":
|
||||
result, err = e.getGCEDefaultProject(ctx, tsdbQuery)
|
||||
case "timeSeriesQuery":
|
||||
fallthrough
|
||||
default:
|
||||
@ -92,19 +93,29 @@ func (e *StackdriverExecutor) Query(ctx context.Context, dsInfo *models.DataSour
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (e *StackdriverExecutor) executeTimeSeriesQuery(ctx context.Context, tsdbQuery *tsdb.TsdbQuery) (*tsdb.Response, error) {
|
||||
func (e *StackdriverExecutor) getGCEDefaultProject(ctx context.Context, tsdbQuery *tsdb.TsdbQuery) (*tsdb.Response, error) {
|
||||
result := &tsdb.Response{
|
||||
Results: make(map[string]*tsdb.QueryResult),
|
||||
}
|
||||
refId := tsdbQuery.Queries[0].RefId
|
||||
queryResult := &tsdb.QueryResult{Meta: simplejson.New(), RefId: refId}
|
||||
|
||||
authenticationType := e.dsInfo.JsonData.Get("authenticationType").MustString(jwtAuthentication)
|
||||
if authenticationType == gceAuthentication {
|
||||
defaultProject, err := e.getDefaultProject(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to retrieve default project from GCE metadata server. error: %v", err)
|
||||
}
|
||||
gceDefaultProject, err := e.getDefaultProject(ctx)
|
||||
if err != nil {
|
||||
slog.Debug("Stackdriver", "Auth", "Failed to use GCE auth: ", err)
|
||||
return nil, fmt.Errorf("Failed to retrieve default project from GCE metadata server. error: %v", err)
|
||||
}
|
||||
slog.Debug("Stackdriver", "Auth", "Successfully use GCE auth: ", gceDefaultProject)
|
||||
|
||||
e.dsInfo.JsonData.Set("defaultProject", defaultProject)
|
||||
queryResult.Meta.Set("defaultProject", gceDefaultProject)
|
||||
result.Results[refId] = queryResult
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (e *StackdriverExecutor) executeTimeSeriesQuery(ctx context.Context, tsdbQuery *tsdb.TsdbQuery) (*tsdb.Response, error) {
|
||||
result := &tsdb.Response{
|
||||
Results: make(map[string]*tsdb.QueryResult),
|
||||
}
|
||||
|
||||
queries, err := e.buildQueries(tsdbQuery)
|
||||
@ -170,11 +181,12 @@ func (e *StackdriverExecutor) buildQueries(tsdbQuery *tsdb.TsdbQuery) ([]*Stackd
|
||||
aliasBy := query.Model.Get("aliasBy").MustString()
|
||||
|
||||
stackdriverQueries = append(stackdriverQueries, &StackdriverQuery{
|
||||
Target: target,
|
||||
Params: params,
|
||||
RefID: query.RefId,
|
||||
GroupBys: groupBysAsStrings,
|
||||
AliasBy: aliasBy,
|
||||
Target: target,
|
||||
Params: params,
|
||||
RefID: query.RefId,
|
||||
GroupBys: groupBysAsStrings,
|
||||
AliasBy: aliasBy,
|
||||
ProjectName: query.Model.Get("projectName").MustString(""),
|
||||
})
|
||||
}
|
||||
|
||||
@ -278,8 +290,7 @@ func setAggParams(params *url.Values, query *tsdb.Query, durationSeconds int) {
|
||||
|
||||
func (e *StackdriverExecutor) executeQuery(ctx context.Context, query *StackdriverQuery, tsdbQuery *tsdb.TsdbQuery) (*tsdb.QueryResult, StackdriverResponse, error) {
|
||||
queryResult := &tsdb.QueryResult{Meta: simplejson.New(), RefId: query.RefID}
|
||||
|
||||
req, err := e.createRequest(ctx, e.dsInfo)
|
||||
req, err := e.createRequest(ctx, e.dsInfo, query, fmt.Sprintf("stackdriver%s", "v3/projects/"+query.ProjectName+"/timeSeries"))
|
||||
if err != nil {
|
||||
queryResult.Error = err
|
||||
return queryResult, StackdriverResponse{}, nil
|
||||
@ -350,6 +361,28 @@ func (e *StackdriverExecutor) unmarshalResponse(res *http.Response) (Stackdriver
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (e *StackdriverExecutor) unmarshalResourceResponse(res *http.Response) (ResourceManagerProjectList, error) {
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
defer res.Body.Close()
|
||||
if err != nil {
|
||||
return ResourceManagerProjectList{}, err
|
||||
}
|
||||
|
||||
if res.StatusCode/100 != 2 {
|
||||
slog.Error("Request failed", "status", res.Status, "body", string(body))
|
||||
return ResourceManagerProjectList{}, fmt.Errorf(string(body))
|
||||
}
|
||||
|
||||
var data ResourceManagerProjectList
|
||||
err = json.Unmarshal(body, &data)
|
||||
if err != nil {
|
||||
slog.Error("Failed to unmarshal Resource manager response", "error", err, "status", res.Status, "body", string(body))
|
||||
return ResourceManagerProjectList{}, err
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (e *StackdriverExecutor) parseResponse(queryRes *tsdb.QueryResult, data StackdriverResponse, query *StackdriverQuery) error {
|
||||
labels := make(map[string]map[string]bool)
|
||||
|
||||
@ -584,7 +617,7 @@ func calcBucketBound(bucketOptions StackdriverBucketOptions, n int) string {
|
||||
return bucketBound
|
||||
}
|
||||
|
||||
func (e *StackdriverExecutor) createRequest(ctx context.Context, dsInfo *models.DataSource) (*http.Request, error) {
|
||||
func (e *StackdriverExecutor) createRequest(ctx context.Context, dsInfo *models.DataSource, query *StackdriverQuery, proxyPass string) (*http.Request, error) {
|
||||
u, _ := url.Parse(dsInfo.Url)
|
||||
u.Path = path.Join(u.Path, "render")
|
||||
|
||||
@ -611,14 +644,44 @@ func (e *StackdriverExecutor) createRequest(ctx context.Context, dsInfo *models.
|
||||
}
|
||||
}
|
||||
|
||||
projectName := dsInfo.JsonData.Get("defaultProject").MustString()
|
||||
proxyPass := fmt.Sprintf("stackdriver%s", "v3/projects/"+projectName+"/timeSeries")
|
||||
|
||||
pluginproxy.ApplyRoute(ctx, req, proxyPass, stackdriverRoute, dsInfo)
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func (e *StackdriverExecutor) createRequestResourceManager(ctx context.Context, dsInfo *models.DataSource) (*http.Request, error) {
|
||||
u, _ := url.Parse(dsInfo.Url)
|
||||
u.Path = path.Join(u.Path, "render")
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, "https://cloudresourcemanager.googleapis.com/", nil)
|
||||
if err != nil {
|
||||
slog.Error("Failed to create request", "error", err)
|
||||
return nil, fmt.Errorf("Failed to create request. error: %v", err)
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("User-Agent", fmt.Sprintf("Grafana/%s", setting.BuildVersion))
|
||||
|
||||
// find plugin
|
||||
plugin, ok := plugins.DataSources[dsInfo.Type]
|
||||
if !ok {
|
||||
return nil, errors.New("Unable to find datasource plugin Stackdriver")
|
||||
}
|
||||
|
||||
var resourceManagerRoute *plugins.AppPluginRoute
|
||||
for _, route := range plugin.Routes {
|
||||
if route.Path == "cloudresourcemanager" {
|
||||
resourceManagerRoute = route
|
||||
break
|
||||
}
|
||||
}
|
||||
proxyPass := "v1/projects"
|
||||
|
||||
pluginproxy.ApplyRoute(ctx, req, proxyPass, resourceManagerRoute, dsInfo)
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func (e *StackdriverExecutor) getDefaultProject(ctx context.Context) (string, error) {
|
||||
authenticationType := e.dsInfo.JsonData.Get("authenticationType").MustString(jwtAuthentication)
|
||||
if authenticationType == gceAuthentication {
|
||||
@ -626,7 +689,67 @@ func (e *StackdriverExecutor) getDefaultProject(ctx context.Context) (string, er
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Failed to retrieve default project from GCE metadata server. error: %v", err)
|
||||
}
|
||||
token, err := defaultCredentials.TokenSource.Token()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Failed to retrieve GCP credential token. error: %v", err)
|
||||
}
|
||||
if !token.Valid() {
|
||||
return "", errors.New("Failed to validate GCP credentials")
|
||||
}
|
||||
|
||||
return defaultCredentials.ProjectID, nil
|
||||
}
|
||||
return e.dsInfo.JsonData.Get("defaultProject").MustString(), nil
|
||||
}
|
||||
|
||||
func (e *StackdriverExecutor) getProjectList(ctx context.Context, tsdbQuery *tsdb.TsdbQuery) (*tsdb.Response, error) {
|
||||
queryResult := &tsdb.QueryResult{Meta: simplejson.New(), RefId: tsdbQuery.Queries[0].RefId}
|
||||
result := &tsdb.Response{
|
||||
Results: make(map[string]*tsdb.QueryResult),
|
||||
}
|
||||
projectsList, err := e.getProjects(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
queryResult.Meta.Set("projectsList", projectsList)
|
||||
result.Results[tsdbQuery.Queries[0].RefId] = queryResult
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (e *StackdriverExecutor) getProjects(ctx context.Context) ([]ResourceManagerProjectSelect, error) {
|
||||
var projects []ResourceManagerProjectSelect
|
||||
|
||||
req, err := e.createRequestResourceManager(ctx, e.dsInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
span, ctx := opentracing.StartSpanFromContext(ctx, "resource manager query")
|
||||
span.SetTag("datasource_id", e.dsInfo.Id)
|
||||
span.SetTag("org_id", e.dsInfo.OrgId)
|
||||
|
||||
defer span.Finish()
|
||||
|
||||
if err := opentracing.GlobalTracer().Inject(
|
||||
span.Context(),
|
||||
opentracing.HTTPHeaders,
|
||||
opentracing.HTTPHeadersCarrier(req.Header)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res, err := ctxhttp.Do(ctx, e.httpClient, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := e.unmarshalResourceResponse(res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, project := range data.Projects {
|
||||
projects = append(projects, ResourceManagerProjectSelect{Label: project.ProjectID, Value: project.ProjectID})
|
||||
}
|
||||
return projects, nil
|
||||
}
|
||||
|
@ -5,72 +5,89 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// StackdriverQuery is the query that Grafana sends from the frontend
|
||||
type StackdriverQuery struct {
|
||||
Target string
|
||||
Params url.Values
|
||||
RefID string
|
||||
GroupBys []string
|
||||
AliasBy string
|
||||
}
|
||||
type (
|
||||
// StackdriverQuery is the query that Grafana sends from the frontend
|
||||
StackdriverQuery struct {
|
||||
Target string
|
||||
Params url.Values
|
||||
RefID string
|
||||
GroupBys []string
|
||||
AliasBy string
|
||||
ProjectName string
|
||||
}
|
||||
|
||||
type StackdriverBucketOptions struct {
|
||||
LinearBuckets *struct {
|
||||
NumFiniteBuckets int64 `json:"numFiniteBuckets"`
|
||||
Width int64 `json:"width"`
|
||||
Offset int64 `json:"offset"`
|
||||
} `json:"linearBuckets"`
|
||||
ExponentialBuckets *struct {
|
||||
NumFiniteBuckets int64 `json:"numFiniteBuckets"`
|
||||
GrowthFactor float64 `json:"growthFactor"`
|
||||
Scale float64 `json:"scale"`
|
||||
} `json:"exponentialBuckets"`
|
||||
ExplicitBuckets *struct {
|
||||
Bounds []float64 `json:"bounds"`
|
||||
} `json:"explicitBuckets"`
|
||||
}
|
||||
StackdriverBucketOptions struct {
|
||||
LinearBuckets *struct {
|
||||
NumFiniteBuckets int64 `json:"numFiniteBuckets"`
|
||||
Width int64 `json:"width"`
|
||||
Offset int64 `json:"offset"`
|
||||
} `json:"linearBuckets"`
|
||||
ExponentialBuckets *struct {
|
||||
NumFiniteBuckets int64 `json:"numFiniteBuckets"`
|
||||
GrowthFactor float64 `json:"growthFactor"`
|
||||
Scale float64 `json:"scale"`
|
||||
} `json:"exponentialBuckets"`
|
||||
ExplicitBuckets *struct {
|
||||
Bounds []float64 `json:"bounds"`
|
||||
} `json:"explicitBuckets"`
|
||||
}
|
||||
|
||||
// StackdriverResponse is the data returned from the external Google Stackdriver API
|
||||
type StackdriverResponse struct {
|
||||
TimeSeries []struct {
|
||||
Metric struct {
|
||||
Labels map[string]string `json:"labels"`
|
||||
Type string `json:"type"`
|
||||
} `json:"metric"`
|
||||
Resource struct {
|
||||
Type string `json:"type"`
|
||||
Labels map[string]string `json:"labels"`
|
||||
} `json:"resource"`
|
||||
MetaData map[string]map[string]interface{} `json:"metadata"`
|
||||
MetricKind string `json:"metricKind"`
|
||||
ValueType string `json:"valueType"`
|
||||
Points []struct {
|
||||
Interval struct {
|
||||
StartTime time.Time `json:"startTime"`
|
||||
EndTime time.Time `json:"endTime"`
|
||||
} `json:"interval"`
|
||||
Value struct {
|
||||
DoubleValue float64 `json:"doubleValue"`
|
||||
StringValue string `json:"stringValue"`
|
||||
BoolValue bool `json:"boolValue"`
|
||||
IntValue string `json:"int64Value"`
|
||||
DistributionValue struct {
|
||||
Count string `json:"count"`
|
||||
Mean float64 `json:"mean"`
|
||||
SumOfSquaredDeviation float64 `json:"sumOfSquaredDeviation"`
|
||||
Range struct {
|
||||
Min int `json:"min"`
|
||||
Max int `json:"max"`
|
||||
} `json:"range"`
|
||||
BucketOptions StackdriverBucketOptions `json:"bucketOptions"`
|
||||
BucketCounts []string `json:"bucketCounts"`
|
||||
Examplars []struct {
|
||||
Value float64 `json:"value"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
// attachments
|
||||
} `json:"examplars"`
|
||||
} `json:"distributionValue"`
|
||||
} `json:"value"`
|
||||
} `json:"points"`
|
||||
} `json:"timeSeries"`
|
||||
}
|
||||
// StackdriverResponse is the data returned from the external Google Stackdriver API
|
||||
StackdriverResponse struct {
|
||||
TimeSeries []struct {
|
||||
Metric struct {
|
||||
Labels map[string]string `json:"labels"`
|
||||
Type string `json:"type"`
|
||||
} `json:"metric"`
|
||||
Resource struct {
|
||||
Type string `json:"type"`
|
||||
Labels map[string]string `json:"labels"`
|
||||
} `json:"resource"`
|
||||
MetaData map[string]map[string]interface{} `json:"metadata"`
|
||||
MetricKind string `json:"metricKind"`
|
||||
ValueType string `json:"valueType"`
|
||||
Points []struct {
|
||||
Interval struct {
|
||||
StartTime time.Time `json:"startTime"`
|
||||
EndTime time.Time `json:"endTime"`
|
||||
} `json:"interval"`
|
||||
Value struct {
|
||||
DoubleValue float64 `json:"doubleValue"`
|
||||
StringValue string `json:"stringValue"`
|
||||
BoolValue bool `json:"boolValue"`
|
||||
IntValue string `json:"int64Value"`
|
||||
DistributionValue struct {
|
||||
Count string `json:"count"`
|
||||
Mean float64 `json:"mean"`
|
||||
SumOfSquaredDeviation float64 `json:"sumOfSquaredDeviation"`
|
||||
Range struct {
|
||||
Min int `json:"min"`
|
||||
Max int `json:"max"`
|
||||
} `json:"range"`
|
||||
BucketOptions StackdriverBucketOptions `json:"bucketOptions"`
|
||||
BucketCounts []string `json:"bucketCounts"`
|
||||
Examplars []struct {
|
||||
Value float64 `json:"value"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
// attachments
|
||||
} `json:"examplars"`
|
||||
} `json:"distributionValue"`
|
||||
} `json:"value"`
|
||||
} `json:"points"`
|
||||
} `json:"timeSeries"`
|
||||
}
|
||||
|
||||
// ResourceManagerProjectList is the data returned from the external Google Resource Manager API
|
||||
ResourceManagerProjectList struct {
|
||||
Projects []ResourceManagerProject `json:"projects"`
|
||||
}
|
||||
|
||||
ResourceManagerProject struct {
|
||||
ProjectID string `json:"projectId"`
|
||||
}
|
||||
|
||||
ResourceManagerProjectSelect struct {
|
||||
Label string `json:"label"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
)
|
||||
|
@ -70,7 +70,6 @@ export class MetricSelect extends React.Component<Props, State> {
|
||||
const { placeholder, className, isSearchable, onChange } = this.props;
|
||||
const { options } = this.state;
|
||||
const selectedOption = this.getSelectedOption();
|
||||
|
||||
return (
|
||||
<Select
|
||||
className={className}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import isString from 'lodash/isString';
|
||||
import { alignmentPeriods } from './constants';
|
||||
import { MetricFindQueryTypes } from './types';
|
||||
import { alignmentPeriods, ValueTypes, MetricKind } from './constants';
|
||||
import StackdriverDatasource from './datasource';
|
||||
import { MetricFindQueryTypes, VariableQueryData } from './types';
|
||||
import {
|
||||
getMetricTypesByService,
|
||||
getAlignmentOptionsByMetric,
|
||||
@ -10,13 +11,19 @@ import {
|
||||
} from './functions';
|
||||
|
||||
export default class StackdriverMetricFindQuery {
|
||||
constructor(private datasource: any) {}
|
||||
constructor(private datasource: StackdriverDatasource) {}
|
||||
|
||||
async execute(query: any) {
|
||||
async execute(query: VariableQueryData) {
|
||||
try {
|
||||
if (!query.projectName) {
|
||||
query.projectName = this.datasource.getDefaultProject();
|
||||
}
|
||||
|
||||
switch (query.selectedQueryType) {
|
||||
case MetricFindQueryTypes.Projects:
|
||||
return this.handleProjectsQuery();
|
||||
case MetricFindQueryTypes.Services:
|
||||
return this.handleServiceQuery();
|
||||
return this.handleServiceQuery(query);
|
||||
case MetricFindQueryTypes.MetricTypes:
|
||||
return this.handleMetricTypesQuery(query);
|
||||
case MetricFindQueryTypes.LabelKeys:
|
||||
@ -40,8 +47,17 @@ export default class StackdriverMetricFindQuery {
|
||||
}
|
||||
}
|
||||
|
||||
async handleServiceQuery() {
|
||||
const metricDescriptors = await this.datasource.getMetricTypes(this.datasource.projectName);
|
||||
async handleProjectsQuery() {
|
||||
const projects = await this.datasource.getProjects();
|
||||
return projects.map((s: { label: string; value: string }) => ({
|
||||
text: s.label,
|
||||
value: s.value,
|
||||
expandable: true,
|
||||
}));
|
||||
}
|
||||
|
||||
async handleServiceQuery({ projectName }: VariableQueryData) {
|
||||
const metricDescriptors = await this.datasource.getMetricTypes(projectName);
|
||||
const services: any[] = extractServicesFromMetricDescriptors(metricDescriptors);
|
||||
return services.map(s => ({
|
||||
text: s.serviceShortName,
|
||||
@ -50,11 +66,11 @@ export default class StackdriverMetricFindQuery {
|
||||
}));
|
||||
}
|
||||
|
||||
async handleMetricTypesQuery({ selectedService }: any) {
|
||||
async handleMetricTypesQuery({ selectedService, projectName }: VariableQueryData) {
|
||||
if (!selectedService) {
|
||||
return [];
|
||||
}
|
||||
const metricDescriptors = await this.datasource.getMetricTypes(this.datasource.projectName);
|
||||
const metricDescriptors = await this.datasource.getMetricTypes(projectName);
|
||||
return getMetricTypesByService(metricDescriptors, this.datasource.templateSrv.replace(selectedService)).map(
|
||||
(s: any) => ({
|
||||
text: s.displayName,
|
||||
@ -64,54 +80,54 @@ export default class StackdriverMetricFindQuery {
|
||||
);
|
||||
}
|
||||
|
||||
async handleLabelKeysQuery({ selectedMetricType }: any) {
|
||||
async handleLabelKeysQuery({ selectedMetricType, projectName }: VariableQueryData) {
|
||||
if (!selectedMetricType) {
|
||||
return [];
|
||||
}
|
||||
const labelKeys = await getLabelKeys(this.datasource, selectedMetricType);
|
||||
const labelKeys = await getLabelKeys(this.datasource, selectedMetricType, projectName);
|
||||
return labelKeys.map(this.toFindQueryResult);
|
||||
}
|
||||
|
||||
async handleLabelValuesQuery({ selectedMetricType, labelKey }: any) {
|
||||
async handleLabelValuesQuery({ selectedMetricType, labelKey, projectName }: VariableQueryData) {
|
||||
if (!selectedMetricType) {
|
||||
return [];
|
||||
}
|
||||
const refId = 'handleLabelValuesQuery';
|
||||
const labels = await this.datasource.getLabels(selectedMetricType, refId, [labelKey]);
|
||||
const labels = await this.datasource.getLabels(selectedMetricType, refId, projectName, [labelKey]);
|
||||
const interpolatedKey = this.datasource.templateSrv.replace(labelKey);
|
||||
const values = labels.hasOwnProperty(interpolatedKey) ? labels[interpolatedKey] : [];
|
||||
return values.map(this.toFindQueryResult);
|
||||
}
|
||||
|
||||
async handleResourceTypeQuery({ selectedMetricType }: any) {
|
||||
async handleResourceTypeQuery({ selectedMetricType, projectName }: VariableQueryData) {
|
||||
if (!selectedMetricType) {
|
||||
return [];
|
||||
}
|
||||
const refId = 'handleResourceTypeQueryQueryType';
|
||||
const labels = await this.datasource.getLabels(selectedMetricType, refId);
|
||||
const labels = await this.datasource.getLabels(selectedMetricType, refId, projectName);
|
||||
return labels['resource.type'].map(this.toFindQueryResult);
|
||||
}
|
||||
|
||||
async handleAlignersQuery({ selectedMetricType }: any) {
|
||||
async handleAlignersQuery({ selectedMetricType, projectName }: VariableQueryData) {
|
||||
if (!selectedMetricType) {
|
||||
return [];
|
||||
}
|
||||
const metricDescriptors = await this.datasource.getMetricTypes(this.datasource.projectName);
|
||||
const metricDescriptors = await this.datasource.getMetricTypes(projectName);
|
||||
const { valueType, metricKind } = metricDescriptors.find(
|
||||
(m: any) => m.type === this.datasource.templateSrv.replace(selectedMetricType)
|
||||
);
|
||||
return getAlignmentOptionsByMetric(valueType, metricKind).map(this.toFindQueryResult);
|
||||
}
|
||||
|
||||
async handleAggregationQuery({ selectedMetricType }: any) {
|
||||
async handleAggregationQuery({ selectedMetricType, projectName }: VariableQueryData) {
|
||||
if (!selectedMetricType) {
|
||||
return [];
|
||||
}
|
||||
const metricDescriptors = await this.datasource.getMetricTypes(this.datasource.projectName);
|
||||
const metricDescriptors = await this.datasource.getMetricTypes(projectName);
|
||||
const { valueType, metricKind } = metricDescriptors.find(
|
||||
(m: any) => m.type === this.datasource.templateSrv.replace(selectedMetricType)
|
||||
);
|
||||
return getAggregationOptionsByMetric(valueType, metricKind).map(this.toFindQueryResult);
|
||||
return getAggregationOptionsByMetric(valueType as ValueTypes, metricKind as MetricKind).map(this.toFindQueryResult);
|
||||
}
|
||||
|
||||
handleAlignmentPeriodQuery() {
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { TemplateSrv } from 'app/features/templating/template_srv';
|
||||
import { AnnotationTarget } from './types';
|
||||
|
||||
export class StackdriverAnnotationsQueryCtrl {
|
||||
static templateUrl = 'partials/annotations.editor.html';
|
||||
@ -11,7 +12,7 @@ export class StackdriverAnnotationsQueryCtrl {
|
||||
this.onQueryChange = this.onQueryChange.bind(this);
|
||||
}
|
||||
|
||||
onQueryChange(target: any) {
|
||||
onQueryChange(target: AnnotationTarget) {
|
||||
Object.assign(this.annotation.target, target);
|
||||
}
|
||||
}
|
||||
|
@ -5,9 +5,10 @@ import { SelectableValue } from '@grafana/data';
|
||||
import { Segment } from '@grafana/ui';
|
||||
import { getAggregationOptionsByMetric } from '../functions';
|
||||
import { ValueTypes, MetricKind } from '../constants';
|
||||
import { MetricDescriptor } from '../types';
|
||||
|
||||
export interface Props {
|
||||
onChange: (metricDescriptor: any) => void;
|
||||
onChange: (metricDescriptor: MetricDescriptor[]) => void;
|
||||
metricDescriptor: {
|
||||
valueType: string;
|
||||
metricKind: string;
|
||||
|
@ -3,7 +3,7 @@ import { debounce } from 'lodash';
|
||||
import { Input } from '@grafana/ui';
|
||||
|
||||
export interface Props {
|
||||
onChange: (alignmentPeriod: any) => void;
|
||||
onChange: (alignmentPeriod: string) => void;
|
||||
value: string;
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@ import { Segment } from '@grafana/ui';
|
||||
import { alignmentPeriods, alignOptions } from '../constants';
|
||||
|
||||
export interface Props {
|
||||
onChange: (alignmentPeriod: any) => void;
|
||||
onChange: (alignmentPeriod: string) => void;
|
||||
templateSrv: TemplateSrv;
|
||||
templateVariableOptions: Array<SelectableValue<string>>;
|
||||
alignmentPeriod: string;
|
||||
|
@ -4,7 +4,7 @@ import { SelectableValue } from '@grafana/data';
|
||||
import { Segment } from '@grafana/ui';
|
||||
|
||||
export interface Props {
|
||||
onChange: (perSeriesAligner: any) => void;
|
||||
onChange: (perSeriesAligner: string) => void;
|
||||
templateVariableOptions: Array<SelectableValue<string>>;
|
||||
alignOptions: Array<SelectableValue<string>>;
|
||||
perSeriesAligner: string;
|
||||
|
@ -5,7 +5,7 @@ import { TemplateSrv } from 'app/features/templating/template_srv';
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
|
||||
import StackdriverDatasource from '../datasource';
|
||||
import { Metrics, Filters, AnnotationsHelp } from './';
|
||||
import { Metrics, Filters, AnnotationsHelp, Project } from './';
|
||||
import { toOption } from '../functions';
|
||||
import { AnnotationTarget, MetricDescriptor } from '../types';
|
||||
|
||||
@ -24,7 +24,8 @@ interface State extends AnnotationTarget {
|
||||
}
|
||||
|
||||
const DefaultTarget: State = {
|
||||
defaultProject: 'loading project...',
|
||||
projectName: '',
|
||||
projects: [],
|
||||
metricType: '',
|
||||
filters: [],
|
||||
metricKind: '',
|
||||
@ -40,20 +41,26 @@ const DefaultTarget: State = {
|
||||
export class AnnotationQueryEditor extends React.Component<Props, State> {
|
||||
state: State = DefaultTarget;
|
||||
|
||||
componentDidMount() {
|
||||
async componentDidMount() {
|
||||
const { target, datasource } = this.props;
|
||||
if (!target.projectName) {
|
||||
target.projectName = datasource.getDefaultProject();
|
||||
}
|
||||
|
||||
const variableOptionGroup = {
|
||||
label: 'Template Variables',
|
||||
options: datasource.variables.map(toOption),
|
||||
};
|
||||
|
||||
const projects = await datasource.getProjects();
|
||||
this.setState({
|
||||
variableOptionGroup,
|
||||
variableOptions: variableOptionGroup.options,
|
||||
...target,
|
||||
projects,
|
||||
});
|
||||
|
||||
datasource.getLabels(target.metricType, target.refId).then(labels => this.setState({ labels }));
|
||||
datasource.getLabels(target.metricType, target.projectName, target.refId).then(labels => this.setState({ labels }));
|
||||
}
|
||||
|
||||
onMetricTypeChange = ({ valueType, metricKind, type, unit }: MetricDescriptor) => {
|
||||
@ -69,7 +76,7 @@ export class AnnotationQueryEditor extends React.Component<Props, State> {
|
||||
onQueryChange(this.state);
|
||||
}
|
||||
);
|
||||
datasource.getLabels(type, this.state.refId).then(labels => this.setState({ labels }));
|
||||
datasource.getLabels(type, this.state.refId, this.state.projectName).then(labels => this.setState({ labels }));
|
||||
};
|
||||
|
||||
onChange(prop: string, value: string | string[]) {
|
||||
@ -79,22 +86,19 @@ export class AnnotationQueryEditor extends React.Component<Props, State> {
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
defaultProject,
|
||||
metricType,
|
||||
filters,
|
||||
title,
|
||||
text,
|
||||
variableOptionGroup,
|
||||
labels,
|
||||
variableOptions,
|
||||
} = this.state;
|
||||
const { projectName, metricType, filters, title, text, variableOptionGroup, labels, variableOptions } = this.state;
|
||||
const { datasource } = this.props;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Project
|
||||
templateVariableOptions={variableOptions}
|
||||
datasource={datasource}
|
||||
projectName={projectName || datasource.getDefaultProject()}
|
||||
onChange={value => this.onChange('projectName', value)}
|
||||
/>
|
||||
<Metrics
|
||||
defaultProject={defaultProject}
|
||||
projectName={projectName}
|
||||
metricType={metricType}
|
||||
templateSrv={datasource.templateSrv}
|
||||
datasource={datasource}
|
||||
|
@ -2,8 +2,8 @@ import React, { FC } from 'react';
|
||||
|
||||
export const AnnotationsHelp: FC = () => {
|
||||
return (
|
||||
<div className="gf-form grafana-info-box" style={{ padding: 0 }}>
|
||||
<pre className="gf-form-pre alert alert-info" style={{ marginRight: 0 }}>
|
||||
<div className="gf-form grafana-info-box alert-info">
|
||||
<div>
|
||||
<h5>Annotation Query Format</h5>
|
||||
<p>
|
||||
An annotation is an event that is overlaid on top of graphs. Annotation rendering is expensive so it is
|
||||
@ -38,7 +38,7 @@ export const AnnotationsHelp: FC = () => {
|
||||
<p>
|
||||
<code>{`${'{{resource.label.label_name}}'}`}</code> = Resource label metadata e.g. resource.label.zone
|
||||
</p>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -1,10 +1,7 @@
|
||||
import React from 'react';
|
||||
import { Project } from './Project';
|
||||
import StackdriverDatasource from '../datasource';
|
||||
import { MetricDescriptor } from '../types';
|
||||
|
||||
export interface Props {
|
||||
datasource: StackdriverDatasource;
|
||||
rawQuery: string;
|
||||
lastQueryError: string;
|
||||
metricDescriptor?: MetricDescriptor;
|
||||
@ -35,12 +32,11 @@ export class Help extends React.Component<Props, State> {
|
||||
|
||||
render() {
|
||||
const { displayHelp, displaRawQuery } = this.state;
|
||||
const { datasource, rawQuery, lastQueryError } = this.props;
|
||||
const { rawQuery, lastQueryError } = this.props;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="gf-form-inline">
|
||||
<Project datasource={datasource} />
|
||||
<div className="gf-form" onClick={this.onHelpClicked}>
|
||||
<label className="gf-form-label query-keyword pointer">
|
||||
Show Help <i className={`fa fa-caret-${displayHelp ? 'down' : 'right'}`} />
|
||||
|
@ -1,20 +1,18 @@
|
||||
import React from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import _ from 'lodash';
|
||||
|
||||
import { TemplateSrv } from 'app/features/templating/template_srv';
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
import StackdriverDatasource from '../datasource';
|
||||
import appEvents from 'app/core/app_events';
|
||||
import { Segment } from '@grafana/ui';
|
||||
import { MetricDescriptor } from '../types';
|
||||
import { CoreEvents } from 'app/types';
|
||||
|
||||
export interface Props {
|
||||
onChange: (metricDescriptor: MetricDescriptor) => void;
|
||||
templateSrv: TemplateSrv;
|
||||
templateVariableOptions: Array<SelectableValue<string>>;
|
||||
datasource: StackdriverDatasource;
|
||||
defaultProject: string;
|
||||
projectName: string;
|
||||
metricType: string;
|
||||
children?: (renderProps: any) => JSX.Element;
|
||||
}
|
||||
@ -26,72 +24,44 @@ interface State {
|
||||
service: string;
|
||||
metric: string;
|
||||
metricDescriptor: MetricDescriptor;
|
||||
defaultProject: string;
|
||||
projectName: string;
|
||||
}
|
||||
|
||||
export class Metrics extends React.Component<Props, State> {
|
||||
state: State = {
|
||||
export function Metrics(props: Props) {
|
||||
const [state, setState] = useState<State>({
|
||||
metricDescriptors: [],
|
||||
metrics: [],
|
||||
services: [],
|
||||
service: '',
|
||||
metric: '',
|
||||
metricDescriptor: null,
|
||||
defaultProject: '',
|
||||
projectName: null,
|
||||
});
|
||||
|
||||
const { services, service, metrics } = state;
|
||||
const { metricType, templateVariableOptions, projectName } = props;
|
||||
|
||||
const loadMetricDescriptors = async () => {
|
||||
if (projectName) {
|
||||
const metricDescriptors = await props.datasource.getMetricTypes(props.projectName);
|
||||
const services = getServicesList(metricDescriptors);
|
||||
const metrics = getMetricsList(metricDescriptors);
|
||||
const service = metrics.length > 0 ? metrics[0].service : '';
|
||||
const metricDescriptor = getSelectedMetricDescriptor(metricDescriptors, props.metricType);
|
||||
setState({ ...state, metricDescriptors, services, metrics, service: service, metricDescriptor });
|
||||
}
|
||||
};
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
}
|
||||
useEffect(() => {
|
||||
loadMetricDescriptors();
|
||||
}, [projectName]);
|
||||
|
||||
componentDidMount() {
|
||||
this.setState({ defaultProject: this.props.defaultProject }, () => {
|
||||
this.getCurrentProject()
|
||||
.then(this.loadMetricDescriptors.bind(this))
|
||||
.then(this.initializeServiceAndMetrics.bind(this));
|
||||
});
|
||||
}
|
||||
const getSelectedMetricDescriptor = (metricDescriptors: MetricDescriptor[], metricType: string) => {
|
||||
return metricDescriptors.find(md => md.type === props.templateSrv.replace(metricType));
|
||||
};
|
||||
|
||||
async getCurrentProject() {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
if (!this.state.defaultProject || this.state.defaultProject === 'loading project...') {
|
||||
const defaultProject = await this.props.datasource.getDefaultProject();
|
||||
this.setState({ defaultProject });
|
||||
}
|
||||
resolve(this.state.defaultProject);
|
||||
} catch (error) {
|
||||
appEvents.emit(CoreEvents.dsRequestError, error);
|
||||
reject();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async loadMetricDescriptors() {
|
||||
if (this.state.defaultProject !== 'loading project...') {
|
||||
const metricDescriptors = await this.props.datasource.getMetricTypes(this.state.defaultProject);
|
||||
this.setState({ metricDescriptors });
|
||||
return metricDescriptors;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
async initializeServiceAndMetrics() {
|
||||
const { metricDescriptors } = this.state;
|
||||
const services = this.getServicesList(metricDescriptors);
|
||||
const metrics = this.getMetricsList(metricDescriptors);
|
||||
const service = metrics.length > 0 ? metrics[0].service : '';
|
||||
const metricDescriptor = this.getSelectedMetricDescriptor(this.props.metricType);
|
||||
this.setState({ metricDescriptors, services, metrics, service: service, metricDescriptor });
|
||||
}
|
||||
|
||||
getSelectedMetricDescriptor(metricType: string) {
|
||||
return this.state.metricDescriptors.find(md => md.type === this.props.templateSrv.replace(metricType));
|
||||
}
|
||||
|
||||
getMetricsList(metricDescriptors: MetricDescriptor[]) {
|
||||
const selectedMetricDescriptor = this.getSelectedMetricDescriptor(this.props.metricType);
|
||||
const getMetricsList = (metricDescriptors: MetricDescriptor[]) => {
|
||||
const selectedMetricDescriptor = getSelectedMetricDescriptor(metricDescriptors, props.metricType);
|
||||
if (!selectedMetricDescriptor) {
|
||||
return [];
|
||||
}
|
||||
@ -104,11 +74,11 @@ export class Metrics extends React.Component<Props, State> {
|
||||
description: m.description,
|
||||
}));
|
||||
return metricsByService;
|
||||
}
|
||||
};
|
||||
|
||||
onServiceChange = ({ value: service }: any) => {
|
||||
const { metricDescriptors } = this.state;
|
||||
const { metricType, templateSrv } = this.props;
|
||||
const onServiceChange = ({ value: service }: any) => {
|
||||
const { metricDescriptors } = state;
|
||||
const { metricType, templateSrv } = props;
|
||||
|
||||
const metrics = metricDescriptors
|
||||
.filter(m => m.service === templateSrv.replace(service))
|
||||
@ -119,74 +89,70 @@ export class Metrics extends React.Component<Props, State> {
|
||||
description: m.description,
|
||||
}));
|
||||
|
||||
this.setState({ service, metrics });
|
||||
|
||||
if (metrics.length > 0 && !metrics.some(m => m.value === templateSrv.replace(metricType))) {
|
||||
this.onMetricTypeChange(metrics[0]);
|
||||
onMetricTypeChange(metrics[0], { service, metrics });
|
||||
} else {
|
||||
setState({ ...state, service, metrics });
|
||||
}
|
||||
};
|
||||
|
||||
onMetricTypeChange = ({ value }: any) => {
|
||||
const metricDescriptor = this.getSelectedMetricDescriptor(value);
|
||||
this.setState({ metricDescriptor });
|
||||
this.props.onChange({ ...metricDescriptor, type: value });
|
||||
const onMetricTypeChange = ({ value }: any, extra: any = {}) => {
|
||||
const metricDescriptor = getSelectedMetricDescriptor(state.metricDescriptors, value);
|
||||
setState({ ...state, metricDescriptor, ...extra });
|
||||
props.onChange({ ...metricDescriptor, type: value });
|
||||
};
|
||||
|
||||
getServicesList(metricDescriptors: MetricDescriptor[]) {
|
||||
const getServicesList = (metricDescriptors: MetricDescriptor[]) => {
|
||||
const services = metricDescriptors.map(m => ({
|
||||
value: m.service,
|
||||
label: _.startCase(m.serviceShortName),
|
||||
}));
|
||||
|
||||
return services.length > 0 ? _.uniqBy(services, s => s.value) : [];
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { services, service, metrics } = this.state;
|
||||
const { metricType, templateVariableOptions } = this.props;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="gf-form-inline">
|
||||
<span className="gf-form-label width-9 query-keyword">Service</span>
|
||||
<Segment
|
||||
onChange={this.onServiceChange}
|
||||
value={[...services, ...templateVariableOptions].find(s => s.value === service)}
|
||||
options={[
|
||||
{
|
||||
label: 'Template Variables',
|
||||
options: templateVariableOptions,
|
||||
},
|
||||
...services,
|
||||
]}
|
||||
placeholder="Select Services"
|
||||
></Segment>
|
||||
<div className="gf-form gf-form--grow">
|
||||
<div className="gf-form-label gf-form-label--grow" />
|
||||
</div>
|
||||
console.log('rerender', { service, metricType });
|
||||
return (
|
||||
<>
|
||||
<div className="gf-form-inline">
|
||||
<span className="gf-form-label width-9 query-keyword">Service</span>
|
||||
<Segment
|
||||
onChange={onServiceChange}
|
||||
value={[...services, ...templateVariableOptions].find(s => s.value === service)}
|
||||
options={[
|
||||
{
|
||||
label: 'Template Variables',
|
||||
options: templateVariableOptions,
|
||||
},
|
||||
...services,
|
||||
]}
|
||||
placeholder="Select Services"
|
||||
></Segment>
|
||||
<div className="gf-form gf-form--grow">
|
||||
<div className="gf-form-label gf-form-label--grow" />
|
||||
</div>
|
||||
<div className="gf-form-inline">
|
||||
<span className="gf-form-label width-9 query-keyword">Metric</span>
|
||||
</div>
|
||||
<div className="gf-form-inline">
|
||||
<span className="gf-form-label width-9 query-keyword">Metric</span>
|
||||
|
||||
<Segment
|
||||
className="query-part"
|
||||
onChange={this.onMetricTypeChange}
|
||||
value={[...metrics, ...templateVariableOptions].find(s => s.value === metricType)}
|
||||
options={[
|
||||
{
|
||||
label: 'Template Variables',
|
||||
options: templateVariableOptions,
|
||||
},
|
||||
...metrics,
|
||||
]}
|
||||
placeholder="Select Metric"
|
||||
></Segment>
|
||||
<div className="gf-form gf-form--grow">
|
||||
<div className="gf-form-label gf-form-label--grow" />
|
||||
</div>
|
||||
<Segment
|
||||
className="query-part"
|
||||
onChange={onMetricTypeChange}
|
||||
value={[...metrics, ...templateVariableOptions].find(s => s.value === metricType)}
|
||||
options={[
|
||||
{
|
||||
label: 'Template Variables',
|
||||
options: templateVariableOptions,
|
||||
},
|
||||
...metrics,
|
||||
]}
|
||||
placeholder="Select Metric"
|
||||
></Segment>
|
||||
<div className="gf-form gf-form--grow">
|
||||
<div className="gf-form-label gf-form-label--grow" />
|
||||
</div>
|
||||
{this.props.children(this.state.metricDescriptor)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
</div>
|
||||
{props.children(state.metricDescriptor)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -1,32 +1,37 @@
|
||||
import React from 'react';
|
||||
import { Input } from '@grafana/ui';
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
import { SegmentAsync } from '@grafana/ui';
|
||||
import StackdriverDatasource from '../datasource';
|
||||
|
||||
export interface Props {
|
||||
datasource: StackdriverDatasource;
|
||||
}
|
||||
|
||||
interface State {
|
||||
onChange: (projectName: string) => void;
|
||||
templateVariableOptions: Array<SelectableValue<string>>;
|
||||
projectName: string;
|
||||
}
|
||||
|
||||
export class Project extends React.Component<Props, State> {
|
||||
state: State = {
|
||||
projectName: 'Loading project...',
|
||||
};
|
||||
|
||||
async componentDidMount() {
|
||||
const projectName = await this.props.datasource.getDefaultProject();
|
||||
this.setState({ projectName });
|
||||
}
|
||||
|
||||
render() {
|
||||
const { projectName } = this.state;
|
||||
return (
|
||||
<div className="gf-form">
|
||||
<span className="gf-form-label width-9 query-keyword">Project</span>
|
||||
<Input className="gf-form-input width-15" disabled type="text" value={projectName} />
|
||||
export function Project({ projectName, datasource, onChange, templateVariableOptions }: Props) {
|
||||
return (
|
||||
<div className="gf-form-inline">
|
||||
<span className="gf-form-label width-9 query-keyword">Project</span>
|
||||
<SegmentAsync
|
||||
allowCustomValue
|
||||
onChange={({ value }) => onChange(value!)}
|
||||
loadOptions={() =>
|
||||
datasource.getProjects().then(projects => [
|
||||
{
|
||||
label: 'Template Variables',
|
||||
options: templateVariableOptions,
|
||||
},
|
||||
...projects,
|
||||
])
|
||||
}
|
||||
value={projectName}
|
||||
placeholder="Select Project"
|
||||
/>
|
||||
<div className="gf-form gf-form--grow">
|
||||
<div className="gf-form-label gf-form-label--grow" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import renderer from 'react-test-renderer';
|
||||
import { QueryEditor, Props, DefaultTarget } from './QueryEditor';
|
||||
import { DefaultTarget, Props, QueryEditor } from './QueryEditor';
|
||||
import { TemplateSrv } from 'app/features/templating/template_srv';
|
||||
|
||||
const props: Props = {
|
||||
@ -9,7 +9,8 @@ const props: Props = {
|
||||
target: DefaultTarget,
|
||||
events: { on: () => {} },
|
||||
datasource: {
|
||||
getDefaultProject: () => Promise.resolve('project'),
|
||||
getProjects: () => Promise.resolve([]),
|
||||
getDefaultProject: () => Promise.resolve('projectName'),
|
||||
getMetricTypes: () => Promise.resolve([]),
|
||||
getLabels: () => Promise.resolve([]),
|
||||
variables: [],
|
||||
|
@ -1,14 +1,12 @@
|
||||
import React from 'react';
|
||||
import _ from 'lodash';
|
||||
|
||||
import { TemplateSrv } from 'app/features/templating/template_srv';
|
||||
|
||||
import { Aggregations, Metrics, Filters, GroupBys, Alignments, AlignmentPeriods, AliasBy, Help } from './';
|
||||
import { Project, Aggregations, Metrics, Filters, GroupBys, Alignments, AlignmentPeriods, AliasBy, Help } from './';
|
||||
import { StackdriverQuery, MetricDescriptor } from '../types';
|
||||
import { getAlignmentPickerData, toOption } from '../functions';
|
||||
import StackdriverDatasource from '../datasource';
|
||||
import { TimeSeries, SelectableValue } from '@grafana/data';
|
||||
import { PanelEvents } from '@grafana/data';
|
||||
import { PanelEvents, SelectableValue, TimeSeries } from '@grafana/data';
|
||||
|
||||
export interface Props {
|
||||
onQueryChange: (target: StackdriverQuery) => void;
|
||||
@ -30,7 +28,7 @@ interface State extends StackdriverQuery {
|
||||
}
|
||||
|
||||
export const DefaultTarget: State = {
|
||||
defaultProject: 'loading project...',
|
||||
projectName: '',
|
||||
metricType: '',
|
||||
metricKind: '',
|
||||
valueType: '',
|
||||
@ -58,23 +56,34 @@ export class QueryEditor extends React.Component<Props, State> {
|
||||
|
||||
async componentDidMount() {
|
||||
const { events, target, templateSrv, datasource } = this.props;
|
||||
if (!target.projectName) {
|
||||
target.projectName = datasource.getDefaultProject();
|
||||
}
|
||||
|
||||
events.on(PanelEvents.dataReceived, this.onDataReceived.bind(this));
|
||||
events.on(PanelEvents.dataError, this.onDataError.bind(this));
|
||||
|
||||
const { perSeriesAligner, alignOptions } = getAlignmentPickerData(target, templateSrv);
|
||||
const variableOptionGroup = {
|
||||
label: 'Template Variables',
|
||||
expanded: false,
|
||||
options: datasource.variables.map(toOption),
|
||||
};
|
||||
this.setState({
|
||||
|
||||
const state: Partial<State> = {
|
||||
...this.props.target,
|
||||
projectName: target.projectName,
|
||||
alignOptions,
|
||||
perSeriesAligner,
|
||||
variableOptionGroup,
|
||||
variableOptions: variableOptionGroup.options,
|
||||
});
|
||||
};
|
||||
|
||||
datasource.getLabels(target.metricType, target.refId, target.groupBys).then(labels => this.setState({ labels }));
|
||||
this.setState(state);
|
||||
|
||||
datasource
|
||||
.getLabels(target.metricType, target.refId, target.projectName, target.groupBys)
|
||||
.then(labels => this.setState({ labels }));
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
@ -118,7 +127,7 @@ export class QueryEditor extends React.Component<Props, State> {
|
||||
{ valueType, metricKind, perSeriesAligner: this.state.perSeriesAligner },
|
||||
templateSrv
|
||||
);
|
||||
const labels = await this.props.datasource.getLabels(type, target.refId, target.groupBys);
|
||||
const labels = await this.props.datasource.getLabels(type, target.refId, this.state.projectName, target.groupBys);
|
||||
this.setState(
|
||||
{
|
||||
alignOptions,
|
||||
@ -131,7 +140,9 @@ export class QueryEditor extends React.Component<Props, State> {
|
||||
},
|
||||
() => {
|
||||
onQueryChange(this.state);
|
||||
onExecuteQuery();
|
||||
if (this.state.projectName !== null) {
|
||||
onExecuteQuery();
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
@ -142,24 +153,28 @@ export class QueryEditor extends React.Component<Props, State> {
|
||||
this.props.onQueryChange(this.state);
|
||||
this.props.onExecuteQuery();
|
||||
});
|
||||
datasource.getLabels(target.metricType, target.refId, value).then(labels => this.setState({ labels }));
|
||||
datasource
|
||||
.getLabels(target.metricType, target.refId, this.state.projectName, value)
|
||||
.then(labels => this.setState({ labels }));
|
||||
}
|
||||
|
||||
onPropertyChange(prop: string, value: string[]) {
|
||||
onPropertyChange(prop: string, value: any) {
|
||||
this.setState({ [prop]: value }, () => {
|
||||
this.props.onQueryChange(this.state);
|
||||
this.props.onExecuteQuery();
|
||||
if (this.state.projectName !== null) {
|
||||
this.props.onExecuteQuery();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
groupBys = [],
|
||||
filters = [],
|
||||
usedAlignmentPeriod,
|
||||
defaultProject,
|
||||
projectName,
|
||||
metricType,
|
||||
crossSeriesReducer,
|
||||
groupBys,
|
||||
filters,
|
||||
perSeriesAligner,
|
||||
alignOptions,
|
||||
alignmentPeriod,
|
||||
@ -169,14 +184,24 @@ export class QueryEditor extends React.Component<Props, State> {
|
||||
labels,
|
||||
variableOptionGroup,
|
||||
variableOptions,
|
||||
refId,
|
||||
} = this.state;
|
||||
const { datasource, templateSrv } = this.props;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Project
|
||||
templateVariableOptions={variableOptions}
|
||||
projectName={projectName}
|
||||
datasource={datasource}
|
||||
onChange={value => {
|
||||
this.onPropertyChange('projectName', value);
|
||||
datasource.getLabels(metricType, refId, value, groupBys).then(labels => this.setState({ labels }));
|
||||
}}
|
||||
/>
|
||||
<Metrics
|
||||
templateSrv={templateSrv}
|
||||
defaultProject={defaultProject}
|
||||
projectName={projectName}
|
||||
metricType={metricType}
|
||||
templateVariableOptions={variableOptions}
|
||||
datasource={datasource}
|
||||
@ -223,7 +248,7 @@ export class QueryEditor extends React.Component<Props, State> {
|
||||
onChange={value => this.onPropertyChange('alignmentPeriod', value)}
|
||||
/>
|
||||
<AliasBy value={aliasBy} onChange={value => this.onPropertyChange('aliasBy', value)} />
|
||||
<Help datasource={datasource} rawQuery={lastQuery} lastQueryError={lastQueryError} />
|
||||
<Help rawQuery={lastQuery} lastQueryError={lastQueryError} />
|
||||
</>
|
||||
)}
|
||||
</Metrics>
|
||||
|
@ -2,7 +2,7 @@ import React, { ChangeEvent, FC } from 'react';
|
||||
|
||||
interface Props {
|
||||
onValueChange: (e: ChangeEvent<HTMLSelectElement>) => void;
|
||||
options: any[];
|
||||
options: Array<{ value: string; name: string }>;
|
||||
value: string;
|
||||
label: string;
|
||||
}
|
||||
|
@ -14,6 +14,8 @@ const props: VariableQueryProps = {
|
||||
onChange: (query, definition) => {},
|
||||
query: {},
|
||||
datasource: {
|
||||
getDefaultProject: () => '',
|
||||
getProjects: async (): Promise<any[]> => [],
|
||||
getMetricTypes: async (p: any): Promise<any[]> => [],
|
||||
},
|
||||
templateSrv: { replace: (s: string) => s, variables: [] },
|
||||
@ -28,7 +30,7 @@ describe('VariableQueryEditor', () => {
|
||||
describe('and a new variable is created', () => {
|
||||
it('should trigger a query using the first query type in the array', done => {
|
||||
props.onChange = (query, definition) => {
|
||||
expect(definition).toBe('Stackdriver - Services');
|
||||
expect(definition).toBe('Stackdriver - Projects');
|
||||
done();
|
||||
};
|
||||
renderer.create(<StackdriverVariableQueryEditor {...props} />).toJSON();
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { ChangeEvent, PureComponent } from 'react';
|
||||
import React, { PureComponent } from 'react';
|
||||
import { VariableQueryProps } from 'app/types/plugins';
|
||||
import { SimpleSelect } from './';
|
||||
import { getMetricTypes, getLabelKeys, extractServicesFromMetricDescriptors } from '../functions';
|
||||
@ -6,6 +6,7 @@ import { MetricFindQueryTypes, VariableQueryData } from '../types';
|
||||
|
||||
export class StackdriverVariableQueryEditor extends PureComponent<VariableQueryProps, VariableQueryData> {
|
||||
queryTypes: Array<{ value: string; name: string }> = [
|
||||
{ value: MetricFindQueryTypes.Projects, name: 'Projects' },
|
||||
{ value: MetricFindQueryTypes.Services, name: 'Services' },
|
||||
{ value: MetricFindQueryTypes.MetricTypes, name: 'Metric Types' },
|
||||
{ value: MetricFindQueryTypes.LabelKeys, name: 'Label Keys' },
|
||||
@ -25,15 +26,24 @@ export class StackdriverVariableQueryEditor extends PureComponent<VariableQueryP
|
||||
labelKey: '',
|
||||
metricTypes: [],
|
||||
services: [],
|
||||
projects: [],
|
||||
projectName: '',
|
||||
};
|
||||
|
||||
constructor(props: VariableQueryProps) {
|
||||
super(props);
|
||||
this.state = Object.assign(this.defaults, this.props.query);
|
||||
this.state = Object.assign(
|
||||
this.defaults,
|
||||
{ projectName: this.props.datasource.getDefaultProject() },
|
||||
this.props.query
|
||||
);
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
const metricDescriptors = await this.props.datasource.getMetricTypes(this.props.datasource.projectName);
|
||||
const projects = await this.props.datasource.getProjects();
|
||||
const metricDescriptors = await this.props.datasource.getMetricTypes(
|
||||
this.props.query.projectName || this.props.datasource.getDefaultProject()
|
||||
);
|
||||
const services = extractServicesFromMetricDescriptors(metricDescriptors).map((m: any) => ({
|
||||
value: m.service,
|
||||
name: m.serviceShortName,
|
||||
@ -52,48 +62,66 @@ export class StackdriverVariableQueryEditor extends PureComponent<VariableQueryP
|
||||
this.props.templateSrv.replace(this.state.selectedMetricType),
|
||||
this.props.templateSrv.replace(selectedService)
|
||||
);
|
||||
|
||||
const state: any = {
|
||||
services,
|
||||
selectedService,
|
||||
metricTypes,
|
||||
selectedMetricType,
|
||||
metricDescriptors,
|
||||
...(await this.getLabels(selectedMetricType)),
|
||||
projects: projects.map(({ value, label }: any) => ({ value, name: label })),
|
||||
...(await this.getLabels(selectedMetricType, this.state.projectName)),
|
||||
};
|
||||
this.setState(state);
|
||||
}
|
||||
|
||||
async onQueryTypeChange(event: ChangeEvent<HTMLSelectElement>) {
|
||||
async onQueryTypeChange(queryType: string) {
|
||||
const state: any = {
|
||||
selectedQueryType: event.target.value,
|
||||
...(await this.getLabels(this.state.selectedMetricType, event.target.value)),
|
||||
selectedQueryType: queryType,
|
||||
...(await this.getLabels(this.state.selectedMetricType, this.state.projectName, queryType)),
|
||||
};
|
||||
this.setState(state);
|
||||
}
|
||||
|
||||
async onServiceChange(event: ChangeEvent<HTMLSelectElement>) {
|
||||
async onProjectChange(projectName: string) {
|
||||
const metricDescriptors = await this.props.datasource.getMetricTypes(projectName);
|
||||
const labels = await this.getLabels(this.state.selectedMetricType, projectName);
|
||||
const { metricTypes, selectedMetricType } = getMetricTypes(
|
||||
metricDescriptors,
|
||||
this.state.selectedMetricType,
|
||||
this.props.templateSrv.replace(this.state.selectedMetricType),
|
||||
this.props.templateSrv.replace(this.state.selectedService)
|
||||
);
|
||||
|
||||
this.setState({ ...labels, metricTypes, selectedMetricType, metricDescriptors, projectName });
|
||||
}
|
||||
|
||||
async onServiceChange(service: string) {
|
||||
const { metricTypes, selectedMetricType } = getMetricTypes(
|
||||
this.state.metricDescriptors,
|
||||
this.state.selectedMetricType,
|
||||
this.props.templateSrv.replace(this.state.selectedMetricType),
|
||||
this.props.templateSrv.replace(event.target.value)
|
||||
this.props.templateSrv.replace(service)
|
||||
);
|
||||
const state: any = {
|
||||
selectedService: event.target.value,
|
||||
selectedService: service,
|
||||
metricTypes,
|
||||
selectedMetricType,
|
||||
...(await this.getLabels(selectedMetricType)),
|
||||
...(await this.getLabels(selectedMetricType, this.state.projectName)),
|
||||
};
|
||||
this.setState(state);
|
||||
}
|
||||
|
||||
async onMetricTypeChange(event: ChangeEvent<HTMLSelectElement>) {
|
||||
const state: any = { selectedMetricType: event.target.value, ...(await this.getLabels(event.target.value)) };
|
||||
async onMetricTypeChange(metricType: string) {
|
||||
const state: any = {
|
||||
selectedMetricType: metricType,
|
||||
...(await this.getLabels(metricType, this.state.projectName)),
|
||||
};
|
||||
this.setState(state);
|
||||
}
|
||||
|
||||
onLabelKeyChange(event: ChangeEvent<HTMLSelectElement>) {
|
||||
this.setState({ labelKey: event.target.value });
|
||||
onLabelKeyChange(labelKey: string) {
|
||||
this.setState({ labelKey });
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
@ -102,10 +130,10 @@ export class StackdriverVariableQueryEditor extends PureComponent<VariableQueryP
|
||||
this.props.onChange(queryModel, `Stackdriver - ${query.name}`);
|
||||
}
|
||||
|
||||
async getLabels(selectedMetricType: string, selectedQueryType = this.state.selectedQueryType) {
|
||||
async getLabels(selectedMetricType: string, projectName: string, selectedQueryType = this.state.selectedQueryType) {
|
||||
let result = { labels: this.state.labels, labelKey: this.state.labelKey };
|
||||
if (selectedMetricType && selectedQueryType === MetricFindQueryTypes.LabelValues) {
|
||||
const labels = await getLabelKeys(this.props.datasource, selectedMetricType);
|
||||
const labels = await getLabelKeys(this.props.datasource, selectedMetricType, projectName);
|
||||
const labelKey = labels.some(l => l === this.props.templateSrv.replace(this.state.labelKey))
|
||||
? this.state.labelKey
|
||||
: labels[0];
|
||||
@ -126,35 +154,49 @@ export class StackdriverVariableQueryEditor extends PureComponent<VariableQueryP
|
||||
switch (queryType) {
|
||||
case MetricFindQueryTypes.MetricTypes:
|
||||
return (
|
||||
<SimpleSelect
|
||||
value={this.state.selectedService}
|
||||
options={this.insertTemplateVariables(this.state.services)}
|
||||
onValueChange={e => this.onServiceChange(e)}
|
||||
label="Service"
|
||||
/>
|
||||
<>
|
||||
<SimpleSelect
|
||||
value={this.state.projectName}
|
||||
options={this.insertTemplateVariables(this.state.projects)}
|
||||
onValueChange={e => this.onProjectChange(e.target.value)}
|
||||
label="Project"
|
||||
/>
|
||||
<SimpleSelect
|
||||
value={this.state.selectedService}
|
||||
options={this.insertTemplateVariables(this.state.services)}
|
||||
onValueChange={e => this.onServiceChange(e.target.value)}
|
||||
label="Service"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
case MetricFindQueryTypes.LabelKeys:
|
||||
case MetricFindQueryTypes.LabelValues:
|
||||
case MetricFindQueryTypes.ResourceTypes:
|
||||
return (
|
||||
<>
|
||||
<SimpleSelect
|
||||
value={this.state.projectName}
|
||||
options={this.insertTemplateVariables(this.state.projects)}
|
||||
onValueChange={e => this.onProjectChange(e.target.value)}
|
||||
label="Project"
|
||||
/>
|
||||
<SimpleSelect
|
||||
value={this.state.selectedService}
|
||||
options={this.insertTemplateVariables(this.state.services)}
|
||||
onValueChange={e => this.onServiceChange(e)}
|
||||
onValueChange={e => this.onServiceChange(e.target.value)}
|
||||
label="Service"
|
||||
/>
|
||||
<SimpleSelect
|
||||
value={this.state.selectedMetricType}
|
||||
options={this.insertTemplateVariables(this.state.metricTypes)}
|
||||
onValueChange={e => this.onMetricTypeChange(e)}
|
||||
onValueChange={e => this.onMetricTypeChange(e.target.value)}
|
||||
label="Metric Type"
|
||||
/>
|
||||
{queryType === MetricFindQueryTypes.LabelValues && (
|
||||
<SimpleSelect
|
||||
value={this.state.labelKey}
|
||||
options={this.insertTemplateVariables(this.state.labels.map(l => ({ value: l, name: l })))}
|
||||
onValueChange={e => this.onLabelKeyChange(e)}
|
||||
onValueChange={e => this.onLabelKeyChange(e.target.value)}
|
||||
label="Label Key"
|
||||
/>
|
||||
)}
|
||||
@ -167,13 +209,13 @@ export class StackdriverVariableQueryEditor extends PureComponent<VariableQueryP
|
||||
<SimpleSelect
|
||||
value={this.state.selectedService}
|
||||
options={this.insertTemplateVariables(this.state.services)}
|
||||
onValueChange={e => this.onServiceChange(e)}
|
||||
onValueChange={e => this.onServiceChange(e.target.value)}
|
||||
label="Service"
|
||||
/>
|
||||
<SimpleSelect
|
||||
value={this.state.selectedMetricType}
|
||||
options={this.insertTemplateVariables(this.state.metricTypes)}
|
||||
onValueChange={e => this.onMetricTypeChange(e)}
|
||||
onValueChange={e => this.onMetricTypeChange(e.target.value)}
|
||||
label="Metric Type"
|
||||
/>
|
||||
</>
|
||||
@ -189,7 +231,7 @@ export class StackdriverVariableQueryEditor extends PureComponent<VariableQueryP
|
||||
<SimpleSelect
|
||||
value={this.state.selectedQueryType}
|
||||
options={this.queryTypes}
|
||||
onValueChange={e => this.onQueryTypeChange(e)}
|
||||
onValueChange={e => this.onQueryTypeChange(e.target.value)}
|
||||
label="Query Type"
|
||||
/>
|
||||
{this.renderQueryTypeSwitch(this.state.selectedQueryType)}
|
||||
|
@ -2,6 +2,32 @@
|
||||
|
||||
exports[`QueryEditor renders correctly 1`] = `
|
||||
Array [
|
||||
<div
|
||||
className="gf-form-inline"
|
||||
>
|
||||
<span
|
||||
className="gf-form-label width-9 query-keyword"
|
||||
>
|
||||
Project
|
||||
</span>
|
||||
<div
|
||||
className="gf-form"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<a
|
||||
className="gf-form-label query-part"
|
||||
>
|
||||
Select Project
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
className="gf-form gf-form--grow"
|
||||
>
|
||||
<div
|
||||
className="gf-form-label gf-form-label--grow"
|
||||
/>
|
||||
</div>
|
||||
</div>,
|
||||
<div
|
||||
className="gf-form-inline"
|
||||
>
|
||||
@ -194,29 +220,6 @@ Array [
|
||||
<div
|
||||
className="gf-form-inline"
|
||||
>
|
||||
<div
|
||||
className="gf-form"
|
||||
>
|
||||
<span
|
||||
className="gf-form-label width-9 query-keyword"
|
||||
>
|
||||
Project
|
||||
</span>
|
||||
<div
|
||||
style={
|
||||
Object {
|
||||
"flexGrow": 1,
|
||||
}
|
||||
}
|
||||
>
|
||||
<input
|
||||
className="gf-form-input gf-form-input width-15"
|
||||
disabled={true}
|
||||
type="text"
|
||||
value="Loading project..."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="gf-form"
|
||||
onClick={[Function]}
|
||||
|
@ -17,8 +17,13 @@ Array [
|
||||
className="gf-form-input"
|
||||
onChange={[Function]}
|
||||
required={true}
|
||||
value="services"
|
||||
value="projects"
|
||||
>
|
||||
<option
|
||||
value="projects"
|
||||
>
|
||||
Projects
|
||||
</option>
|
||||
<option
|
||||
value="services"
|
||||
>
|
||||
|
@ -1,36 +1,40 @@
|
||||
import DatasourceSrv from 'app/features/plugins/datasource_srv';
|
||||
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
|
||||
import StackdriverDatasource from './datasource';
|
||||
import { AuthType, authTypes } from './types';
|
||||
|
||||
export interface JWT {
|
||||
private_key: any;
|
||||
token_uri: any;
|
||||
client_email: any;
|
||||
project_id: any;
|
||||
private_key: string;
|
||||
token_uri: string;
|
||||
client_email: string;
|
||||
project_id: string;
|
||||
}
|
||||
|
||||
export class StackdriverConfigCtrl {
|
||||
static templateUrl = 'public/app/plugins/datasource/stackdriver/partials/config.html';
|
||||
datasourceSrv: any;
|
||||
datasourceSrv: DatasourceSrv;
|
||||
current: any;
|
||||
meta: any;
|
||||
jsonText: string;
|
||||
validationErrors: string[] = [];
|
||||
inputDataValid: boolean;
|
||||
authenticationTypes: any[];
|
||||
authenticationTypes: Array<{ key: AuthType; value: string }>;
|
||||
defaultAuthenticationType: string;
|
||||
name: string;
|
||||
gceError: string;
|
||||
|
||||
/** @ngInject */
|
||||
constructor(datasourceSrv: DatasourceSrv) {
|
||||
this.defaultAuthenticationType = 'jwt';
|
||||
constructor(datasourceSrv: DatasourceSrv, private $scope: any) {
|
||||
this.defaultAuthenticationType = AuthType.JWT;
|
||||
this.datasourceSrv = datasourceSrv;
|
||||
this.name = this.meta.name;
|
||||
this.current.jsonData = this.current.jsonData || {};
|
||||
this.current.jsonData.authenticationType = this.current.jsonData.authenticationType
|
||||
? this.current.jsonData.authenticationType
|
||||
: this.defaultAuthenticationType;
|
||||
this.current.secureJsonData = this.current.secureJsonData || {};
|
||||
this.current.secureJsonFields = this.current.secureJsonFields || {};
|
||||
this.authenticationTypes = [
|
||||
{ key: this.defaultAuthenticationType, value: 'Google JWT File' },
|
||||
{ key: 'gce', value: 'GCE Default Service Account' },
|
||||
];
|
||||
this.authenticationTypes = authTypes;
|
||||
}
|
||||
|
||||
save(jwt: JWT) {
|
||||
@ -94,4 +98,19 @@ export class StackdriverConfigCtrl {
|
||||
this.current.secureJsonData = {};
|
||||
this.current.secureJsonFields = {};
|
||||
}
|
||||
|
||||
async loadGCEDefaultAccount() {
|
||||
this.gceError = '';
|
||||
const ds = (await getDatasourceSrv().loadDatasource(this.name)) as StackdriverDatasource;
|
||||
try {
|
||||
const defaultProject = await ds.getGCEDefaultProject();
|
||||
this.$scope.$apply(() => {
|
||||
this.current.jsonData.gceDefaultProject = defaultProject;
|
||||
});
|
||||
} catch (error) {
|
||||
this.$scope.$apply(() => {
|
||||
this.gceError = error;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,14 @@ import { stackdriverUnitMappings } from './constants';
|
||||
import appEvents from 'app/core/app_events';
|
||||
import _ from 'lodash';
|
||||
import StackdriverMetricFindQuery from './StackdriverMetricFindQuery';
|
||||
import { StackdriverQuery, MetricDescriptor, StackdriverOptions, Filter } from './types';
|
||||
import { DataSourceApi, DataQueryRequest, DataSourceInstanceSettings, ScopedVars } from '@grafana/data';
|
||||
import { StackdriverQuery, MetricDescriptor, StackdriverOptions, Filter, VariableQueryData } from './types';
|
||||
import {
|
||||
DataSourceApi,
|
||||
DataQueryRequest,
|
||||
DataSourceInstanceSettings,
|
||||
ScopedVars,
|
||||
DataQueryResponse,
|
||||
} from '@grafana/data';
|
||||
import { getBackendSrv } from '@grafana/runtime';
|
||||
import { TemplateSrv } from 'app/features/templating/template_srv';
|
||||
import { TimeSrv } from 'app/features/dashboard/services/TimeSrv';
|
||||
@ -12,35 +18,34 @@ import { CoreEvents } from 'app/types';
|
||||
export default class StackdriverDatasource extends DataSourceApi<StackdriverQuery, StackdriverOptions> {
|
||||
url: string;
|
||||
baseUrl: string;
|
||||
projectName: string;
|
||||
projectList: Array<{ label: string; value: string }>;
|
||||
authenticationType: string;
|
||||
queryPromise: Promise<any>;
|
||||
metricTypes: any[];
|
||||
metricTypesCache: { [key: string]: MetricDescriptor[] };
|
||||
|
||||
/** @ngInject */
|
||||
constructor(
|
||||
instanceSettings: DataSourceInstanceSettings<StackdriverOptions>,
|
||||
private instanceSettings: DataSourceInstanceSettings<StackdriverOptions>,
|
||||
public templateSrv: TemplateSrv,
|
||||
private timeSrv: TimeSrv
|
||||
) {
|
||||
super(instanceSettings);
|
||||
this.baseUrl = `/stackdriver/`;
|
||||
this.url = instanceSettings.url;
|
||||
this.projectName = instanceSettings.jsonData.defaultProject || '';
|
||||
this.url = instanceSettings.url!;
|
||||
this.authenticationType = instanceSettings.jsonData.authenticationType || 'jwt';
|
||||
this.metricTypes = [];
|
||||
this.metricTypesCache = {};
|
||||
}
|
||||
|
||||
get variables() {
|
||||
return this.templateSrv.variables.map(v => `$${v.name}`);
|
||||
}
|
||||
|
||||
async getTimeSeries(options: any) {
|
||||
async getTimeSeries(options: DataQueryRequest<StackdriverQuery>) {
|
||||
const queries = options.targets
|
||||
.filter((target: any) => {
|
||||
.filter((target: StackdriverQuery) => {
|
||||
return !target.hide && target.metricType;
|
||||
})
|
||||
.map((t: any) => {
|
||||
.map((t: StackdriverQuery) => {
|
||||
return {
|
||||
refId: t.refId,
|
||||
intervalMs: options.intervalMs,
|
||||
@ -48,12 +53,13 @@ export default class StackdriverDatasource extends DataSourceApi<StackdriverQuer
|
||||
metricType: this.templateSrv.replace(t.metricType, options.scopedVars || {}),
|
||||
crossSeriesReducer: this.templateSrv.replace(t.crossSeriesReducer || 'REDUCE_MEAN', options.scopedVars || {}),
|
||||
perSeriesAligner: this.templateSrv.replace(t.perSeriesAligner, options.scopedVars || {}),
|
||||
alignmentPeriod: this.templateSrv.replace(t.alignmentPeriod, options.scopedVars || {}),
|
||||
groupBys: this.interpolateGroupBys(t.groupBys, options.scopedVars),
|
||||
alignmentPeriod: this.templateSrv.replace(t.alignmentPeriod!, options.scopedVars || {}),
|
||||
groupBys: this.interpolateGroupBys(t.groupBys || [], options.scopedVars),
|
||||
view: t.view || 'FULL',
|
||||
filters: this.interpolateFilters(t.filters, options.scopedVars),
|
||||
aliasBy: this.templateSrv.replace(t.aliasBy, options.scopedVars || {}),
|
||||
filters: this.interpolateFilters(t.filters || [], options.scopedVars),
|
||||
aliasBy: this.templateSrv.replace(t.aliasBy!, options.scopedVars || {}),
|
||||
type: 'timeSeriesQuery',
|
||||
projectName: this.templateSrv.replace(t.projectName ? t.projectName : this.getDefaultProject()),
|
||||
};
|
||||
});
|
||||
|
||||
@ -84,20 +90,24 @@ export default class StackdriverDatasource extends DataSourceApi<StackdriverQuer
|
||||
.reduce((res, filter) => (filter.value ? [...res, filter] : res), []);
|
||||
|
||||
const filterArray = _.flatten(
|
||||
completeFilter.map(({ key, operator, value, condition }: Filter) => [key, operator, value, condition])
|
||||
completeFilter.map(({ key, operator, value, condition }: Filter) => [
|
||||
this.templateSrv.replace(key, scopedVars || {}),
|
||||
operator,
|
||||
this.templateSrv.replace(value, scopedVars || {}, 'regex'),
|
||||
condition,
|
||||
])
|
||||
);
|
||||
|
||||
return (filterArray || []).map(f => {
|
||||
return this.templateSrv.replace(f, scopedVars || {}, 'regex');
|
||||
});
|
||||
return filterArray || [];
|
||||
}
|
||||
|
||||
async getLabels(metricType: string, refId: string, groupBys?: string[]) {
|
||||
async getLabels(metricType: string, refId: string, projectName: string, groupBys?: string[]) {
|
||||
const response = await this.getTimeSeries({
|
||||
targets: [
|
||||
{
|
||||
refId: refId,
|
||||
datasourceId: this.id,
|
||||
projectName: this.templateSrv.replace(projectName),
|
||||
metricType: this.templateSrv.replace(metricType),
|
||||
groupBys: this.interpolateGroupBys(groupBys || [], {}),
|
||||
crossSeriesReducer: 'REDUCE_NONE',
|
||||
@ -105,13 +115,13 @@ export default class StackdriverDatasource extends DataSourceApi<StackdriverQuer
|
||||
},
|
||||
],
|
||||
range: this.timeSrv.timeRange(),
|
||||
});
|
||||
} as DataQueryRequest<StackdriverQuery>);
|
||||
const result = response.results[refId];
|
||||
return result && result.meta ? result.meta.labels : {};
|
||||
}
|
||||
|
||||
interpolateGroupBys(groupBys: string[], scopedVars: {}): string[] {
|
||||
let interpolatedGroupBys: any[] = [];
|
||||
let interpolatedGroupBys: string[] = [];
|
||||
(groupBys || []).forEach(gb => {
|
||||
const interpolated = this.templateSrv.replace(gb, scopedVars || {}, 'csv').split(',');
|
||||
if (Array.isArray(interpolated)) {
|
||||
@ -123,10 +133,10 @@ export default class StackdriverDatasource extends DataSourceApi<StackdriverQuer
|
||||
return interpolatedGroupBys;
|
||||
}
|
||||
|
||||
resolvePanelUnitFromTargets(targets: any[]) {
|
||||
resolvePanelUnitFromTargets(targets: StackdriverQuery[]) {
|
||||
let unit;
|
||||
if (targets.length > 0 && targets.every(t => t.unit === targets[0].unit)) {
|
||||
if (stackdriverUnitMappings.hasOwnProperty(targets[0].unit)) {
|
||||
if (stackdriverUnitMappings.hasOwnProperty(targets[0].unit!)) {
|
||||
// @ts-ignore
|
||||
unit = stackdriverUnitMappings[targets[0].unit];
|
||||
}
|
||||
@ -134,11 +144,11 @@ export default class StackdriverDatasource extends DataSourceApi<StackdriverQuer
|
||||
return unit;
|
||||
}
|
||||
|
||||
async query(options: DataQueryRequest<StackdriverQuery>) {
|
||||
const result: any[] = [];
|
||||
async query(options: DataQueryRequest<StackdriverQuery>): Promise<DataQueryResponse> {
|
||||
const result: DataQueryResponse[] = [];
|
||||
const data = await this.getTimeSeries(options);
|
||||
if (data.results) {
|
||||
Object['values'](data.results).forEach((queryRes: any) => {
|
||||
Object.values(data.results).forEach((queryRes: any) => {
|
||||
if (!queryRes.series) {
|
||||
return;
|
||||
}
|
||||
@ -177,6 +187,10 @@ export default class StackdriverDatasource extends DataSourceApi<StackdriverQuer
|
||||
view: 'FULL',
|
||||
filters: this.interpolateFilters(annotation.target.filters || [], options.scopedVars),
|
||||
type: 'annotationQuery',
|
||||
projectName: this.templateSrv.replace(
|
||||
annotation.target.projectName ? annotation.target.projectName : this.getDefaultProject(),
|
||||
options.scopedVars || {}
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
@ -203,7 +217,7 @@ export default class StackdriverDatasource extends DataSourceApi<StackdriverQuer
|
||||
return results;
|
||||
}
|
||||
|
||||
async metricFindQuery(query: string) {
|
||||
async metricFindQuery(query: VariableQueryData) {
|
||||
const stackdriverMetricFindQuery = new StackdriverMetricFindQuery(this);
|
||||
return stackdriverMetricFindQuery.execute(query);
|
||||
}
|
||||
@ -212,8 +226,7 @@ export default class StackdriverDatasource extends DataSourceApi<StackdriverQuer
|
||||
let status, message;
|
||||
const defaultErrorMessage = 'Cannot connect to Stackdriver API';
|
||||
try {
|
||||
const projectName = await this.getDefaultProject();
|
||||
const path = `v3/projects/${projectName}/metricDescriptors`;
|
||||
const path = `v3/projects/${this.getDefaultProject()}/metricDescriptors`;
|
||||
const response = await this.doRequest(`${this.baseUrl}${path}`);
|
||||
if (response.status === 200) {
|
||||
status = 'success';
|
||||
@ -241,6 +254,31 @@ export default class StackdriverDatasource extends DataSourceApi<StackdriverQuer
|
||||
}
|
||||
}
|
||||
|
||||
async getGCEDefaultProject() {
|
||||
return getBackendSrv()
|
||||
.datasourceRequest({
|
||||
url: '/api/tsdb/query',
|
||||
method: 'POST',
|
||||
data: {
|
||||
queries: [
|
||||
{
|
||||
refId: 'getGCEDefaultProject',
|
||||
type: 'getGCEDefaultProject',
|
||||
datasourceId: this.id,
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
.then(({ data }) => {
|
||||
return data && data.results && data.results.getGCEDefaultProject && data.results.getGCEDefaultProject.meta
|
||||
? data.results.getGCEDefaultProject.meta.defaultProject
|
||||
: '';
|
||||
})
|
||||
.catch(err => {
|
||||
throw err.data.error;
|
||||
});
|
||||
}
|
||||
|
||||
formatStackdriverError(error: any) {
|
||||
let message = 'Stackdriver: ';
|
||||
message += error.statusText ? error.statusText + ': ' : '';
|
||||
@ -257,50 +295,62 @@ export default class StackdriverDatasource extends DataSourceApi<StackdriverQuer
|
||||
return message;
|
||||
}
|
||||
|
||||
async getDefaultProject() {
|
||||
async getProjects() {
|
||||
try {
|
||||
if (this.authenticationType === 'gce' || !this.projectName) {
|
||||
const { data } = await getBackendSrv().datasourceRequest({
|
||||
url: '/api/tsdb/query',
|
||||
method: 'POST',
|
||||
data: {
|
||||
queries: [
|
||||
{
|
||||
refId: 'ensureDefaultProjectQuery',
|
||||
type: 'ensureDefaultProjectQuery',
|
||||
datasourceId: this.id,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
this.projectName = data.results.ensureDefaultProjectQuery.meta.defaultProject;
|
||||
return this.projectName;
|
||||
} else {
|
||||
return this.projectName;
|
||||
}
|
||||
const { data } = await getBackendSrv().datasourceRequest({
|
||||
url: '/api/tsdb/query',
|
||||
method: 'POST',
|
||||
data: {
|
||||
queries: [
|
||||
{
|
||||
refId: 'getProjectsListQuery',
|
||||
type: 'getProjectsListQuery',
|
||||
datasourceId: this.id,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
return data.results.getProjectsListQuery.meta.projectsList;
|
||||
} catch (error) {
|
||||
throw this.formatStackdriverError(error);
|
||||
console.log(this.formatStackdriverError(error));
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
getDefaultProject(): string {
|
||||
const { defaultProject, authenticationType, gceDefaultProject } = this.instanceSettings.jsonData;
|
||||
if (authenticationType === 'gce') {
|
||||
return gceDefaultProject || defaultProject || '';
|
||||
}
|
||||
|
||||
return defaultProject || '';
|
||||
}
|
||||
|
||||
async getMetricTypes(projectName: string): Promise<MetricDescriptor[]> {
|
||||
try {
|
||||
if (this.metricTypes.length === 0) {
|
||||
const metricsApiPath = `v3/projects/${projectName}/metricDescriptors`;
|
||||
const { data } = await this.doRequest(`${this.baseUrl}${metricsApiPath}`);
|
||||
|
||||
this.metricTypes = data.metricDescriptors.map((m: any) => {
|
||||
const [service] = m.type.split('/');
|
||||
const [serviceShortName] = service.split('.');
|
||||
m.service = service;
|
||||
m.serviceShortName = serviceShortName;
|
||||
m.displayName = m.displayName || m.type;
|
||||
|
||||
return m;
|
||||
});
|
||||
if (!projectName) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return this.metricTypes;
|
||||
const interpolatedProject = this.templateSrv.replace(projectName);
|
||||
if (this.metricTypesCache[interpolatedProject]) {
|
||||
return this.metricTypesCache[interpolatedProject];
|
||||
}
|
||||
|
||||
const metricsApiPath = `v3/projects/${interpolatedProject}/metricDescriptors`;
|
||||
const { data } = await this.doRequest(`${this.baseUrl}${metricsApiPath}`);
|
||||
|
||||
this.metricTypesCache[interpolatedProject] = data.metricDescriptors.map((m: any) => {
|
||||
const [service] = m.type.split('/');
|
||||
const [serviceShortName] = service.split('.');
|
||||
m.service = service;
|
||||
m.serviceShortName = serviceShortName;
|
||||
m.displayName = m.displayName || m.type;
|
||||
|
||||
return m;
|
||||
});
|
||||
|
||||
return this.metricTypesCache[interpolatedProject];
|
||||
} catch (error) {
|
||||
appEvents.emit(CoreEvents.dsRequestError, { error: { data: { error: this.formatStackdriverError(error) } } });
|
||||
return [];
|
||||
|
@ -1,25 +1,29 @@
|
||||
import _ from 'lodash';
|
||||
import { alignOptions, aggOptions, ValueTypes, MetricKind, systemLabels } from './constants';
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
import StackdriverDatasource from './datasource';
|
||||
import { TemplateSrv } from 'app/features/templating/template_srv';
|
||||
import { StackdriverQuery } from './types';
|
||||
import { StackdriverQuery, MetricDescriptor } from './types';
|
||||
|
||||
export const extractServicesFromMetricDescriptors = (metricDescriptors: any) => _.uniqBy(metricDescriptors, 'service');
|
||||
export const extractServicesFromMetricDescriptors = (metricDescriptors: MetricDescriptor[]) =>
|
||||
_.uniqBy(metricDescriptors, 'service');
|
||||
|
||||
export const getMetricTypesByService = (metricDescriptors: any, service: any) =>
|
||||
metricDescriptors.filter((m: any) => m.service === service);
|
||||
export const getMetricTypesByService = (metricDescriptors: MetricDescriptor[], service: string) =>
|
||||
metricDescriptors.filter((m: MetricDescriptor) => m.service === service);
|
||||
|
||||
export const getMetricTypes = (
|
||||
metricDescriptors: any[],
|
||||
metricDescriptors: MetricDescriptor[],
|
||||
metricType: string,
|
||||
interpolatedMetricType: any,
|
||||
selectedService: any
|
||||
interpolatedMetricType: string,
|
||||
selectedService: string
|
||||
) => {
|
||||
const metricTypes = getMetricTypesByService(metricDescriptors, selectedService).map((m: any) => ({
|
||||
value: m.type,
|
||||
name: m.displayName,
|
||||
}));
|
||||
const metricTypeExistInArray = metricTypes.some((m: any) => m.value === interpolatedMetricType);
|
||||
const metricTypeExistInArray = metricTypes.some(
|
||||
(m: { value: string; name: string }) => m.value === interpolatedMetricType
|
||||
);
|
||||
const selectedMetricType = metricTypeExistInArray ? metricType : metricTypes[0].value;
|
||||
return {
|
||||
metricTypes,
|
||||
@ -27,11 +31,14 @@ export const getMetricTypes = (
|
||||
};
|
||||
};
|
||||
|
||||
export const getAlignmentOptionsByMetric = (metricValueType: any, metricKind: any) => {
|
||||
export const getAlignmentOptionsByMetric = (metricValueType: string, metricKind: string) => {
|
||||
return !metricValueType
|
||||
? []
|
||||
: alignOptions.filter(i => {
|
||||
return i.valueTypes.indexOf(metricValueType) !== -1 && i.metricKinds.indexOf(metricKind) !== -1;
|
||||
return (
|
||||
i.valueTypes.indexOf(metricValueType as ValueTypes) !== -1 &&
|
||||
i.metricKinds.indexOf(metricKind as MetricKind) !== -1
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
@ -43,9 +50,13 @@ export const getAggregationOptionsByMetric = (valueType: ValueTypes, metricKind:
|
||||
});
|
||||
};
|
||||
|
||||
export const getLabelKeys = async (datasource: any, selectedMetricType: any) => {
|
||||
export const getLabelKeys = async (
|
||||
datasource: StackdriverDatasource,
|
||||
selectedMetricType: string,
|
||||
projectName: string
|
||||
) => {
|
||||
const refId = 'handleLabelKeysQuery';
|
||||
const labels = await datasource.getLabels(selectedMetricType, refId);
|
||||
const labels = await datasource.getLabels(selectedMetricType, refId, projectName);
|
||||
return [...Object.keys(labels), ...systemLabels];
|
||||
};
|
||||
|
||||
@ -53,11 +64,11 @@ export const getAlignmentPickerData = (
|
||||
{ valueType, metricKind, perSeriesAligner }: Partial<StackdriverQuery>,
|
||||
templateSrv: TemplateSrv
|
||||
) => {
|
||||
const alignOptions = getAlignmentOptionsByMetric(valueType, metricKind).map(option => ({
|
||||
const alignOptions = getAlignmentOptionsByMetric(valueType!, metricKind!).map(option => ({
|
||||
...option,
|
||||
label: option.text,
|
||||
}));
|
||||
if (!alignOptions.some(o => o.value === templateSrv.replace(perSeriesAligner))) {
|
||||
if (!alignOptions.some((o: { value: string }) => o.value === templateSrv.replace(perSeriesAligner!))) {
|
||||
perSeriesAligner = alignOptions.length > 0 ? alignOptions[0].value : '';
|
||||
}
|
||||
return { alignOptions, perSeriesAligner };
|
||||
|
@ -1,33 +1,42 @@
|
||||
<div class="gf-form-group">
|
||||
<div class="grafana-info-box">
|
||||
<h4>Stackdriver Authentication</h4>
|
||||
<p>There are two ways to authenticate the Stackdriver plugin - either by uploading a Service Account key file, or by
|
||||
automatically retrieving credentials from the Google metadata server. The latter option is only available
|
||||
when running Grafana on a GCE virtual machine.</p>
|
||||
<p>
|
||||
There are two ways to authenticate the Stackdriver plugin - either by uploading a Service Account key file, or by
|
||||
automatically retrieving credentials from the Google metadata server. The latter option is only available when
|
||||
running Grafana on a GCE virtual machine.
|
||||
</p>
|
||||
|
||||
<h5>Uploading a Service Account Key File</h5>
|
||||
<p>
|
||||
First you need to create a Google Cloud Platform (GCP) Service Account for
|
||||
the Project you want to show data for. A Grafana datasource integrates with one GCP Project. If you want to
|
||||
visualize data from multiple GCP Projects then you need to create one datasource per GCP Project.
|
||||
First you need to create a Google Cloud Platform (GCP) Service Account for the Project you want to show data for.
|
||||
A Grafana datasource integrates with one GCP Project. If you want to visualize data from multiple GCP Projects
|
||||
then you need to create one datasource per GCP Project.
|
||||
</p>
|
||||
<p>
|
||||
The <strong>Monitoring Viewer</strong> role provides all the permissions that Grafana needs. The following API
|
||||
needs to be enabled on GCP for the datasource to work: <a class="external-link" target="_blank" href="https://console.cloud.google.com/apis/library/monitoring.googleapis.com">Monitoring
|
||||
API</a>
|
||||
needs to be enabled on GCP for the datasource to work:
|
||||
<a
|
||||
class="external-link"
|
||||
target="_blank"
|
||||
href="https://console.cloud.google.com/apis/library/monitoring.googleapis.com"
|
||||
>Monitoring API</a
|
||||
>
|
||||
</p>
|
||||
|
||||
<h5>GCE Default Service Account</h5>
|
||||
<p>
|
||||
If Grafana is running on a Google Compute Engine (GCE) virtual machine, it is possible for Grafana to
|
||||
automatically retrieve the default project id and authentication token from the metadata server. In order for this to
|
||||
work, you need to make sure that you have a service account that is setup as the default account for the virtual
|
||||
machine and that the service account has been given read access to the Stackdriver Monitoring API.
|
||||
automatically retrieve the default project id and authentication token from the metadata server. In order for this
|
||||
to work, you need to make sure that you have a service account that is setup as the default account for the
|
||||
virtual machine and that the service account has been given read access to the Stackdriver Monitoring API.
|
||||
</p>
|
||||
|
||||
<p>Detailed instructions on how to create a Service Account can be found <a class="external-link" target="_blank"
|
||||
href="http://docs.grafana.org/datasources/stackdriver/">in
|
||||
the documentation.</a>
|
||||
<p>
|
||||
Detailed instructions on how to create a Service Account can be found
|
||||
<a class="external-link" target="_blank" href="http://docs.grafana.org/datasources/stackdriver/"
|
||||
>in the documentation.</a
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -35,20 +44,42 @@
|
||||
<div class="gf-form-group">
|
||||
<div class="gf-form">
|
||||
<h3>Authentication</h3>
|
||||
<info-popover mode="header">Upload your Service Account key file or paste in the contents of the file. The file
|
||||
contents will be encrypted and saved in the Grafana database.</info-popover>
|
||||
<info-popover mode="header"
|
||||
>Upload your Service Account key file or paste in the contents of the file. The file contents will be encrypted
|
||||
and saved in the Grafana database.</info-popover
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form max-width-30">
|
||||
<span class="gf-form-label width-10">Authentication Type</span>
|
||||
<div class="gf-form-select-wrapper max-width-24">
|
||||
<select class="gf-form-input" ng-model="ctrl.current.jsonData.authenticationType" ng-options="f.key as f.value for f in ctrl.authenticationTypes"></select>
|
||||
<select
|
||||
class="gf-form-input"
|
||||
ng-change="ctrl.gceError = ''"
|
||||
ng-model="ctrl.current.jsonData.authenticationType"
|
||||
ng-options="f.key as f.value for f in ctrl.authenticationTypes"
|
||||
></select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="ctrl.current.jsonData.authenticationType === ctrl.defaultAuthenticationType && !ctrl.current.jsonData.clientEmail && !ctrl.inputDataValid">
|
||||
<div class="gf-form-inline" ng-if="ctrl.current.jsonData.authenticationType !== ctrl.defaultAuthenticationType">
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label width-10">Project</span>
|
||||
<input class="gf-form-input width-40" disabled type="text" ng-model="ctrl.current.jsonData.gceDefaultProject" />
|
||||
</div>
|
||||
|
||||
<div class="gf-form width-18">
|
||||
<a class="btn btn-secondary gf-form-btn" style="margin-top: 2px;" href="#" ng-click="ctrl.loadGCEDefaultAccount()"
|
||||
>{{ctrl.current.jsonData.gceDefaultProject ? 'Reload project name' : 'Load project name'}}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
ng-if="ctrl.current.jsonData.authenticationType === ctrl.defaultAuthenticationType && !ctrl.current.jsonData.clientEmail && !ctrl.inputDataValid"
|
||||
>
|
||||
<div class="gf-form-group" ng-if="!ctrl.inputDataValid">
|
||||
<div class="gf-form">
|
||||
<form>
|
||||
@ -59,7 +90,13 @@
|
||||
<div class="gf-form-group">
|
||||
<h5 class="section-heading" ng-if="!ctrl.inputDataValid">Or paste Service Account key JSON</h5>
|
||||
<div class="gf-form" ng-if="!ctrl.inputDataValid">
|
||||
<textarea rows="10" data-share-panel-url="" class="gf-form-input" ng-model="ctrl.jsonText" ng-paste="ctrl.onPasteJwt($event)"></textarea>
|
||||
<textarea
|
||||
rows="10"
|
||||
data-share-panel-url=""
|
||||
class="gf-form-input"
|
||||
ng-model="ctrl.jsonText"
|
||||
ng-paste="ctrl.onPasteJwt($event)"
|
||||
></textarea>
|
||||
</div>
|
||||
<div ng-repeat="valError in ctrl.validationErrors" class="text-error p-l-1">
|
||||
<i class="fa fa-warning"></i>
|
||||
@ -69,7 +106,10 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gf-form-group" ng-if="ctrl.current.jsonData.authenticationType === ctrl.defaultAuthenticationType && (ctrl.inputDataValid || ctrl.current.jsonData.clientEmail)">
|
||||
<div
|
||||
class="gf-form-group"
|
||||
ng-if="ctrl.current.jsonData.authenticationType === ctrl.defaultAuthenticationType && (ctrl.inputDataValid || ctrl.current.jsonData.clientEmail)"
|
||||
>
|
||||
<h6>Uploaded Key Details</h6>
|
||||
|
||||
<div class="gf-form">
|
||||
@ -82,24 +122,44 @@
|
||||
</div>
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label width-10">Token URI</span>
|
||||
<input class="gf-form-input width-40" disabled type="text" ng-model='ctrl.current.jsonData.tokenUri' />
|
||||
<input class="gf-form-input width-40" disabled type="text" ng-model="ctrl.current.jsonData.tokenUri" />
|
||||
</div>
|
||||
<div class="gf-form" ng-if="ctrl.current.secureJsonFields.privateKey">
|
||||
<span class="gf-form-label width-10">Private Key</span>
|
||||
<input type="text" class="gf-form-input max-width-12" disabled="disabled" value="configured">
|
||||
<input type="text" class="gf-form-input max-width-12" disabled="disabled" value="configured" />
|
||||
</div>
|
||||
|
||||
<div class="gf-form width-18">
|
||||
<a class="btn btn-secondary gf-form-btn" href="#" ng-click="ctrl.resetValidationMessages()">Reset Service
|
||||
Account Key </a>
|
||||
<div class="gf-form width-18" style="margin-top: 24px;">
|
||||
<a class="btn btn-secondary gf-form-btn" href="#" ng-click="ctrl.resetValidationMessages()"
|
||||
>Reset Service Account Key
|
||||
</a>
|
||||
<info-popover mode="right-normal">
|
||||
Reset to clear the uploaded key and upload a new file.
|
||||
</info-popover>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="gf-form-label" ng-hide="ctrl.current.secureJsonFields.privateKey || ctrl.current.jsonData.authenticationType !== ctrl.defaultAuthenticationType"><i
|
||||
class="fa fa-save"></i> Do not forget to save your changes after uploading a file.</p>
|
||||
<p
|
||||
class="gf-form-label"
|
||||
ng-hide="ctrl.current.secureJsonFields.privateKey || ctrl.current.jsonData.authenticationType !== ctrl.defaultAuthenticationType"
|
||||
>
|
||||
<i class="fa fa-save"></i> Do not forget to save your changes after uploading a file.
|
||||
</p>
|
||||
|
||||
<p class="gf-form-label" ng-show="ctrl.current.jsonData.authenticationType !== ctrl.defaultAuthenticationType"><i class="fa fa-save"></i>
|
||||
Verify GCE default service account by clicking Save & Test</p>
|
||||
<p
|
||||
class="gf-form-label"
|
||||
ng-show="ctrl.current.jsonData.authenticationType !== ctrl.defaultAuthenticationType && !ctrl.current.jsonData.gceDefaultProject"
|
||||
>
|
||||
<i class="fa fa-warning"></i> Before saving, load the Project name that is associated with the default service account
|
||||
</p>
|
||||
|
||||
<div class="gf-form" ng-if="ctrl.gceError">
|
||||
<pre class="gf-form-pre alert alert-error">{{ctrl.gceError}}</pre>
|
||||
</div>
|
||||
|
||||
<p
|
||||
class="gf-form-label"
|
||||
ng-show="ctrl.current.jsonData.authenticationType !== ctrl.defaultAuthenticationType && ctrl.current.jsonData.gceDefaultProject"
|
||||
>
|
||||
<i class="fa fa-save"></i> Verify GCE default service account by clicking Save & Test
|
||||
</p>
|
||||
|
@ -3,7 +3,7 @@ import { metricDescriptors } from './testData';
|
||||
import { TemplateSrv } from 'app/features/templating/template_srv';
|
||||
import { CustomVariable } from 'app/features/templating/all';
|
||||
import { DataSourceInstanceSettings, toUtc } from '@grafana/data';
|
||||
import { StackdriverOptions } from '../types';
|
||||
import { StackdriverOptions, StackdriverQuery } from '../types';
|
||||
import { backendSrv } from 'app/core/services/backend_srv'; // will use the version in __mocks__
|
||||
import { TimeSrv } from 'app/features/dashboard/services/TimeSrv';
|
||||
|
||||
@ -155,7 +155,7 @@ describe('StackdriverDataSource', () => {
|
||||
|
||||
ds = new StackdriverDataSource(instanceSettings, templateSrv, timeSrv);
|
||||
// @ts-ignore
|
||||
result = await ds.getMetricTypes();
|
||||
result = await ds.getMetricTypes('proj');
|
||||
});
|
||||
|
||||
it('should return successfully', () => {
|
||||
@ -185,6 +185,19 @@ describe('StackdriverDataSource', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('and is single value variable for the label part', () => {
|
||||
beforeEach(() => {
|
||||
const filterTemplateSrv = initTemplateSrv('resource.label.zone');
|
||||
const ds = new StackdriverDataSource(instanceSettings, filterTemplateSrv, timeSrv);
|
||||
interpolated = ds.interpolateFilters(['${test}', '=~', 'europe-north-1a'], {});
|
||||
});
|
||||
|
||||
it('should replace the variable with the value and not with regex formatting', () => {
|
||||
expect(interpolated.length).toBe(4);
|
||||
expect(interpolated[0]).toBe('resource.label.zone');
|
||||
});
|
||||
});
|
||||
|
||||
describe('and is multi value variable', () => {
|
||||
beforeEach(() => {
|
||||
const filterTemplateSrv = initTemplateSrv(['filtervalue1', 'filtervalue2'], true);
|
||||
@ -237,7 +250,7 @@ describe('StackdriverDataSource', () => {
|
||||
describe('when theres only one target', () => {
|
||||
describe('and the stackdriver unit doesnt have a corresponding grafana unit', () => {
|
||||
beforeEach(() => {
|
||||
res = ds.resolvePanelUnitFromTargets([{ unit: 'megaseconds' }]);
|
||||
res = ds.resolvePanelUnitFromTargets([{ unit: 'megaseconds' }] as StackdriverQuery[]);
|
||||
});
|
||||
it('should return undefined', () => {
|
||||
expect(res).toBeUndefined();
|
||||
@ -245,7 +258,7 @@ describe('StackdriverDataSource', () => {
|
||||
});
|
||||
describe('and the stackdriver unit has a corresponding grafana unit', () => {
|
||||
beforeEach(() => {
|
||||
res = ds.resolvePanelUnitFromTargets([{ unit: 'bit' }]);
|
||||
res = ds.resolvePanelUnitFromTargets([{ unit: 'bit' }] as StackdriverQuery[]);
|
||||
});
|
||||
it('should return bits', () => {
|
||||
expect(res).toEqual('bits');
|
||||
@ -256,7 +269,7 @@ describe('StackdriverDataSource', () => {
|
||||
describe('when theres more than one target', () => {
|
||||
describe('and all target units are the same', () => {
|
||||
beforeEach(() => {
|
||||
res = ds.resolvePanelUnitFromTargets([{ unit: 'bit' }, { unit: 'bit' }]);
|
||||
res = ds.resolvePanelUnitFromTargets([{ unit: 'bit' }, { unit: 'bit' }] as StackdriverQuery[]);
|
||||
});
|
||||
it('should return bits', () => {
|
||||
expect(res).toEqual('bits');
|
||||
@ -264,7 +277,10 @@ describe('StackdriverDataSource', () => {
|
||||
});
|
||||
describe('and all target units are the same but doesnt have grafana mappings', () => {
|
||||
beforeEach(() => {
|
||||
res = ds.resolvePanelUnitFromTargets([{ unit: 'megaseconds' }, { unit: 'megaseconds' }]);
|
||||
res = ds.resolvePanelUnitFromTargets([
|
||||
{ unit: 'megaseconds' },
|
||||
{ unit: 'megaseconds' },
|
||||
] as StackdriverQuery[]);
|
||||
});
|
||||
it('should return the default value of undefined', () => {
|
||||
expect(res).toBeUndefined();
|
||||
@ -272,7 +288,7 @@ describe('StackdriverDataSource', () => {
|
||||
});
|
||||
describe('and all target units are not the same', () => {
|
||||
beforeEach(() => {
|
||||
res = ds.resolvePanelUnitFromTargets([{ unit: 'bit' }, { unit: 'min' }]);
|
||||
res = ds.resolvePanelUnitFromTargets([{ unit: 'bit' }, { unit: 'min' }] as StackdriverQuery[]);
|
||||
});
|
||||
it('should return the default value of undefined', () => {
|
||||
expect(res).toBeUndefined();
|
||||
|
@ -1,7 +1,19 @@
|
||||
import { DataQuery, DataSourceJsonData } from '@grafana/data';
|
||||
|
||||
export enum AuthType {
|
||||
JWT = 'jwt',
|
||||
GCE = 'gce',
|
||||
}
|
||||
|
||||
export const authTypes = [
|
||||
{ value: 'Google JWT File', key: AuthType.JWT },
|
||||
{ value: 'GCE Default Service Account', key: AuthType.GCE },
|
||||
];
|
||||
|
||||
export enum MetricFindQueryTypes {
|
||||
Projects = 'projects',
|
||||
Services = 'services',
|
||||
DefaultProject = 'defaultProject',
|
||||
MetricTypes = 'metricTypes',
|
||||
LabelKeys = 'labelKeys',
|
||||
LabelValues = 'labelValues',
|
||||
@ -13,17 +25,19 @@ export enum MetricFindQueryTypes {
|
||||
|
||||
export interface VariableQueryData {
|
||||
selectedQueryType: string;
|
||||
metricDescriptors: any[];
|
||||
metricDescriptors: MetricDescriptor[];
|
||||
selectedService: string;
|
||||
selectedMetricType: string;
|
||||
labels: string[];
|
||||
labelKey: string;
|
||||
metricTypes: Array<{ value: string; name: string }>;
|
||||
services: Array<{ value: string; name: string }>;
|
||||
projects: Array<{ value: string; name: string }>;
|
||||
projectName: string;
|
||||
}
|
||||
|
||||
export interface StackdriverQuery extends DataQuery {
|
||||
defaultProject?: string;
|
||||
projectName: string;
|
||||
unit?: string;
|
||||
metricType: string;
|
||||
service?: string;
|
||||
@ -42,11 +56,12 @@ export interface StackdriverQuery extends DataQuery {
|
||||
|
||||
export interface StackdriverOptions extends DataSourceJsonData {
|
||||
defaultProject?: string;
|
||||
gceDefaultProject?: string;
|
||||
authenticationType?: string;
|
||||
}
|
||||
|
||||
export interface AnnotationTarget {
|
||||
defaultProject: string;
|
||||
projectName: string;
|
||||
metricType: string;
|
||||
refId: string;
|
||||
filters: string[];
|
||||
|
15
vendor/cloud.google.com/go/AUTHORS
generated
vendored
15
vendor/cloud.google.com/go/AUTHORS
generated
vendored
@ -1,15 +0,0 @@
|
||||
# This is the official list of cloud authors for copyright purposes.
|
||||
# This file is distinct from the CONTRIBUTORS files.
|
||||
# See the latter for an explanation.
|
||||
|
||||
# Names should be added to this file as:
|
||||
# Name or Organization <email address>
|
||||
# The email address is not required for organizations.
|
||||
|
||||
Filippo Valsorda <hi@filippo.io>
|
||||
Google Inc.
|
||||
Ingo Oeser <nightlyone@googlemail.com>
|
||||
Palm Stone Games, Inc.
|
||||
Paweł Knap <pawelknap88@gmail.com>
|
||||
Péter Szilágyi <peterke@gmail.com>
|
||||
Tyler Treat <ttreat31@gmail.com>
|
40
vendor/cloud.google.com/go/CONTRIBUTORS
generated
vendored
40
vendor/cloud.google.com/go/CONTRIBUTORS
generated
vendored
@ -1,40 +0,0 @@
|
||||
# People who have agreed to one of the CLAs and can contribute patches.
|
||||
# The AUTHORS file lists the copyright holders; this file
|
||||
# lists people. For example, Google employees are listed here
|
||||
# but not in AUTHORS, because Google holds the copyright.
|
||||
#
|
||||
# https://developers.google.com/open-source/cla/individual
|
||||
# https://developers.google.com/open-source/cla/corporate
|
||||
#
|
||||
# Names should be added to this file as:
|
||||
# Name <email address>
|
||||
|
||||
# Keep the list alphabetically sorted.
|
||||
|
||||
Alexis Hunt <lexer@google.com>
|
||||
Andreas Litt <andreas.litt@gmail.com>
|
||||
Andrew Gerrand <adg@golang.org>
|
||||
Brad Fitzpatrick <bradfitz@golang.org>
|
||||
Burcu Dogan <jbd@google.com>
|
||||
Dave Day <djd@golang.org>
|
||||
David Sansome <me@davidsansome.com>
|
||||
David Symonds <dsymonds@golang.org>
|
||||
Filippo Valsorda <hi@filippo.io>
|
||||
Glenn Lewis <gmlewis@google.com>
|
||||
Ingo Oeser <nightlyone@googlemail.com>
|
||||
James Hall <james.hall@shopify.com>
|
||||
Johan Euphrosine <proppy@google.com>
|
||||
Jonathan Amsterdam <jba@google.com>
|
||||
Kunpei Sakai <namusyaka@gmail.com>
|
||||
Luna Duclos <luna.duclos@palmstonegames.com>
|
||||
Magnus Hiie <magnus.hiie@gmail.com>
|
||||
Mario Castro <mariocaster@gmail.com>
|
||||
Michael McGreevy <mcgreevy@golang.org>
|
||||
Omar Jarjur <ojarjur@google.com>
|
||||
Paweł Knap <pawelknap88@gmail.com>
|
||||
Péter Szilágyi <peterke@gmail.com>
|
||||
Sarah Adams <shadams@google.com>
|
||||
Thanatat Tamtan <acoshift@gmail.com>
|
||||
Toby Burress <kurin@google.com>
|
||||
Tuo Shan <shantuo@google.com>
|
||||
Tyler Treat <ttreat31@gmail.com>
|
24
vendor/cloud.google.com/go/compute/metadata/metadata.go
generated
vendored
24
vendor/cloud.google.com/go/compute/metadata/metadata.go
generated
vendored
@ -137,7 +137,7 @@ func testOnGCE() bool {
|
||||
resc := make(chan bool, 2)
|
||||
|
||||
// Try two strategies in parallel.
|
||||
// See https://github.com/GoogleCloudPlatform/google-cloud-go/issues/194
|
||||
// See https://github.com/googleapis/google-cloud-go/issues/194
|
||||
go func() {
|
||||
req, _ := http.NewRequest("GET", "http://"+metadataIP, nil)
|
||||
req.Header.Set("User-Agent", userAgent)
|
||||
@ -300,8 +300,8 @@ func (c *Client) getETag(suffix string) (value, etag string, err error) {
|
||||
// being stable anyway.
|
||||
host = metadataIP
|
||||
}
|
||||
url := "http://" + host + "/computeMetadata/v1/" + suffix
|
||||
req, _ := http.NewRequest("GET", url, nil)
|
||||
u := "http://" + host + "/computeMetadata/v1/" + suffix
|
||||
req, _ := http.NewRequest("GET", u, nil)
|
||||
req.Header.Set("Metadata-Flavor", "Google")
|
||||
req.Header.Set("User-Agent", userAgent)
|
||||
res, err := c.hc.Do(req)
|
||||
@ -312,13 +312,13 @@ func (c *Client) getETag(suffix string) (value, etag string, err error) {
|
||||
if res.StatusCode == http.StatusNotFound {
|
||||
return "", "", NotDefinedError(suffix)
|
||||
}
|
||||
if res.StatusCode != 200 {
|
||||
return "", "", fmt.Errorf("status code %d trying to fetch %s", res.StatusCode, url)
|
||||
}
|
||||
all, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
if res.StatusCode != 200 {
|
||||
return "", "", &Error{Code: res.StatusCode, Message: string(all)}
|
||||
}
|
||||
return string(all), res.Header.Get("Etag"), nil
|
||||
}
|
||||
|
||||
@ -499,3 +499,15 @@ func (c *Client) Subscribe(suffix string, fn func(v string, ok bool) error) erro
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Error contains an error response from the server.
|
||||
type Error struct {
|
||||
// Code is the HTTP response status code.
|
||||
Code int
|
||||
// Message is the server response message.
|
||||
Message string
|
||||
}
|
||||
|
||||
func (e *Error) Error() string {
|
||||
return fmt.Sprintf("compute: Received %d `%s`", e.Code, e.Message)
|
||||
}
|
||||
|
27
vendor/github.com/googleapis/gax-go/v2/LICENSE
generated
vendored
Normal file
27
vendor/github.com/googleapis/gax-go/v2/LICENSE
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
Copyright 2016, Google Inc.
|
||||
All rights reserved.
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
161
vendor/github.com/googleapis/gax-go/v2/call_option.go
generated
vendored
Normal file
161
vendor/github.com/googleapis/gax-go/v2/call_option.go
generated
vendored
Normal file
@ -0,0 +1,161 @@
|
||||
// Copyright 2016, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package gax
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// CallOption is an option used by Invoke to control behaviors of RPC calls.
|
||||
// CallOption works by modifying relevant fields of CallSettings.
|
||||
type CallOption interface {
|
||||
// Resolve applies the option by modifying cs.
|
||||
Resolve(cs *CallSettings)
|
||||
}
|
||||
|
||||
// Retryer is used by Invoke to determine retry behavior.
|
||||
type Retryer interface {
|
||||
// Retry reports whether a request should be retriedand how long to pause before retrying
|
||||
// if the previous attempt returned with err. Invoke never calls Retry with nil error.
|
||||
Retry(err error) (pause time.Duration, shouldRetry bool)
|
||||
}
|
||||
|
||||
type retryerOption func() Retryer
|
||||
|
||||
func (o retryerOption) Resolve(s *CallSettings) {
|
||||
s.Retry = o
|
||||
}
|
||||
|
||||
// WithRetry sets CallSettings.Retry to fn.
|
||||
func WithRetry(fn func() Retryer) CallOption {
|
||||
return retryerOption(fn)
|
||||
}
|
||||
|
||||
// OnCodes returns a Retryer that retries if and only if
|
||||
// the previous attempt returns a GRPC error whose error code is stored in cc.
|
||||
// Pause times between retries are specified by bo.
|
||||
//
|
||||
// bo is only used for its parameters; each Retryer has its own copy.
|
||||
func OnCodes(cc []codes.Code, bo Backoff) Retryer {
|
||||
return &boRetryer{
|
||||
backoff: bo,
|
||||
codes: append([]codes.Code(nil), cc...),
|
||||
}
|
||||
}
|
||||
|
||||
type boRetryer struct {
|
||||
backoff Backoff
|
||||
codes []codes.Code
|
||||
}
|
||||
|
||||
func (r *boRetryer) Retry(err error) (time.Duration, bool) {
|
||||
st, ok := status.FromError(err)
|
||||
if !ok {
|
||||
return 0, false
|
||||
}
|
||||
c := st.Code()
|
||||
for _, rc := range r.codes {
|
||||
if c == rc {
|
||||
return r.backoff.Pause(), true
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// Backoff implements exponential backoff.
|
||||
// The wait time between retries is a random value between 0 and the "retry envelope".
|
||||
// The envelope starts at Initial and increases by the factor of Multiplier every retry,
|
||||
// but is capped at Max.
|
||||
type Backoff struct {
|
||||
// Initial is the initial value of the retry envelope, defaults to 1 second.
|
||||
Initial time.Duration
|
||||
|
||||
// Max is the maximum value of the retry envelope, defaults to 30 seconds.
|
||||
Max time.Duration
|
||||
|
||||
// Multiplier is the factor by which the retry envelope increases.
|
||||
// It should be greater than 1 and defaults to 2.
|
||||
Multiplier float64
|
||||
|
||||
// cur is the current retry envelope
|
||||
cur time.Duration
|
||||
}
|
||||
|
||||
// Pause returns the next time.Duration that the caller should use to backoff.
|
||||
func (bo *Backoff) Pause() time.Duration {
|
||||
if bo.Initial == 0 {
|
||||
bo.Initial = time.Second
|
||||
}
|
||||
if bo.cur == 0 {
|
||||
bo.cur = bo.Initial
|
||||
}
|
||||
if bo.Max == 0 {
|
||||
bo.Max = 30 * time.Second
|
||||
}
|
||||
if bo.Multiplier < 1 {
|
||||
bo.Multiplier = 2
|
||||
}
|
||||
// Select a duration between 1ns and the current max. It might seem
|
||||
// counterintuitive to have so much jitter, but
|
||||
// https://www.awsarchitectureblog.com/2015/03/backoff.html argues that
|
||||
// that is the best strategy.
|
||||
d := time.Duration(1 + rand.Int63n(int64(bo.cur)))
|
||||
bo.cur = time.Duration(float64(bo.cur) * bo.Multiplier)
|
||||
if bo.cur > bo.Max {
|
||||
bo.cur = bo.Max
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
type grpcOpt []grpc.CallOption
|
||||
|
||||
func (o grpcOpt) Resolve(s *CallSettings) {
|
||||
s.GRPC = o
|
||||
}
|
||||
|
||||
// WithGRPCOptions allows passing gRPC call options during client creation.
|
||||
func WithGRPCOptions(opt ...grpc.CallOption) CallOption {
|
||||
return grpcOpt(append([]grpc.CallOption(nil), opt...))
|
||||
}
|
||||
|
||||
// CallSettings allow fine-grained control over how calls are made.
|
||||
type CallSettings struct {
|
||||
// Retry returns a Retryer to be used to control retry logic of a method call.
|
||||
// If Retry is nil or the returned Retryer is nil, the call will not be retried.
|
||||
Retry func() Retryer
|
||||
|
||||
// CallOptions to be forwarded to GRPC.
|
||||
GRPC []grpc.CallOption
|
||||
}
|
39
vendor/github.com/googleapis/gax-go/v2/gax.go
generated
vendored
Normal file
39
vendor/github.com/googleapis/gax-go/v2/gax.go
generated
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
// Copyright 2016, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Package gax contains a set of modules which aid the development of APIs
|
||||
// for clients and servers based on gRPC and Google API conventions.
|
||||
//
|
||||
// Application code will rarely need to use this library directly.
|
||||
// However, code generated automatically from API definition files can use it
|
||||
// to simplify code generation and to provide more convenient and idiomatic API surfaces.
|
||||
package gax
|
||||
|
||||
// Version specifies the gax-go version being used.
|
||||
const Version = "2.0.4"
|
3
vendor/github.com/googleapis/gax-go/v2/go.mod
generated
vendored
Normal file
3
vendor/github.com/googleapis/gax-go/v2/go.mod
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
module github.com/googleapis/gax-go/v2
|
||||
|
||||
require google.golang.org/grpc v1.19.0
|
25
vendor/github.com/googleapis/gax-go/v2/go.sum
generated
vendored
Normal file
25
vendor/github.com/googleapis/gax-go/v2/go.sum
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||
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 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d h1:g9qWBGx4puODJTMVyoPrpoxPFgVGd+z1DZwjfRu4d0I=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522 h1:Ve1ORMCxvRmSXBwJK+t3Oy+V2vRW2OetUQBq4rJIkZE=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/grpc v1.19.0 h1:cfg4PD8YEdSFnm7qLV4++93WcmhH2nIUhMjhdCvl3j8=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
53
vendor/github.com/googleapis/gax-go/v2/header.go
generated
vendored
Normal file
53
vendor/github.com/googleapis/gax-go/v2/header.go
generated
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
// Copyright 2018, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package gax
|
||||
|
||||
import "bytes"
|
||||
|
||||
// XGoogHeader is for use by the Google Cloud Libraries only.
|
||||
//
|
||||
// XGoogHeader formats key-value pairs.
|
||||
// The resulting string is suitable for x-goog-api-client header.
|
||||
func XGoogHeader(keyval ...string) string {
|
||||
if len(keyval) == 0 {
|
||||
return ""
|
||||
}
|
||||
if len(keyval)%2 != 0 {
|
||||
panic("gax.Header: odd argument count")
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
for i := 0; i < len(keyval); i += 2 {
|
||||
buf.WriteByte(' ')
|
||||
buf.WriteString(keyval[i])
|
||||
buf.WriteByte('/')
|
||||
buf.WriteString(keyval[i+1])
|
||||
}
|
||||
return buf.String()[1:]
|
||||
}
|
99
vendor/github.com/googleapis/gax-go/v2/invoke.go
generated
vendored
Normal file
99
vendor/github.com/googleapis/gax-go/v2/invoke.go
generated
vendored
Normal file
@ -0,0 +1,99 @@
|
||||
// Copyright 2016, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package gax
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// APICall is a user defined call stub.
|
||||
type APICall func(context.Context, CallSettings) error
|
||||
|
||||
// Invoke calls the given APICall,
|
||||
// performing retries as specified by opts, if any.
|
||||
func Invoke(ctx context.Context, call APICall, opts ...CallOption) error {
|
||||
var settings CallSettings
|
||||
for _, opt := range opts {
|
||||
opt.Resolve(&settings)
|
||||
}
|
||||
return invoke(ctx, call, settings, Sleep)
|
||||
}
|
||||
|
||||
// Sleep is similar to time.Sleep, but it can be interrupted by ctx.Done() closing.
|
||||
// If interrupted, Sleep returns ctx.Err().
|
||||
func Sleep(ctx context.Context, d time.Duration) error {
|
||||
t := time.NewTimer(d)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
t.Stop()
|
||||
return ctx.Err()
|
||||
case <-t.C:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
type sleeper func(ctx context.Context, d time.Duration) error
|
||||
|
||||
// invoke implements Invoke, taking an additional sleeper argument for testing.
|
||||
func invoke(ctx context.Context, call APICall, settings CallSettings, sp sleeper) error {
|
||||
var retryer Retryer
|
||||
for {
|
||||
err := call(ctx, settings)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
if settings.Retry == nil {
|
||||
return err
|
||||
}
|
||||
// Never retry permanent certificate errors. (e.x. if ca-certificates
|
||||
// are not installed). We should only make very few, targeted
|
||||
// exceptions: many (other) status=Unavailable should be retried, such
|
||||
// as if there's a network hiccup, or the internet goes out for a
|
||||
// minute. This is also why here we are doing string parsing instead of
|
||||
// simply making Unavailable a non-retried code elsewhere.
|
||||
if strings.Contains(err.Error(), "x509: certificate signed by unknown authority") {
|
||||
return err
|
||||
}
|
||||
if retryer == nil {
|
||||
if r := settings.Retry(); r != nil {
|
||||
retryer = r
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if d, ok := retryer.Retry(err); !ok {
|
||||
return err
|
||||
} else if err = sp(ctx, d); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
362
vendor/github.com/hashicorp/golang-lru/LICENSE
generated
vendored
Normal file
362
vendor/github.com/hashicorp/golang-lru/LICENSE
generated
vendored
Normal file
@ -0,0 +1,362 @@
|
||||
Mozilla Public License, version 2.0
|
||||
|
||||
1. Definitions
|
||||
|
||||
1.1. "Contributor"
|
||||
|
||||
means each individual or legal entity that creates, contributes to the
|
||||
creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
|
||||
means the combination of the Contributions of others (if any) used by a
|
||||
Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
|
||||
means Source Code Form to which the initial Contributor has attached the
|
||||
notice in Exhibit A, the Executable Form of such Source Code Form, and
|
||||
Modifications of such Source Code Form, in each case including portions
|
||||
thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
a. that the initial Contributor has attached the notice described in
|
||||
Exhibit B to the Covered Software; or
|
||||
|
||||
b. that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the terms of
|
||||
a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
|
||||
means a work that combines Covered Software with other material, in a
|
||||
separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
|
||||
means having the right to grant, to the maximum extent possible, whether
|
||||
at the time of the initial grant or subsequently, any and all of the
|
||||
rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
|
||||
means any of the following:
|
||||
|
||||
a. any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered Software; or
|
||||
|
||||
b. any new file in Source Code Form that contains any Covered Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the License,
|
||||
by the making, using, selling, offering for sale, having made, import,
|
||||
or transfer of either its Contributions or its Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
|
||||
means either the GNU General Public License, Version 2.0, the GNU Lesser
|
||||
General Public License, Version 2.1, the GNU Affero General Public
|
||||
License, Version 3.0, or any later versions of those licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that controls, is
|
||||
controlled by, or is under common control with You. For purposes of this
|
||||
definition, "control" means (a) the power, direct or indirect, to cause
|
||||
the direction or management of such entity, whether by contract or
|
||||
otherwise, or (b) ownership of more than fifty percent (50%) of the
|
||||
outstanding shares or beneficial ownership of such entity.
|
||||
|
||||
|
||||
2. License Grants and Conditions
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
a. under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
b. under Patent Claims of such Contributor to make, use, sell, offer for
|
||||
sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
a. for any code that a Contributor has removed from Covered Software; or
|
||||
|
||||
b. for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
c. under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights to
|
||||
grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
|
||||
Section 2.1.
|
||||
|
||||
|
||||
3. Responsibilities
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
a. such Covered Software must also be made available in Source Code Form,
|
||||
as described in Section 3.1, and You must inform recipients of the
|
||||
Executable Form how they can obtain a copy of such Source Code Form by
|
||||
reasonable means in a timely manner, at a charge no more than the cost
|
||||
of distribution to the recipient; and
|
||||
|
||||
b. You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter the
|
||||
recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty, or
|
||||
limitations of liability) contained within the Source Code Form of the
|
||||
Covered Software, except that You may alter any license notices to the
|
||||
extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this License
|
||||
with respect to some or all of the Covered Software due to statute,
|
||||
judicial order, or regulation then You must: (a) comply with the terms of
|
||||
this License to the maximum extent possible; and (b) describe the
|
||||
limitations and the code they affect. Such description must be placed in a
|
||||
text file included with all distributions of the Covered Software under
|
||||
this License. Except to the extent prohibited by statute or regulation,
|
||||
such description must be sufficiently detailed for a recipient of ordinary
|
||||
skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically if You
|
||||
fail to comply with any of its terms. However, if You become compliant,
|
||||
then the rights granted under this License from a particular Contributor
|
||||
are reinstated (a) provisionally, unless and until such Contributor
|
||||
explicitly and finally terminates Your grants, and (b) on an ongoing
|
||||
basis, if such Contributor fails to notify You of the non-compliance by
|
||||
some reasonable means prior to 60 days after You have come back into
|
||||
compliance. Moreover, Your grants from a particular Contributor are
|
||||
reinstated on an ongoing basis if such Contributor notifies You of the
|
||||
non-compliance by some reasonable means, this is the first time You have
|
||||
received notice of non-compliance with this License from such
|
||||
Contributor, and You become compliant prior to 30 days after Your receipt
|
||||
of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
|
||||
license agreements (excluding distributors and resellers) which have been
|
||||
validly granted by You or Your distributors under this License prior to
|
||||
termination shall survive termination.
|
||||
|
||||
6. Disclaimer of Warranty
|
||||
|
||||
Covered Software is provided under this License on an "as is" basis,
|
||||
without warranty of any kind, either expressed, implied, or statutory,
|
||||
including, without limitation, warranties that the Covered Software is free
|
||||
of defects, merchantable, fit for a particular purpose or non-infringing.
|
||||
The entire risk as to the quality and performance of the Covered Software
|
||||
is with You. Should any Covered Software prove defective in any respect,
|
||||
You (not any Contributor) assume the cost of any necessary servicing,
|
||||
repair, or correction. This disclaimer of warranty constitutes an essential
|
||||
part of this License. No use of any Covered Software is authorized under
|
||||
this License except under this disclaimer.
|
||||
|
||||
7. Limitation of Liability
|
||||
|
||||
Under no circumstances and under no legal theory, whether tort (including
|
||||
negligence), contract, or otherwise, shall any Contributor, or anyone who
|
||||
distributes Covered Software as permitted above, be liable to You for any
|
||||
direct, indirect, special, incidental, or consequential damages of any
|
||||
character including, without limitation, damages for lost profits, loss of
|
||||
goodwill, work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses, even if such party shall have been
|
||||
informed of the possibility of such damages. This limitation of liability
|
||||
shall not apply to liability for death or personal injury resulting from
|
||||
such party's negligence to the extent applicable law prohibits such
|
||||
limitation. Some jurisdictions do not allow the exclusion or limitation of
|
||||
incidental or consequential damages, so this exclusion and limitation may
|
||||
not apply to You.
|
||||
|
||||
8. Litigation
|
||||
|
||||
Any litigation relating to this License may be brought only in the courts
|
||||
of a jurisdiction where the defendant maintains its principal place of
|
||||
business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions. Nothing
|
||||
in this Section shall prevent a party's ability to bring cross-claims or
|
||||
counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides that
|
||||
the language of a contract shall be construed against the drafter shall not
|
||||
be used to construe this License against a Contributor.
|
||||
|
||||
|
||||
10. Versions of the License
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses If You choose to distribute Source Code Form that is
|
||||
Incompatible With Secondary Licenses under the terms of this version of
|
||||
the License, the notice described in Exhibit B of this License must be
|
||||
attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular file,
|
||||
then You may include the notice in a location (such as a LICENSE file in a
|
||||
relevant directory) where a recipient would be likely to look for such a
|
||||
notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
|
||||
This Source Code Form is "Incompatible
|
||||
With Secondary Licenses", as defined by
|
||||
the Mozilla Public License, v. 2.0.
|
161
vendor/github.com/hashicorp/golang-lru/simplelru/lru.go
generated
vendored
Normal file
161
vendor/github.com/hashicorp/golang-lru/simplelru/lru.go
generated
vendored
Normal file
@ -0,0 +1,161 @@
|
||||
package simplelru
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"errors"
|
||||
)
|
||||
|
||||
// EvictCallback is used to get a callback when a cache entry is evicted
|
||||
type EvictCallback func(key interface{}, value interface{})
|
||||
|
||||
// LRU implements a non-thread safe fixed size LRU cache
|
||||
type LRU struct {
|
||||
size int
|
||||
evictList *list.List
|
||||
items map[interface{}]*list.Element
|
||||
onEvict EvictCallback
|
||||
}
|
||||
|
||||
// entry is used to hold a value in the evictList
|
||||
type entry struct {
|
||||
key interface{}
|
||||
value interface{}
|
||||
}
|
||||
|
||||
// NewLRU constructs an LRU of the given size
|
||||
func NewLRU(size int, onEvict EvictCallback) (*LRU, error) {
|
||||
if size <= 0 {
|
||||
return nil, errors.New("Must provide a positive size")
|
||||
}
|
||||
c := &LRU{
|
||||
size: size,
|
||||
evictList: list.New(),
|
||||
items: make(map[interface{}]*list.Element),
|
||||
onEvict: onEvict,
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// Purge is used to completely clear the cache.
|
||||
func (c *LRU) Purge() {
|
||||
for k, v := range c.items {
|
||||
if c.onEvict != nil {
|
||||
c.onEvict(k, v.Value.(*entry).value)
|
||||
}
|
||||
delete(c.items, k)
|
||||
}
|
||||
c.evictList.Init()
|
||||
}
|
||||
|
||||
// Add adds a value to the cache. Returns true if an eviction occurred.
|
||||
func (c *LRU) Add(key, value interface{}) (evicted bool) {
|
||||
// Check for existing item
|
||||
if ent, ok := c.items[key]; ok {
|
||||
c.evictList.MoveToFront(ent)
|
||||
ent.Value.(*entry).value = value
|
||||
return false
|
||||
}
|
||||
|
||||
// Add new item
|
||||
ent := &entry{key, value}
|
||||
entry := c.evictList.PushFront(ent)
|
||||
c.items[key] = entry
|
||||
|
||||
evict := c.evictList.Len() > c.size
|
||||
// Verify size not exceeded
|
||||
if evict {
|
||||
c.removeOldest()
|
||||
}
|
||||
return evict
|
||||
}
|
||||
|
||||
// Get looks up a key's value from the cache.
|
||||
func (c *LRU) Get(key interface{}) (value interface{}, ok bool) {
|
||||
if ent, ok := c.items[key]; ok {
|
||||
c.evictList.MoveToFront(ent)
|
||||
return ent.Value.(*entry).value, true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Contains checks if a key is in the cache, without updating the recent-ness
|
||||
// or deleting it for being stale.
|
||||
func (c *LRU) Contains(key interface{}) (ok bool) {
|
||||
_, ok = c.items[key]
|
||||
return ok
|
||||
}
|
||||
|
||||
// Peek returns the key value (or undefined if not found) without updating
|
||||
// the "recently used"-ness of the key.
|
||||
func (c *LRU) Peek(key interface{}) (value interface{}, ok bool) {
|
||||
var ent *list.Element
|
||||
if ent, ok = c.items[key]; ok {
|
||||
return ent.Value.(*entry).value, true
|
||||
}
|
||||
return nil, ok
|
||||
}
|
||||
|
||||
// Remove removes the provided key from the cache, returning if the
|
||||
// key was contained.
|
||||
func (c *LRU) Remove(key interface{}) (present bool) {
|
||||
if ent, ok := c.items[key]; ok {
|
||||
c.removeElement(ent)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// RemoveOldest removes the oldest item from the cache.
|
||||
func (c *LRU) RemoveOldest() (key interface{}, value interface{}, ok bool) {
|
||||
ent := c.evictList.Back()
|
||||
if ent != nil {
|
||||
c.removeElement(ent)
|
||||
kv := ent.Value.(*entry)
|
||||
return kv.key, kv.value, true
|
||||
}
|
||||
return nil, nil, false
|
||||
}
|
||||
|
||||
// GetOldest returns the oldest entry
|
||||
func (c *LRU) GetOldest() (key interface{}, value interface{}, ok bool) {
|
||||
ent := c.evictList.Back()
|
||||
if ent != nil {
|
||||
kv := ent.Value.(*entry)
|
||||
return kv.key, kv.value, true
|
||||
}
|
||||
return nil, nil, false
|
||||
}
|
||||
|
||||
// Keys returns a slice of the keys in the cache, from oldest to newest.
|
||||
func (c *LRU) Keys() []interface{} {
|
||||
keys := make([]interface{}, len(c.items))
|
||||
i := 0
|
||||
for ent := c.evictList.Back(); ent != nil; ent = ent.Prev() {
|
||||
keys[i] = ent.Value.(*entry).key
|
||||
i++
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
// Len returns the number of items in the cache.
|
||||
func (c *LRU) Len() int {
|
||||
return c.evictList.Len()
|
||||
}
|
||||
|
||||
// removeOldest removes the oldest item from the cache.
|
||||
func (c *LRU) removeOldest() {
|
||||
ent := c.evictList.Back()
|
||||
if ent != nil {
|
||||
c.removeElement(ent)
|
||||
}
|
||||
}
|
||||
|
||||
// removeElement is used to remove a given list element from the cache
|
||||
func (c *LRU) removeElement(e *list.Element) {
|
||||
c.evictList.Remove(e)
|
||||
kv := e.Value.(*entry)
|
||||
delete(c.items, kv.key)
|
||||
if c.onEvict != nil {
|
||||
c.onEvict(kv.key, kv.value)
|
||||
}
|
||||
}
|
36
vendor/github.com/hashicorp/golang-lru/simplelru/lru_interface.go
generated
vendored
Normal file
36
vendor/github.com/hashicorp/golang-lru/simplelru/lru_interface.go
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
package simplelru
|
||||
|
||||
// LRUCache is the interface for simple LRU cache.
|
||||
type LRUCache interface {
|
||||
// Adds a value to the cache, returns true if an eviction occurred and
|
||||
// updates the "recently used"-ness of the key.
|
||||
Add(key, value interface{}) bool
|
||||
|
||||
// Returns key's value from the cache and
|
||||
// updates the "recently used"-ness of the key. #value, isFound
|
||||
Get(key interface{}) (value interface{}, ok bool)
|
||||
|
||||
// Check if a key exsists in cache without updating the recent-ness.
|
||||
Contains(key interface{}) (ok bool)
|
||||
|
||||
// Returns key's value without updating the "recently used"-ness of the key.
|
||||
Peek(key interface{}) (value interface{}, ok bool)
|
||||
|
||||
// Removes a key from the cache.
|
||||
Remove(key interface{}) bool
|
||||
|
||||
// Removes the oldest entry from cache.
|
||||
RemoveOldest() (interface{}, interface{}, bool)
|
||||
|
||||
// Returns the oldest entry from the cache. #key, value, isFound
|
||||
GetOldest() (interface{}, interface{}, bool)
|
||||
|
||||
// Returns a slice of the keys in the cache, from oldest to newest.
|
||||
Keys() []interface{}
|
||||
|
||||
// Returns the number of items in the cache.
|
||||
Len() int
|
||||
|
||||
// Clear all cache entries
|
||||
Purge()
|
||||
}
|
9
vendor/go.opencensus.io/.gitignore
generated
vendored
Normal file
9
vendor/go.opencensus.io/.gitignore
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
/.idea/
|
||||
|
||||
# go.opencensus.io/exporter/aws
|
||||
/exporter/aws/
|
||||
|
||||
# Exclude vendor, use dep ensure after checkout:
|
||||
/vendor/github.com/
|
||||
/vendor/golang.org/
|
||||
/vendor/google.golang.org/
|
1
vendor/go.opencensus.io/AUTHORS
generated
vendored
Normal file
1
vendor/go.opencensus.io/AUTHORS
generated
vendored
Normal file
@ -0,0 +1 @@
|
||||
Google Inc.
|
63
vendor/go.opencensus.io/CONTRIBUTING.md
generated
vendored
Normal file
63
vendor/go.opencensus.io/CONTRIBUTING.md
generated
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
# How to contribute
|
||||
|
||||
We'd love to accept your patches and contributions to this project. There are
|
||||
just a few small guidelines you need to follow.
|
||||
|
||||
## Contributor License Agreement
|
||||
|
||||
Contributions to this project must be accompanied by a Contributor License
|
||||
Agreement. You (or your employer) retain the copyright to your contribution,
|
||||
this simply gives us permission to use and redistribute your contributions as
|
||||
part of the project. Head over to <https://cla.developers.google.com/> to see
|
||||
your current agreements on file or to sign a new one.
|
||||
|
||||
You generally only need to submit a CLA once, so if you've already submitted one
|
||||
(even if it was for a different project), you probably don't need to do it
|
||||
again.
|
||||
|
||||
## Code reviews
|
||||
|
||||
All submissions, including submissions by project members, require review. We
|
||||
use GitHub pull requests for this purpose. Consult [GitHub Help] for more
|
||||
information on using pull requests.
|
||||
|
||||
[GitHub Help]: https://help.github.com/articles/about-pull-requests/
|
||||
|
||||
## Instructions
|
||||
|
||||
Fork the repo, checkout the upstream repo to your GOPATH by:
|
||||
|
||||
```
|
||||
$ go get -d go.opencensus.io
|
||||
```
|
||||
|
||||
Add your fork as an origin:
|
||||
|
||||
```
|
||||
cd $(go env GOPATH)/src/go.opencensus.io
|
||||
git remote add fork git@github.com:YOUR_GITHUB_USERNAME/opencensus-go.git
|
||||
```
|
||||
|
||||
Run tests:
|
||||
|
||||
```
|
||||
$ make install-tools # Only first time.
|
||||
$ make
|
||||
```
|
||||
|
||||
Checkout a new branch, make modifications and push the branch to your fork:
|
||||
|
||||
```
|
||||
$ git checkout -b feature
|
||||
# edit files
|
||||
$ git commit
|
||||
$ git push fork feature
|
||||
```
|
||||
|
||||
Open a pull request against the main opencensus-go repo.
|
||||
|
||||
## General Notes
|
||||
This project uses Appveyor and Travis for CI.
|
||||
|
||||
The dependencies are managed with `go mod` if you work with the sources under your
|
||||
`$GOPATH` you need to set the environment variable `GO111MODULE=on`.
|
231
vendor/go.opencensus.io/Gopkg.lock
generated
vendored
Normal file
231
vendor/go.opencensus.io/Gopkg.lock
generated
vendored
Normal file
@ -0,0 +1,231 @@
|
||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:eee9386329f4fcdf8d6c0def0c9771b634bdd5ba460d888aa98c17d59b37a76c"
|
||||
name = "git.apache.org/thrift.git"
|
||||
packages = ["lib/go/thrift"]
|
||||
pruneopts = "UT"
|
||||
revision = "6e67faa92827ece022380b211c2caaadd6145bf5"
|
||||
source = "github.com/apache/thrift"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:d6afaeed1502aa28e80a4ed0981d570ad91b2579193404256ce672ed0a609e0d"
|
||||
name = "github.com/beorn7/perks"
|
||||
packages = ["quantile"]
|
||||
pruneopts = "UT"
|
||||
revision = "3a771d992973f24aa725d07868b467d1ddfceafb"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:4c0989ca0bcd10799064318923b9bc2db6b4d6338dd75f3f2d86c3511aaaf5cf"
|
||||
name = "github.com/golang/protobuf"
|
||||
packages = [
|
||||
"proto",
|
||||
"ptypes",
|
||||
"ptypes/any",
|
||||
"ptypes/duration",
|
||||
"ptypes/timestamp",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "aa810b61a9c79d51363740d207bb46cf8e620ed5"
|
||||
version = "v1.2.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:ff5ebae34cfbf047d505ee150de27e60570e8c394b3b8fdbb720ff6ac71985fc"
|
||||
name = "github.com/matttproud/golang_protobuf_extensions"
|
||||
packages = ["pbutil"]
|
||||
pruneopts = "UT"
|
||||
revision = "c12348ce28de40eed0136aa2b644d0ee0650e56c"
|
||||
version = "v1.0.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:824c8f3aa4c5f23928fa84ebbd5ed2e9443b3f0cb958a40c1f2fbed5cf5e64b1"
|
||||
name = "github.com/openzipkin/zipkin-go"
|
||||
packages = [
|
||||
".",
|
||||
"idgenerator",
|
||||
"model",
|
||||
"propagation",
|
||||
"reporter",
|
||||
"reporter/http",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "d455a5674050831c1e187644faa4046d653433c2"
|
||||
version = "v0.1.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:d14a5f4bfecf017cb780bdde1b6483e5deb87e12c332544d2c430eda58734bcb"
|
||||
name = "github.com/prometheus/client_golang"
|
||||
packages = [
|
||||
"prometheus",
|
||||
"prometheus/promhttp",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "c5b7fccd204277076155f10851dad72b76a49317"
|
||||
version = "v0.8.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:2d5cd61daa5565187e1d96bae64dbbc6080dacf741448e9629c64fd93203b0d4"
|
||||
name = "github.com/prometheus/client_model"
|
||||
packages = ["go"]
|
||||
pruneopts = "UT"
|
||||
revision = "5c3871d89910bfb32f5fcab2aa4b9ec68e65a99f"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:63b68062b8968092eb86bedc4e68894bd096ea6b24920faca8b9dcf451f54bb5"
|
||||
name = "github.com/prometheus/common"
|
||||
packages = [
|
||||
"expfmt",
|
||||
"internal/bitbucket.org/ww/goautoneg",
|
||||
"model",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "c7de2306084e37d54b8be01f3541a8464345e9a5"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:8c49953a1414305f2ff5465147ee576dd705487c35b15918fcd4efdc0cb7a290"
|
||||
name = "github.com/prometheus/procfs"
|
||||
packages = [
|
||||
".",
|
||||
"internal/util",
|
||||
"nfs",
|
||||
"xfs",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "05ee40e3a273f7245e8777337fc7b46e533a9a92"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:deafe4ab271911fec7de5b693d7faae3f38796d9eb8622e2b9e7df42bb3dfea9"
|
||||
name = "golang.org/x/net"
|
||||
packages = [
|
||||
"context",
|
||||
"http/httpguts",
|
||||
"http2",
|
||||
"http2/hpack",
|
||||
"idna",
|
||||
"internal/timeseries",
|
||||
"trace",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "922f4815f713f213882e8ef45e0d315b164d705c"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:e0140c0c868c6e0f01c0380865194592c011fe521d6e12d78bfd33e756fe018a"
|
||||
name = "golang.org/x/sync"
|
||||
packages = ["semaphore"]
|
||||
pruneopts = "UT"
|
||||
revision = "1d60e4601c6fd243af51cc01ddf169918a5407ca"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:a3f00ac457c955fe86a41e1495e8f4c54cb5399d609374c5cc26aa7d72e542c8"
|
||||
name = "golang.org/x/sys"
|
||||
packages = ["unix"]
|
||||
pruneopts = "UT"
|
||||
revision = "3b58ed4ad3395d483fc92d5d14123ce2c3581fec"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:a2ab62866c75542dd18d2b069fec854577a20211d7c0ea6ae746072a1dccdd18"
|
||||
name = "golang.org/x/text"
|
||||
packages = [
|
||||
"collate",
|
||||
"collate/build",
|
||||
"internal/colltab",
|
||||
"internal/gen",
|
||||
"internal/tag",
|
||||
"internal/triegen",
|
||||
"internal/ucd",
|
||||
"language",
|
||||
"secure/bidirule",
|
||||
"transform",
|
||||
"unicode/bidi",
|
||||
"unicode/cldr",
|
||||
"unicode/norm",
|
||||
"unicode/rangetable",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
|
||||
version = "v0.3.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:c0c17c94fe8bc1ab34e7f586a4a8b788c5e1f4f9f750ff23395b8b2f5a523530"
|
||||
name = "google.golang.org/api"
|
||||
packages = ["support/bundler"]
|
||||
pruneopts = "UT"
|
||||
revision = "e21acd801f91da814261b938941d193bb036441a"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:077c1c599507b3b3e9156d17d36e1e61928ee9b53a5b420f10f28ebd4a0b275c"
|
||||
name = "google.golang.org/genproto"
|
||||
packages = ["googleapis/rpc/status"]
|
||||
pruneopts = "UT"
|
||||
revision = "c66870c02cf823ceb633bcd05be3c7cda29976f4"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:3dd7996ce6bf52dec6a2f69fa43e7c4cefea1d4dfa3c8ab7a5f8a9f7434e239d"
|
||||
name = "google.golang.org/grpc"
|
||||
packages = [
|
||||
".",
|
||||
"balancer",
|
||||
"balancer/base",
|
||||
"balancer/roundrobin",
|
||||
"codes",
|
||||
"connectivity",
|
||||
"credentials",
|
||||
"encoding",
|
||||
"encoding/proto",
|
||||
"grpclog",
|
||||
"internal",
|
||||
"internal/backoff",
|
||||
"internal/channelz",
|
||||
"internal/envconfig",
|
||||
"internal/grpcrand",
|
||||
"internal/transport",
|
||||
"keepalive",
|
||||
"metadata",
|
||||
"naming",
|
||||
"peer",
|
||||
"resolver",
|
||||
"resolver/dns",
|
||||
"resolver/passthrough",
|
||||
"stats",
|
||||
"status",
|
||||
"tap",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "32fb0ac620c32ba40a4626ddf94d90d12cce3455"
|
||||
version = "v1.14.0"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
input-imports = [
|
||||
"git.apache.org/thrift.git/lib/go/thrift",
|
||||
"github.com/golang/protobuf/proto",
|
||||
"github.com/openzipkin/zipkin-go",
|
||||
"github.com/openzipkin/zipkin-go/model",
|
||||
"github.com/openzipkin/zipkin-go/reporter",
|
||||
"github.com/openzipkin/zipkin-go/reporter/http",
|
||||
"github.com/prometheus/client_golang/prometheus",
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp",
|
||||
"golang.org/x/net/context",
|
||||
"golang.org/x/net/http2",
|
||||
"google.golang.org/api/support/bundler",
|
||||
"google.golang.org/grpc",
|
||||
"google.golang.org/grpc/codes",
|
||||
"google.golang.org/grpc/grpclog",
|
||||
"google.golang.org/grpc/metadata",
|
||||
"google.golang.org/grpc/stats",
|
||||
"google.golang.org/grpc/status",
|
||||
]
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
36
vendor/go.opencensus.io/Gopkg.toml
generated
vendored
Normal file
36
vendor/go.opencensus.io/Gopkg.toml
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
# For v0.x.y dependencies, prefer adding a constraints of the form: version=">= 0.x.y"
|
||||
# to avoid locking to a particular minor version which can cause dep to not be
|
||||
# able to find a satisfying dependency graph.
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "git.apache.org/thrift.git"
|
||||
source = "github.com/apache/thrift"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/golang/protobuf"
|
||||
version = "1.0.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/openzipkin/zipkin-go"
|
||||
version = ">=0.1.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/prometheus/client_golang"
|
||||
version = ">=0.8.0"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/net"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "google.golang.org/api"
|
||||
|
||||
[[constraint]]
|
||||
name = "google.golang.org/grpc"
|
||||
version = "1.11.3"
|
||||
|
||||
[prune]
|
||||
go-tests = true
|
||||
unused-packages = true
|
202
vendor/go.opencensus.io/LICENSE
generated
vendored
Normal file
202
vendor/go.opencensus.io/LICENSE
generated
vendored
Normal file
@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
96
vendor/go.opencensus.io/Makefile
generated
vendored
Normal file
96
vendor/go.opencensus.io/Makefile
generated
vendored
Normal file
@ -0,0 +1,96 @@
|
||||
# TODO: Fix this on windows.
|
||||
ALL_SRC := $(shell find . -name '*.go' \
|
||||
-not -path './vendor/*' \
|
||||
-not -path '*/gen-go/*' \
|
||||
-type f | sort)
|
||||
ALL_PKGS := $(shell go list $(sort $(dir $(ALL_SRC))))
|
||||
|
||||
GOTEST_OPT?=-v -race -timeout 30s
|
||||
GOTEST_OPT_WITH_COVERAGE = $(GOTEST_OPT) -coverprofile=coverage.txt -covermode=atomic
|
||||
GOTEST=go test
|
||||
GOFMT=gofmt
|
||||
GOLINT=golint
|
||||
GOVET=go vet
|
||||
EMBEDMD=embedmd
|
||||
# TODO decide if we need to change these names.
|
||||
TRACE_ID_LINT_EXCEPTION="type name will be used as trace.TraceID by other packages"
|
||||
TRACE_OPTION_LINT_EXCEPTION="type name will be used as trace.TraceOptions by other packages"
|
||||
README_FILES := $(shell find . -name '*README.md' | sort | tr '\n' ' ')
|
||||
|
||||
.DEFAULT_GOAL := fmt-lint-vet-embedmd-test
|
||||
|
||||
.PHONY: fmt-lint-vet-embedmd-test
|
||||
fmt-lint-vet-embedmd-test: fmt lint vet embedmd test
|
||||
|
||||
# TODO enable test-with-coverage in tavis
|
||||
.PHONY: travis-ci
|
||||
travis-ci: fmt lint vet embedmd test test-386
|
||||
|
||||
all-pkgs:
|
||||
@echo $(ALL_PKGS) | tr ' ' '\n' | sort
|
||||
|
||||
all-srcs:
|
||||
@echo $(ALL_SRC) | tr ' ' '\n' | sort
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
$(GOTEST) $(GOTEST_OPT) $(ALL_PKGS)
|
||||
|
||||
.PHONY: test-386
|
||||
test-386:
|
||||
GOARCH=386 $(GOTEST) -v -timeout 30s $(ALL_PKGS)
|
||||
|
||||
.PHONY: test-with-coverage
|
||||
test-with-coverage:
|
||||
$(GOTEST) $(GOTEST_OPT_WITH_COVERAGE) $(ALL_PKGS)
|
||||
|
||||
.PHONY: fmt
|
||||
fmt:
|
||||
@FMTOUT=`$(GOFMT) -s -l $(ALL_SRC) 2>&1`; \
|
||||
if [ "$$FMTOUT" ]; then \
|
||||
echo "$(GOFMT) FAILED => gofmt the following files:\n"; \
|
||||
echo "$$FMTOUT\n"; \
|
||||
exit 1; \
|
||||
else \
|
||||
echo "Fmt finished successfully"; \
|
||||
fi
|
||||
|
||||
.PHONY: lint
|
||||
lint:
|
||||
@LINTOUT=`$(GOLINT) $(ALL_PKGS) | grep -v $(TRACE_ID_LINT_EXCEPTION) | grep -v $(TRACE_OPTION_LINT_EXCEPTION) 2>&1`; \
|
||||
if [ "$$LINTOUT" ]; then \
|
||||
echo "$(GOLINT) FAILED => clean the following lint errors:\n"; \
|
||||
echo "$$LINTOUT\n"; \
|
||||
exit 1; \
|
||||
else \
|
||||
echo "Lint finished successfully"; \
|
||||
fi
|
||||
|
||||
.PHONY: vet
|
||||
vet:
|
||||
# TODO: Understand why go vet downloads "github.com/google/go-cmp v0.2.0"
|
||||
@VETOUT=`$(GOVET) ./... | grep -v "go: downloading" 2>&1`; \
|
||||
if [ "$$VETOUT" ]; then \
|
||||
echo "$(GOVET) FAILED => go vet the following files:\n"; \
|
||||
echo "$$VETOUT\n"; \
|
||||
exit 1; \
|
||||
else \
|
||||
echo "Vet finished successfully"; \
|
||||
fi
|
||||
|
||||
.PHONY: embedmd
|
||||
embedmd:
|
||||
@EMBEDMDOUT=`$(EMBEDMD) -d $(README_FILES) 2>&1`; \
|
||||
if [ "$$EMBEDMDOUT" ]; then \
|
||||
echo "$(EMBEDMD) FAILED => embedmd the following files:\n"; \
|
||||
echo "$$EMBEDMDOUT\n"; \
|
||||
exit 1; \
|
||||
else \
|
||||
echo "Embedmd finished successfully"; \
|
||||
fi
|
||||
|
||||
.PHONY: install-tools
|
||||
install-tools:
|
||||
go get -u golang.org/x/tools/cmd/cover
|
||||
go get -u golang.org/x/lint/golint
|
||||
go get -u github.com/rakyll/embedmd
|
263
vendor/go.opencensus.io/README.md
generated
vendored
Normal file
263
vendor/go.opencensus.io/README.md
generated
vendored
Normal file
@ -0,0 +1,263 @@
|
||||
# OpenCensus Libraries for Go
|
||||
|
||||
[![Build Status][travis-image]][travis-url]
|
||||
[![Windows Build Status][appveyor-image]][appveyor-url]
|
||||
[![GoDoc][godoc-image]][godoc-url]
|
||||
[![Gitter chat][gitter-image]][gitter-url]
|
||||
|
||||
OpenCensus Go is a Go implementation of OpenCensus, a toolkit for
|
||||
collecting application performance and behavior monitoring data.
|
||||
Currently it consists of three major components: tags, stats and tracing.
|
||||
|
||||
## Installation
|
||||
|
||||
```
|
||||
$ go get -u go.opencensus.io
|
||||
```
|
||||
|
||||
The API of this project is still evolving, see: [Deprecation Policy](#deprecation-policy).
|
||||
The use of vendoring or a dependency management tool is recommended.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
OpenCensus Go libraries require Go 1.8 or later.
|
||||
|
||||
## Getting Started
|
||||
|
||||
The easiest way to get started using OpenCensus in your application is to use an existing
|
||||
integration with your RPC framework:
|
||||
|
||||
* [net/http](https://godoc.org/go.opencensus.io/plugin/ochttp)
|
||||
* [gRPC](https://godoc.org/go.opencensus.io/plugin/ocgrpc)
|
||||
* [database/sql](https://godoc.org/github.com/opencensus-integrations/ocsql)
|
||||
* [Go kit](https://godoc.org/github.com/go-kit/kit/tracing/opencensus)
|
||||
* [Groupcache](https://godoc.org/github.com/orijtech/groupcache)
|
||||
* [Caddy webserver](https://godoc.org/github.com/orijtech/caddy)
|
||||
* [MongoDB](https://godoc.org/github.com/orijtech/mongo-go-driver)
|
||||
* [Redis gomodule/redigo](https://godoc.org/github.com/orijtech/redigo)
|
||||
* [Redis goredis/redis](https://godoc.org/github.com/orijtech/redis)
|
||||
* [Memcache](https://godoc.org/github.com/orijtech/gomemcache)
|
||||
|
||||
If you're using a framework not listed here, you could either implement your own middleware for your
|
||||
framework or use [custom stats](#stats) and [spans](#spans) directly in your application.
|
||||
|
||||
## Exporters
|
||||
|
||||
OpenCensus can export instrumentation data to various backends.
|
||||
OpenCensus has exporter implementations for the following, users
|
||||
can implement their own exporters by implementing the exporter interfaces
|
||||
([stats](https://godoc.org/go.opencensus.io/stats/view#Exporter),
|
||||
[trace](https://godoc.org/go.opencensus.io/trace#Exporter)):
|
||||
|
||||
* [Prometheus][exporter-prom] for stats
|
||||
* [OpenZipkin][exporter-zipkin] for traces
|
||||
* [Stackdriver][exporter-stackdriver] Monitoring for stats and Trace for traces
|
||||
* [Jaeger][exporter-jaeger] for traces
|
||||
* [AWS X-Ray][exporter-xray] for traces
|
||||
* [Datadog][exporter-datadog] for stats and traces
|
||||
* [Graphite][exporter-graphite] for stats
|
||||
* [Honeycomb][exporter-honeycomb] for traces
|
||||
|
||||
## Overview
|
||||
|
||||
![OpenCensus Overview](https://i.imgur.com/cf4ElHE.jpg)
|
||||
|
||||
In a microservices environment, a user request may go through
|
||||
multiple services until there is a response. OpenCensus allows
|
||||
you to instrument your services and collect diagnostics data all
|
||||
through your services end-to-end.
|
||||
|
||||
## Tags
|
||||
|
||||
Tags represent propagated key-value pairs. They are propagated using `context.Context`
|
||||
in the same process or can be encoded to be transmitted on the wire. Usually, this will
|
||||
be handled by an integration plugin, e.g. `ocgrpc.ServerHandler` and `ocgrpc.ClientHandler`
|
||||
for gRPC.
|
||||
|
||||
Package `tag` allows adding or modifying tags in the current context.
|
||||
|
||||
[embedmd]:# (internal/readme/tags.go new)
|
||||
```go
|
||||
ctx, err = tag.New(ctx,
|
||||
tag.Insert(osKey, "macOS-10.12.5"),
|
||||
tag.Upsert(userIDKey, "cde36753ed"),
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
```
|
||||
|
||||
## Stats
|
||||
|
||||
OpenCensus is a low-overhead framework even if instrumentation is always enabled.
|
||||
In order to be so, it is optimized to make recording of data points fast
|
||||
and separate from the data aggregation.
|
||||
|
||||
OpenCensus stats collection happens in two stages:
|
||||
|
||||
* Definition of measures and recording of data points
|
||||
* Definition of views and aggregation of the recorded data
|
||||
|
||||
### Recording
|
||||
|
||||
Measurements are data points associated with a measure.
|
||||
Recording implicitly tags the set of Measurements with the tags from the
|
||||
provided context:
|
||||
|
||||
[embedmd]:# (internal/readme/stats.go record)
|
||||
```go
|
||||
stats.Record(ctx, videoSize.M(102478))
|
||||
```
|
||||
|
||||
### Views
|
||||
|
||||
Views are how Measures are aggregated. You can think of them as queries over the
|
||||
set of recorded data points (measurements).
|
||||
|
||||
Views have two parts: the tags to group by and the aggregation type used.
|
||||
|
||||
Currently three types of aggregations are supported:
|
||||
* CountAggregation is used to count the number of times a sample was recorded.
|
||||
* DistributionAggregation is used to provide a histogram of the values of the samples.
|
||||
* SumAggregation is used to sum up all sample values.
|
||||
|
||||
[embedmd]:# (internal/readme/stats.go aggs)
|
||||
```go
|
||||
distAgg := view.Distribution(1<<32, 2<<32, 3<<32)
|
||||
countAgg := view.Count()
|
||||
sumAgg := view.Sum()
|
||||
```
|
||||
|
||||
Here we create a view with the DistributionAggregation over our measure.
|
||||
|
||||
[embedmd]:# (internal/readme/stats.go view)
|
||||
```go
|
||||
if err := view.Register(&view.View{
|
||||
Name: "example.com/video_size_distribution",
|
||||
Description: "distribution of processed video size over time",
|
||||
Measure: videoSize,
|
||||
Aggregation: view.Distribution(1<<32, 2<<32, 3<<32),
|
||||
}); err != nil {
|
||||
log.Fatalf("Failed to register view: %v", err)
|
||||
}
|
||||
```
|
||||
|
||||
Register begins collecting data for the view. Registered views' data will be
|
||||
exported via the registered exporters.
|
||||
|
||||
## Traces
|
||||
|
||||
A distributed trace tracks the progression of a single user request as
|
||||
it is handled by the services and processes that make up an application.
|
||||
Each step is called a span in the trace. Spans include metadata about the step,
|
||||
including especially the time spent in the step, called the span’s latency.
|
||||
|
||||
Below you see a trace and several spans underneath it.
|
||||
|
||||
![Traces and spans](https://i.imgur.com/7hZwRVj.png)
|
||||
|
||||
### Spans
|
||||
|
||||
Span is the unit step in a trace. Each span has a name, latency, status and
|
||||
additional metadata.
|
||||
|
||||
Below we are starting a span for a cache read and ending it
|
||||
when we are done:
|
||||
|
||||
[embedmd]:# (internal/readme/trace.go startend)
|
||||
```go
|
||||
ctx, span := trace.StartSpan(ctx, "cache.Get")
|
||||
defer span.End()
|
||||
|
||||
// Do work to get from cache.
|
||||
```
|
||||
|
||||
### Propagation
|
||||
|
||||
Spans can have parents or can be root spans if they don't have any parents.
|
||||
The current span is propagated in-process and across the network to allow associating
|
||||
new child spans with the parent.
|
||||
|
||||
In the same process, `context.Context` is used to propagate spans.
|
||||
`trace.StartSpan` creates a new span as a root if the current context
|
||||
doesn't contain a span. Or, it creates a child of the span that is
|
||||
already in current context. The returned context can be used to keep
|
||||
propagating the newly created span in the current context.
|
||||
|
||||
[embedmd]:# (internal/readme/trace.go startend)
|
||||
```go
|
||||
ctx, span := trace.StartSpan(ctx, "cache.Get")
|
||||
defer span.End()
|
||||
|
||||
// Do work to get from cache.
|
||||
```
|
||||
|
||||
Across the network, OpenCensus provides different propagation
|
||||
methods for different protocols.
|
||||
|
||||
* gRPC integrations use the OpenCensus' [binary propagation format](https://godoc.org/go.opencensus.io/trace/propagation).
|
||||
* HTTP integrations use Zipkin's [B3](https://github.com/openzipkin/b3-propagation)
|
||||
by default but can be configured to use a custom propagation method by setting another
|
||||
[propagation.HTTPFormat](https://godoc.org/go.opencensus.io/trace/propagation#HTTPFormat).
|
||||
|
||||
## Execution Tracer
|
||||
|
||||
With Go 1.11, OpenCensus Go will support integration with the Go execution tracer.
|
||||
See [Debugging Latency in Go](https://medium.com/observability/debugging-latency-in-go-1-11-9f97a7910d68)
|
||||
for an example of their mutual use.
|
||||
|
||||
## Profiles
|
||||
|
||||
OpenCensus tags can be applied as profiler labels
|
||||
for users who are on Go 1.9 and above.
|
||||
|
||||
[embedmd]:# (internal/readme/tags.go profiler)
|
||||
```go
|
||||
ctx, err = tag.New(ctx,
|
||||
tag.Insert(osKey, "macOS-10.12.5"),
|
||||
tag.Insert(userIDKey, "fff0989878"),
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
tag.Do(ctx, func(ctx context.Context) {
|
||||
// Do work.
|
||||
// When profiling is on, samples will be
|
||||
// recorded with the key/values from the tag map.
|
||||
})
|
||||
```
|
||||
|
||||
A screenshot of the CPU profile from the program above:
|
||||
|
||||
![CPU profile](https://i.imgur.com/jBKjlkw.png)
|
||||
|
||||
## Deprecation Policy
|
||||
|
||||
Before version 1.0.0, the following deprecation policy will be observed:
|
||||
|
||||
No backwards-incompatible changes will be made except for the removal of symbols that have
|
||||
been marked as *Deprecated* for at least one minor release (e.g. 0.9.0 to 0.10.0). A release
|
||||
removing the *Deprecated* functionality will be made no sooner than 28 days after the first
|
||||
release in which the functionality was marked *Deprecated*.
|
||||
|
||||
[travis-image]: https://travis-ci.org/census-instrumentation/opencensus-go.svg?branch=master
|
||||
[travis-url]: https://travis-ci.org/census-instrumentation/opencensus-go
|
||||
[appveyor-image]: https://ci.appveyor.com/api/projects/status/vgtt29ps1783ig38?svg=true
|
||||
[appveyor-url]: https://ci.appveyor.com/project/opencensusgoteam/opencensus-go/branch/master
|
||||
[godoc-image]: https://godoc.org/go.opencensus.io?status.svg
|
||||
[godoc-url]: https://godoc.org/go.opencensus.io
|
||||
[gitter-image]: https://badges.gitter.im/census-instrumentation/lobby.svg
|
||||
[gitter-url]: https://gitter.im/census-instrumentation/lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
|
||||
|
||||
|
||||
[new-ex]: https://godoc.org/go.opencensus.io/tag#example-NewMap
|
||||
[new-replace-ex]: https://godoc.org/go.opencensus.io/tag#example-NewMap--Replace
|
||||
|
||||
[exporter-prom]: https://godoc.org/go.opencensus.io/exporter/prometheus
|
||||
[exporter-stackdriver]: https://godoc.org/contrib.go.opencensus.io/exporter/stackdriver
|
||||
[exporter-zipkin]: https://godoc.org/go.opencensus.io/exporter/zipkin
|
||||
[exporter-jaeger]: https://godoc.org/go.opencensus.io/exporter/jaeger
|
||||
[exporter-xray]: https://github.com/census-ecosystem/opencensus-go-exporter-aws
|
||||
[exporter-datadog]: https://github.com/DataDog/opencensus-go-exporter-datadog
|
||||
[exporter-graphite]: https://github.com/census-ecosystem/opencensus-go-exporter-graphite
|
||||
[exporter-honeycomb]: https://github.com/honeycombio/opencensus-exporter
|
10
vendor/go.opencensus.io/go.mod
generated
vendored
Normal file
10
vendor/go.opencensus.io/go.mod
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
module go.opencensus.io
|
||||
|
||||
require (
|
||||
github.com/golang/protobuf v1.2.0
|
||||
github.com/google/go-cmp v0.2.0
|
||||
github.com/hashicorp/golang-lru v0.5.0
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19 // indirect
|
||||
google.golang.org/grpc v1.19.0
|
||||
)
|
50
vendor/go.opencensus.io/go.sum
generated
vendored
Normal file
50
vendor/go.opencensus.io/go.sum
generated
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||
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 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo=
|
||||
github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
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/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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTmV7VDcZyvRZ+QQXkXTZQ=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 h1:bjcUS9ztw9kFmmIxJInhon/0Is3p+EHBKNgquIzo1OI=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/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-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
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 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19 h1:Lj2SnHtxkRGJDqnGaSjo+CCdIieEnwVazbOXILwQemk=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/grpc v1.19.0 h1:cfg4PD8YEdSFnm7qLV4++93WcmhH2nIUhMjhdCvl3j8=
|
||||
google.golang.org/grpc v1.19.0 h1:cfg4PD8YEdSFnm7qLV4++93WcmhH2nIUhMjhdCvl3j8=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
37
vendor/go.opencensus.io/internal/internal.go
generated
vendored
Normal file
37
vendor/go.opencensus.io/internal/internal.go
generated
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
// Copyright 2017, OpenCensus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package internal // import "go.opencensus.io/internal"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
opencensus "go.opencensus.io"
|
||||
)
|
||||
|
||||
// UserAgent is the user agent to be added to the outgoing
|
||||
// requests from the exporters.
|
||||
var UserAgent = fmt.Sprintf("opencensus-go/%s", opencensus.Version())
|
||||
|
||||
// MonotonicEndTime returns the end time at present
|
||||
// but offset from start, monotonically.
|
||||
//
|
||||
// The monotonic clock is used in subtractions hence
|
||||
// the duration since start added back to start gives
|
||||
// end as a monotonic time.
|
||||
// See https://golang.org/pkg/time/#hdr-Monotonic_Clocks
|
||||
func MonotonicEndTime(start time.Time) time.Time {
|
||||
return start.Add(time.Now().Sub(start))
|
||||
}
|
50
vendor/go.opencensus.io/internal/sanitize.go
generated
vendored
Normal file
50
vendor/go.opencensus.io/internal/sanitize.go
generated
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
// Copyright 2017, OpenCensus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
const labelKeySizeLimit = 100
|
||||
|
||||
// Sanitize returns a string that is trunacated to 100 characters if it's too
|
||||
// long, and replaces non-alphanumeric characters to underscores.
|
||||
func Sanitize(s string) string {
|
||||
if len(s) == 0 {
|
||||
return s
|
||||
}
|
||||
if len(s) > labelKeySizeLimit {
|
||||
s = s[:labelKeySizeLimit]
|
||||
}
|
||||
s = strings.Map(sanitizeRune, s)
|
||||
if unicode.IsDigit(rune(s[0])) {
|
||||
s = "key_" + s
|
||||
}
|
||||
if s[0] == '_' {
|
||||
s = "key" + s
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// converts anything that is not a letter or digit to an underscore
|
||||
func sanitizeRune(r rune) rune {
|
||||
if unicode.IsLetter(r) || unicode.IsDigit(r) {
|
||||
return r
|
||||
}
|
||||
// Everything else turns into an underscore
|
||||
return '_'
|
||||
}
|
75
vendor/go.opencensus.io/internal/tagencoding/tagencoding.go
generated
vendored
Normal file
75
vendor/go.opencensus.io/internal/tagencoding/tagencoding.go
generated
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
// Copyright 2017, OpenCensus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
// Package tagencoding contains the tag encoding
|
||||
// used interally by the stats collector.
|
||||
package tagencoding // import "go.opencensus.io/internal/tagencoding"
|
||||
|
||||
// Values represent the encoded buffer for the values.
|
||||
type Values struct {
|
||||
Buffer []byte
|
||||
WriteIndex int
|
||||
ReadIndex int
|
||||
}
|
||||
|
||||
func (vb *Values) growIfRequired(expected int) {
|
||||
if len(vb.Buffer)-vb.WriteIndex < expected {
|
||||
tmp := make([]byte, 2*(len(vb.Buffer)+1)+expected)
|
||||
copy(tmp, vb.Buffer)
|
||||
vb.Buffer = tmp
|
||||
}
|
||||
}
|
||||
|
||||
// WriteValue is the helper method to encode Values from map[Key][]byte.
|
||||
func (vb *Values) WriteValue(v []byte) {
|
||||
length := len(v) & 0xff
|
||||
vb.growIfRequired(1 + length)
|
||||
|
||||
// writing length of v
|
||||
vb.Buffer[vb.WriteIndex] = byte(length)
|
||||
vb.WriteIndex++
|
||||
|
||||
if length == 0 {
|
||||
// No value was encoded for this key
|
||||
return
|
||||
}
|
||||
|
||||
// writing v
|
||||
copy(vb.Buffer[vb.WriteIndex:], v[:length])
|
||||
vb.WriteIndex += length
|
||||
}
|
||||
|
||||
// ReadValue is the helper method to decode Values to a map[Key][]byte.
|
||||
func (vb *Values) ReadValue() []byte {
|
||||
// read length of v
|
||||
length := int(vb.Buffer[vb.ReadIndex])
|
||||
vb.ReadIndex++
|
||||
if length == 0 {
|
||||
// No value was encoded for this key
|
||||
return nil
|
||||
}
|
||||
|
||||
// read value of v
|
||||
v := make([]byte, length)
|
||||
endIdx := vb.ReadIndex + length
|
||||
copy(v, vb.Buffer[vb.ReadIndex:endIdx])
|
||||
vb.ReadIndex = endIdx
|
||||
return v
|
||||
}
|
||||
|
||||
// Bytes returns a reference to already written bytes in the Buffer.
|
||||
func (vb *Values) Bytes() []byte {
|
||||
return vb.Buffer[:vb.WriteIndex]
|
||||
}
|
53
vendor/go.opencensus.io/internal/traceinternals.go
generated
vendored
Normal file
53
vendor/go.opencensus.io/internal/traceinternals.go
generated
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
// Copyright 2017, OpenCensus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Trace allows internal access to some trace functionality.
|
||||
// TODO(#412): remove this
|
||||
var Trace interface{}
|
||||
|
||||
// LocalSpanStoreEnabled true if the local span store is enabled.
|
||||
var LocalSpanStoreEnabled bool
|
||||
|
||||
// BucketConfiguration stores the number of samples to store for span buckets
|
||||
// for successful and failed spans for a particular span name.
|
||||
type BucketConfiguration struct {
|
||||
Name string
|
||||
MaxRequestsSucceeded int
|
||||
MaxRequestsErrors int
|
||||
}
|
||||
|
||||
// PerMethodSummary is a summary of the spans stored for a single span name.
|
||||
type PerMethodSummary struct {
|
||||
Active int
|
||||
LatencyBuckets []LatencyBucketSummary
|
||||
ErrorBuckets []ErrorBucketSummary
|
||||
}
|
||||
|
||||
// LatencyBucketSummary is a summary of a latency bucket.
|
||||
type LatencyBucketSummary struct {
|
||||
MinLatency, MaxLatency time.Duration
|
||||
Size int
|
||||
}
|
||||
|
||||
// ErrorBucketSummary is a summary of an error bucket.
|
||||
type ErrorBucketSummary struct {
|
||||
ErrorCode int32
|
||||
Size int
|
||||
}
|
19
vendor/go.opencensus.io/metric/metricdata/doc.go
generated
vendored
Normal file
19
vendor/go.opencensus.io/metric/metricdata/doc.go
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
// Copyright 2018, OpenCensus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package metricdata contains the metrics data model.
|
||||
//
|
||||
// This is an EXPERIMENTAL package, and may change in arbitrary ways without
|
||||
// notice.
|
||||
package metricdata // import "go.opencensus.io/metric/metricdata"
|
38
vendor/go.opencensus.io/metric/metricdata/exemplar.go
generated
vendored
Normal file
38
vendor/go.opencensus.io/metric/metricdata/exemplar.go
generated
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
// Copyright 2018, OpenCensus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package metricdata
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Exemplars keys.
|
||||
const (
|
||||
AttachmentKeySpanContext = "SpanContext"
|
||||
)
|
||||
|
||||
// Exemplar is an example data point associated with each bucket of a
|
||||
// distribution type aggregation.
|
||||
//
|
||||
// Their purpose is to provide an example of the kind of thing
|
||||
// (request, RPC, trace span, etc.) that resulted in that measurement.
|
||||
type Exemplar struct {
|
||||
Value float64 // the value that was recorded
|
||||
Timestamp time.Time // the time the value was recorded
|
||||
Attachments Attachments // attachments (if any)
|
||||
}
|
||||
|
||||
// Attachments is a map of extra values associated with a recorded data point.
|
||||
type Attachments map[string]interface{}
|
35
vendor/go.opencensus.io/metric/metricdata/label.go
generated
vendored
Normal file
35
vendor/go.opencensus.io/metric/metricdata/label.go
generated
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
// Copyright 2018, OpenCensus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package metricdata
|
||||
|
||||
// LabelKey represents key of a label. It has optional
|
||||
// description attribute.
|
||||
type LabelKey struct {
|
||||
Key string
|
||||
Description string
|
||||
}
|
||||
|
||||
// LabelValue represents the value of a label.
|
||||
// The zero value represents a missing label value, which may be treated
|
||||
// differently to an empty string value by some back ends.
|
||||
type LabelValue struct {
|
||||
Value string // string value of the label
|
||||
Present bool // flag that indicated whether a value is present or not
|
||||
}
|
||||
|
||||
// NewLabelValue creates a new non-nil LabelValue that represents the given string.
|
||||
func NewLabelValue(val string) LabelValue {
|
||||
return LabelValue{Value: val, Present: true}
|
||||
}
|
46
vendor/go.opencensus.io/metric/metricdata/metric.go
generated
vendored
Normal file
46
vendor/go.opencensus.io/metric/metricdata/metric.go
generated
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
// Copyright 2018, OpenCensus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package metricdata
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"go.opencensus.io/resource"
|
||||
)
|
||||
|
||||
// Descriptor holds metadata about a metric.
|
||||
type Descriptor struct {
|
||||
Name string // full name of the metric
|
||||
Description string // human-readable description
|
||||
Unit Unit // units for the measure
|
||||
Type Type // type of measure
|
||||
LabelKeys []LabelKey // label keys
|
||||
}
|
||||
|
||||
// Metric represents a quantity measured against a resource with different
|
||||
// label value combinations.
|
||||
type Metric struct {
|
||||
Descriptor Descriptor // metric descriptor
|
||||
Resource *resource.Resource // resource against which this was measured
|
||||
TimeSeries []*TimeSeries // one time series for each combination of label values
|
||||
}
|
||||
|
||||
// TimeSeries is a sequence of points associated with a combination of label
|
||||
// values.
|
||||
type TimeSeries struct {
|
||||
LabelValues []LabelValue // label values, same order as keys in the metric descriptor
|
||||
Points []Point // points sequence
|
||||
StartTime time.Time // time we started recording this time series
|
||||
}
|
193
vendor/go.opencensus.io/metric/metricdata/point.go
generated
vendored
Normal file
193
vendor/go.opencensus.io/metric/metricdata/point.go
generated
vendored
Normal file
@ -0,0 +1,193 @@
|
||||
// Copyright 2018, OpenCensus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package metricdata
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Point is a single data point of a time series.
|
||||
type Point struct {
|
||||
// Time is the point in time that this point represents in a time series.
|
||||
Time time.Time
|
||||
// Value is the value of this point. Prefer using ReadValue to switching on
|
||||
// the value type, since new value types might be added.
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
//go:generate stringer -type ValueType
|
||||
|
||||
// NewFloat64Point creates a new Point holding a float64 value.
|
||||
func NewFloat64Point(t time.Time, val float64) Point {
|
||||
return Point{
|
||||
Value: val,
|
||||
Time: t,
|
||||
}
|
||||
}
|
||||
|
||||
// NewInt64Point creates a new Point holding an int64 value.
|
||||
func NewInt64Point(t time.Time, val int64) Point {
|
||||
return Point{
|
||||
Value: val,
|
||||
Time: t,
|
||||
}
|
||||
}
|
||||
|
||||
// NewDistributionPoint creates a new Point holding a Distribution value.
|
||||
func NewDistributionPoint(t time.Time, val *Distribution) Point {
|
||||
return Point{
|
||||
Value: val,
|
||||
Time: t,
|
||||
}
|
||||
}
|
||||
|
||||
// NewSummaryPoint creates a new Point holding a Summary value.
|
||||
func NewSummaryPoint(t time.Time, val *Summary) Point {
|
||||
return Point{
|
||||
Value: val,
|
||||
Time: t,
|
||||
}
|
||||
}
|
||||
|
||||
// ValueVisitor allows reading the value of a point.
|
||||
type ValueVisitor interface {
|
||||
VisitFloat64Value(float64)
|
||||
VisitInt64Value(int64)
|
||||
VisitDistributionValue(*Distribution)
|
||||
VisitSummaryValue(*Summary)
|
||||
}
|
||||
|
||||
// ReadValue accepts a ValueVisitor and calls the appropriate method with the
|
||||
// value of this point.
|
||||
// Consumers of Point should use this in preference to switching on the type
|
||||
// of the value directly, since new value types may be added.
|
||||
func (p Point) ReadValue(vv ValueVisitor) {
|
||||
switch v := p.Value.(type) {
|
||||
case int64:
|
||||
vv.VisitInt64Value(v)
|
||||
case float64:
|
||||
vv.VisitFloat64Value(v)
|
||||
case *Distribution:
|
||||
vv.VisitDistributionValue(v)
|
||||
case *Summary:
|
||||
vv.VisitSummaryValue(v)
|
||||
default:
|
||||
panic("unexpected value type")
|
||||
}
|
||||
}
|
||||
|
||||
// Distribution contains summary statistics for a population of values. It
|
||||
// optionally contains a histogram representing the distribution of those
|
||||
// values across a set of buckets.
|
||||
type Distribution struct {
|
||||
// Count is the number of values in the population. Must be non-negative. This value
|
||||
// must equal the sum of the values in bucket_counts if a histogram is
|
||||
// provided.
|
||||
Count int64
|
||||
// Sum is the sum of the values in the population. If count is zero then this field
|
||||
// must be zero.
|
||||
Sum float64
|
||||
// SumOfSquaredDeviation is the sum of squared deviations from the mean of the values in the
|
||||
// population. For values x_i this is:
|
||||
//
|
||||
// Sum[i=1..n]((x_i - mean)^2)
|
||||
//
|
||||
// Knuth, "The Art of Computer Programming", Vol. 2, page 323, 3rd edition
|
||||
// describes Welford's method for accumulating this sum in one pass.
|
||||
//
|
||||
// If count is zero then this field must be zero.
|
||||
SumOfSquaredDeviation float64
|
||||
// BucketOptions describes the bounds of the histogram buckets in this
|
||||
// distribution.
|
||||
//
|
||||
// A Distribution may optionally contain a histogram of the values in the
|
||||
// population.
|
||||
//
|
||||
// If nil, there is no associated histogram.
|
||||
BucketOptions *BucketOptions
|
||||
// Bucket If the distribution does not have a histogram, then omit this field.
|
||||
// If there is a histogram, then the sum of the values in the Bucket counts
|
||||
// must equal the value in the count field of the distribution.
|
||||
Buckets []Bucket
|
||||
}
|
||||
|
||||
// BucketOptions describes the bounds of the histogram buckets in this
|
||||
// distribution.
|
||||
type BucketOptions struct {
|
||||
// Bounds specifies a set of bucket upper bounds.
|
||||
// This defines len(bounds) + 1 (= N) buckets. The boundaries for bucket
|
||||
// index i are:
|
||||
//
|
||||
// [0, Bounds[i]) for i == 0
|
||||
// [Bounds[i-1], Bounds[i]) for 0 < i < N-1
|
||||
// [Bounds[i-1], +infinity) for i == N-1
|
||||
Bounds []float64
|
||||
}
|
||||
|
||||
// Bucket represents a single bucket (value range) in a distribution.
|
||||
type Bucket struct {
|
||||
// Count is the number of values in each bucket of the histogram, as described in
|
||||
// bucket_bounds.
|
||||
Count int64
|
||||
// Exemplar associated with this bucket (if any).
|
||||
Exemplar *Exemplar
|
||||
}
|
||||
|
||||
// Summary is a representation of percentiles.
|
||||
type Summary struct {
|
||||
// Count is the cumulative count (if available).
|
||||
Count int64
|
||||
// Sum is the cumulative sum of values (if available).
|
||||
Sum float64
|
||||
// HasCountAndSum is true if Count and Sum are available.
|
||||
HasCountAndSum bool
|
||||
// Snapshot represents percentiles calculated over an arbitrary time window.
|
||||
// The values in this struct can be reset at arbitrary unknown times, with
|
||||
// the requirement that all of them are reset at the same time.
|
||||
Snapshot Snapshot
|
||||
}
|
||||
|
||||
// Snapshot represents percentiles over an arbitrary time.
|
||||
// The values in this struct can be reset at arbitrary unknown times, with
|
||||
// the requirement that all of them are reset at the same time.
|
||||
type Snapshot struct {
|
||||
// Count is the number of values in the snapshot. Optional since some systems don't
|
||||
// expose this. Set to 0 if not available.
|
||||
Count int64
|
||||
// Sum is the sum of values in the snapshot. Optional since some systems don't
|
||||
// expose this. If count is 0 then this field must be zero.
|
||||
Sum float64
|
||||
// Percentiles is a map from percentile (range (0-100.0]) to the value of
|
||||
// the percentile.
|
||||
Percentiles map[float64]float64
|
||||
}
|
||||
|
||||
//go:generate stringer -type Type
|
||||
|
||||
// Type is the overall type of metric, including its value type and whether it
|
||||
// represents a cumulative total (since the start time) or if it represents a
|
||||
// gauge value.
|
||||
type Type int
|
||||
|
||||
// Metric types.
|
||||
const (
|
||||
TypeGaugeInt64 Type = iota
|
||||
TypeGaugeFloat64
|
||||
TypeGaugeDistribution
|
||||
TypeCumulativeInt64
|
||||
TypeCumulativeFloat64
|
||||
TypeCumulativeDistribution
|
||||
TypeSummary
|
||||
)
|
16
vendor/go.opencensus.io/metric/metricdata/type_string.go
generated
vendored
Normal file
16
vendor/go.opencensus.io/metric/metricdata/type_string.go
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
// Code generated by "stringer -type Type"; DO NOT EDIT.
|
||||
|
||||
package metricdata
|
||||
|
||||
import "strconv"
|
||||
|
||||
const _Type_name = "TypeGaugeInt64TypeGaugeFloat64TypeGaugeDistributionTypeCumulativeInt64TypeCumulativeFloat64TypeCumulativeDistributionTypeSummary"
|
||||
|
||||
var _Type_index = [...]uint8{0, 14, 30, 51, 70, 91, 117, 128}
|
||||
|
||||
func (i Type) String() string {
|
||||
if i < 0 || i >= Type(len(_Type_index)-1) {
|
||||
return "Type(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _Type_name[_Type_index[i]:_Type_index[i+1]]
|
||||
}
|
27
vendor/go.opencensus.io/metric/metricdata/unit.go
generated
vendored
Normal file
27
vendor/go.opencensus.io/metric/metricdata/unit.go
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
// Copyright 2018, OpenCensus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package metricdata
|
||||
|
||||
// Unit is a string encoded according to the case-sensitive abbreviations from the
|
||||
// Unified Code for Units of Measure: http://unitsofmeasure.org/ucum.html
|
||||
type Unit string
|
||||
|
||||
// Predefined units. To record against a unit not represented here, create your
|
||||
// own Unit type constant from a string.
|
||||
const (
|
||||
UnitDimensionless Unit = "1"
|
||||
UnitBytes Unit = "By"
|
||||
UnitMilliseconds Unit = "ms"
|
||||
)
|
78
vendor/go.opencensus.io/metric/metricproducer/manager.go
generated
vendored
Normal file
78
vendor/go.opencensus.io/metric/metricproducer/manager.go
generated
vendored
Normal file
@ -0,0 +1,78 @@
|
||||
// Copyright 2019, OpenCensus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package metricproducer
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Manager maintains a list of active producers. Producers can register
|
||||
// with the manager to allow readers to read all metrics provided by them.
|
||||
// Readers can retrieve all producers registered with the manager,
|
||||
// read metrics from the producers and export them.
|
||||
type Manager struct {
|
||||
mu sync.RWMutex
|
||||
producers map[Producer]struct{}
|
||||
}
|
||||
|
||||
var prodMgr *Manager
|
||||
var once sync.Once
|
||||
|
||||
// GlobalManager is a single instance of producer manager
|
||||
// that is used by all producers and all readers.
|
||||
func GlobalManager() *Manager {
|
||||
once.Do(func() {
|
||||
prodMgr = &Manager{}
|
||||
prodMgr.producers = make(map[Producer]struct{})
|
||||
})
|
||||
return prodMgr
|
||||
}
|
||||
|
||||
// AddProducer adds the producer to the Manager if it is not already present.
|
||||
func (pm *Manager) AddProducer(producer Producer) {
|
||||
if producer == nil {
|
||||
return
|
||||
}
|
||||
pm.mu.Lock()
|
||||
defer pm.mu.Unlock()
|
||||
pm.producers[producer] = struct{}{}
|
||||
}
|
||||
|
||||
// DeleteProducer deletes the producer from the Manager if it is present.
|
||||
func (pm *Manager) DeleteProducer(producer Producer) {
|
||||
if producer == nil {
|
||||
return
|
||||
}
|
||||
pm.mu.Lock()
|
||||
defer pm.mu.Unlock()
|
||||
delete(pm.producers, producer)
|
||||
}
|
||||
|
||||
// GetAll returns a slice of all producer currently registered with
|
||||
// the Manager. For each call it generates a new slice. The slice
|
||||
// should not be cached as registration may change at any time. It is
|
||||
// typically called periodically by exporter to read metrics from
|
||||
// the producers.
|
||||
func (pm *Manager) GetAll() []Producer {
|
||||
pm.mu.Lock()
|
||||
defer pm.mu.Unlock()
|
||||
producers := make([]Producer, len(pm.producers))
|
||||
i := 0
|
||||
for producer := range pm.producers {
|
||||
producers[i] = producer
|
||||
i++
|
||||
}
|
||||
return producers
|
||||
}
|
28
vendor/go.opencensus.io/metric/metricproducer/producer.go
generated
vendored
Normal file
28
vendor/go.opencensus.io/metric/metricproducer/producer.go
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
// Copyright 2019, OpenCensus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package metricproducer
|
||||
|
||||
import (
|
||||
"go.opencensus.io/metric/metricdata"
|
||||
)
|
||||
|
||||
// Producer is a source of metrics.
|
||||
type Producer interface {
|
||||
// Read should return the current values of all metrics supported by this
|
||||
// metric provider.
|
||||
// The returned metrics should be unique for each combination of name and
|
||||
// resource.
|
||||
Read() []*metricdata.Metric
|
||||
}
|
21
vendor/go.opencensus.io/opencensus.go
generated
vendored
Normal file
21
vendor/go.opencensus.io/opencensus.go
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
// Copyright 2017, OpenCensus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package opencensus contains Go support for OpenCensus.
|
||||
package opencensus // import "go.opencensus.io"
|
||||
|
||||
// Version is the current release version of OpenCensus in use.
|
||||
func Version() string {
|
||||
return "0.21.0"
|
||||
}
|
117
vendor/go.opencensus.io/plugin/ochttp/client.go
generated
vendored
Normal file
117
vendor/go.opencensus.io/plugin/ochttp/client.go
generated
vendored
Normal file
@ -0,0 +1,117 @@
|
||||
// Copyright 2018, OpenCensus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ochttp
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptrace"
|
||||
|
||||
"go.opencensus.io/trace"
|
||||
"go.opencensus.io/trace/propagation"
|
||||
)
|
||||
|
||||
// Transport is an http.RoundTripper that instruments all outgoing requests with
|
||||
// OpenCensus stats and tracing.
|
||||
//
|
||||
// The zero value is intended to be a useful default, but for
|
||||
// now it's recommended that you explicitly set Propagation, since the default
|
||||
// for this may change.
|
||||
type Transport struct {
|
||||
// Base may be set to wrap another http.RoundTripper that does the actual
|
||||
// requests. By default http.DefaultTransport is used.
|
||||
//
|
||||
// If base HTTP roundtripper implements CancelRequest,
|
||||
// the returned round tripper will be cancelable.
|
||||
Base http.RoundTripper
|
||||
|
||||
// Propagation defines how traces are propagated. If unspecified, a default
|
||||
// (currently B3 format) will be used.
|
||||
Propagation propagation.HTTPFormat
|
||||
|
||||
// StartOptions are applied to the span started by this Transport around each
|
||||
// request.
|
||||
//
|
||||
// StartOptions.SpanKind will always be set to trace.SpanKindClient
|
||||
// for spans started by this transport.
|
||||
StartOptions trace.StartOptions
|
||||
|
||||
// GetStartOptions allows to set start options per request. If set,
|
||||
// StartOptions is going to be ignored.
|
||||
GetStartOptions func(*http.Request) trace.StartOptions
|
||||
|
||||
// NameFromRequest holds the function to use for generating the span name
|
||||
// from the information found in the outgoing HTTP Request. By default the
|
||||
// name equals the URL Path.
|
||||
FormatSpanName func(*http.Request) string
|
||||
|
||||
// NewClientTrace may be set to a function allowing the current *trace.Span
|
||||
// to be annotated with HTTP request event information emitted by the
|
||||
// httptrace package.
|
||||
NewClientTrace func(*http.Request, *trace.Span) *httptrace.ClientTrace
|
||||
|
||||
// TODO: Implement tag propagation for HTTP.
|
||||
}
|
||||
|
||||
// RoundTrip implements http.RoundTripper, delegating to Base and recording stats and traces for the request.
|
||||
func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
rt := t.base()
|
||||
if isHealthEndpoint(req.URL.Path) {
|
||||
return rt.RoundTrip(req)
|
||||
}
|
||||
// TODO: remove excessive nesting of http.RoundTrippers here.
|
||||
format := t.Propagation
|
||||
if format == nil {
|
||||
format = defaultFormat
|
||||
}
|
||||
spanNameFormatter := t.FormatSpanName
|
||||
if spanNameFormatter == nil {
|
||||
spanNameFormatter = spanNameFromURL
|
||||
}
|
||||
|
||||
startOpts := t.StartOptions
|
||||
if t.GetStartOptions != nil {
|
||||
startOpts = t.GetStartOptions(req)
|
||||
}
|
||||
|
||||
rt = &traceTransport{
|
||||
base: rt,
|
||||
format: format,
|
||||
startOptions: trace.StartOptions{
|
||||
Sampler: startOpts.Sampler,
|
||||
SpanKind: trace.SpanKindClient,
|
||||
},
|
||||
formatSpanName: spanNameFormatter,
|
||||
newClientTrace: t.NewClientTrace,
|
||||
}
|
||||
rt = statsTransport{base: rt}
|
||||
return rt.RoundTrip(req)
|
||||
}
|
||||
|
||||
func (t *Transport) base() http.RoundTripper {
|
||||
if t.Base != nil {
|
||||
return t.Base
|
||||
}
|
||||
return http.DefaultTransport
|
||||
}
|
||||
|
||||
// CancelRequest cancels an in-flight request by closing its connection.
|
||||
func (t *Transport) CancelRequest(req *http.Request) {
|
||||
type canceler interface {
|
||||
CancelRequest(*http.Request)
|
||||
}
|
||||
if cr, ok := t.base().(canceler); ok {
|
||||
cr.CancelRequest(req)
|
||||
}
|
||||
}
|
143
vendor/go.opencensus.io/plugin/ochttp/client_stats.go
generated
vendored
Normal file
143
vendor/go.opencensus.io/plugin/ochttp/client_stats.go
generated
vendored
Normal file
@ -0,0 +1,143 @@
|
||||
// Copyright 2018, OpenCensus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ochttp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"go.opencensus.io/stats"
|
||||
"go.opencensus.io/tag"
|
||||
)
|
||||
|
||||
// statsTransport is an http.RoundTripper that collects stats for the outgoing requests.
|
||||
type statsTransport struct {
|
||||
base http.RoundTripper
|
||||
}
|
||||
|
||||
// RoundTrip implements http.RoundTripper, delegating to Base and recording stats for the request.
|
||||
func (t statsTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
ctx, _ := tag.New(req.Context(),
|
||||
tag.Upsert(KeyClientHost, req.Host),
|
||||
tag.Upsert(Host, req.Host),
|
||||
tag.Upsert(KeyClientPath, req.URL.Path),
|
||||
tag.Upsert(Path, req.URL.Path),
|
||||
tag.Upsert(KeyClientMethod, req.Method),
|
||||
tag.Upsert(Method, req.Method))
|
||||
req = req.WithContext(ctx)
|
||||
track := &tracker{
|
||||
start: time.Now(),
|
||||
ctx: ctx,
|
||||
}
|
||||
if req.Body == nil {
|
||||
// TODO: Handle cases where ContentLength is not set.
|
||||
track.reqSize = -1
|
||||
} else if req.ContentLength > 0 {
|
||||
track.reqSize = req.ContentLength
|
||||
}
|
||||
stats.Record(ctx, ClientRequestCount.M(1))
|
||||
|
||||
// Perform request.
|
||||
resp, err := t.base.RoundTrip(req)
|
||||
|
||||
if err != nil {
|
||||
track.statusCode = http.StatusInternalServerError
|
||||
track.end()
|
||||
} else {
|
||||
track.statusCode = resp.StatusCode
|
||||
if req.Method != "HEAD" {
|
||||
track.respContentLength = resp.ContentLength
|
||||
}
|
||||
if resp.Body == nil {
|
||||
track.end()
|
||||
} else {
|
||||
track.body = resp.Body
|
||||
resp.Body = wrappedBody(track, resp.Body)
|
||||
}
|
||||
}
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// CancelRequest cancels an in-flight request by closing its connection.
|
||||
func (t statsTransport) CancelRequest(req *http.Request) {
|
||||
type canceler interface {
|
||||
CancelRequest(*http.Request)
|
||||
}
|
||||
if cr, ok := t.base.(canceler); ok {
|
||||
cr.CancelRequest(req)
|
||||
}
|
||||
}
|
||||
|
||||
type tracker struct {
|
||||
ctx context.Context
|
||||
respSize int64
|
||||
respContentLength int64
|
||||
reqSize int64
|
||||
start time.Time
|
||||
body io.ReadCloser
|
||||
statusCode int
|
||||
endOnce sync.Once
|
||||
}
|
||||
|
||||
var _ io.ReadCloser = (*tracker)(nil)
|
||||
|
||||
func (t *tracker) end() {
|
||||
t.endOnce.Do(func() {
|
||||
latencyMs := float64(time.Since(t.start)) / float64(time.Millisecond)
|
||||
respSize := t.respSize
|
||||
if t.respSize == 0 && t.respContentLength > 0 {
|
||||
respSize = t.respContentLength
|
||||
}
|
||||
m := []stats.Measurement{
|
||||
ClientSentBytes.M(t.reqSize),
|
||||
ClientReceivedBytes.M(respSize),
|
||||
ClientRoundtripLatency.M(latencyMs),
|
||||
ClientLatency.M(latencyMs),
|
||||
ClientResponseBytes.M(t.respSize),
|
||||
}
|
||||
if t.reqSize >= 0 {
|
||||
m = append(m, ClientRequestBytes.M(t.reqSize))
|
||||
}
|
||||
|
||||
stats.RecordWithTags(t.ctx, []tag.Mutator{
|
||||
tag.Upsert(StatusCode, strconv.Itoa(t.statusCode)),
|
||||
tag.Upsert(KeyClientStatus, strconv.Itoa(t.statusCode)),
|
||||
}, m...)
|
||||
})
|
||||
}
|
||||
|
||||
func (t *tracker) Read(b []byte) (int, error) {
|
||||
n, err := t.body.Read(b)
|
||||
t.respSize += int64(n)
|
||||
switch err {
|
||||
case nil:
|
||||
return n, nil
|
||||
case io.EOF:
|
||||
t.end()
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (t *tracker) Close() error {
|
||||
// Invoking endSpan on Close will help catch the cases
|
||||
// in which a read returned a non-nil error, we set the
|
||||
// span status but didn't end the span.
|
||||
t.end()
|
||||
return t.body.Close()
|
||||
}
|
19
vendor/go.opencensus.io/plugin/ochttp/doc.go
generated
vendored
Normal file
19
vendor/go.opencensus.io/plugin/ochttp/doc.go
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
// Copyright 2018, OpenCensus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package ochttp provides OpenCensus instrumentation for net/http package.
|
||||
//
|
||||
// For server instrumentation, see Handler. For client-side instrumentation,
|
||||
// see Transport.
|
||||
package ochttp // import "go.opencensus.io/plugin/ochttp"
|
123
vendor/go.opencensus.io/plugin/ochttp/propagation/b3/b3.go
generated
vendored
Normal file
123
vendor/go.opencensus.io/plugin/ochttp/propagation/b3/b3.go
generated
vendored
Normal file
@ -0,0 +1,123 @@
|
||||
// Copyright 2018, OpenCensus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package b3 contains a propagation.HTTPFormat implementation
|
||||
// for B3 propagation. See https://github.com/openzipkin/b3-propagation
|
||||
// for more details.
|
||||
package b3 // import "go.opencensus.io/plugin/ochttp/propagation/b3"
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"net/http"
|
||||
|
||||
"go.opencensus.io/trace"
|
||||
"go.opencensus.io/trace/propagation"
|
||||
)
|
||||
|
||||
// B3 headers that OpenCensus understands.
|
||||
const (
|
||||
TraceIDHeader = "X-B3-TraceId"
|
||||
SpanIDHeader = "X-B3-SpanId"
|
||||
SampledHeader = "X-B3-Sampled"
|
||||
)
|
||||
|
||||
// HTTPFormat implements propagation.HTTPFormat to propagate
|
||||
// traces in HTTP headers in B3 propagation format.
|
||||
// HTTPFormat skips the X-B3-ParentId and X-B3-Flags headers
|
||||
// because there are additional fields not represented in the
|
||||
// OpenCensus span context. Spans created from the incoming
|
||||
// header will be the direct children of the client-side span.
|
||||
// Similarly, receiver of the outgoing spans should use client-side
|
||||
// span created by OpenCensus as the parent.
|
||||
type HTTPFormat struct{}
|
||||
|
||||
var _ propagation.HTTPFormat = (*HTTPFormat)(nil)
|
||||
|
||||
// SpanContextFromRequest extracts a B3 span context from incoming requests.
|
||||
func (f *HTTPFormat) SpanContextFromRequest(req *http.Request) (sc trace.SpanContext, ok bool) {
|
||||
tid, ok := ParseTraceID(req.Header.Get(TraceIDHeader))
|
||||
if !ok {
|
||||
return trace.SpanContext{}, false
|
||||
}
|
||||
sid, ok := ParseSpanID(req.Header.Get(SpanIDHeader))
|
||||
if !ok {
|
||||
return trace.SpanContext{}, false
|
||||
}
|
||||
sampled, _ := ParseSampled(req.Header.Get(SampledHeader))
|
||||
return trace.SpanContext{
|
||||
TraceID: tid,
|
||||
SpanID: sid,
|
||||
TraceOptions: sampled,
|
||||
}, true
|
||||
}
|
||||
|
||||
// ParseTraceID parses the value of the X-B3-TraceId header.
|
||||
func ParseTraceID(tid string) (trace.TraceID, bool) {
|
||||
if tid == "" {
|
||||
return trace.TraceID{}, false
|
||||
}
|
||||
b, err := hex.DecodeString(tid)
|
||||
if err != nil {
|
||||
return trace.TraceID{}, false
|
||||
}
|
||||
var traceID trace.TraceID
|
||||
if len(b) <= 8 {
|
||||
// The lower 64-bits.
|
||||
start := 8 + (8 - len(b))
|
||||
copy(traceID[start:], b)
|
||||
} else {
|
||||
start := 16 - len(b)
|
||||
copy(traceID[start:], b)
|
||||
}
|
||||
|
||||
return traceID, true
|
||||
}
|
||||
|
||||
// ParseSpanID parses the value of the X-B3-SpanId or X-B3-ParentSpanId headers.
|
||||
func ParseSpanID(sid string) (spanID trace.SpanID, ok bool) {
|
||||
if sid == "" {
|
||||
return trace.SpanID{}, false
|
||||
}
|
||||
b, err := hex.DecodeString(sid)
|
||||
if err != nil {
|
||||
return trace.SpanID{}, false
|
||||
}
|
||||
start := 8 - len(b)
|
||||
copy(spanID[start:], b)
|
||||
return spanID, true
|
||||
}
|
||||
|
||||
// ParseSampled parses the value of the X-B3-Sampled header.
|
||||
func ParseSampled(sampled string) (trace.TraceOptions, bool) {
|
||||
switch sampled {
|
||||
case "true", "1":
|
||||
return trace.TraceOptions(1), true
|
||||
default:
|
||||
return trace.TraceOptions(0), false
|
||||
}
|
||||
}
|
||||
|
||||
// SpanContextToRequest modifies the given request to include B3 headers.
|
||||
func (f *HTTPFormat) SpanContextToRequest(sc trace.SpanContext, req *http.Request) {
|
||||
req.Header.Set(TraceIDHeader, hex.EncodeToString(sc.TraceID[:]))
|
||||
req.Header.Set(SpanIDHeader, hex.EncodeToString(sc.SpanID[:]))
|
||||
|
||||
var sampled string
|
||||
if sc.IsSampled() {
|
||||
sampled = "1"
|
||||
} else {
|
||||
sampled = "0"
|
||||
}
|
||||
req.Header.Set(SampledHeader, sampled)
|
||||
}
|
61
vendor/go.opencensus.io/plugin/ochttp/route.go
generated
vendored
Normal file
61
vendor/go.opencensus.io/plugin/ochttp/route.go
generated
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
// Copyright 2018, OpenCensus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ochttp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"go.opencensus.io/tag"
|
||||
)
|
||||
|
||||
// SetRoute sets the http_server_route tag to the given value.
|
||||
// It's useful when an HTTP framework does not support the http.Handler interface
|
||||
// and using WithRouteTag is not an option, but provides a way to hook into the request flow.
|
||||
func SetRoute(ctx context.Context, route string) {
|
||||
if a, ok := ctx.Value(addedTagsKey{}).(*addedTags); ok {
|
||||
a.t = append(a.t, tag.Upsert(KeyServerRoute, route))
|
||||
}
|
||||
}
|
||||
|
||||
// WithRouteTag returns an http.Handler that records stats with the
|
||||
// http_server_route tag set to the given value.
|
||||
func WithRouteTag(handler http.Handler, route string) http.Handler {
|
||||
return taggedHandlerFunc(func(w http.ResponseWriter, r *http.Request) []tag.Mutator {
|
||||
addRoute := []tag.Mutator{tag.Upsert(KeyServerRoute, route)}
|
||||
ctx, _ := tag.New(r.Context(), addRoute...)
|
||||
r = r.WithContext(ctx)
|
||||
handler.ServeHTTP(w, r)
|
||||
return addRoute
|
||||
})
|
||||
}
|
||||
|
||||
// taggedHandlerFunc is a http.Handler that returns tags describing the
|
||||
// processing of the request. These tags will be recorded along with the
|
||||
// measures in this package at the end of the request.
|
||||
type taggedHandlerFunc func(w http.ResponseWriter, r *http.Request) []tag.Mutator
|
||||
|
||||
func (h taggedHandlerFunc) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
tags := h(w, r)
|
||||
if a, ok := r.Context().Value(addedTagsKey{}).(*addedTags); ok {
|
||||
a.t = append(a.t, tags...)
|
||||
}
|
||||
}
|
||||
|
||||
type addedTagsKey struct{}
|
||||
|
||||
type addedTags struct {
|
||||
t []tag.Mutator
|
||||
}
|
449
vendor/go.opencensus.io/plugin/ochttp/server.go
generated
vendored
Normal file
449
vendor/go.opencensus.io/plugin/ochttp/server.go
generated
vendored
Normal file
@ -0,0 +1,449 @@
|
||||
// Copyright 2018, OpenCensus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ochttp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"go.opencensus.io/stats"
|
||||
"go.opencensus.io/tag"
|
||||
"go.opencensus.io/trace"
|
||||
"go.opencensus.io/trace/propagation"
|
||||
)
|
||||
|
||||
// Handler is an http.Handler wrapper to instrument your HTTP server with
|
||||
// OpenCensus. It supports both stats and tracing.
|
||||
//
|
||||
// Tracing
|
||||
//
|
||||
// This handler is aware of the incoming request's span, reading it from request
|
||||
// headers as configured using the Propagation field.
|
||||
// The extracted span can be accessed from the incoming request's
|
||||
// context.
|
||||
//
|
||||
// span := trace.FromContext(r.Context())
|
||||
//
|
||||
// The server span will be automatically ended at the end of ServeHTTP.
|
||||
type Handler struct {
|
||||
// Propagation defines how traces are propagated. If unspecified,
|
||||
// B3 propagation will be used.
|
||||
Propagation propagation.HTTPFormat
|
||||
|
||||
// Handler is the handler used to handle the incoming request.
|
||||
Handler http.Handler
|
||||
|
||||
// StartOptions are applied to the span started by this Handler around each
|
||||
// request.
|
||||
//
|
||||
// StartOptions.SpanKind will always be set to trace.SpanKindServer
|
||||
// for spans started by this transport.
|
||||
StartOptions trace.StartOptions
|
||||
|
||||
// GetStartOptions allows to set start options per request. If set,
|
||||
// StartOptions is going to be ignored.
|
||||
GetStartOptions func(*http.Request) trace.StartOptions
|
||||
|
||||
// IsPublicEndpoint should be set to true for publicly accessible HTTP(S)
|
||||
// servers. If true, any trace metadata set on the incoming request will
|
||||
// be added as a linked trace instead of being added as a parent of the
|
||||
// current trace.
|
||||
IsPublicEndpoint bool
|
||||
|
||||
// FormatSpanName holds the function to use for generating the span name
|
||||
// from the information found in the incoming HTTP Request. By default the
|
||||
// name equals the URL Path.
|
||||
FormatSpanName func(*http.Request) string
|
||||
}
|
||||
|
||||
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
var tags addedTags
|
||||
r, traceEnd := h.startTrace(w, r)
|
||||
defer traceEnd()
|
||||
w, statsEnd := h.startStats(w, r)
|
||||
defer statsEnd(&tags)
|
||||
handler := h.Handler
|
||||
if handler == nil {
|
||||
handler = http.DefaultServeMux
|
||||
}
|
||||
r = r.WithContext(context.WithValue(r.Context(), addedTagsKey{}, &tags))
|
||||
handler.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
func (h *Handler) startTrace(w http.ResponseWriter, r *http.Request) (*http.Request, func()) {
|
||||
if isHealthEndpoint(r.URL.Path) {
|
||||
return r, func() {}
|
||||
}
|
||||
var name string
|
||||
if h.FormatSpanName == nil {
|
||||
name = spanNameFromURL(r)
|
||||
} else {
|
||||
name = h.FormatSpanName(r)
|
||||
}
|
||||
ctx := r.Context()
|
||||
|
||||
startOpts := h.StartOptions
|
||||
if h.GetStartOptions != nil {
|
||||
startOpts = h.GetStartOptions(r)
|
||||
}
|
||||
|
||||
var span *trace.Span
|
||||
sc, ok := h.extractSpanContext(r)
|
||||
if ok && !h.IsPublicEndpoint {
|
||||
ctx, span = trace.StartSpanWithRemoteParent(ctx, name, sc,
|
||||
trace.WithSampler(startOpts.Sampler),
|
||||
trace.WithSpanKind(trace.SpanKindServer))
|
||||
} else {
|
||||
ctx, span = trace.StartSpan(ctx, name,
|
||||
trace.WithSampler(startOpts.Sampler),
|
||||
trace.WithSpanKind(trace.SpanKindServer),
|
||||
)
|
||||
if ok {
|
||||
span.AddLink(trace.Link{
|
||||
TraceID: sc.TraceID,
|
||||
SpanID: sc.SpanID,
|
||||
Type: trace.LinkTypeParent,
|
||||
Attributes: nil,
|
||||
})
|
||||
}
|
||||
}
|
||||
span.AddAttributes(requestAttrs(r)...)
|
||||
if r.Body == nil {
|
||||
// TODO: Handle cases where ContentLength is not set.
|
||||
} else if r.ContentLength > 0 {
|
||||
span.AddMessageReceiveEvent(0, /* TODO: messageID */
|
||||
int64(r.ContentLength), -1)
|
||||
}
|
||||
return r.WithContext(ctx), span.End
|
||||
}
|
||||
|
||||
func (h *Handler) extractSpanContext(r *http.Request) (trace.SpanContext, bool) {
|
||||
if h.Propagation == nil {
|
||||
return defaultFormat.SpanContextFromRequest(r)
|
||||
}
|
||||
return h.Propagation.SpanContextFromRequest(r)
|
||||
}
|
||||
|
||||
func (h *Handler) startStats(w http.ResponseWriter, r *http.Request) (http.ResponseWriter, func(tags *addedTags)) {
|
||||
ctx, _ := tag.New(r.Context(),
|
||||
tag.Upsert(Host, r.Host),
|
||||
tag.Upsert(Path, r.URL.Path),
|
||||
tag.Upsert(Method, r.Method))
|
||||
track := &trackingResponseWriter{
|
||||
start: time.Now(),
|
||||
ctx: ctx,
|
||||
writer: w,
|
||||
}
|
||||
if r.Body == nil {
|
||||
// TODO: Handle cases where ContentLength is not set.
|
||||
track.reqSize = -1
|
||||
} else if r.ContentLength > 0 {
|
||||
track.reqSize = r.ContentLength
|
||||
}
|
||||
stats.Record(ctx, ServerRequestCount.M(1))
|
||||
return track.wrappedResponseWriter(), track.end
|
||||
}
|
||||
|
||||
type trackingResponseWriter struct {
|
||||
ctx context.Context
|
||||
reqSize int64
|
||||
respSize int64
|
||||
start time.Time
|
||||
statusCode int
|
||||
statusLine string
|
||||
endOnce sync.Once
|
||||
writer http.ResponseWriter
|
||||
}
|
||||
|
||||
// Compile time assertion for ResponseWriter interface
|
||||
var _ http.ResponseWriter = (*trackingResponseWriter)(nil)
|
||||
|
||||
var logTagsErrorOnce sync.Once
|
||||
|
||||
func (t *trackingResponseWriter) end(tags *addedTags) {
|
||||
t.endOnce.Do(func() {
|
||||
if t.statusCode == 0 {
|
||||
t.statusCode = 200
|
||||
}
|
||||
|
||||
span := trace.FromContext(t.ctx)
|
||||
span.SetStatus(TraceStatus(t.statusCode, t.statusLine))
|
||||
span.AddAttributes(trace.Int64Attribute(StatusCodeAttribute, int64(t.statusCode)))
|
||||
|
||||
m := []stats.Measurement{
|
||||
ServerLatency.M(float64(time.Since(t.start)) / float64(time.Millisecond)),
|
||||
ServerResponseBytes.M(t.respSize),
|
||||
}
|
||||
if t.reqSize >= 0 {
|
||||
m = append(m, ServerRequestBytes.M(t.reqSize))
|
||||
}
|
||||
allTags := make([]tag.Mutator, len(tags.t)+1)
|
||||
allTags[0] = tag.Upsert(StatusCode, strconv.Itoa(t.statusCode))
|
||||
copy(allTags[1:], tags.t)
|
||||
stats.RecordWithTags(t.ctx, allTags, m...)
|
||||
})
|
||||
}
|
||||
|
||||
func (t *trackingResponseWriter) Header() http.Header {
|
||||
return t.writer.Header()
|
||||
}
|
||||
|
||||
func (t *trackingResponseWriter) Write(data []byte) (int, error) {
|
||||
n, err := t.writer.Write(data)
|
||||
t.respSize += int64(n)
|
||||
// Add message event for request bytes sent.
|
||||
span := trace.FromContext(t.ctx)
|
||||
span.AddMessageSendEvent(0 /* TODO: messageID */, int64(n), -1)
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (t *trackingResponseWriter) WriteHeader(statusCode int) {
|
||||
t.writer.WriteHeader(statusCode)
|
||||
t.statusCode = statusCode
|
||||
t.statusLine = http.StatusText(t.statusCode)
|
||||
}
|
||||
|
||||
// wrappedResponseWriter returns a wrapped version of the original
|
||||
// ResponseWriter and only implements the same combination of additional
|
||||
// interfaces as the original.
|
||||
// This implementation is based on https://github.com/felixge/httpsnoop.
|
||||
func (t *trackingResponseWriter) wrappedResponseWriter() http.ResponseWriter {
|
||||
var (
|
||||
hj, i0 = t.writer.(http.Hijacker)
|
||||
cn, i1 = t.writer.(http.CloseNotifier)
|
||||
pu, i2 = t.writer.(http.Pusher)
|
||||
fl, i3 = t.writer.(http.Flusher)
|
||||
rf, i4 = t.writer.(io.ReaderFrom)
|
||||
)
|
||||
|
||||
switch {
|
||||
case !i0 && !i1 && !i2 && !i3 && !i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
}{t}
|
||||
case !i0 && !i1 && !i2 && !i3 && i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
io.ReaderFrom
|
||||
}{t, rf}
|
||||
case !i0 && !i1 && !i2 && i3 && !i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Flusher
|
||||
}{t, fl}
|
||||
case !i0 && !i1 && !i2 && i3 && i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Flusher
|
||||
io.ReaderFrom
|
||||
}{t, fl, rf}
|
||||
case !i0 && !i1 && i2 && !i3 && !i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Pusher
|
||||
}{t, pu}
|
||||
case !i0 && !i1 && i2 && !i3 && i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Pusher
|
||||
io.ReaderFrom
|
||||
}{t, pu, rf}
|
||||
case !i0 && !i1 && i2 && i3 && !i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Pusher
|
||||
http.Flusher
|
||||
}{t, pu, fl}
|
||||
case !i0 && !i1 && i2 && i3 && i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Pusher
|
||||
http.Flusher
|
||||
io.ReaderFrom
|
||||
}{t, pu, fl, rf}
|
||||
case !i0 && i1 && !i2 && !i3 && !i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.CloseNotifier
|
||||
}{t, cn}
|
||||
case !i0 && i1 && !i2 && !i3 && i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.CloseNotifier
|
||||
io.ReaderFrom
|
||||
}{t, cn, rf}
|
||||
case !i0 && i1 && !i2 && i3 && !i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.CloseNotifier
|
||||
http.Flusher
|
||||
}{t, cn, fl}
|
||||
case !i0 && i1 && !i2 && i3 && i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.CloseNotifier
|
||||
http.Flusher
|
||||
io.ReaderFrom
|
||||
}{t, cn, fl, rf}
|
||||
case !i0 && i1 && i2 && !i3 && !i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.CloseNotifier
|
||||
http.Pusher
|
||||
}{t, cn, pu}
|
||||
case !i0 && i1 && i2 && !i3 && i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.CloseNotifier
|
||||
http.Pusher
|
||||
io.ReaderFrom
|
||||
}{t, cn, pu, rf}
|
||||
case !i0 && i1 && i2 && i3 && !i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.CloseNotifier
|
||||
http.Pusher
|
||||
http.Flusher
|
||||
}{t, cn, pu, fl}
|
||||
case !i0 && i1 && i2 && i3 && i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.CloseNotifier
|
||||
http.Pusher
|
||||
http.Flusher
|
||||
io.ReaderFrom
|
||||
}{t, cn, pu, fl, rf}
|
||||
case i0 && !i1 && !i2 && !i3 && !i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Hijacker
|
||||
}{t, hj}
|
||||
case i0 && !i1 && !i2 && !i3 && i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Hijacker
|
||||
io.ReaderFrom
|
||||
}{t, hj, rf}
|
||||
case i0 && !i1 && !i2 && i3 && !i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Hijacker
|
||||
http.Flusher
|
||||
}{t, hj, fl}
|
||||
case i0 && !i1 && !i2 && i3 && i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Hijacker
|
||||
http.Flusher
|
||||
io.ReaderFrom
|
||||
}{t, hj, fl, rf}
|
||||
case i0 && !i1 && i2 && !i3 && !i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Hijacker
|
||||
http.Pusher
|
||||
}{t, hj, pu}
|
||||
case i0 && !i1 && i2 && !i3 && i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Hijacker
|
||||
http.Pusher
|
||||
io.ReaderFrom
|
||||
}{t, hj, pu, rf}
|
||||
case i0 && !i1 && i2 && i3 && !i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Hijacker
|
||||
http.Pusher
|
||||
http.Flusher
|
||||
}{t, hj, pu, fl}
|
||||
case i0 && !i1 && i2 && i3 && i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Hijacker
|
||||
http.Pusher
|
||||
http.Flusher
|
||||
io.ReaderFrom
|
||||
}{t, hj, pu, fl, rf}
|
||||
case i0 && i1 && !i2 && !i3 && !i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Hijacker
|
||||
http.CloseNotifier
|
||||
}{t, hj, cn}
|
||||
case i0 && i1 && !i2 && !i3 && i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Hijacker
|
||||
http.CloseNotifier
|
||||
io.ReaderFrom
|
||||
}{t, hj, cn, rf}
|
||||
case i0 && i1 && !i2 && i3 && !i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Hijacker
|
||||
http.CloseNotifier
|
||||
http.Flusher
|
||||
}{t, hj, cn, fl}
|
||||
case i0 && i1 && !i2 && i3 && i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Hijacker
|
||||
http.CloseNotifier
|
||||
http.Flusher
|
||||
io.ReaderFrom
|
||||
}{t, hj, cn, fl, rf}
|
||||
case i0 && i1 && i2 && !i3 && !i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Hijacker
|
||||
http.CloseNotifier
|
||||
http.Pusher
|
||||
}{t, hj, cn, pu}
|
||||
case i0 && i1 && i2 && !i3 && i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Hijacker
|
||||
http.CloseNotifier
|
||||
http.Pusher
|
||||
io.ReaderFrom
|
||||
}{t, hj, cn, pu, rf}
|
||||
case i0 && i1 && i2 && i3 && !i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Hijacker
|
||||
http.CloseNotifier
|
||||
http.Pusher
|
||||
http.Flusher
|
||||
}{t, hj, cn, pu, fl}
|
||||
case i0 && i1 && i2 && i3 && i4:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
http.Hijacker
|
||||
http.CloseNotifier
|
||||
http.Pusher
|
||||
http.Flusher
|
||||
io.ReaderFrom
|
||||
}{t, hj, cn, pu, fl, rf}
|
||||
default:
|
||||
return struct {
|
||||
http.ResponseWriter
|
||||
}{t}
|
||||
}
|
||||
}
|
169
vendor/go.opencensus.io/plugin/ochttp/span_annotating_client_trace.go
generated
vendored
Normal file
169
vendor/go.opencensus.io/plugin/ochttp/span_annotating_client_trace.go
generated
vendored
Normal file
@ -0,0 +1,169 @@
|
||||
// Copyright 2018, OpenCensus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ochttp
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net/http"
|
||||
"net/http/httptrace"
|
||||
"strings"
|
||||
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
type spanAnnotator struct {
|
||||
sp *trace.Span
|
||||
}
|
||||
|
||||
// TODO: Remove NewSpanAnnotator at the next release.
|
||||
|
||||
// NewSpanAnnotator returns a httptrace.ClientTrace which annotates
|
||||
// all emitted httptrace events on the provided Span.
|
||||
// Deprecated: Use NewSpanAnnotatingClientTrace instead
|
||||
func NewSpanAnnotator(r *http.Request, s *trace.Span) *httptrace.ClientTrace {
|
||||
return NewSpanAnnotatingClientTrace(r, s)
|
||||
}
|
||||
|
||||
// NewSpanAnnotatingClientTrace returns a httptrace.ClientTrace which annotates
|
||||
// all emitted httptrace events on the provided Span.
|
||||
func NewSpanAnnotatingClientTrace(_ *http.Request, s *trace.Span) *httptrace.ClientTrace {
|
||||
sa := spanAnnotator{sp: s}
|
||||
|
||||
return &httptrace.ClientTrace{
|
||||
GetConn: sa.getConn,
|
||||
GotConn: sa.gotConn,
|
||||
PutIdleConn: sa.putIdleConn,
|
||||
GotFirstResponseByte: sa.gotFirstResponseByte,
|
||||
Got100Continue: sa.got100Continue,
|
||||
DNSStart: sa.dnsStart,
|
||||
DNSDone: sa.dnsDone,
|
||||
ConnectStart: sa.connectStart,
|
||||
ConnectDone: sa.connectDone,
|
||||
TLSHandshakeStart: sa.tlsHandshakeStart,
|
||||
TLSHandshakeDone: sa.tlsHandshakeDone,
|
||||
WroteHeaders: sa.wroteHeaders,
|
||||
Wait100Continue: sa.wait100Continue,
|
||||
WroteRequest: sa.wroteRequest,
|
||||
}
|
||||
}
|
||||
|
||||
func (s spanAnnotator) getConn(hostPort string) {
|
||||
attrs := []trace.Attribute{
|
||||
trace.StringAttribute("httptrace.get_connection.host_port", hostPort),
|
||||
}
|
||||
s.sp.Annotate(attrs, "GetConn")
|
||||
}
|
||||
|
||||
func (s spanAnnotator) gotConn(info httptrace.GotConnInfo) {
|
||||
attrs := []trace.Attribute{
|
||||
trace.BoolAttribute("httptrace.got_connection.reused", info.Reused),
|
||||
trace.BoolAttribute("httptrace.got_connection.was_idle", info.WasIdle),
|
||||
}
|
||||
if info.WasIdle {
|
||||
attrs = append(attrs,
|
||||
trace.StringAttribute("httptrace.got_connection.idle_time", info.IdleTime.String()))
|
||||
}
|
||||
s.sp.Annotate(attrs, "GotConn")
|
||||
}
|
||||
|
||||
// PutIdleConn implements a httptrace.ClientTrace hook
|
||||
func (s spanAnnotator) putIdleConn(err error) {
|
||||
var attrs []trace.Attribute
|
||||
if err != nil {
|
||||
attrs = append(attrs,
|
||||
trace.StringAttribute("httptrace.put_idle_connection.error", err.Error()))
|
||||
}
|
||||
s.sp.Annotate(attrs, "PutIdleConn")
|
||||
}
|
||||
|
||||
func (s spanAnnotator) gotFirstResponseByte() {
|
||||
s.sp.Annotate(nil, "GotFirstResponseByte")
|
||||
}
|
||||
|
||||
func (s spanAnnotator) got100Continue() {
|
||||
s.sp.Annotate(nil, "Got100Continue")
|
||||
}
|
||||
|
||||
func (s spanAnnotator) dnsStart(info httptrace.DNSStartInfo) {
|
||||
attrs := []trace.Attribute{
|
||||
trace.StringAttribute("httptrace.dns_start.host", info.Host),
|
||||
}
|
||||
s.sp.Annotate(attrs, "DNSStart")
|
||||
}
|
||||
|
||||
func (s spanAnnotator) dnsDone(info httptrace.DNSDoneInfo) {
|
||||
var addrs []string
|
||||
for _, addr := range info.Addrs {
|
||||
addrs = append(addrs, addr.String())
|
||||
}
|
||||
attrs := []trace.Attribute{
|
||||
trace.StringAttribute("httptrace.dns_done.addrs", strings.Join(addrs, " , ")),
|
||||
}
|
||||
if info.Err != nil {
|
||||
attrs = append(attrs,
|
||||
trace.StringAttribute("httptrace.dns_done.error", info.Err.Error()))
|
||||
}
|
||||
s.sp.Annotate(attrs, "DNSDone")
|
||||
}
|
||||
|
||||
func (s spanAnnotator) connectStart(network, addr string) {
|
||||
attrs := []trace.Attribute{
|
||||
trace.StringAttribute("httptrace.connect_start.network", network),
|
||||
trace.StringAttribute("httptrace.connect_start.addr", addr),
|
||||
}
|
||||
s.sp.Annotate(attrs, "ConnectStart")
|
||||
}
|
||||
|
||||
func (s spanAnnotator) connectDone(network, addr string, err error) {
|
||||
attrs := []trace.Attribute{
|
||||
trace.StringAttribute("httptrace.connect_done.network", network),
|
||||
trace.StringAttribute("httptrace.connect_done.addr", addr),
|
||||
}
|
||||
if err != nil {
|
||||
attrs = append(attrs,
|
||||
trace.StringAttribute("httptrace.connect_done.error", err.Error()))
|
||||
}
|
||||
s.sp.Annotate(attrs, "ConnectDone")
|
||||
}
|
||||
|
||||
func (s spanAnnotator) tlsHandshakeStart() {
|
||||
s.sp.Annotate(nil, "TLSHandshakeStart")
|
||||
}
|
||||
|
||||
func (s spanAnnotator) tlsHandshakeDone(_ tls.ConnectionState, err error) {
|
||||
var attrs []trace.Attribute
|
||||
if err != nil {
|
||||
attrs = append(attrs,
|
||||
trace.StringAttribute("httptrace.tls_handshake_done.error", err.Error()))
|
||||
}
|
||||
s.sp.Annotate(attrs, "TLSHandshakeDone")
|
||||
}
|
||||
|
||||
func (s spanAnnotator) wroteHeaders() {
|
||||
s.sp.Annotate(nil, "WroteHeaders")
|
||||
}
|
||||
|
||||
func (s spanAnnotator) wait100Continue() {
|
||||
s.sp.Annotate(nil, "Wait100Continue")
|
||||
}
|
||||
|
||||
func (s spanAnnotator) wroteRequest(info httptrace.WroteRequestInfo) {
|
||||
var attrs []trace.Attribute
|
||||
if info.Err != nil {
|
||||
attrs = append(attrs,
|
||||
trace.StringAttribute("httptrace.wrote_request.error", info.Err.Error()))
|
||||
}
|
||||
s.sp.Annotate(attrs, "WroteRequest")
|
||||
}
|
292
vendor/go.opencensus.io/plugin/ochttp/stats.go
generated
vendored
Normal file
292
vendor/go.opencensus.io/plugin/ochttp/stats.go
generated
vendored
Normal file
@ -0,0 +1,292 @@
|
||||
// Copyright 2018, OpenCensus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ochttp
|
||||
|
||||
import (
|
||||
"go.opencensus.io/stats"
|
||||
"go.opencensus.io/stats/view"
|
||||
"go.opencensus.io/tag"
|
||||
)
|
||||
|
||||
// Deprecated: client HTTP measures.
|
||||
var (
|
||||
// Deprecated: Use a Count aggregation over one of the other client measures to achieve the same effect.
|
||||
ClientRequestCount = stats.Int64(
|
||||
"opencensus.io/http/client/request_count",
|
||||
"Number of HTTP requests started",
|
||||
stats.UnitDimensionless)
|
||||
// Deprecated: Use ClientSentBytes.
|
||||
ClientRequestBytes = stats.Int64(
|
||||
"opencensus.io/http/client/request_bytes",
|
||||
"HTTP request body size if set as ContentLength (uncompressed)",
|
||||
stats.UnitBytes)
|
||||
// Deprecated: Use ClientReceivedBytes.
|
||||
ClientResponseBytes = stats.Int64(
|
||||
"opencensus.io/http/client/response_bytes",
|
||||
"HTTP response body size (uncompressed)",
|
||||
stats.UnitBytes)
|
||||
// Deprecated: Use ClientRoundtripLatency.
|
||||
ClientLatency = stats.Float64(
|
||||
"opencensus.io/http/client/latency",
|
||||
"End-to-end latency",
|
||||
stats.UnitMilliseconds)
|
||||
)
|
||||
|
||||
// The following client HTTP measures are supported for use in custom views.
|
||||
var (
|
||||
ClientSentBytes = stats.Int64(
|
||||
"opencensus.io/http/client/sent_bytes",
|
||||
"Total bytes sent in request body (not including headers)",
|
||||
stats.UnitBytes,
|
||||
)
|
||||
ClientReceivedBytes = stats.Int64(
|
||||
"opencensus.io/http/client/received_bytes",
|
||||
"Total bytes received in response bodies (not including headers but including error responses with bodies)",
|
||||
stats.UnitBytes,
|
||||
)
|
||||
ClientRoundtripLatency = stats.Float64(
|
||||
"opencensus.io/http/client/roundtrip_latency",
|
||||
"Time between first byte of request headers sent to last byte of response received, or terminal error",
|
||||
stats.UnitMilliseconds,
|
||||
)
|
||||
)
|
||||
|
||||
// The following server HTTP measures are supported for use in custom views:
|
||||
var (
|
||||
ServerRequestCount = stats.Int64(
|
||||
"opencensus.io/http/server/request_count",
|
||||
"Number of HTTP requests started",
|
||||
stats.UnitDimensionless)
|
||||
ServerRequestBytes = stats.Int64(
|
||||
"opencensus.io/http/server/request_bytes",
|
||||
"HTTP request body size if set as ContentLength (uncompressed)",
|
||||
stats.UnitBytes)
|
||||
ServerResponseBytes = stats.Int64(
|
||||
"opencensus.io/http/server/response_bytes",
|
||||
"HTTP response body size (uncompressed)",
|
||||
stats.UnitBytes)
|
||||
ServerLatency = stats.Float64(
|
||||
"opencensus.io/http/server/latency",
|
||||
"End-to-end latency",
|
||||
stats.UnitMilliseconds)
|
||||
)
|
||||
|
||||
// The following tags are applied to stats recorded by this package. Host, Path
|
||||
// and Method are applied to all measures. StatusCode is not applied to
|
||||
// ClientRequestCount or ServerRequestCount, since it is recorded before the status is known.
|
||||
var (
|
||||
// Host is the value of the HTTP Host header.
|
||||
//
|
||||
// The value of this tag can be controlled by the HTTP client, so you need
|
||||
// to watch out for potentially generating high-cardinality labels in your
|
||||
// metrics backend if you use this tag in views.
|
||||
Host, _ = tag.NewKey("http.host")
|
||||
|
||||
// StatusCode is the numeric HTTP response status code,
|
||||
// or "error" if a transport error occurred and no status code was read.
|
||||
StatusCode, _ = tag.NewKey("http.status")
|
||||
|
||||
// Path is the URL path (not including query string) in the request.
|
||||
//
|
||||
// The value of this tag can be controlled by the HTTP client, so you need
|
||||
// to watch out for potentially generating high-cardinality labels in your
|
||||
// metrics backend if you use this tag in views.
|
||||
Path, _ = tag.NewKey("http.path")
|
||||
|
||||
// Method is the HTTP method of the request, capitalized (GET, POST, etc.).
|
||||
Method, _ = tag.NewKey("http.method")
|
||||
|
||||
// KeyServerRoute is a low cardinality string representing the logical
|
||||
// handler of the request. This is usually the pattern registered on the a
|
||||
// ServeMux (or similar string).
|
||||
KeyServerRoute, _ = tag.NewKey("http_server_route")
|
||||
)
|
||||
|
||||
// Client tag keys.
|
||||
var (
|
||||
// KeyClientMethod is the HTTP method, capitalized (i.e. GET, POST, PUT, DELETE, etc.).
|
||||
KeyClientMethod, _ = tag.NewKey("http_client_method")
|
||||
// KeyClientPath is the URL path (not including query string).
|
||||
KeyClientPath, _ = tag.NewKey("http_client_path")
|
||||
// KeyClientStatus is the HTTP status code as an integer (e.g. 200, 404, 500.), or "error" if no response status line was received.
|
||||
KeyClientStatus, _ = tag.NewKey("http_client_status")
|
||||
// KeyClientHost is the value of the request Host header.
|
||||
KeyClientHost, _ = tag.NewKey("http_client_host")
|
||||
)
|
||||
|
||||
// Default distributions used by views in this package.
|
||||
var (
|
||||
DefaultSizeDistribution = view.Distribution(1024, 2048, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216, 67108864, 268435456, 1073741824, 4294967296)
|
||||
DefaultLatencyDistribution = view.Distribution(1, 2, 3, 4, 5, 6, 8, 10, 13, 16, 20, 25, 30, 40, 50, 65, 80, 100, 130, 160, 200, 250, 300, 400, 500, 650, 800, 1000, 2000, 5000, 10000, 20000, 50000, 100000)
|
||||
)
|
||||
|
||||
// Package ochttp provides some convenience views for client measures.
|
||||
// You still need to register these views for data to actually be collected.
|
||||
var (
|
||||
ClientSentBytesDistribution = &view.View{
|
||||
Name: "opencensus.io/http/client/sent_bytes",
|
||||
Measure: ClientSentBytes,
|
||||
Aggregation: DefaultSizeDistribution,
|
||||
Description: "Total bytes sent in request body (not including headers), by HTTP method and response status",
|
||||
TagKeys: []tag.Key{KeyClientMethod, KeyClientStatus},
|
||||
}
|
||||
|
||||
ClientReceivedBytesDistribution = &view.View{
|
||||
Name: "opencensus.io/http/client/received_bytes",
|
||||
Measure: ClientReceivedBytes,
|
||||
Aggregation: DefaultSizeDistribution,
|
||||
Description: "Total bytes received in response bodies (not including headers but including error responses with bodies), by HTTP method and response status",
|
||||
TagKeys: []tag.Key{KeyClientMethod, KeyClientStatus},
|
||||
}
|
||||
|
||||
ClientRoundtripLatencyDistribution = &view.View{
|
||||
Name: "opencensus.io/http/client/roundtrip_latency",
|
||||
Measure: ClientRoundtripLatency,
|
||||
Aggregation: DefaultLatencyDistribution,
|
||||
Description: "End-to-end latency, by HTTP method and response status",
|
||||
TagKeys: []tag.Key{KeyClientMethod, KeyClientStatus},
|
||||
}
|
||||
|
||||
ClientCompletedCount = &view.View{
|
||||
Name: "opencensus.io/http/client/completed_count",
|
||||
Measure: ClientRoundtripLatency,
|
||||
Aggregation: view.Count(),
|
||||
Description: "Count of completed requests, by HTTP method and response status",
|
||||
TagKeys: []tag.Key{KeyClientMethod, KeyClientStatus},
|
||||
}
|
||||
)
|
||||
|
||||
// Deprecated: Old client Views.
|
||||
var (
|
||||
// Deprecated: No direct replacement, but see ClientCompletedCount.
|
||||
ClientRequestCountView = &view.View{
|
||||
Name: "opencensus.io/http/client/request_count",
|
||||
Description: "Count of HTTP requests started",
|
||||
Measure: ClientRequestCount,
|
||||
Aggregation: view.Count(),
|
||||
}
|
||||
|
||||
// Deprecated: Use ClientSentBytesDistribution.
|
||||
ClientRequestBytesView = &view.View{
|
||||
Name: "opencensus.io/http/client/request_bytes",
|
||||
Description: "Size distribution of HTTP request body",
|
||||
Measure: ClientSentBytes,
|
||||
Aggregation: DefaultSizeDistribution,
|
||||
}
|
||||
|
||||
// Deprecated: Use ClientReceivedBytesDistribution instead.
|
||||
ClientResponseBytesView = &view.View{
|
||||
Name: "opencensus.io/http/client/response_bytes",
|
||||
Description: "Size distribution of HTTP response body",
|
||||
Measure: ClientReceivedBytes,
|
||||
Aggregation: DefaultSizeDistribution,
|
||||
}
|
||||
|
||||
// Deprecated: Use ClientRoundtripLatencyDistribution instead.
|
||||
ClientLatencyView = &view.View{
|
||||
Name: "opencensus.io/http/client/latency",
|
||||
Description: "Latency distribution of HTTP requests",
|
||||
Measure: ClientRoundtripLatency,
|
||||
Aggregation: DefaultLatencyDistribution,
|
||||
}
|
||||
|
||||
// Deprecated: Use ClientCompletedCount instead.
|
||||
ClientRequestCountByMethod = &view.View{
|
||||
Name: "opencensus.io/http/client/request_count_by_method",
|
||||
Description: "Client request count by HTTP method",
|
||||
TagKeys: []tag.Key{Method},
|
||||
Measure: ClientSentBytes,
|
||||
Aggregation: view.Count(),
|
||||
}
|
||||
|
||||
// Deprecated: Use ClientCompletedCount instead.
|
||||
ClientResponseCountByStatusCode = &view.View{
|
||||
Name: "opencensus.io/http/client/response_count_by_status_code",
|
||||
Description: "Client response count by status code",
|
||||
TagKeys: []tag.Key{StatusCode},
|
||||
Measure: ClientRoundtripLatency,
|
||||
Aggregation: view.Count(),
|
||||
}
|
||||
)
|
||||
|
||||
// Package ochttp provides some convenience views for server measures.
|
||||
// You still need to register these views for data to actually be collected.
|
||||
var (
|
||||
ServerRequestCountView = &view.View{
|
||||
Name: "opencensus.io/http/server/request_count",
|
||||
Description: "Count of HTTP requests started",
|
||||
Measure: ServerRequestCount,
|
||||
Aggregation: view.Count(),
|
||||
}
|
||||
|
||||
ServerRequestBytesView = &view.View{
|
||||
Name: "opencensus.io/http/server/request_bytes",
|
||||
Description: "Size distribution of HTTP request body",
|
||||
Measure: ServerRequestBytes,
|
||||
Aggregation: DefaultSizeDistribution,
|
||||
}
|
||||
|
||||
ServerResponseBytesView = &view.View{
|
||||
Name: "opencensus.io/http/server/response_bytes",
|
||||
Description: "Size distribution of HTTP response body",
|
||||
Measure: ServerResponseBytes,
|
||||
Aggregation: DefaultSizeDistribution,
|
||||
}
|
||||
|
||||
ServerLatencyView = &view.View{
|
||||
Name: "opencensus.io/http/server/latency",
|
||||
Description: "Latency distribution of HTTP requests",
|
||||
Measure: ServerLatency,
|
||||
Aggregation: DefaultLatencyDistribution,
|
||||
}
|
||||
|
||||
ServerRequestCountByMethod = &view.View{
|
||||
Name: "opencensus.io/http/server/request_count_by_method",
|
||||
Description: "Server request count by HTTP method",
|
||||
TagKeys: []tag.Key{Method},
|
||||
Measure: ServerRequestCount,
|
||||
Aggregation: view.Count(),
|
||||
}
|
||||
|
||||
ServerResponseCountByStatusCode = &view.View{
|
||||
Name: "opencensus.io/http/server/response_count_by_status_code",
|
||||
Description: "Server response count by status code",
|
||||
TagKeys: []tag.Key{StatusCode},
|
||||
Measure: ServerLatency,
|
||||
Aggregation: view.Count(),
|
||||
}
|
||||
)
|
||||
|
||||
// DefaultClientViews are the default client views provided by this package.
|
||||
// Deprecated: No replacement. Register the views you would like individually.
|
||||
var DefaultClientViews = []*view.View{
|
||||
ClientRequestCountView,
|
||||
ClientRequestBytesView,
|
||||
ClientResponseBytesView,
|
||||
ClientLatencyView,
|
||||
ClientRequestCountByMethod,
|
||||
ClientResponseCountByStatusCode,
|
||||
}
|
||||
|
||||
// DefaultServerViews are the default server views provided by this package.
|
||||
// Deprecated: No replacement. Register the views you would like individually.
|
||||
var DefaultServerViews = []*view.View{
|
||||
ServerRequestCountView,
|
||||
ServerRequestBytesView,
|
||||
ServerResponseBytesView,
|
||||
ServerLatencyView,
|
||||
ServerRequestCountByMethod,
|
||||
ServerResponseCountByStatusCode,
|
||||
}
|
239
vendor/go.opencensus.io/plugin/ochttp/trace.go
generated
vendored
Normal file
239
vendor/go.opencensus.io/plugin/ochttp/trace.go
generated
vendored
Normal file
@ -0,0 +1,239 @@
|
||||
// Copyright 2018, OpenCensus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ochttp
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptrace"
|
||||
|
||||
"go.opencensus.io/plugin/ochttp/propagation/b3"
|
||||
"go.opencensus.io/trace"
|
||||
"go.opencensus.io/trace/propagation"
|
||||
)
|
||||
|
||||
// TODO(jbd): Add godoc examples.
|
||||
|
||||
var defaultFormat propagation.HTTPFormat = &b3.HTTPFormat{}
|
||||
|
||||
// Attributes recorded on the span for the requests.
|
||||
// Only trace exporters will need them.
|
||||
const (
|
||||
HostAttribute = "http.host"
|
||||
MethodAttribute = "http.method"
|
||||
PathAttribute = "http.path"
|
||||
URLAttribute = "http.url"
|
||||
UserAgentAttribute = "http.user_agent"
|
||||
StatusCodeAttribute = "http.status_code"
|
||||
)
|
||||
|
||||
type traceTransport struct {
|
||||
base http.RoundTripper
|
||||
startOptions trace.StartOptions
|
||||
format propagation.HTTPFormat
|
||||
formatSpanName func(*http.Request) string
|
||||
newClientTrace func(*http.Request, *trace.Span) *httptrace.ClientTrace
|
||||
}
|
||||
|
||||
// TODO(jbd): Add message events for request and response size.
|
||||
|
||||
// RoundTrip creates a trace.Span and inserts it into the outgoing request's headers.
|
||||
// The created span can follow a parent span, if a parent is presented in
|
||||
// the request's context.
|
||||
func (t *traceTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
name := t.formatSpanName(req)
|
||||
// TODO(jbd): Discuss whether we want to prefix
|
||||
// outgoing requests with Sent.
|
||||
ctx, span := trace.StartSpan(req.Context(), name,
|
||||
trace.WithSampler(t.startOptions.Sampler),
|
||||
trace.WithSpanKind(trace.SpanKindClient))
|
||||
|
||||
if t.newClientTrace != nil {
|
||||
req = req.WithContext(httptrace.WithClientTrace(ctx, t.newClientTrace(req, span)))
|
||||
} else {
|
||||
req = req.WithContext(ctx)
|
||||
}
|
||||
|
||||
if t.format != nil {
|
||||
// SpanContextToRequest will modify its Request argument, which is
|
||||
// contrary to the contract for http.RoundTripper, so we need to
|
||||
// pass it a copy of the Request.
|
||||
// However, the Request struct itself was already copied by
|
||||
// the WithContext calls above and so we just need to copy the header.
|
||||
header := make(http.Header)
|
||||
for k, v := range req.Header {
|
||||
header[k] = v
|
||||
}
|
||||
req.Header = header
|
||||
t.format.SpanContextToRequest(span.SpanContext(), req)
|
||||
}
|
||||
|
||||
span.AddAttributes(requestAttrs(req)...)
|
||||
resp, err := t.base.RoundTrip(req)
|
||||
if err != nil {
|
||||
span.SetStatus(trace.Status{Code: trace.StatusCodeUnknown, Message: err.Error()})
|
||||
span.End()
|
||||
return resp, err
|
||||
}
|
||||
|
||||
span.AddAttributes(responseAttrs(resp)...)
|
||||
span.SetStatus(TraceStatus(resp.StatusCode, resp.Status))
|
||||
|
||||
// span.End() will be invoked after
|
||||
// a read from resp.Body returns io.EOF or when
|
||||
// resp.Body.Close() is invoked.
|
||||
bt := &bodyTracker{rc: resp.Body, span: span}
|
||||
resp.Body = wrappedBody(bt, resp.Body)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// bodyTracker wraps a response.Body and invokes
|
||||
// trace.EndSpan on encountering io.EOF on reading
|
||||
// the body of the original response.
|
||||
type bodyTracker struct {
|
||||
rc io.ReadCloser
|
||||
span *trace.Span
|
||||
}
|
||||
|
||||
var _ io.ReadCloser = (*bodyTracker)(nil)
|
||||
|
||||
func (bt *bodyTracker) Read(b []byte) (int, error) {
|
||||
n, err := bt.rc.Read(b)
|
||||
|
||||
switch err {
|
||||
case nil:
|
||||
return n, nil
|
||||
case io.EOF:
|
||||
bt.span.End()
|
||||
default:
|
||||
// For all other errors, set the span status
|
||||
bt.span.SetStatus(trace.Status{
|
||||
// Code 2 is the error code for Internal server error.
|
||||
Code: 2,
|
||||
Message: err.Error(),
|
||||
})
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (bt *bodyTracker) Close() error {
|
||||
// Invoking endSpan on Close will help catch the cases
|
||||
// in which a read returned a non-nil error, we set the
|
||||
// span status but didn't end the span.
|
||||
bt.span.End()
|
||||
return bt.rc.Close()
|
||||
}
|
||||
|
||||
// CancelRequest cancels an in-flight request by closing its connection.
|
||||
func (t *traceTransport) CancelRequest(req *http.Request) {
|
||||
type canceler interface {
|
||||
CancelRequest(*http.Request)
|
||||
}
|
||||
if cr, ok := t.base.(canceler); ok {
|
||||
cr.CancelRequest(req)
|
||||
}
|
||||
}
|
||||
|
||||
func spanNameFromURL(req *http.Request) string {
|
||||
return req.URL.Path
|
||||
}
|
||||
|
||||
func requestAttrs(r *http.Request) []trace.Attribute {
|
||||
userAgent := r.UserAgent()
|
||||
|
||||
attrs := make([]trace.Attribute, 0, 5)
|
||||
attrs = append(attrs,
|
||||
trace.StringAttribute(PathAttribute, r.URL.Path),
|
||||
trace.StringAttribute(URLAttribute, r.URL.String()),
|
||||
trace.StringAttribute(HostAttribute, r.Host),
|
||||
trace.StringAttribute(MethodAttribute, r.Method),
|
||||
)
|
||||
|
||||
if userAgent != "" {
|
||||
attrs = append(attrs, trace.StringAttribute(UserAgentAttribute, userAgent))
|
||||
}
|
||||
|
||||
return attrs
|
||||
}
|
||||
|
||||
func responseAttrs(resp *http.Response) []trace.Attribute {
|
||||
return []trace.Attribute{
|
||||
trace.Int64Attribute(StatusCodeAttribute, int64(resp.StatusCode)),
|
||||
}
|
||||
}
|
||||
|
||||
// TraceStatus is a utility to convert the HTTP status code to a trace.Status that
|
||||
// represents the outcome as closely as possible.
|
||||
func TraceStatus(httpStatusCode int, statusLine string) trace.Status {
|
||||
var code int32
|
||||
if httpStatusCode < 200 || httpStatusCode >= 400 {
|
||||
code = trace.StatusCodeUnknown
|
||||
}
|
||||
switch httpStatusCode {
|
||||
case 499:
|
||||
code = trace.StatusCodeCancelled
|
||||
case http.StatusBadRequest:
|
||||
code = trace.StatusCodeInvalidArgument
|
||||
case http.StatusGatewayTimeout:
|
||||
code = trace.StatusCodeDeadlineExceeded
|
||||
case http.StatusNotFound:
|
||||
code = trace.StatusCodeNotFound
|
||||
case http.StatusForbidden:
|
||||
code = trace.StatusCodePermissionDenied
|
||||
case http.StatusUnauthorized: // 401 is actually unauthenticated.
|
||||
code = trace.StatusCodeUnauthenticated
|
||||
case http.StatusTooManyRequests:
|
||||
code = trace.StatusCodeResourceExhausted
|
||||
case http.StatusNotImplemented:
|
||||
code = trace.StatusCodeUnimplemented
|
||||
case http.StatusServiceUnavailable:
|
||||
code = trace.StatusCodeUnavailable
|
||||
case http.StatusOK:
|
||||
code = trace.StatusCodeOK
|
||||
}
|
||||
return trace.Status{Code: code, Message: codeToStr[code]}
|
||||
}
|
||||
|
||||
var codeToStr = map[int32]string{
|
||||
trace.StatusCodeOK: `OK`,
|
||||
trace.StatusCodeCancelled: `CANCELLED`,
|
||||
trace.StatusCodeUnknown: `UNKNOWN`,
|
||||
trace.StatusCodeInvalidArgument: `INVALID_ARGUMENT`,
|
||||
trace.StatusCodeDeadlineExceeded: `DEADLINE_EXCEEDED`,
|
||||
trace.StatusCodeNotFound: `NOT_FOUND`,
|
||||
trace.StatusCodeAlreadyExists: `ALREADY_EXISTS`,
|
||||
trace.StatusCodePermissionDenied: `PERMISSION_DENIED`,
|
||||
trace.StatusCodeResourceExhausted: `RESOURCE_EXHAUSTED`,
|
||||
trace.StatusCodeFailedPrecondition: `FAILED_PRECONDITION`,
|
||||
trace.StatusCodeAborted: `ABORTED`,
|
||||
trace.StatusCodeOutOfRange: `OUT_OF_RANGE`,
|
||||
trace.StatusCodeUnimplemented: `UNIMPLEMENTED`,
|
||||
trace.StatusCodeInternal: `INTERNAL`,
|
||||
trace.StatusCodeUnavailable: `UNAVAILABLE`,
|
||||
trace.StatusCodeDataLoss: `DATA_LOSS`,
|
||||
trace.StatusCodeUnauthenticated: `UNAUTHENTICATED`,
|
||||
}
|
||||
|
||||
func isHealthEndpoint(path string) bool {
|
||||
// Health checking is pretty frequent and
|
||||
// traces collected for health endpoints
|
||||
// can be extremely noisy and expensive.
|
||||
// Disable canonical health checking endpoints
|
||||
// like /healthz and /_ah/health for now.
|
||||
if path == "/healthz" || path == "/_ah/health" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
44
vendor/go.opencensus.io/plugin/ochttp/wrapped_body.go
generated
vendored
Normal file
44
vendor/go.opencensus.io/plugin/ochttp/wrapped_body.go
generated
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
// Copyright 2019, OpenCensus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ochttp
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// wrappedBody returns a wrapped version of the original
|
||||
// Body and only implements the same combination of additional
|
||||
// interfaces as the original.
|
||||
func wrappedBody(wrapper io.ReadCloser, body io.ReadCloser) io.ReadCloser {
|
||||
var (
|
||||
wr, i0 = body.(io.Writer)
|
||||
)
|
||||
switch {
|
||||
case !i0:
|
||||
return struct {
|
||||
io.ReadCloser
|
||||
}{wrapper}
|
||||
|
||||
case i0:
|
||||
return struct {
|
||||
io.ReadCloser
|
||||
io.Writer
|
||||
}{wrapper, wr}
|
||||
default:
|
||||
return struct {
|
||||
io.ReadCloser
|
||||
}{wrapper}
|
||||
}
|
||||
}
|
164
vendor/go.opencensus.io/resource/resource.go
generated
vendored
Normal file
164
vendor/go.opencensus.io/resource/resource.go
generated
vendored
Normal file
@ -0,0 +1,164 @@
|
||||
// Copyright 2018, OpenCensus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package resource provides functionality for resource, which capture
|
||||
// identifying information about the entities for which signals are exported.
|
||||
package resource
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Environment variables used by FromEnv to decode a resource.
|
||||
const (
|
||||
EnvVarType = "OC_RESOURCE_TYPE"
|
||||
EnvVarLabels = "OC_RESOURCE_LABELS"
|
||||
)
|
||||
|
||||
// Resource describes an entity about which identifying information and metadata is exposed.
|
||||
// For example, a type "k8s.io/container" may hold labels describing the pod name and namespace.
|
||||
type Resource struct {
|
||||
Type string
|
||||
Labels map[string]string
|
||||
}
|
||||
|
||||
// EncodeLabels encodes a labels map to a string as provided via the OC_RESOURCE_LABELS environment variable.
|
||||
func EncodeLabels(labels map[string]string) string {
|
||||
sortedKeys := make([]string, 0, len(labels))
|
||||
for k := range labels {
|
||||
sortedKeys = append(sortedKeys, k)
|
||||
}
|
||||
sort.Strings(sortedKeys)
|
||||
|
||||
s := ""
|
||||
for i, k := range sortedKeys {
|
||||
if i > 0 {
|
||||
s += ","
|
||||
}
|
||||
s += k + "=" + strconv.Quote(labels[k])
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
var labelRegex = regexp.MustCompile(`^\s*([[:ascii:]]{1,256}?)=("[[:ascii:]]{0,256}?")\s*,`)
|
||||
|
||||
// DecodeLabels decodes a serialized label map as used in the OC_RESOURCE_LABELS variable.
|
||||
// A list of labels of the form `<key1>="<value1>",<key2>="<value2>",...` is accepted.
|
||||
// Domain names and paths are accepted as label keys.
|
||||
// Most users will want to use FromEnv instead.
|
||||
func DecodeLabels(s string) (map[string]string, error) {
|
||||
m := map[string]string{}
|
||||
// Ensure a trailing comma, which allows us to keep the regex simpler
|
||||
s = strings.TrimRight(strings.TrimSpace(s), ",") + ","
|
||||
|
||||
for len(s) > 0 {
|
||||
match := labelRegex.FindStringSubmatch(s)
|
||||
if len(match) == 0 {
|
||||
return nil, fmt.Errorf("invalid label formatting, remainder: %s", s)
|
||||
}
|
||||
v := match[2]
|
||||
if v == "" {
|
||||
v = match[3]
|
||||
} else {
|
||||
var err error
|
||||
if v, err = strconv.Unquote(v); err != nil {
|
||||
return nil, fmt.Errorf("invalid label formatting, remainder: %s, err: %s", s, err)
|
||||
}
|
||||
}
|
||||
m[match[1]] = v
|
||||
|
||||
s = s[len(match[0]):]
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// FromEnv is a detector that loads resource information from the OC_RESOURCE_TYPE
|
||||
// and OC_RESOURCE_labelS environment variables.
|
||||
func FromEnv(context.Context) (*Resource, error) {
|
||||
res := &Resource{
|
||||
Type: strings.TrimSpace(os.Getenv(EnvVarType)),
|
||||
}
|
||||
labels := strings.TrimSpace(os.Getenv(EnvVarLabels))
|
||||
if labels == "" {
|
||||
return res, nil
|
||||
}
|
||||
var err error
|
||||
if res.Labels, err = DecodeLabels(labels); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
var _ Detector = FromEnv
|
||||
|
||||
// merge resource information from b into a. In case of a collision, a takes precedence.
|
||||
func merge(a, b *Resource) *Resource {
|
||||
if a == nil {
|
||||
return b
|
||||
}
|
||||
if b == nil {
|
||||
return a
|
||||
}
|
||||
res := &Resource{
|
||||
Type: a.Type,
|
||||
Labels: map[string]string{},
|
||||
}
|
||||
if res.Type == "" {
|
||||
res.Type = b.Type
|
||||
}
|
||||
for k, v := range b.Labels {
|
||||
res.Labels[k] = v
|
||||
}
|
||||
// Labels from resource a overwrite labels from resource b.
|
||||
for k, v := range a.Labels {
|
||||
res.Labels[k] = v
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// Detector attempts to detect resource information.
|
||||
// If the detector cannot find resource information, the returned resource is nil but no
|
||||
// error is returned.
|
||||
// An error is only returned on unexpected failures.
|
||||
type Detector func(context.Context) (*Resource, error)
|
||||
|
||||
// MultiDetector returns a Detector that calls all input detectors in order and
|
||||
// merges each result with the previous one. In case a type of label key is already set,
|
||||
// the first set value is takes precedence.
|
||||
// It returns on the first error that a sub-detector encounters.
|
||||
func MultiDetector(detectors ...Detector) Detector {
|
||||
return func(ctx context.Context) (*Resource, error) {
|
||||
return detectAll(ctx, detectors...)
|
||||
}
|
||||
}
|
||||
|
||||
// detectall calls all input detectors sequentially an merges each result with the previous one.
|
||||
// It returns on the first error that a sub-detector encounters.
|
||||
func detectAll(ctx context.Context, detectors ...Detector) (*Resource, error) {
|
||||
var res *Resource
|
||||
for _, d := range detectors {
|
||||
r, err := d(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res = merge(res, r)
|
||||
}
|
||||
return res, nil
|
||||
}
|
69
vendor/go.opencensus.io/stats/doc.go
generated
vendored
Normal file
69
vendor/go.opencensus.io/stats/doc.go
generated
vendored
Normal file
@ -0,0 +1,69 @@
|
||||
// Copyright 2017, OpenCensus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
/*
|
||||
Package stats contains support for OpenCensus stats recording.
|
||||
|
||||
OpenCensus allows users to create typed measures, record measurements,
|
||||
aggregate the collected data, and export the aggregated data.
|
||||
|
||||
Measures
|
||||
|
||||
A measure represents a type of data point to be tracked and recorded.
|
||||
For example, latency, request Mb/s, and response Mb/s are measures
|
||||
to collect from a server.
|
||||
|
||||
Measure constructors such as Int64 and Float64 automatically
|
||||
register the measure by the given name. Each registered measure needs
|
||||
to be unique by name. Measures also have a description and a unit.
|
||||
|
||||
Libraries can define and export measures. Application authors can then
|
||||
create views and collect and break down measures by the tags they are
|
||||
interested in.
|
||||
|
||||
Recording measurements
|
||||
|
||||
Measurement is a data point to be collected for a measure. For example,
|
||||
for a latency (ms) measure, 100 is a measurement that represents a 100ms
|
||||
latency event. Measurements are created from measures with
|
||||
the current context. Tags from the current context are recorded with the
|
||||
measurements if they are any.
|
||||
|
||||
Recorded measurements are dropped immediately if no views are registered for them.
|
||||
There is usually no need to conditionally enable and disable
|
||||
recording to reduce cost. Recording of measurements is cheap.
|
||||
|
||||
Libraries can always record measurements, and applications can later decide
|
||||
on which measurements they want to collect by registering views. This allows
|
||||
libraries to turn on the instrumentation by default.
|
||||
|
||||
Exemplars
|
||||
|
||||
For a given recorded measurement, the associated exemplar is a diagnostic map
|
||||
that gives more information about the measurement.
|
||||
|
||||
When aggregated using a Distribution aggregation, an exemplar is kept for each
|
||||
bucket in the Distribution. This allows you to easily find an example of a
|
||||
measurement that fell into each bucket.
|
||||
|
||||
For example, if you also use the OpenCensus trace package and you
|
||||
record a measurement with a context that contains a sampled trace span,
|
||||
then the trace span will be added to the exemplar associated with the measurement.
|
||||
|
||||
When exported to a supporting back end, you should be able to easily navigate
|
||||
to example traces that fell into each bucket in the Distribution.
|
||||
|
||||
*/
|
||||
package stats // import "go.opencensus.io/stats"
|
25
vendor/go.opencensus.io/stats/internal/record.go
generated
vendored
Normal file
25
vendor/go.opencensus.io/stats/internal/record.go
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
// Copyright 2018, OpenCensus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"go.opencensus.io/tag"
|
||||
)
|
||||
|
||||
// DefaultRecorder will be called for each Record call.
|
||||
var DefaultRecorder func(tags *tag.Map, measurement interface{}, attachments map[string]interface{})
|
||||
|
||||
// SubscriptionReporter reports when a view subscribed with a measure.
|
||||
var SubscriptionReporter func(measure string)
|
109
vendor/go.opencensus.io/stats/measure.go
generated
vendored
Normal file
109
vendor/go.opencensus.io/stats/measure.go
generated
vendored
Normal file
@ -0,0 +1,109 @@
|
||||
// Copyright 2017, OpenCensus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
package stats
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// Measure represents a single numeric value to be tracked and recorded.
|
||||
// For example, latency, request bytes, and response bytes could be measures
|
||||
// to collect from a server.
|
||||
//
|
||||
// Measures by themselves have no outside effects. In order to be exported,
|
||||
// the measure needs to be used in a View. If no Views are defined over a
|
||||
// measure, there is very little cost in recording it.
|
||||
type Measure interface {
|
||||
// Name returns the name of this measure.
|
||||
//
|
||||
// Measure names are globally unique (among all libraries linked into your program).
|
||||
// We recommend prefixing the measure name with a domain name relevant to your
|
||||
// project or application.
|
||||
//
|
||||
// Measure names are never sent over the wire or exported to backends.
|
||||
// They are only used to create Views.
|
||||
Name() string
|
||||
|
||||
// Description returns the human-readable description of this measure.
|
||||
Description() string
|
||||
|
||||
// Unit returns the units for the values this measure takes on.
|
||||
//
|
||||
// Units are encoded according to the case-sensitive abbreviations from the
|
||||
// Unified Code for Units of Measure: http://unitsofmeasure.org/ucum.html
|
||||
Unit() string
|
||||
}
|
||||
|
||||
// measureDescriptor is the untyped descriptor associated with each measure.
|
||||
// Int64Measure and Float64Measure wrap measureDescriptor to provide typed
|
||||
// recording APIs.
|
||||
// Two Measures with the same name will have the same measureDescriptor.
|
||||
type measureDescriptor struct {
|
||||
subs int32 // access atomically
|
||||
|
||||
name string
|
||||
description string
|
||||
unit string
|
||||
}
|
||||
|
||||
func (m *measureDescriptor) subscribe() {
|
||||
atomic.StoreInt32(&m.subs, 1)
|
||||
}
|
||||
|
||||
func (m *measureDescriptor) subscribed() bool {
|
||||
return atomic.LoadInt32(&m.subs) == 1
|
||||
}
|
||||
|
||||
var (
|
||||
mu sync.RWMutex
|
||||
measures = make(map[string]*measureDescriptor)
|
||||
)
|
||||
|
||||
func registerMeasureHandle(name, desc, unit string) *measureDescriptor {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
if stored, ok := measures[name]; ok {
|
||||
return stored
|
||||
}
|
||||
m := &measureDescriptor{
|
||||
name: name,
|
||||
description: desc,
|
||||
unit: unit,
|
||||
}
|
||||
measures[name] = m
|
||||
return m
|
||||
}
|
||||
|
||||
// Measurement is the numeric value measured when recording stats. Each measure
|
||||
// provides methods to create measurements of their kind. For example, Int64Measure
|
||||
// provides M to convert an int64 into a measurement.
|
||||
type Measurement struct {
|
||||
v float64
|
||||
m Measure
|
||||
desc *measureDescriptor
|
||||
}
|
||||
|
||||
// Value returns the value of the Measurement as a float64.
|
||||
func (m Measurement) Value() float64 {
|
||||
return m.v
|
||||
}
|
||||
|
||||
// Measure returns the Measure from which this Measurement was created.
|
||||
func (m Measurement) Measure() Measure {
|
||||
return m.m
|
||||
}
|
55
vendor/go.opencensus.io/stats/measure_float64.go
generated
vendored
Normal file
55
vendor/go.opencensus.io/stats/measure_float64.go
generated
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
// Copyright 2017, OpenCensus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
package stats
|
||||
|
||||
// Float64Measure is a measure for float64 values.
|
||||
type Float64Measure struct {
|
||||
desc *measureDescriptor
|
||||
}
|
||||
|
||||
// M creates a new float64 measurement.
|
||||
// Use Record to record measurements.
|
||||
func (m *Float64Measure) M(v float64) Measurement {
|
||||
return Measurement{
|
||||
m: m,
|
||||
desc: m.desc,
|
||||
v: v,
|
||||
}
|
||||
}
|
||||
|
||||
// Float64 creates a new measure for float64 values.
|
||||
//
|
||||
// See the documentation for interface Measure for more guidance on the
|
||||
// parameters of this function.
|
||||
func Float64(name, description, unit string) *Float64Measure {
|
||||
mi := registerMeasureHandle(name, description, unit)
|
||||
return &Float64Measure{mi}
|
||||
}
|
||||
|
||||
// Name returns the name of the measure.
|
||||
func (m *Float64Measure) Name() string {
|
||||
return m.desc.name
|
||||
}
|
||||
|
||||
// Description returns the description of the measure.
|
||||
func (m *Float64Measure) Description() string {
|
||||
return m.desc.description
|
||||
}
|
||||
|
||||
// Unit returns the unit of the measure.
|
||||
func (m *Float64Measure) Unit() string {
|
||||
return m.desc.unit
|
||||
}
|
55
vendor/go.opencensus.io/stats/measure_int64.go
generated
vendored
Normal file
55
vendor/go.opencensus.io/stats/measure_int64.go
generated
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
// Copyright 2017, OpenCensus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
package stats
|
||||
|
||||
// Int64Measure is a measure for int64 values.
|
||||
type Int64Measure struct {
|
||||
desc *measureDescriptor
|
||||
}
|
||||
|
||||
// M creates a new int64 measurement.
|
||||
// Use Record to record measurements.
|
||||
func (m *Int64Measure) M(v int64) Measurement {
|
||||
return Measurement{
|
||||
m: m,
|
||||
desc: m.desc,
|
||||
v: float64(v),
|
||||
}
|
||||
}
|
||||
|
||||
// Int64 creates a new measure for int64 values.
|
||||
//
|
||||
// See the documentation for interface Measure for more guidance on the
|
||||
// parameters of this function.
|
||||
func Int64(name, description, unit string) *Int64Measure {
|
||||
mi := registerMeasureHandle(name, description, unit)
|
||||
return &Int64Measure{mi}
|
||||
}
|
||||
|
||||
// Name returns the name of the measure.
|
||||
func (m *Int64Measure) Name() string {
|
||||
return m.desc.name
|
||||
}
|
||||
|
||||
// Description returns the description of the measure.
|
||||
func (m *Int64Measure) Description() string {
|
||||
return m.desc.description
|
||||
}
|
||||
|
||||
// Unit returns the unit of the measure.
|
||||
func (m *Int64Measure) Unit() string {
|
||||
return m.desc.unit
|
||||
}
|
117
vendor/go.opencensus.io/stats/record.go
generated
vendored
Normal file
117
vendor/go.opencensus.io/stats/record.go
generated
vendored
Normal file
@ -0,0 +1,117 @@
|
||||
// Copyright 2018, OpenCensus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
package stats
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.opencensus.io/metric/metricdata"
|
||||
"go.opencensus.io/stats/internal"
|
||||
"go.opencensus.io/tag"
|
||||
)
|
||||
|
||||
func init() {
|
||||
internal.SubscriptionReporter = func(measure string) {
|
||||
mu.Lock()
|
||||
measures[measure].subscribe()
|
||||
mu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
type recordOptions struct {
|
||||
attachments metricdata.Attachments
|
||||
mutators []tag.Mutator
|
||||
measurements []Measurement
|
||||
}
|
||||
|
||||
// WithAttachments applies provided exemplar attachments.
|
||||
func WithAttachments(attachments metricdata.Attachments) Options {
|
||||
return func(ro *recordOptions) {
|
||||
ro.attachments = attachments
|
||||
}
|
||||
}
|
||||
|
||||
// WithTags applies provided tag mutators.
|
||||
func WithTags(mutators ...tag.Mutator) Options {
|
||||
return func(ro *recordOptions) {
|
||||
ro.mutators = mutators
|
||||
}
|
||||
}
|
||||
|
||||
// WithMeasurements applies provided measurements.
|
||||
func WithMeasurements(measurements ...Measurement) Options {
|
||||
return func(ro *recordOptions) {
|
||||
ro.measurements = measurements
|
||||
}
|
||||
}
|
||||
|
||||
// Options apply changes to recordOptions.
|
||||
type Options func(*recordOptions)
|
||||
|
||||
func createRecordOption(ros ...Options) *recordOptions {
|
||||
o := &recordOptions{}
|
||||
for _, ro := range ros {
|
||||
ro(o)
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
||||
// Record records one or multiple measurements with the same context at once.
|
||||
// If there are any tags in the context, measurements will be tagged with them.
|
||||
func Record(ctx context.Context, ms ...Measurement) {
|
||||
RecordWithOptions(ctx, WithMeasurements(ms...))
|
||||
}
|
||||
|
||||
// RecordWithTags records one or multiple measurements at once.
|
||||
//
|
||||
// Measurements will be tagged with the tags in the context mutated by the mutators.
|
||||
// RecordWithTags is useful if you want to record with tag mutations but don't want
|
||||
// to propagate the mutations in the context.
|
||||
func RecordWithTags(ctx context.Context, mutators []tag.Mutator, ms ...Measurement) error {
|
||||
return RecordWithOptions(ctx, WithTags(mutators...), WithMeasurements(ms...))
|
||||
}
|
||||
|
||||
// RecordWithOptions records measurements from the given options (if any) against context
|
||||
// and tags and attachments in the options (if any).
|
||||
// If there are any tags in the context, measurements will be tagged with them.
|
||||
func RecordWithOptions(ctx context.Context, ros ...Options) error {
|
||||
o := createRecordOption(ros...)
|
||||
if len(o.measurements) == 0 {
|
||||
return nil
|
||||
}
|
||||
recorder := internal.DefaultRecorder
|
||||
if recorder == nil {
|
||||
return nil
|
||||
}
|
||||
record := false
|
||||
for _, m := range o.measurements {
|
||||
if m.desc.subscribed() {
|
||||
record = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !record {
|
||||
return nil
|
||||
}
|
||||
if len(o.mutators) > 0 {
|
||||
var err error
|
||||
if ctx, err = tag.New(ctx, o.mutators...); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
recorder(tag.FromContext(ctx), o.measurements, o.attachments)
|
||||
return nil
|
||||
}
|
25
vendor/go.opencensus.io/stats/units.go
generated
vendored
Normal file
25
vendor/go.opencensus.io/stats/units.go
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
// Copyright 2018, OpenCensus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
package stats
|
||||
|
||||
// Units are encoded according to the case-sensitive abbreviations from the
|
||||
// Unified Code for Units of Measure: http://unitsofmeasure.org/ucum.html
|
||||
const (
|
||||
UnitNone = "1" // Deprecated: Use UnitDimensionless.
|
||||
UnitDimensionless = "1"
|
||||
UnitBytes = "By"
|
||||
UnitMilliseconds = "ms"
|
||||
)
|
120
vendor/go.opencensus.io/stats/view/aggregation.go
generated
vendored
Normal file
120
vendor/go.opencensus.io/stats/view/aggregation.go
generated
vendored
Normal file
@ -0,0 +1,120 @@
|
||||
// Copyright 2017, OpenCensus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
package view
|
||||
|
||||
// AggType represents the type of aggregation function used on a View.
|
||||
type AggType int
|
||||
|
||||
// All available aggregation types.
|
||||
const (
|
||||
AggTypeNone AggType = iota // no aggregation; reserved for future use.
|
||||
AggTypeCount // the count aggregation, see Count.
|
||||
AggTypeSum // the sum aggregation, see Sum.
|
||||
AggTypeDistribution // the distribution aggregation, see Distribution.
|
||||
AggTypeLastValue // the last value aggregation, see LastValue.
|
||||
)
|
||||
|
||||
func (t AggType) String() string {
|
||||
return aggTypeName[t]
|
||||
}
|
||||
|
||||
var aggTypeName = map[AggType]string{
|
||||
AggTypeNone: "None",
|
||||
AggTypeCount: "Count",
|
||||
AggTypeSum: "Sum",
|
||||
AggTypeDistribution: "Distribution",
|
||||
AggTypeLastValue: "LastValue",
|
||||
}
|
||||
|
||||
// Aggregation represents a data aggregation method. Use one of the functions:
|
||||
// Count, Sum, or Distribution to construct an Aggregation.
|
||||
type Aggregation struct {
|
||||
Type AggType // Type is the AggType of this Aggregation.
|
||||
Buckets []float64 // Buckets are the bucket endpoints if this Aggregation represents a distribution, see Distribution.
|
||||
|
||||
newData func() AggregationData
|
||||
}
|
||||
|
||||
var (
|
||||
aggCount = &Aggregation{
|
||||
Type: AggTypeCount,
|
||||
newData: func() AggregationData {
|
||||
return &CountData{}
|
||||
},
|
||||
}
|
||||
aggSum = &Aggregation{
|
||||
Type: AggTypeSum,
|
||||
newData: func() AggregationData {
|
||||
return &SumData{}
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// Count indicates that data collected and aggregated
|
||||
// with this method will be turned into a count value.
|
||||
// For example, total number of accepted requests can be
|
||||
// aggregated by using Count.
|
||||
func Count() *Aggregation {
|
||||
return aggCount
|
||||
}
|
||||
|
||||
// Sum indicates that data collected and aggregated
|
||||
// with this method will be summed up.
|
||||
// For example, accumulated request bytes can be aggregated by using
|
||||
// Sum.
|
||||
func Sum() *Aggregation {
|
||||
return aggSum
|
||||
}
|
||||
|
||||
// Distribution indicates that the desired aggregation is
|
||||
// a histogram distribution.
|
||||
//
|
||||
// An distribution aggregation may contain a histogram of the values in the
|
||||
// population. The bucket boundaries for that histogram are described
|
||||
// by the bounds. This defines len(bounds)+1 buckets.
|
||||
//
|
||||
// If len(bounds) >= 2 then the boundaries for bucket index i are:
|
||||
//
|
||||
// [-infinity, bounds[i]) for i = 0
|
||||
// [bounds[i-1], bounds[i]) for 0 < i < length
|
||||
// [bounds[i-1], +infinity) for i = length
|
||||
//
|
||||
// If len(bounds) is 0 then there is no histogram associated with the
|
||||
// distribution. There will be a single bucket with boundaries
|
||||
// (-infinity, +infinity).
|
||||
//
|
||||
// If len(bounds) is 1 then there is no finite buckets, and that single
|
||||
// element is the common boundary of the overflow and underflow buckets.
|
||||
func Distribution(bounds ...float64) *Aggregation {
|
||||
return &Aggregation{
|
||||
Type: AggTypeDistribution,
|
||||
Buckets: bounds,
|
||||
newData: func() AggregationData {
|
||||
return newDistributionData(bounds)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// LastValue only reports the last value recorded using this
|
||||
// aggregation. All other measurements will be dropped.
|
||||
func LastValue() *Aggregation {
|
||||
return &Aggregation{
|
||||
Type: AggTypeLastValue,
|
||||
newData: func() AggregationData {
|
||||
return &LastValueData{}
|
||||
},
|
||||
}
|
||||
}
|
293
vendor/go.opencensus.io/stats/view/aggregation_data.go
generated
vendored
Normal file
293
vendor/go.opencensus.io/stats/view/aggregation_data.go
generated
vendored
Normal file
@ -0,0 +1,293 @@
|
||||
// Copyright 2017, OpenCensus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
package view
|
||||
|
||||
import (
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"go.opencensus.io/metric/metricdata"
|
||||
)
|
||||
|
||||
// AggregationData represents an aggregated value from a collection.
|
||||
// They are reported on the view data during exporting.
|
||||
// Mosts users won't directly access aggregration data.
|
||||
type AggregationData interface {
|
||||
isAggregationData() bool
|
||||
addSample(v float64, attachments map[string]interface{}, t time.Time)
|
||||
clone() AggregationData
|
||||
equal(other AggregationData) bool
|
||||
toPoint(t metricdata.Type, time time.Time) metricdata.Point
|
||||
}
|
||||
|
||||
const epsilon = 1e-9
|
||||
|
||||
// CountData is the aggregated data for the Count aggregation.
|
||||
// A count aggregation processes data and counts the recordings.
|
||||
//
|
||||
// Most users won't directly access count data.
|
||||
type CountData struct {
|
||||
Value int64
|
||||
}
|
||||
|
||||
func (a *CountData) isAggregationData() bool { return true }
|
||||
|
||||
func (a *CountData) addSample(_ float64, _ map[string]interface{}, _ time.Time) {
|
||||
a.Value = a.Value + 1
|
||||
}
|
||||
|
||||
func (a *CountData) clone() AggregationData {
|
||||
return &CountData{Value: a.Value}
|
||||
}
|
||||
|
||||
func (a *CountData) equal(other AggregationData) bool {
|
||||
a2, ok := other.(*CountData)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return a.Value == a2.Value
|
||||
}
|
||||
|
||||
func (a *CountData) toPoint(metricType metricdata.Type, t time.Time) metricdata.Point {
|
||||
switch metricType {
|
||||
case metricdata.TypeCumulativeInt64:
|
||||
return metricdata.NewInt64Point(t, a.Value)
|
||||
default:
|
||||
panic("unsupported metricdata.Type")
|
||||
}
|
||||
}
|
||||
|
||||
// SumData is the aggregated data for the Sum aggregation.
|
||||
// A sum aggregation processes data and sums up the recordings.
|
||||
//
|
||||
// Most users won't directly access sum data.
|
||||
type SumData struct {
|
||||
Value float64
|
||||
}
|
||||
|
||||
func (a *SumData) isAggregationData() bool { return true }
|
||||
|
||||
func (a *SumData) addSample(v float64, _ map[string]interface{}, _ time.Time) {
|
||||
a.Value += v
|
||||
}
|
||||
|
||||
func (a *SumData) clone() AggregationData {
|
||||
return &SumData{Value: a.Value}
|
||||
}
|
||||
|
||||
func (a *SumData) equal(other AggregationData) bool {
|
||||
a2, ok := other.(*SumData)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return math.Pow(a.Value-a2.Value, 2) < epsilon
|
||||
}
|
||||
|
||||
func (a *SumData) toPoint(metricType metricdata.Type, t time.Time) metricdata.Point {
|
||||
switch metricType {
|
||||
case metricdata.TypeCumulativeInt64:
|
||||
return metricdata.NewInt64Point(t, int64(a.Value))
|
||||
case metricdata.TypeCumulativeFloat64:
|
||||
return metricdata.NewFloat64Point(t, a.Value)
|
||||
default:
|
||||
panic("unsupported metricdata.Type")
|
||||
}
|
||||
}
|
||||
|
||||
// DistributionData is the aggregated data for the
|
||||
// Distribution aggregation.
|
||||
//
|
||||
// Most users won't directly access distribution data.
|
||||
//
|
||||
// For a distribution with N bounds, the associated DistributionData will have
|
||||
// N+1 buckets.
|
||||
type DistributionData struct {
|
||||
Count int64 // number of data points aggregated
|
||||
Min float64 // minimum value in the distribution
|
||||
Max float64 // max value in the distribution
|
||||
Mean float64 // mean of the distribution
|
||||
SumOfSquaredDev float64 // sum of the squared deviation from the mean
|
||||
CountPerBucket []int64 // number of occurrences per bucket
|
||||
// ExemplarsPerBucket is slice the same length as CountPerBucket containing
|
||||
// an exemplar for the associated bucket, or nil.
|
||||
ExemplarsPerBucket []*metricdata.Exemplar
|
||||
bounds []float64 // histogram distribution of the values
|
||||
}
|
||||
|
||||
func newDistributionData(bounds []float64) *DistributionData {
|
||||
bucketCount := len(bounds) + 1
|
||||
return &DistributionData{
|
||||
CountPerBucket: make([]int64, bucketCount),
|
||||
ExemplarsPerBucket: make([]*metricdata.Exemplar, bucketCount),
|
||||
bounds: bounds,
|
||||
Min: math.MaxFloat64,
|
||||
Max: math.SmallestNonzeroFloat64,
|
||||
}
|
||||
}
|
||||
|
||||
// Sum returns the sum of all samples collected.
|
||||
func (a *DistributionData) Sum() float64 { return a.Mean * float64(a.Count) }
|
||||
|
||||
func (a *DistributionData) variance() float64 {
|
||||
if a.Count <= 1 {
|
||||
return 0
|
||||
}
|
||||
return a.SumOfSquaredDev / float64(a.Count-1)
|
||||
}
|
||||
|
||||
func (a *DistributionData) isAggregationData() bool { return true }
|
||||
|
||||
// TODO(songy23): support exemplar attachments.
|
||||
func (a *DistributionData) addSample(v float64, attachments map[string]interface{}, t time.Time) {
|
||||
if v < a.Min {
|
||||
a.Min = v
|
||||
}
|
||||
if v > a.Max {
|
||||
a.Max = v
|
||||
}
|
||||
a.Count++
|
||||
a.addToBucket(v, attachments, t)
|
||||
|
||||
if a.Count == 1 {
|
||||
a.Mean = v
|
||||
return
|
||||
}
|
||||
|
||||
oldMean := a.Mean
|
||||
a.Mean = a.Mean + (v-a.Mean)/float64(a.Count)
|
||||
a.SumOfSquaredDev = a.SumOfSquaredDev + (v-oldMean)*(v-a.Mean)
|
||||
}
|
||||
|
||||
func (a *DistributionData) addToBucket(v float64, attachments map[string]interface{}, t time.Time) {
|
||||
var count *int64
|
||||
var i int
|
||||
var b float64
|
||||
for i, b = range a.bounds {
|
||||
if v < b {
|
||||
count = &a.CountPerBucket[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
if count == nil { // Last bucket.
|
||||
i = len(a.bounds)
|
||||
count = &a.CountPerBucket[i]
|
||||
}
|
||||
*count++
|
||||
if exemplar := getExemplar(v, attachments, t); exemplar != nil {
|
||||
a.ExemplarsPerBucket[i] = exemplar
|
||||
}
|
||||
}
|
||||
|
||||
func getExemplar(v float64, attachments map[string]interface{}, t time.Time) *metricdata.Exemplar {
|
||||
if len(attachments) == 0 {
|
||||
return nil
|
||||
}
|
||||
return &metricdata.Exemplar{
|
||||
Value: v,
|
||||
Timestamp: t,
|
||||
Attachments: attachments,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *DistributionData) clone() AggregationData {
|
||||
c := *a
|
||||
c.CountPerBucket = append([]int64(nil), a.CountPerBucket...)
|
||||
c.ExemplarsPerBucket = append([]*metricdata.Exemplar(nil), a.ExemplarsPerBucket...)
|
||||
return &c
|
||||
}
|
||||
|
||||
func (a *DistributionData) equal(other AggregationData) bool {
|
||||
a2, ok := other.(*DistributionData)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if a2 == nil {
|
||||
return false
|
||||
}
|
||||
if len(a.CountPerBucket) != len(a2.CountPerBucket) {
|
||||
return false
|
||||
}
|
||||
for i := range a.CountPerBucket {
|
||||
if a.CountPerBucket[i] != a2.CountPerBucket[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return a.Count == a2.Count && a.Min == a2.Min && a.Max == a2.Max && math.Pow(a.Mean-a2.Mean, 2) < epsilon && math.Pow(a.variance()-a2.variance(), 2) < epsilon
|
||||
}
|
||||
|
||||
func (a *DistributionData) toPoint(metricType metricdata.Type, t time.Time) metricdata.Point {
|
||||
switch metricType {
|
||||
case metricdata.TypeCumulativeDistribution:
|
||||
buckets := []metricdata.Bucket{}
|
||||
for i := 0; i < len(a.CountPerBucket); i++ {
|
||||
buckets = append(buckets, metricdata.Bucket{
|
||||
Count: a.CountPerBucket[i],
|
||||
Exemplar: a.ExemplarsPerBucket[i],
|
||||
})
|
||||
}
|
||||
bucketOptions := &metricdata.BucketOptions{Bounds: a.bounds}
|
||||
|
||||
val := &metricdata.Distribution{
|
||||
Count: a.Count,
|
||||
Sum: a.Sum(),
|
||||
SumOfSquaredDeviation: a.SumOfSquaredDev,
|
||||
BucketOptions: bucketOptions,
|
||||
Buckets: buckets,
|
||||
}
|
||||
return metricdata.NewDistributionPoint(t, val)
|
||||
|
||||
default:
|
||||
// TODO: [rghetia] when we have a use case for TypeGaugeDistribution.
|
||||
panic("unsupported metricdata.Type")
|
||||
}
|
||||
}
|
||||
|
||||
// LastValueData returns the last value recorded for LastValue aggregation.
|
||||
type LastValueData struct {
|
||||
Value float64
|
||||
}
|
||||
|
||||
func (l *LastValueData) isAggregationData() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (l *LastValueData) addSample(v float64, _ map[string]interface{}, _ time.Time) {
|
||||
l.Value = v
|
||||
}
|
||||
|
||||
func (l *LastValueData) clone() AggregationData {
|
||||
return &LastValueData{l.Value}
|
||||
}
|
||||
|
||||
func (l *LastValueData) equal(other AggregationData) bool {
|
||||
a2, ok := other.(*LastValueData)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return l.Value == a2.Value
|
||||
}
|
||||
|
||||
func (l *LastValueData) toPoint(metricType metricdata.Type, t time.Time) metricdata.Point {
|
||||
switch metricType {
|
||||
case metricdata.TypeGaugeInt64:
|
||||
return metricdata.NewInt64Point(t, int64(l.Value))
|
||||
case metricdata.TypeGaugeFloat64:
|
||||
return metricdata.NewFloat64Point(t, l.Value)
|
||||
default:
|
||||
panic("unsupported metricdata.Type")
|
||||
}
|
||||
}
|
86
vendor/go.opencensus.io/stats/view/collector.go
generated
vendored
Normal file
86
vendor/go.opencensus.io/stats/view/collector.go
generated
vendored
Normal file
@ -0,0 +1,86 @@
|
||||
// Copyright 2017, OpenCensus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
package view
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"go.opencensus.io/internal/tagencoding"
|
||||
"go.opencensus.io/tag"
|
||||
)
|
||||
|
||||
type collector struct {
|
||||
// signatures holds the aggregations values for each unique tag signature
|
||||
// (values for all keys) to its aggregator.
|
||||
signatures map[string]AggregationData
|
||||
// Aggregation is the description of the aggregation to perform for this
|
||||
// view.
|
||||
a *Aggregation
|
||||
}
|
||||
|
||||
func (c *collector) addSample(s string, v float64, attachments map[string]interface{}, t time.Time) {
|
||||
aggregator, ok := c.signatures[s]
|
||||
if !ok {
|
||||
aggregator = c.a.newData()
|
||||
c.signatures[s] = aggregator
|
||||
}
|
||||
aggregator.addSample(v, attachments, t)
|
||||
}
|
||||
|
||||
// collectRows returns a snapshot of the collected Row values.
|
||||
func (c *collector) collectedRows(keys []tag.Key) []*Row {
|
||||
rows := make([]*Row, 0, len(c.signatures))
|
||||
for sig, aggregator := range c.signatures {
|
||||
tags := decodeTags([]byte(sig), keys)
|
||||
row := &Row{Tags: tags, Data: aggregator.clone()}
|
||||
rows = append(rows, row)
|
||||
}
|
||||
return rows
|
||||
}
|
||||
|
||||
func (c *collector) clearRows() {
|
||||
c.signatures = make(map[string]AggregationData)
|
||||
}
|
||||
|
||||
// encodeWithKeys encodes the map by using values
|
||||
// only associated with the keys provided.
|
||||
func encodeWithKeys(m *tag.Map, keys []tag.Key) []byte {
|
||||
vb := &tagencoding.Values{
|
||||
Buffer: make([]byte, len(keys)),
|
||||
}
|
||||
for _, k := range keys {
|
||||
v, _ := m.Value(k)
|
||||
vb.WriteValue([]byte(v))
|
||||
}
|
||||
return vb.Bytes()
|
||||
}
|
||||
|
||||
// decodeTags decodes tags from the buffer and
|
||||
// orders them by the keys.
|
||||
func decodeTags(buf []byte, keys []tag.Key) []tag.Tag {
|
||||
vb := &tagencoding.Values{Buffer: buf}
|
||||
var tags []tag.Tag
|
||||
for _, k := range keys {
|
||||
v := vb.ReadValue()
|
||||
if v != nil {
|
||||
tags = append(tags, tag.Tag{Key: k, Value: string(v)})
|
||||
}
|
||||
}
|
||||
vb.ReadIndex = 0
|
||||
sort.Slice(tags, func(i, j int) bool { return tags[i].Key.Name() < tags[j].Key.Name() })
|
||||
return tags
|
||||
}
|
47
vendor/go.opencensus.io/stats/view/doc.go
generated
vendored
Normal file
47
vendor/go.opencensus.io/stats/view/doc.go
generated
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
// Copyright 2017, OpenCensus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
// Package view contains support for collecting and exposing aggregates over stats.
|
||||
//
|
||||
// In order to collect measurements, views need to be defined and registered.
|
||||
// A view allows recorded measurements to be filtered and aggregated.
|
||||
//
|
||||
// All recorded measurements can be grouped by a list of tags.
|
||||
//
|
||||
// OpenCensus provides several aggregation methods: Count, Distribution and Sum.
|
||||
//
|
||||
// Count only counts the number of measurement points recorded.
|
||||
// Distribution provides statistical summary of the aggregated data by counting
|
||||
// how many recorded measurements fall into each bucket.
|
||||
// Sum adds up the measurement values.
|
||||
// LastValue just keeps track of the most recently recorded measurement value.
|
||||
// All aggregations are cumulative.
|
||||
//
|
||||
// Views can be registerd and unregistered at any time during program execution.
|
||||
//
|
||||
// Libraries can define views but it is recommended that in most cases registering
|
||||
// views be left up to applications.
|
||||
//
|
||||
// Exporting
|
||||
//
|
||||
// Collected and aggregated data can be exported to a metric collection
|
||||
// backend by registering its exporter.
|
||||
//
|
||||
// Multiple exporters can be registered to upload the data to various
|
||||
// different back ends.
|
||||
package view // import "go.opencensus.io/stats/view"
|
||||
|
||||
// TODO(acetechnologist): Add a link to the language independent OpenCensus
|
||||
// spec when it is available.
|
58
vendor/go.opencensus.io/stats/view/export.go
generated
vendored
Normal file
58
vendor/go.opencensus.io/stats/view/export.go
generated
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
// Copyright 2017, OpenCensus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package view
|
||||
|
||||
import "sync"
|
||||
|
||||
var (
|
||||
exportersMu sync.RWMutex // guards exporters
|
||||
exporters = make(map[Exporter]struct{})
|
||||
)
|
||||
|
||||
// Exporter exports the collected records as view data.
|
||||
//
|
||||
// The ExportView method should return quickly; if an
|
||||
// Exporter takes a significant amount of time to
|
||||
// process a Data, that work should be done on another goroutine.
|
||||
//
|
||||
// It is safe to assume that ExportView will not be called concurrently from
|
||||
// multiple goroutines.
|
||||
//
|
||||
// The Data should not be modified.
|
||||
type Exporter interface {
|
||||
ExportView(viewData *Data)
|
||||
}
|
||||
|
||||
// RegisterExporter registers an exporter.
|
||||
// Collected data will be reported via all the
|
||||
// registered exporters. Once you no longer
|
||||
// want data to be exported, invoke UnregisterExporter
|
||||
// with the previously registered exporter.
|
||||
//
|
||||
// Binaries can register exporters, libraries shouldn't register exporters.
|
||||
func RegisterExporter(e Exporter) {
|
||||
exportersMu.Lock()
|
||||
defer exportersMu.Unlock()
|
||||
|
||||
exporters[e] = struct{}{}
|
||||
}
|
||||
|
||||
// UnregisterExporter unregisters an exporter.
|
||||
func UnregisterExporter(e Exporter) {
|
||||
exportersMu.Lock()
|
||||
defer exportersMu.Unlock()
|
||||
|
||||
delete(exporters, e)
|
||||
}
|
221
vendor/go.opencensus.io/stats/view/view.go
generated
vendored
Normal file
221
vendor/go.opencensus.io/stats/view/view.go
generated
vendored
Normal file
@ -0,0 +1,221 @@
|
||||
// Copyright 2017, OpenCensus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
package view
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"go.opencensus.io/metric/metricdata"
|
||||
"go.opencensus.io/stats"
|
||||
"go.opencensus.io/tag"
|
||||
)
|
||||
|
||||
// View allows users to aggregate the recorded stats.Measurements.
|
||||
// Views need to be passed to the Register function to be before data will be
|
||||
// collected and sent to Exporters.
|
||||
type View struct {
|
||||
Name string // Name of View. Must be unique. If unset, will default to the name of the Measure.
|
||||
Description string // Description is a human-readable description for this view.
|
||||
|
||||
// TagKeys are the tag keys describing the grouping of this view.
|
||||
// A single Row will be produced for each combination of associated tag values.
|
||||
TagKeys []tag.Key
|
||||
|
||||
// Measure is a stats.Measure to aggregate in this view.
|
||||
Measure stats.Measure
|
||||
|
||||
// Aggregation is the aggregation function tp apply to the set of Measurements.
|
||||
Aggregation *Aggregation
|
||||
}
|
||||
|
||||
// WithName returns a copy of the View with a new name. This is useful for
|
||||
// renaming views to cope with limitations placed on metric names by various
|
||||
// backends.
|
||||
func (v *View) WithName(name string) *View {
|
||||
vNew := *v
|
||||
vNew.Name = name
|
||||
return &vNew
|
||||
}
|
||||
|
||||
// same compares two views and returns true if they represent the same aggregation.
|
||||
func (v *View) same(other *View) bool {
|
||||
if v == other {
|
||||
return true
|
||||
}
|
||||
if v == nil {
|
||||
return false
|
||||
}
|
||||
return reflect.DeepEqual(v.Aggregation, other.Aggregation) &&
|
||||
v.Measure.Name() == other.Measure.Name()
|
||||
}
|
||||
|
||||
// ErrNegativeBucketBounds error returned if histogram contains negative bounds.
|
||||
//
|
||||
// Deprecated: this should not be public.
|
||||
var ErrNegativeBucketBounds = errors.New("negative bucket bounds not supported")
|
||||
|
||||
// canonicalize canonicalizes v by setting explicit
|
||||
// defaults for Name and Description and sorting the TagKeys
|
||||
func (v *View) canonicalize() error {
|
||||
if v.Measure == nil {
|
||||
return fmt.Errorf("cannot register view %q: measure not set", v.Name)
|
||||
}
|
||||
if v.Aggregation == nil {
|
||||
return fmt.Errorf("cannot register view %q: aggregation not set", v.Name)
|
||||
}
|
||||
if v.Name == "" {
|
||||
v.Name = v.Measure.Name()
|
||||
}
|
||||
if v.Description == "" {
|
||||
v.Description = v.Measure.Description()
|
||||
}
|
||||
if err := checkViewName(v.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
sort.Slice(v.TagKeys, func(i, j int) bool {
|
||||
return v.TagKeys[i].Name() < v.TagKeys[j].Name()
|
||||
})
|
||||
sort.Float64s(v.Aggregation.Buckets)
|
||||
for _, b := range v.Aggregation.Buckets {
|
||||
if b < 0 {
|
||||
return ErrNegativeBucketBounds
|
||||
}
|
||||
}
|
||||
// drop 0 bucket silently.
|
||||
v.Aggregation.Buckets = dropZeroBounds(v.Aggregation.Buckets...)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func dropZeroBounds(bounds ...float64) []float64 {
|
||||
for i, bound := range bounds {
|
||||
if bound > 0 {
|
||||
return bounds[i:]
|
||||
}
|
||||
}
|
||||
return []float64{}
|
||||
}
|
||||
|
||||
// viewInternal is the internal representation of a View.
|
||||
type viewInternal struct {
|
||||
view *View // view is the canonicalized View definition associated with this view.
|
||||
subscribed uint32 // 1 if someone is subscribed and data need to be exported, use atomic to access
|
||||
collector *collector
|
||||
metricDescriptor *metricdata.Descriptor
|
||||
}
|
||||
|
||||
func newViewInternal(v *View) (*viewInternal, error) {
|
||||
return &viewInternal{
|
||||
view: v,
|
||||
collector: &collector{make(map[string]AggregationData), v.Aggregation},
|
||||
metricDescriptor: viewToMetricDescriptor(v),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (v *viewInternal) subscribe() {
|
||||
atomic.StoreUint32(&v.subscribed, 1)
|
||||
}
|
||||
|
||||
func (v *viewInternal) unsubscribe() {
|
||||
atomic.StoreUint32(&v.subscribed, 0)
|
||||
}
|
||||
|
||||
// isSubscribed returns true if the view is exporting
|
||||
// data by subscription.
|
||||
func (v *viewInternal) isSubscribed() bool {
|
||||
return atomic.LoadUint32(&v.subscribed) == 1
|
||||
}
|
||||
|
||||
func (v *viewInternal) clearRows() {
|
||||
v.collector.clearRows()
|
||||
}
|
||||
|
||||
func (v *viewInternal) collectedRows() []*Row {
|
||||
return v.collector.collectedRows(v.view.TagKeys)
|
||||
}
|
||||
|
||||
func (v *viewInternal) addSample(m *tag.Map, val float64, attachments map[string]interface{}, t time.Time) {
|
||||
if !v.isSubscribed() {
|
||||
return
|
||||
}
|
||||
sig := string(encodeWithKeys(m, v.view.TagKeys))
|
||||
v.collector.addSample(sig, val, attachments, t)
|
||||
}
|
||||
|
||||
// A Data is a set of rows about usage of the single measure associated
|
||||
// with the given view. Each row is specific to a unique set of tags.
|
||||
type Data struct {
|
||||
View *View
|
||||
Start, End time.Time
|
||||
Rows []*Row
|
||||
}
|
||||
|
||||
// Row is the collected value for a specific set of key value pairs a.k.a tags.
|
||||
type Row struct {
|
||||
Tags []tag.Tag
|
||||
Data AggregationData
|
||||
}
|
||||
|
||||
func (r *Row) String() string {
|
||||
var buffer bytes.Buffer
|
||||
buffer.WriteString("{ ")
|
||||
buffer.WriteString("{ ")
|
||||
for _, t := range r.Tags {
|
||||
buffer.WriteString(fmt.Sprintf("{%v %v}", t.Key.Name(), t.Value))
|
||||
}
|
||||
buffer.WriteString(" }")
|
||||
buffer.WriteString(fmt.Sprintf("%v", r.Data))
|
||||
buffer.WriteString(" }")
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// Equal returns true if both rows are equal. Tags are expected to be ordered
|
||||
// by the key name. Even both rows have the same tags but the tags appear in
|
||||
// different orders it will return false.
|
||||
func (r *Row) Equal(other *Row) bool {
|
||||
if r == other {
|
||||
return true
|
||||
}
|
||||
return reflect.DeepEqual(r.Tags, other.Tags) && r.Data.equal(other.Data)
|
||||
}
|
||||
|
||||
const maxNameLength = 255
|
||||
|
||||
// Returns true if the given string contains only printable characters.
|
||||
func isPrintable(str string) bool {
|
||||
for _, r := range str {
|
||||
if !(r >= ' ' && r <= '~') {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func checkViewName(name string) error {
|
||||
if len(name) > maxNameLength {
|
||||
return fmt.Errorf("view name cannot be larger than %v", maxNameLength)
|
||||
}
|
||||
if !isPrintable(name) {
|
||||
return fmt.Errorf("view name needs to be an ASCII string")
|
||||
}
|
||||
return nil
|
||||
}
|
140
vendor/go.opencensus.io/stats/view/view_to_metric.go
generated
vendored
Normal file
140
vendor/go.opencensus.io/stats/view/view_to_metric.go
generated
vendored
Normal file
@ -0,0 +1,140 @@
|
||||
// Copyright 2019, OpenCensus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
package view
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"go.opencensus.io/metric/metricdata"
|
||||
"go.opencensus.io/stats"
|
||||
)
|
||||
|
||||
func getUnit(unit string) metricdata.Unit {
|
||||
switch unit {
|
||||
case "1":
|
||||
return metricdata.UnitDimensionless
|
||||
case "ms":
|
||||
return metricdata.UnitMilliseconds
|
||||
case "By":
|
||||
return metricdata.UnitBytes
|
||||
}
|
||||
return metricdata.UnitDimensionless
|
||||
}
|
||||
|
||||
func getType(v *View) metricdata.Type {
|
||||
m := v.Measure
|
||||
agg := v.Aggregation
|
||||
|
||||
switch agg.Type {
|
||||
case AggTypeSum:
|
||||
switch m.(type) {
|
||||
case *stats.Int64Measure:
|
||||
return metricdata.TypeCumulativeInt64
|
||||
case *stats.Float64Measure:
|
||||
return metricdata.TypeCumulativeFloat64
|
||||
default:
|
||||
panic("unexpected measure type")
|
||||
}
|
||||
case AggTypeDistribution:
|
||||
return metricdata.TypeCumulativeDistribution
|
||||
case AggTypeLastValue:
|
||||
switch m.(type) {
|
||||
case *stats.Int64Measure:
|
||||
return metricdata.TypeGaugeInt64
|
||||
case *stats.Float64Measure:
|
||||
return metricdata.TypeGaugeFloat64
|
||||
default:
|
||||
panic("unexpected measure type")
|
||||
}
|
||||
case AggTypeCount:
|
||||
switch m.(type) {
|
||||
case *stats.Int64Measure:
|
||||
return metricdata.TypeCumulativeInt64
|
||||
case *stats.Float64Measure:
|
||||
return metricdata.TypeCumulativeInt64
|
||||
default:
|
||||
panic("unexpected measure type")
|
||||
}
|
||||
default:
|
||||
panic("unexpected aggregation type")
|
||||
}
|
||||
}
|
||||
|
||||
func getLableKeys(v *View) []metricdata.LabelKey {
|
||||
labelKeys := []metricdata.LabelKey{}
|
||||
for _, k := range v.TagKeys {
|
||||
labelKeys = append(labelKeys, metricdata.LabelKey{Key: k.Name()})
|
||||
}
|
||||
return labelKeys
|
||||
}
|
||||
|
||||
func viewToMetricDescriptor(v *View) *metricdata.Descriptor {
|
||||
return &metricdata.Descriptor{
|
||||
Name: v.Name,
|
||||
Description: v.Description,
|
||||
Unit: getUnit(v.Measure.Unit()),
|
||||
Type: getType(v),
|
||||
LabelKeys: getLableKeys(v),
|
||||
}
|
||||
}
|
||||
|
||||
func toLabelValues(row *Row, expectedKeys []metricdata.LabelKey) []metricdata.LabelValue {
|
||||
labelValues := []metricdata.LabelValue{}
|
||||
tagMap := make(map[string]string)
|
||||
for _, tag := range row.Tags {
|
||||
tagMap[tag.Key.Name()] = tag.Value
|
||||
}
|
||||
|
||||
for _, key := range expectedKeys {
|
||||
if val, ok := tagMap[key.Key]; ok {
|
||||
labelValues = append(labelValues, metricdata.NewLabelValue(val))
|
||||
} else {
|
||||
labelValues = append(labelValues, metricdata.LabelValue{})
|
||||
}
|
||||
}
|
||||
return labelValues
|
||||
}
|
||||
|
||||
func rowToTimeseries(v *viewInternal, row *Row, now time.Time, startTime time.Time) *metricdata.TimeSeries {
|
||||
return &metricdata.TimeSeries{
|
||||
Points: []metricdata.Point{row.Data.toPoint(v.metricDescriptor.Type, now)},
|
||||
LabelValues: toLabelValues(row, v.metricDescriptor.LabelKeys),
|
||||
StartTime: startTime,
|
||||
}
|
||||
}
|
||||
|
||||
func viewToMetric(v *viewInternal, now time.Time, startTime time.Time) *metricdata.Metric {
|
||||
if v.metricDescriptor.Type == metricdata.TypeGaugeInt64 ||
|
||||
v.metricDescriptor.Type == metricdata.TypeGaugeFloat64 {
|
||||
startTime = time.Time{}
|
||||
}
|
||||
|
||||
rows := v.collectedRows()
|
||||
if len(rows) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
ts := []*metricdata.TimeSeries{}
|
||||
for _, row := range rows {
|
||||
ts = append(ts, rowToTimeseries(v, row, now, startTime))
|
||||
}
|
||||
|
||||
m := &metricdata.Metric{
|
||||
Descriptor: *v.metricDescriptor,
|
||||
TimeSeries: ts,
|
||||
}
|
||||
return m
|
||||
}
|
281
vendor/go.opencensus.io/stats/view/worker.go
generated
vendored
Normal file
281
vendor/go.opencensus.io/stats/view/worker.go
generated
vendored
Normal file
@ -0,0 +1,281 @@
|
||||
// Copyright 2017, OpenCensus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
package view
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"go.opencensus.io/metric/metricdata"
|
||||
"go.opencensus.io/metric/metricproducer"
|
||||
"go.opencensus.io/stats"
|
||||
"go.opencensus.io/stats/internal"
|
||||
"go.opencensus.io/tag"
|
||||
)
|
||||
|
||||
func init() {
|
||||
defaultWorker = newWorker()
|
||||
go defaultWorker.start()
|
||||
internal.DefaultRecorder = record
|
||||
}
|
||||
|
||||
type measureRef struct {
|
||||
measure string
|
||||
views map[*viewInternal]struct{}
|
||||
}
|
||||
|
||||
type worker struct {
|
||||
measures map[string]*measureRef
|
||||
views map[string]*viewInternal
|
||||
startTimes map[*viewInternal]time.Time
|
||||
|
||||
timer *time.Ticker
|
||||
c chan command
|
||||
quit, done chan bool
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
var defaultWorker *worker
|
||||
|
||||
var defaultReportingDuration = 10 * time.Second
|
||||
|
||||
// Find returns a registered view associated with this name.
|
||||
// If no registered view is found, nil is returned.
|
||||
func Find(name string) (v *View) {
|
||||
req := &getViewByNameReq{
|
||||
name: name,
|
||||
c: make(chan *getViewByNameResp),
|
||||
}
|
||||
defaultWorker.c <- req
|
||||
resp := <-req.c
|
||||
return resp.v
|
||||
}
|
||||
|
||||
// Register begins collecting data for the given views.
|
||||
// Once a view is registered, it reports data to the registered exporters.
|
||||
func Register(views ...*View) error {
|
||||
req := ®isterViewReq{
|
||||
views: views,
|
||||
err: make(chan error),
|
||||
}
|
||||
defaultWorker.c <- req
|
||||
return <-req.err
|
||||
}
|
||||
|
||||
// Unregister the given views. Data will not longer be exported for these views
|
||||
// after Unregister returns.
|
||||
// It is not necessary to unregister from views you expect to collect for the
|
||||
// duration of your program execution.
|
||||
func Unregister(views ...*View) {
|
||||
names := make([]string, len(views))
|
||||
for i := range views {
|
||||
names[i] = views[i].Name
|
||||
}
|
||||
req := &unregisterFromViewReq{
|
||||
views: names,
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
defaultWorker.c <- req
|
||||
<-req.done
|
||||
}
|
||||
|
||||
// RetrieveData gets a snapshot of the data collected for the the view registered
|
||||
// with the given name. It is intended for testing only.
|
||||
func RetrieveData(viewName string) ([]*Row, error) {
|
||||
req := &retrieveDataReq{
|
||||
now: time.Now(),
|
||||
v: viewName,
|
||||
c: make(chan *retrieveDataResp),
|
||||
}
|
||||
defaultWorker.c <- req
|
||||
resp := <-req.c
|
||||
return resp.rows, resp.err
|
||||
}
|
||||
|
||||
func record(tags *tag.Map, ms interface{}, attachments map[string]interface{}) {
|
||||
req := &recordReq{
|
||||
tm: tags,
|
||||
ms: ms.([]stats.Measurement),
|
||||
attachments: attachments,
|
||||
t: time.Now(),
|
||||
}
|
||||
defaultWorker.c <- req
|
||||
}
|
||||
|
||||
// SetReportingPeriod sets the interval between reporting aggregated views in
|
||||
// the program. If duration is less than or equal to zero, it enables the
|
||||
// default behavior.
|
||||
//
|
||||
// Note: each exporter makes different promises about what the lowest supported
|
||||
// duration is. For example, the Stackdriver exporter recommends a value no
|
||||
// lower than 1 minute. Consult each exporter per your needs.
|
||||
func SetReportingPeriod(d time.Duration) {
|
||||
// TODO(acetechnologist): ensure that the duration d is more than a certain
|
||||
// value. e.g. 1s
|
||||
req := &setReportingPeriodReq{
|
||||
d: d,
|
||||
c: make(chan bool),
|
||||
}
|
||||
defaultWorker.c <- req
|
||||
<-req.c // don't return until the timer is set to the new duration.
|
||||
}
|
||||
|
||||
func newWorker() *worker {
|
||||
return &worker{
|
||||
measures: make(map[string]*measureRef),
|
||||
views: make(map[string]*viewInternal),
|
||||
startTimes: make(map[*viewInternal]time.Time),
|
||||
timer: time.NewTicker(defaultReportingDuration),
|
||||
c: make(chan command, 1024),
|
||||
quit: make(chan bool),
|
||||
done: make(chan bool),
|
||||
}
|
||||
}
|
||||
|
||||
func (w *worker) start() {
|
||||
prodMgr := metricproducer.GlobalManager()
|
||||
prodMgr.AddProducer(w)
|
||||
|
||||
for {
|
||||
select {
|
||||
case cmd := <-w.c:
|
||||
cmd.handleCommand(w)
|
||||
case <-w.timer.C:
|
||||
w.reportUsage(time.Now())
|
||||
case <-w.quit:
|
||||
w.timer.Stop()
|
||||
close(w.c)
|
||||
w.done <- true
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (w *worker) stop() {
|
||||
prodMgr := metricproducer.GlobalManager()
|
||||
prodMgr.DeleteProducer(w)
|
||||
|
||||
w.quit <- true
|
||||
<-w.done
|
||||
}
|
||||
|
||||
func (w *worker) getMeasureRef(name string) *measureRef {
|
||||
if mr, ok := w.measures[name]; ok {
|
||||
return mr
|
||||
}
|
||||
mr := &measureRef{
|
||||
measure: name,
|
||||
views: make(map[*viewInternal]struct{}),
|
||||
}
|
||||
w.measures[name] = mr
|
||||
return mr
|
||||
}
|
||||
|
||||
func (w *worker) tryRegisterView(v *View) (*viewInternal, error) {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
vi, err := newViewInternal(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if x, ok := w.views[vi.view.Name]; ok {
|
||||
if !x.view.same(vi.view) {
|
||||
return nil, fmt.Errorf("cannot register view %q; a different view with the same name is already registered", v.Name)
|
||||
}
|
||||
|
||||
// the view is already registered so there is nothing to do and the
|
||||
// command is considered successful.
|
||||
return x, nil
|
||||
}
|
||||
w.views[vi.view.Name] = vi
|
||||
ref := w.getMeasureRef(vi.view.Measure.Name())
|
||||
ref.views[vi] = struct{}{}
|
||||
return vi, nil
|
||||
}
|
||||
|
||||
func (w *worker) unregisterView(viewName string) {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
delete(w.views, viewName)
|
||||
}
|
||||
|
||||
func (w *worker) reportView(v *viewInternal, now time.Time) {
|
||||
if !v.isSubscribed() {
|
||||
return
|
||||
}
|
||||
rows := v.collectedRows()
|
||||
_, ok := w.startTimes[v]
|
||||
if !ok {
|
||||
w.startTimes[v] = now
|
||||
}
|
||||
viewData := &Data{
|
||||
View: v.view,
|
||||
Start: w.startTimes[v],
|
||||
End: time.Now(),
|
||||
Rows: rows,
|
||||
}
|
||||
exportersMu.Lock()
|
||||
for e := range exporters {
|
||||
e.ExportView(viewData)
|
||||
}
|
||||
exportersMu.Unlock()
|
||||
}
|
||||
|
||||
func (w *worker) reportUsage(now time.Time) {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
for _, v := range w.views {
|
||||
w.reportView(v, now)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *worker) toMetric(v *viewInternal, now time.Time) *metricdata.Metric {
|
||||
if !v.isSubscribed() {
|
||||
return nil
|
||||
}
|
||||
|
||||
_, ok := w.startTimes[v]
|
||||
if !ok {
|
||||
w.startTimes[v] = now
|
||||
}
|
||||
|
||||
var startTime time.Time
|
||||
if v.metricDescriptor.Type == metricdata.TypeGaugeInt64 ||
|
||||
v.metricDescriptor.Type == metricdata.TypeGaugeFloat64 {
|
||||
startTime = time.Time{}
|
||||
} else {
|
||||
startTime = w.startTimes[v]
|
||||
}
|
||||
|
||||
return viewToMetric(v, now, startTime)
|
||||
}
|
||||
|
||||
// Read reads all view data and returns them as metrics.
|
||||
// It is typically invoked by metric reader to export stats in metric format.
|
||||
func (w *worker) Read() []*metricdata.Metric {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
now := time.Now()
|
||||
metrics := make([]*metricdata.Metric, 0, len(w.views))
|
||||
for _, v := range w.views {
|
||||
metric := w.toMetric(v, now)
|
||||
if metric != nil {
|
||||
metrics = append(metrics, metric)
|
||||
}
|
||||
}
|
||||
return metrics
|
||||
}
|
186
vendor/go.opencensus.io/stats/view/worker_commands.go
generated
vendored
Normal file
186
vendor/go.opencensus.io/stats/view/worker_commands.go
generated
vendored
Normal file
@ -0,0 +1,186 @@
|
||||
// Copyright 2017, OpenCensus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
package view
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.opencensus.io/stats"
|
||||
"go.opencensus.io/stats/internal"
|
||||
"go.opencensus.io/tag"
|
||||
)
|
||||
|
||||
type command interface {
|
||||
handleCommand(w *worker)
|
||||
}
|
||||
|
||||
// getViewByNameReq is the command to get a view given its name.
|
||||
type getViewByNameReq struct {
|
||||
name string
|
||||
c chan *getViewByNameResp
|
||||
}
|
||||
|
||||
type getViewByNameResp struct {
|
||||
v *View
|
||||
}
|
||||
|
||||
func (cmd *getViewByNameReq) handleCommand(w *worker) {
|
||||
v := w.views[cmd.name]
|
||||
if v == nil {
|
||||
cmd.c <- &getViewByNameResp{nil}
|
||||
return
|
||||
}
|
||||
cmd.c <- &getViewByNameResp{v.view}
|
||||
}
|
||||
|
||||
// registerViewReq is the command to register a view.
|
||||
type registerViewReq struct {
|
||||
views []*View
|
||||
err chan error
|
||||
}
|
||||
|
||||
func (cmd *registerViewReq) handleCommand(w *worker) {
|
||||
for _, v := range cmd.views {
|
||||
if err := v.canonicalize(); err != nil {
|
||||
cmd.err <- err
|
||||
return
|
||||
}
|
||||
}
|
||||
var errstr []string
|
||||
for _, view := range cmd.views {
|
||||
vi, err := w.tryRegisterView(view)
|
||||
if err != nil {
|
||||
errstr = append(errstr, fmt.Sprintf("%s: %v", view.Name, err))
|
||||
continue
|
||||
}
|
||||
internal.SubscriptionReporter(view.Measure.Name())
|
||||
vi.subscribe()
|
||||
}
|
||||
if len(errstr) > 0 {
|
||||
cmd.err <- errors.New(strings.Join(errstr, "\n"))
|
||||
} else {
|
||||
cmd.err <- nil
|
||||
}
|
||||
}
|
||||
|
||||
// unregisterFromViewReq is the command to unregister to a view. Has no
|
||||
// impact on the data collection for client that are pulling data from the
|
||||
// library.
|
||||
type unregisterFromViewReq struct {
|
||||
views []string
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
func (cmd *unregisterFromViewReq) handleCommand(w *worker) {
|
||||
for _, name := range cmd.views {
|
||||
vi, ok := w.views[name]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// Report pending data for this view before removing it.
|
||||
w.reportView(vi, time.Now())
|
||||
|
||||
vi.unsubscribe()
|
||||
if !vi.isSubscribed() {
|
||||
// this was the last subscription and view is not collecting anymore.
|
||||
// The collected data can be cleared.
|
||||
vi.clearRows()
|
||||
}
|
||||
w.unregisterView(name)
|
||||
}
|
||||
cmd.done <- struct{}{}
|
||||
}
|
||||
|
||||
// retrieveDataReq is the command to retrieve data for a view.
|
||||
type retrieveDataReq struct {
|
||||
now time.Time
|
||||
v string
|
||||
c chan *retrieveDataResp
|
||||
}
|
||||
|
||||
type retrieveDataResp struct {
|
||||
rows []*Row
|
||||
err error
|
||||
}
|
||||
|
||||
func (cmd *retrieveDataReq) handleCommand(w *worker) {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
vi, ok := w.views[cmd.v]
|
||||
if !ok {
|
||||
cmd.c <- &retrieveDataResp{
|
||||
nil,
|
||||
fmt.Errorf("cannot retrieve data; view %q is not registered", cmd.v),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if !vi.isSubscribed() {
|
||||
cmd.c <- &retrieveDataResp{
|
||||
nil,
|
||||
fmt.Errorf("cannot retrieve data; view %q has no subscriptions or collection is not forcibly started", cmd.v),
|
||||
}
|
||||
return
|
||||
}
|
||||
cmd.c <- &retrieveDataResp{
|
||||
vi.collectedRows(),
|
||||
nil,
|
||||
}
|
||||
}
|
||||
|
||||
// recordReq is the command to record data related to multiple measures
|
||||
// at once.
|
||||
type recordReq struct {
|
||||
tm *tag.Map
|
||||
ms []stats.Measurement
|
||||
attachments map[string]interface{}
|
||||
t time.Time
|
||||
}
|
||||
|
||||
func (cmd *recordReq) handleCommand(w *worker) {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
for _, m := range cmd.ms {
|
||||
if (m == stats.Measurement{}) { // not registered
|
||||
continue
|
||||
}
|
||||
ref := w.getMeasureRef(m.Measure().Name())
|
||||
for v := range ref.views {
|
||||
v.addSample(cmd.tm, m.Value(), cmd.attachments, time.Now())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// setReportingPeriodReq is the command to modify the duration between
|
||||
// reporting the collected data to the registered clients.
|
||||
type setReportingPeriodReq struct {
|
||||
d time.Duration
|
||||
c chan bool
|
||||
}
|
||||
|
||||
func (cmd *setReportingPeriodReq) handleCommand(w *worker) {
|
||||
w.timer.Stop()
|
||||
if cmd.d <= 0 {
|
||||
w.timer = time.NewTicker(defaultReportingDuration)
|
||||
} else {
|
||||
w.timer = time.NewTicker(cmd.d)
|
||||
}
|
||||
cmd.c <- true
|
||||
}
|
43
vendor/go.opencensus.io/tag/context.go
generated
vendored
Normal file
43
vendor/go.opencensus.io/tag/context.go
generated
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
// Copyright 2017, OpenCensus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
package tag
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
// FromContext returns the tag map stored in the context.
|
||||
func FromContext(ctx context.Context) *Map {
|
||||
// The returned tag map shouldn't be mutated.
|
||||
ts := ctx.Value(mapCtxKey)
|
||||
if ts == nil {
|
||||
return nil
|
||||
}
|
||||
return ts.(*Map)
|
||||
}
|
||||
|
||||
// NewContext creates a new context with the given tag map.
|
||||
// To propagate a tag map to downstream methods and downstream RPCs, add a tag map
|
||||
// to the current context. NewContext will return a copy of the current context,
|
||||
// and put the tag map into the returned one.
|
||||
// If there is already a tag map in the current context, it will be replaced with m.
|
||||
func NewContext(ctx context.Context, m *Map) context.Context {
|
||||
return context.WithValue(ctx, mapCtxKey, m)
|
||||
}
|
||||
|
||||
type ctxKey struct{}
|
||||
|
||||
var mapCtxKey = ctxKey{}
|
26
vendor/go.opencensus.io/tag/doc.go
generated
vendored
Normal file
26
vendor/go.opencensus.io/tag/doc.go
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
// Copyright 2017, OpenCensus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
/*
|
||||
Package tag contains OpenCensus tags.
|
||||
|
||||
Tags are key-value pairs. Tags provide additional cardinality to
|
||||
the OpenCensus instrumentation data.
|
||||
|
||||
Tags can be propagated on the wire and in the same
|
||||
process via context.Context. Encode and Decode should be
|
||||
used to represent tags into their binary propagation form.
|
||||
*/
|
||||
package tag // import "go.opencensus.io/tag"
|
35
vendor/go.opencensus.io/tag/key.go
generated
vendored
Normal file
35
vendor/go.opencensus.io/tag/key.go
generated
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
// Copyright 2017, OpenCensus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
package tag
|
||||
|
||||
// Key represents a tag key.
|
||||
type Key struct {
|
||||
name string
|
||||
}
|
||||
|
||||
// NewKey creates or retrieves a string key identified by name.
|
||||
// Calling NewKey consequently with the same name returns the same key.
|
||||
func NewKey(name string) (Key, error) {
|
||||
if !checkKeyName(name) {
|
||||
return Key{}, errInvalidKeyName
|
||||
}
|
||||
return Key{name: name}, nil
|
||||
}
|
||||
|
||||
// Name returns the name of the key.
|
||||
func (k Key) Name() string {
|
||||
return k.name
|
||||
}
|
229
vendor/go.opencensus.io/tag/map.go
generated
vendored
Normal file
229
vendor/go.opencensus.io/tag/map.go
generated
vendored
Normal file
@ -0,0 +1,229 @@
|
||||
// Copyright 2017, OpenCensus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
package tag
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// Tag is a key value pair that can be propagated on wire.
|
||||
type Tag struct {
|
||||
Key Key
|
||||
Value string
|
||||
}
|
||||
|
||||
type tagContent struct {
|
||||
value string
|
||||
m metadatas
|
||||
}
|
||||
|
||||
// Map is a map of tags. Use New to create a context containing
|
||||
// a new Map.
|
||||
type Map struct {
|
||||
m map[Key]tagContent
|
||||
}
|
||||
|
||||
// Value returns the value for the key if a value for the key exists.
|
||||
func (m *Map) Value(k Key) (string, bool) {
|
||||
if m == nil {
|
||||
return "", false
|
||||
}
|
||||
v, ok := m.m[k]
|
||||
return v.value, ok
|
||||
}
|
||||
|
||||
func (m *Map) String() string {
|
||||
if m == nil {
|
||||
return "nil"
|
||||
}
|
||||
keys := make([]Key, 0, len(m.m))
|
||||
for k := range m.m {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Slice(keys, func(i, j int) bool { return keys[i].Name() < keys[j].Name() })
|
||||
|
||||
var buffer bytes.Buffer
|
||||
buffer.WriteString("{ ")
|
||||
for _, k := range keys {
|
||||
buffer.WriteString(fmt.Sprintf("{%v %v}", k.name, m.m[k]))
|
||||
}
|
||||
buffer.WriteString(" }")
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
func (m *Map) insert(k Key, v string, md metadatas) {
|
||||
if _, ok := m.m[k]; ok {
|
||||
return
|
||||
}
|
||||
m.m[k] = tagContent{value: v, m: md}
|
||||
}
|
||||
|
||||
func (m *Map) update(k Key, v string, md metadatas) {
|
||||
if _, ok := m.m[k]; ok {
|
||||
m.m[k] = tagContent{value: v, m: md}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Map) upsert(k Key, v string, md metadatas) {
|
||||
m.m[k] = tagContent{value: v, m: md}
|
||||
}
|
||||
|
||||
func (m *Map) delete(k Key) {
|
||||
delete(m.m, k)
|
||||
}
|
||||
|
||||
func newMap() *Map {
|
||||
return &Map{m: make(map[Key]tagContent)}
|
||||
}
|
||||
|
||||
// Mutator modifies a tag map.
|
||||
type Mutator interface {
|
||||
Mutate(t *Map) (*Map, error)
|
||||
}
|
||||
|
||||
// Insert returns a mutator that inserts a
|
||||
// value associated with k. If k already exists in the tag map,
|
||||
// mutator doesn't update the value.
|
||||
// Metadata applies metadata to the tag. It is optional.
|
||||
// Metadatas are applied in the order in which it is provided.
|
||||
// If more than one metadata updates the same attribute then
|
||||
// the update from the last metadata prevails.
|
||||
func Insert(k Key, v string, mds ...Metadata) Mutator {
|
||||
return &mutator{
|
||||
fn: func(m *Map) (*Map, error) {
|
||||
if !checkValue(v) {
|
||||
return nil, errInvalidValue
|
||||
}
|
||||
m.insert(k, v, createMetadatas(mds...))
|
||||
return m, nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Update returns a mutator that updates the
|
||||
// value of the tag associated with k with v. If k doesn't
|
||||
// exists in the tag map, the mutator doesn't insert the value.
|
||||
// Metadata applies metadata to the tag. It is optional.
|
||||
// Metadatas are applied in the order in which it is provided.
|
||||
// If more than one metadata updates the same attribute then
|
||||
// the update from the last metadata prevails.
|
||||
func Update(k Key, v string, mds ...Metadata) Mutator {
|
||||
return &mutator{
|
||||
fn: func(m *Map) (*Map, error) {
|
||||
if !checkValue(v) {
|
||||
return nil, errInvalidValue
|
||||
}
|
||||
m.update(k, v, createMetadatas(mds...))
|
||||
return m, nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Upsert returns a mutator that upserts the
|
||||
// value of the tag associated with k with v. It inserts the
|
||||
// value if k doesn't exist already. It mutates the value
|
||||
// if k already exists.
|
||||
// Metadata applies metadata to the tag. It is optional.
|
||||
// Metadatas are applied in the order in which it is provided.
|
||||
// If more than one metadata updates the same attribute then
|
||||
// the update from the last metadata prevails.
|
||||
func Upsert(k Key, v string, mds ...Metadata) Mutator {
|
||||
return &mutator{
|
||||
fn: func(m *Map) (*Map, error) {
|
||||
if !checkValue(v) {
|
||||
return nil, errInvalidValue
|
||||
}
|
||||
m.upsert(k, v, createMetadatas(mds...))
|
||||
return m, nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func createMetadatas(mds ...Metadata) metadatas {
|
||||
var metas metadatas
|
||||
if len(mds) > 0 {
|
||||
for _, md := range mds {
|
||||
if md != nil {
|
||||
md(&metas)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
WithTTL(TTLUnlimitedPropagation)(&metas)
|
||||
}
|
||||
return metas
|
||||
|
||||
}
|
||||
|
||||
// Delete returns a mutator that deletes
|
||||
// the value associated with k.
|
||||
func Delete(k Key) Mutator {
|
||||
return &mutator{
|
||||
fn: func(m *Map) (*Map, error) {
|
||||
m.delete(k)
|
||||
return m, nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// New returns a new context that contains a tag map
|
||||
// originated from the incoming context and modified
|
||||
// with the provided mutators.
|
||||
func New(ctx context.Context, mutator ...Mutator) (context.Context, error) {
|
||||
m := newMap()
|
||||
orig := FromContext(ctx)
|
||||
if orig != nil {
|
||||
for k, v := range orig.m {
|
||||
if !checkKeyName(k.Name()) {
|
||||
return ctx, fmt.Errorf("key:%q: %v", k, errInvalidKeyName)
|
||||
}
|
||||
if !checkValue(v.value) {
|
||||
return ctx, fmt.Errorf("key:%q value:%q: %v", k.Name(), v, errInvalidValue)
|
||||
}
|
||||
m.insert(k, v.value, v.m)
|
||||
}
|
||||
}
|
||||
var err error
|
||||
for _, mod := range mutator {
|
||||
m, err = mod.Mutate(m)
|
||||
if err != nil {
|
||||
return ctx, err
|
||||
}
|
||||
}
|
||||
return NewContext(ctx, m), nil
|
||||
}
|
||||
|
||||
// Do is similar to pprof.Do: a convenience for installing the tags
|
||||
// from the context as Go profiler labels. This allows you to
|
||||
// correlated runtime profiling with stats.
|
||||
//
|
||||
// It converts the key/values from the given map to Go profiler labels
|
||||
// and calls pprof.Do.
|
||||
//
|
||||
// Do is going to do nothing if your Go version is below 1.9.
|
||||
func Do(ctx context.Context, f func(ctx context.Context)) {
|
||||
do(ctx, f)
|
||||
}
|
||||
|
||||
type mutator struct {
|
||||
fn func(t *Map) (*Map, error)
|
||||
}
|
||||
|
||||
func (m *mutator) Mutate(t *Map) (*Map, error) {
|
||||
return m.fn(t)
|
||||
}
|
239
vendor/go.opencensus.io/tag/map_codec.go
generated
vendored
Normal file
239
vendor/go.opencensus.io/tag/map_codec.go
generated
vendored
Normal file
@ -0,0 +1,239 @@
|
||||
// Copyright 2017, OpenCensus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
package tag
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// KeyType defines the types of keys allowed. Currently only keyTypeString is
|
||||
// supported.
|
||||
type keyType byte
|
||||
|
||||
const (
|
||||
keyTypeString keyType = iota
|
||||
keyTypeInt64
|
||||
keyTypeTrue
|
||||
keyTypeFalse
|
||||
|
||||
tagsVersionID = byte(0)
|
||||
)
|
||||
|
||||
type encoderGRPC struct {
|
||||
buf []byte
|
||||
writeIdx, readIdx int
|
||||
}
|
||||
|
||||
// writeKeyString writes the fieldID '0' followed by the key string and value
|
||||
// string.
|
||||
func (eg *encoderGRPC) writeTagString(k, v string) {
|
||||
eg.writeByte(byte(keyTypeString))
|
||||
eg.writeStringWithVarintLen(k)
|
||||
eg.writeStringWithVarintLen(v)
|
||||
}
|
||||
|
||||
func (eg *encoderGRPC) writeTagUint64(k string, i uint64) {
|
||||
eg.writeByte(byte(keyTypeInt64))
|
||||
eg.writeStringWithVarintLen(k)
|
||||
eg.writeUint64(i)
|
||||
}
|
||||
|
||||
func (eg *encoderGRPC) writeTagTrue(k string) {
|
||||
eg.writeByte(byte(keyTypeTrue))
|
||||
eg.writeStringWithVarintLen(k)
|
||||
}
|
||||
|
||||
func (eg *encoderGRPC) writeTagFalse(k string) {
|
||||
eg.writeByte(byte(keyTypeFalse))
|
||||
eg.writeStringWithVarintLen(k)
|
||||
}
|
||||
|
||||
func (eg *encoderGRPC) writeBytesWithVarintLen(bytes []byte) {
|
||||
length := len(bytes)
|
||||
|
||||
eg.growIfRequired(binary.MaxVarintLen64 + length)
|
||||
eg.writeIdx += binary.PutUvarint(eg.buf[eg.writeIdx:], uint64(length))
|
||||
copy(eg.buf[eg.writeIdx:], bytes)
|
||||
eg.writeIdx += length
|
||||
}
|
||||
|
||||
func (eg *encoderGRPC) writeStringWithVarintLen(s string) {
|
||||
length := len(s)
|
||||
|
||||
eg.growIfRequired(binary.MaxVarintLen64 + length)
|
||||
eg.writeIdx += binary.PutUvarint(eg.buf[eg.writeIdx:], uint64(length))
|
||||
copy(eg.buf[eg.writeIdx:], s)
|
||||
eg.writeIdx += length
|
||||
}
|
||||
|
||||
func (eg *encoderGRPC) writeByte(v byte) {
|
||||
eg.growIfRequired(1)
|
||||
eg.buf[eg.writeIdx] = v
|
||||
eg.writeIdx++
|
||||
}
|
||||
|
||||
func (eg *encoderGRPC) writeUint32(i uint32) {
|
||||
eg.growIfRequired(4)
|
||||
binary.LittleEndian.PutUint32(eg.buf[eg.writeIdx:], i)
|
||||
eg.writeIdx += 4
|
||||
}
|
||||
|
||||
func (eg *encoderGRPC) writeUint64(i uint64) {
|
||||
eg.growIfRequired(8)
|
||||
binary.LittleEndian.PutUint64(eg.buf[eg.writeIdx:], i)
|
||||
eg.writeIdx += 8
|
||||
}
|
||||
|
||||
func (eg *encoderGRPC) readByte() byte {
|
||||
b := eg.buf[eg.readIdx]
|
||||
eg.readIdx++
|
||||
return b
|
||||
}
|
||||
|
||||
func (eg *encoderGRPC) readUint32() uint32 {
|
||||
i := binary.LittleEndian.Uint32(eg.buf[eg.readIdx:])
|
||||
eg.readIdx += 4
|
||||
return i
|
||||
}
|
||||
|
||||
func (eg *encoderGRPC) readUint64() uint64 {
|
||||
i := binary.LittleEndian.Uint64(eg.buf[eg.readIdx:])
|
||||
eg.readIdx += 8
|
||||
return i
|
||||
}
|
||||
|
||||
func (eg *encoderGRPC) readBytesWithVarintLen() ([]byte, error) {
|
||||
if eg.readEnded() {
|
||||
return nil, fmt.Errorf("unexpected end while readBytesWithVarintLen '%x' starting at idx '%v'", eg.buf, eg.readIdx)
|
||||
}
|
||||
length, valueStart := binary.Uvarint(eg.buf[eg.readIdx:])
|
||||
if valueStart <= 0 {
|
||||
return nil, fmt.Errorf("unexpected end while readBytesWithVarintLen '%x' starting at idx '%v'", eg.buf, eg.readIdx)
|
||||
}
|
||||
|
||||
valueStart += eg.readIdx
|
||||
valueEnd := valueStart + int(length)
|
||||
if valueEnd > len(eg.buf) {
|
||||
return nil, fmt.Errorf("malformed encoding: length:%v, upper:%v, maxLength:%v", length, valueEnd, len(eg.buf))
|
||||
}
|
||||
|
||||
eg.readIdx = valueEnd
|
||||
return eg.buf[valueStart:valueEnd], nil
|
||||
}
|
||||
|
||||
func (eg *encoderGRPC) readStringWithVarintLen() (string, error) {
|
||||
bytes, err := eg.readBytesWithVarintLen()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(bytes), nil
|
||||
}
|
||||
|
||||
func (eg *encoderGRPC) growIfRequired(expected int) {
|
||||
if len(eg.buf)-eg.writeIdx < expected {
|
||||
tmp := make([]byte, 2*(len(eg.buf)+1)+expected)
|
||||
copy(tmp, eg.buf)
|
||||
eg.buf = tmp
|
||||
}
|
||||
}
|
||||
|
||||
func (eg *encoderGRPC) readEnded() bool {
|
||||
return eg.readIdx >= len(eg.buf)
|
||||
}
|
||||
|
||||
func (eg *encoderGRPC) bytes() []byte {
|
||||
return eg.buf[:eg.writeIdx]
|
||||
}
|
||||
|
||||
// Encode encodes the tag map into a []byte. It is useful to propagate
|
||||
// the tag maps on wire in binary format.
|
||||
func Encode(m *Map) []byte {
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
eg := &encoderGRPC{
|
||||
buf: make([]byte, len(m.m)),
|
||||
}
|
||||
eg.writeByte(byte(tagsVersionID))
|
||||
for k, v := range m.m {
|
||||
if v.m.ttl.ttl == valueTTLUnlimitedPropagation {
|
||||
eg.writeByte(byte(keyTypeString))
|
||||
eg.writeStringWithVarintLen(k.name)
|
||||
eg.writeBytesWithVarintLen([]byte(v.value))
|
||||
}
|
||||
}
|
||||
return eg.bytes()
|
||||
}
|
||||
|
||||
// Decode decodes the given []byte into a tag map.
|
||||
func Decode(bytes []byte) (*Map, error) {
|
||||
ts := newMap()
|
||||
err := DecodeEach(bytes, ts.upsert)
|
||||
if err != nil {
|
||||
// no partial failures
|
||||
return nil, err
|
||||
}
|
||||
return ts, nil
|
||||
}
|
||||
|
||||
// DecodeEach decodes the given serialized tag map, calling handler for each
|
||||
// tag key and value decoded.
|
||||
func DecodeEach(bytes []byte, fn func(key Key, val string, md metadatas)) error {
|
||||
eg := &encoderGRPC{
|
||||
buf: bytes,
|
||||
}
|
||||
if len(eg.buf) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
version := eg.readByte()
|
||||
if version > tagsVersionID {
|
||||
return fmt.Errorf("cannot decode: unsupported version: %q; supports only up to: %q", version, tagsVersionID)
|
||||
}
|
||||
|
||||
for !eg.readEnded() {
|
||||
typ := keyType(eg.readByte())
|
||||
|
||||
if typ != keyTypeString {
|
||||
return fmt.Errorf("cannot decode: invalid key type: %q", typ)
|
||||
}
|
||||
|
||||
k, err := eg.readBytesWithVarintLen()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
v, err := eg.readBytesWithVarintLen()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
key, err := NewKey(string(k))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
val := string(v)
|
||||
if !checkValue(val) {
|
||||
return errInvalidValue
|
||||
}
|
||||
fn(key, val, createMetadatas(WithTTL(TTLUnlimitedPropagation)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user