fix go module related merge conflict
This commit is contained in:
parent
d08fe7a91f
commit
97ac1dedba
24
go.mod
24
go.mod
|
@ -2,7 +2,6 @@ module github.com/hashicorp/terraform
|
||||||
|
|
||||||
require (
|
require (
|
||||||
cloud.google.com/go v0.15.0
|
cloud.google.com/go v0.15.0
|
||||||
contrib.go.opencensus.io/exporter/stackdriver v0.6.0 // indirect
|
|
||||||
github.com/Azure/azure-sdk-for-go v10.3.0-beta+incompatible
|
github.com/Azure/azure-sdk-for-go v10.3.0-beta+incompatible
|
||||||
github.com/Azure/go-autorest v8.3.1+incompatible
|
github.com/Azure/go-autorest v8.3.1+incompatible
|
||||||
github.com/Azure/go-ntlmssp v0.0.0-20170803034930-c92175d54006 // indirect
|
github.com/Azure/go-ntlmssp v0.0.0-20170803034930-c92175d54006 // indirect
|
||||||
|
@ -20,6 +19,7 @@ require (
|
||||||
github.com/armon/go-radix v0.0.0-20160115234725-4239b77079c7 // indirect
|
github.com/armon/go-radix v0.0.0-20160115234725-4239b77079c7 // indirect
|
||||||
github.com/aws/aws-sdk-go v1.14.31
|
github.com/aws/aws-sdk-go v1.14.31
|
||||||
github.com/beevik/etree v0.0.0-20171015221209-af219c0c7ea1 // indirect
|
github.com/beevik/etree v0.0.0-20171015221209-af219c0c7ea1 // indirect
|
||||||
|
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 // indirect
|
||||||
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
|
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
|
||||||
github.com/bgentry/speakeasy v0.0.0-20161015143505-675b82c74c0e // indirect
|
github.com/bgentry/speakeasy v0.0.0-20161015143505-675b82c74c0e // indirect
|
||||||
github.com/blang/semver v0.0.0-20170202183821-4a1e882c79dc
|
github.com/blang/semver v0.0.0-20170202183821-4a1e882c79dc
|
||||||
|
@ -90,6 +90,7 @@ require (
|
||||||
github.com/mattn/go-colorable v0.0.0-20160220075935-9cbef7c35391
|
github.com/mattn/go-colorable v0.0.0-20160220075935-9cbef7c35391
|
||||||
github.com/mattn/go-isatty v0.0.0-20161123143637-30a891c33c7c // indirect
|
github.com/mattn/go-isatty v0.0.0-20161123143637-30a891c33c7c // indirect
|
||||||
github.com/mattn/go-shellwords v1.0.1
|
github.com/mattn/go-shellwords v1.0.1
|
||||||
|
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||||
github.com/miekg/dns v1.0.8 // indirect
|
github.com/miekg/dns v1.0.8 // indirect
|
||||||
github.com/mitchellh/cli v0.0.0-20171129193617-33edc47170b5
|
github.com/mitchellh/cli v0.0.0-20171129193617-33edc47170b5
|
||||||
github.com/mitchellh/colorstring v0.0.0-20150917214807-8631ce90f286
|
github.com/mitchellh/colorstring v0.0.0-20150917214807-8631ce90f286
|
||||||
|
@ -109,9 +110,10 @@ require (
|
||||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c // indirect
|
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c // indirect
|
||||||
github.com/pkg/errors v0.0.0-20170505043639-c605e284fe17 // indirect
|
github.com/pkg/errors v0.0.0-20170505043639-c605e284fe17 // indirect
|
||||||
github.com/posener/complete v0.0.0-20171219111128-6bee943216c8
|
github.com/posener/complete v0.0.0-20171219111128-6bee943216c8
|
||||||
github.com/prometheus/client_golang v0.9.0 // indirect
|
github.com/prometheus/client_golang v0.8.0 // indirect
|
||||||
github.com/prometheus/common v0.0.0-20181015124227-bcb74de08d37 // indirect
|
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 // indirect
|
||||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d // indirect
|
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e // indirect
|
||||||
|
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273 // indirect
|
||||||
github.com/satori/go.uuid v0.0.0-20160927100844-b061729afc07 // indirect
|
github.com/satori/go.uuid v0.0.0-20160927100844-b061729afc07 // indirect
|
||||||
github.com/satori/uuid v0.0.0-20160927100844-b061729afc07 // indirect
|
github.com/satori/uuid v0.0.0-20160927100844-b061729afc07 // indirect
|
||||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect
|
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect
|
||||||
|
@ -128,13 +130,15 @@ require (
|
||||||
github.com/xanzy/ssh-agent v0.1.0
|
github.com/xanzy/ssh-agent v0.1.0
|
||||||
github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18 // indirect
|
github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18 // indirect
|
||||||
github.com/xlab/treeprint v0.0.0-20161029104018-1d6e34225557
|
github.com/xlab/treeprint v0.0.0-20161029104018-1d6e34225557
|
||||||
github.com/zclconf/go-cty v0.0.0-20180907002636-07dee8a1cfd4
|
github.com/zclconf/go-cty v0.0.0-20180925180032-d9b87d891d0b
|
||||||
go.opencensus.io v0.17.0 // indirect
|
golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b
|
||||||
golang.org/x/crypto v0.0.0-20180816225734-aabede6cba87
|
golang.org/x/net v0.0.0-20180925072008-f04abc6bdfa7
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd
|
golang.org/x/oauth2 v0.0.0-20170928010508-bb50c06baba3
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be
|
golang.org/x/sys v0.0.0-20180925112736-b09afc3d579e // indirect
|
||||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2 // indirect
|
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2 // indirect
|
||||||
google.golang.org/api v0.0.0-20180921000521-920bb1beccf7
|
google.golang.org/api v0.0.0-20171005000305-7a7376eff6a5
|
||||||
|
google.golang.org/appengine v1.2.0 // indirect
|
||||||
|
google.golang.org/genproto v0.0.0-20171002232614-f676e0f3ac63 // indirect
|
||||||
google.golang.org/grpc v1.14.0
|
google.golang.org/grpc v1.14.0
|
||||||
gopkg.in/vmihailenco/msgpack.v2 v2.9.1 // indirect
|
gopkg.in/vmihailenco/msgpack.v2 v2.9.1 // indirect
|
||||||
)
|
)
|
||||||
|
|
45
go.sum
45
go.sum
|
@ -1,8 +1,5 @@
|
||||||
cloud.google.com/go v0.15.0 h1:/e2wXYguItvFu4fJCvhMRPIwwrimuUxI+aCVx/ahLjg=
|
cloud.google.com/go v0.15.0 h1:/e2wXYguItvFu4fJCvhMRPIwwrimuUxI+aCVx/ahLjg=
|
||||||
cloud.google.com/go v0.15.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.15.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
contrib.go.opencensus.io/exporter/stackdriver v0.6.0 h1:U0FQWsZU3aO8W+BrZc88T8fdd24qe3Phawa9V9oaVUE=
|
|
||||||
contrib.go.opencensus.io/exporter/stackdriver v0.6.0/go.mod h1:QeFzMJDAw8TXt5+aRaSuE8l5BwaMIOIlaVkBOPRuMuw=
|
|
||||||
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
|
|
||||||
github.com/Azure/azure-sdk-for-go v10.3.0-beta+incompatible h1:TP+nmGmOP7psi7CvIq/1pCliRBRj73vmMTDjaPrTnr8=
|
github.com/Azure/azure-sdk-for-go v10.3.0-beta+incompatible h1:TP+nmGmOP7psi7CvIq/1pCliRBRj73vmMTDjaPrTnr8=
|
||||||
github.com/Azure/azure-sdk-for-go v10.3.0-beta+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
github.com/Azure/azure-sdk-for-go v10.3.0-beta+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||||
github.com/Azure/go-autorest v8.3.1+incompatible h1:1+jMCOJcCh3GmI7FGJVOo8AlfPWDyjS7fLbbkZGzEGY=
|
github.com/Azure/go-autorest v8.3.1+incompatible h1:1+jMCOJcCh3GmI7FGJVOo8AlfPWDyjS7fLbbkZGzEGY=
|
||||||
|
@ -233,7 +230,6 @@ github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/
|
||||||
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U=
|
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U=
|
||||||
github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
|
github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
|
||||||
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||||
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
|
|
||||||
github.com/packer-community/winrmcp v0.0.0-20180102160824-81144009af58 h1:m3CEgv3ah1Rhy82L+c0QG/U3VyY1UsvsIdkh0/rU97Y=
|
github.com/packer-community/winrmcp v0.0.0-20180102160824-81144009af58 h1:m3CEgv3ah1Rhy82L+c0QG/U3VyY1UsvsIdkh0/rU97Y=
|
||||||
github.com/packer-community/winrmcp v0.0.0-20180102160824-81144009af58/go.mod h1:f6Izs6JvFTdnRbziASagjZ2vmf55NSIkC/weStxCHqk=
|
github.com/packer-community/winrmcp v0.0.0-20180102160824-81144009af58/go.mod h1:f6Izs6JvFTdnRbziASagjZ2vmf55NSIkC/weStxCHqk=
|
||||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c h1:Lgl0gzECD8GnQ5QCWA8o6BtfL6mDH5rQgM4/fX3avOs=
|
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c h1:Lgl0gzECD8GnQ5QCWA8o6BtfL6mDH5rQgM4/fX3avOs=
|
||||||
|
@ -244,17 +240,14 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/posener/complete v0.0.0-20171219111128-6bee943216c8 h1:lcb1zvdlaZyEbl2OXifN3uOYYyIvllofUbmp9bwbL+0=
|
github.com/posener/complete v0.0.0-20171219111128-6bee943216c8 h1:lcb1zvdlaZyEbl2OXifN3uOYYyIvllofUbmp9bwbL+0=
|
||||||
github.com/posener/complete v0.0.0-20171219111128-6bee943216c8/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
github.com/posener/complete v0.0.0-20171219111128-6bee943216c8/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||||
|
github.com/prometheus/client_golang v0.8.0 h1:1921Yw9Gc3iSc4VQh3PIoOqgPCZS7G/4xQNVUp8Mda8=
|
||||||
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||||
github.com/prometheus/client_golang v0.9.0 h1:tXuTFVHC03mW0D+Ua1Q2d1EAVqLTuggX50V0VLICCzY=
|
|
||||||
github.com/prometheus/client_golang v0.9.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
|
||||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8=
|
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8=
|
||||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||||
|
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e h1:n/3MEhJQjQxrOUCzh1Y3Re6aJUUWRp2M9+Oc3eVn/54=
|
||||||
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||||
github.com/prometheus/common v0.0.0-20181015124227-bcb74de08d37 h1:Y7YdJ9Xb3MoQOzAWXnDunAJYpvhVwZdTirNfGUgPKaA=
|
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273 h1:agujYaXJSxSo18YNX3jzl+4G6Bstwt+kqv47GS12uL0=
|
||||||
github.com/prometheus/common v0.0.0-20181015124227-bcb74de08d37/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
|
||||||
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d h1:GoAlyOgbOEIFdaDqxJVlbOQ1DtGmZWs/Qau0hIlk+WQ=
|
|
||||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
|
||||||
github.com/satori/go.uuid v0.0.0-20160927100844-b061729afc07 h1:DEZDfcCVq3xDJrjqdCgyN/dHYVoqR92MCsdqCdxmnhM=
|
github.com/satori/go.uuid v0.0.0-20160927100844-b061729afc07 h1:DEZDfcCVq3xDJrjqdCgyN/dHYVoqR92MCsdqCdxmnhM=
|
||||||
github.com/satori/go.uuid v0.0.0-20160927100844-b061729afc07/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
github.com/satori/go.uuid v0.0.0-20160927100844-b061729afc07/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||||
github.com/satori/uuid v0.0.0-20160927100844-b061729afc07 h1:81vvGlnI/AZ1/TxGDirw3ofUoS64TyjmPQt5C9XODTw=
|
github.com/satori/uuid v0.0.0-20160927100844-b061729afc07 h1:81vvGlnI/AZ1/TxGDirw3ofUoS64TyjmPQt5C9XODTw=
|
||||||
|
@ -295,35 +288,37 @@ github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18/go.mod h1:UETIi67q
|
||||||
github.com/xlab/treeprint v0.0.0-20161029104018-1d6e34225557 h1:Jpn2j6wHkC9wJv5iMfJhKqrZJx3TahFx+7sbZ7zQdxs=
|
github.com/xlab/treeprint v0.0.0-20161029104018-1d6e34225557 h1:Jpn2j6wHkC9wJv5iMfJhKqrZJx3TahFx+7sbZ7zQdxs=
|
||||||
github.com/xlab/treeprint v0.0.0-20161029104018-1d6e34225557/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
|
github.com/xlab/treeprint v0.0.0-20161029104018-1d6e34225557/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
|
||||||
github.com/zclconf/go-cty v0.0.0-20180815031001-58bb2bc0302a/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
|
github.com/zclconf/go-cty v0.0.0-20180815031001-58bb2bc0302a/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
|
||||||
github.com/zclconf/go-cty v0.0.0-20180907002636-07dee8a1cfd4 h1:C02D0gjAVFMKqFUaZvaZK2YWGK1HAQwVTZWDAENYDjA=
|
github.com/zclconf/go-cty v0.0.0-20180925180032-d9b87d891d0b h1:9rQAtgrPBuyPjmPEcx4pqJs6D+u41FYbbVE/hhdsrtk=
|
||||||
github.com/zclconf/go-cty v0.0.0-20180907002636-07dee8a1cfd4/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
|
github.com/zclconf/go-cty v0.0.0-20180925180032-d9b87d891d0b/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
|
||||||
go.opencensus.io v0.17.0 h1:2Cu88MYg+1LU+WVD+NWwYhyP0kKgRlN9QjWGaX0jKTE=
|
|
||||||
go.opencensus.io v0.17.0/go.mod h1:mp1VrMQxhlqqDpKvH4UcQUa4YwlzNmymAjPrDdfxNpI=
|
|
||||||
golang.org/x/crypto v0.0.0-20180816225734-aabede6cba87 h1:gCHhzI+1R9peHIMyiWVxoVaWlk1cYK7VThX5ptLtbXY=
|
golang.org/x/crypto v0.0.0-20180816225734-aabede6cba87 h1:gCHhzI+1R9peHIMyiWVxoVaWlk1cYK7VThX5ptLtbXY=
|
||||||
golang.org/x/crypto v0.0.0-20180816225734-aabede6cba87/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20180816225734-aabede6cba87/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b h1:2b9XGzhjiYsYPnKXoEfL7klWZQIt8IfyRCz62gCqqlQ=
|
||||||
|
golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180811021610-c39426892332 h1:efGso+ep0DjyCBJPjvoz0HI6UldX4Md2F1rZFe1ir0E=
|
golang.org/x/net v0.0.0-20180811021610-c39426892332 h1:efGso+ep0DjyCBJPjvoz0HI6UldX4Md2F1rZFe1ir0E=
|
||||||
golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA=
|
golang.org/x/net v0.0.0-20180925072008-f04abc6bdfa7 h1:zKzVgSQ8WOSHzD7I4k8LQjrHUUCNOlBsgc0PcYLVNnY=
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180925072008-f04abc6bdfa7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
|
golang.org/x/oauth2 v0.0.0-20170928010508-bb50c06baba3 h1:YGx0PRKSN/2n/OcdFycCC0JUA/Ln+i5lPcN8VoNDus0=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20170928010508-bb50c06baba3/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 h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20180816055513-1c9583448a9c h1:uHnKXcvx6SNkuwC+nrzxkJ+TpPwZOtumbhWrrOYN5YA=
|
golang.org/x/sys v0.0.0-20180816055513-1c9583448a9c h1:uHnKXcvx6SNkuwC+nrzxkJ+TpPwZOtumbhWrrOYN5YA=
|
||||||
golang.org/x/sys v0.0.0-20180816055513-1c9583448a9c/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180816055513-1c9583448a9c/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e h1:o3PsSEY8E4eXWkXrIP9YJALUkVZqzHJT5DOasTyn8Vs=
|
golang.org/x/sys v0.0.0-20180925112736-b09afc3d579e h1:LSlw/Dbj0MkNvPYAAkGinYmGliq+aqS7eKPYlE4oWC4=
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180925112736-b09afc3d579e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
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.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2 h1:+DCIGbF/swA92ohVg0//6X2IVY3KZs6p9mix0ziNYJM=
|
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2 h1:+DCIGbF/swA92ohVg0//6X2IVY3KZs6p9mix0ziNYJM=
|
||||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
google.golang.org/api v0.0.0-20171005000305-7a7376eff6a5 h1:PDkJGYjSvxJyevtZRGmBSO+HjbIKuqYEEc8gB51or4o=
|
||||||
google.golang.org/api v0.0.0-20180921000521-920bb1beccf7 h1:XKT3Wlpn+o6Car1ot74Z4R+R9CeRfITCLZb0Q9/mpx4=
|
google.golang.org/api v0.0.0-20171005000305-7a7376eff6a5/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||||
google.golang.org/api v0.0.0-20180921000521-920bb1beccf7/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
|
||||||
google.golang.org/appengine v1.1.0 h1:igQkv0AAhEIvTEpD5LIpAfav2eeVO9HBTjvKHVJPRSs=
|
google.golang.org/appengine v1.1.0 h1:igQkv0AAhEIvTEpD5LIpAfav2eeVO9HBTjvKHVJPRSs=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b h1:lohp5blsw53GBXtLyLNaTXPXS9pJ1tiTw61ZHUoE9Qw=
|
google.golang.org/appengine v1.2.0 h1:S0iUepdCWODXRvtE+gcRDd15L+k+k1AiHlMiMjefH24=
|
||||||
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/genproto v0.0.0-20171002232614-f676e0f3ac63 h1:yNBw5bwywOTguAu+h6SkCUaWdEZ7ZXgfiwb2YTN1eQw=
|
||||||
|
google.golang.org/genproto v0.0.0-20171002232614-f676e0f3ac63/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
google.golang.org/grpc v1.14.0 h1:ArxJuB1NWfPY6r9Gp9gqwplT0Ge7nqv9msgu03lHLmo=
|
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.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
Google Inc.
|
|
|
@ -1,202 +0,0 @@
|
||||||
|
|
||||||
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.
|
|
|
@ -1,94 +0,0 @@
|
||||||
// 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 propagation implement X-Cloud-Trace-Context header propagation used
|
|
||||||
// by Google Cloud products.
|
|
||||||
package propagation // import "contrib.go.opencensus.io/exporter/stackdriver/propagation"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
"encoding/hex"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"go.opencensus.io/trace"
|
|
||||||
"go.opencensus.io/trace/propagation"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
httpHeaderMaxSize = 200
|
|
||||||
httpHeader = `X-Cloud-Trace-Context`
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ propagation.HTTPFormat = (*HTTPFormat)(nil)
|
|
||||||
|
|
||||||
// HTTPFormat implements propagation.HTTPFormat to propagate
|
|
||||||
// traces in HTTP headers for Google Cloud Platform and Stackdriver Trace.
|
|
||||||
type HTTPFormat struct{}
|
|
||||||
|
|
||||||
// SpanContextFromRequest extracts a Stackdriver Trace span context from incoming requests.
|
|
||||||
func (f *HTTPFormat) SpanContextFromRequest(req *http.Request) (sc trace.SpanContext, ok bool) {
|
|
||||||
h := req.Header.Get(httpHeader)
|
|
||||||
// See https://cloud.google.com/trace/docs/faq for the header HTTPFormat.
|
|
||||||
// Return if the header is empty or missing, or if the header is unreasonably
|
|
||||||
// large, to avoid making unnecessary copies of a large string.
|
|
||||||
if h == "" || len(h) > httpHeaderMaxSize {
|
|
||||||
return trace.SpanContext{}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse the trace id field.
|
|
||||||
slash := strings.Index(h, `/`)
|
|
||||||
if slash == -1 {
|
|
||||||
return trace.SpanContext{}, false
|
|
||||||
}
|
|
||||||
tid, h := h[:slash], h[slash+1:]
|
|
||||||
|
|
||||||
buf, err := hex.DecodeString(tid)
|
|
||||||
if err != nil {
|
|
||||||
return trace.SpanContext{}, false
|
|
||||||
}
|
|
||||||
copy(sc.TraceID[:], buf)
|
|
||||||
|
|
||||||
// Parse the span id field.
|
|
||||||
spanstr := h
|
|
||||||
semicolon := strings.Index(h, `;`)
|
|
||||||
if semicolon != -1 {
|
|
||||||
spanstr, h = h[:semicolon], h[semicolon+1:]
|
|
||||||
}
|
|
||||||
sid, err := strconv.ParseUint(spanstr, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return trace.SpanContext{}, false
|
|
||||||
}
|
|
||||||
binary.BigEndian.PutUint64(sc.SpanID[:], sid)
|
|
||||||
|
|
||||||
// Parse the options field, options field is optional.
|
|
||||||
if !strings.HasPrefix(h, "o=") {
|
|
||||||
return sc, true
|
|
||||||
}
|
|
||||||
o, err := strconv.ParseUint(h[2:], 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return trace.SpanContext{}, false
|
|
||||||
}
|
|
||||||
sc.TraceOptions = trace.TraceOptions(o)
|
|
||||||
return sc, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// SpanContextToRequest modifies the given request to include a Stackdriver Trace header.
|
|
||||||
func (f *HTTPFormat) SpanContextToRequest(sc trace.SpanContext, req *http.Request) {
|
|
||||||
sid := binary.BigEndian.Uint64(sc.SpanID[:])
|
|
||||||
header := fmt.Sprintf("%s/%d;o=%d", hex.EncodeToString(sc.TraceID[:]), sid, int64(sc.TraceOptions))
|
|
||||||
req.Header.Set(httpHeader, header)
|
|
||||||
}
|
|
|
@ -0,0 +1,385 @@
|
||||||
|
package stdlib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/zclconf/go-cty/cty"
|
||||||
|
"github.com/zclconf/go-cty/cty/function"
|
||||||
|
)
|
||||||
|
|
||||||
|
var FormatDateFunc = function.New(&function.Spec{
|
||||||
|
Params: []function.Parameter{
|
||||||
|
{
|
||||||
|
Name: "format",
|
||||||
|
Type: cty.String,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "time",
|
||||||
|
Type: cty.String,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Type: function.StaticReturnType(cty.String),
|
||||||
|
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||||
|
formatStr := args[0].AsString()
|
||||||
|
timeStr := args[1].AsString()
|
||||||
|
t, err := parseTimestamp(timeStr)
|
||||||
|
if err != nil {
|
||||||
|
return cty.DynamicVal, function.NewArgError(1, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
sc := bufio.NewScanner(strings.NewReader(formatStr))
|
||||||
|
sc.Split(splitDateFormat)
|
||||||
|
const esc = '\''
|
||||||
|
for sc.Scan() {
|
||||||
|
tok := sc.Bytes()
|
||||||
|
|
||||||
|
// The leading byte signals the token type
|
||||||
|
switch {
|
||||||
|
case tok[0] == esc:
|
||||||
|
if tok[len(tok)-1] != esc || len(tok) == 1 {
|
||||||
|
return cty.DynamicVal, function.NewArgErrorf(0, "unterminated literal '")
|
||||||
|
}
|
||||||
|
if len(tok) == 2 {
|
||||||
|
// Must be a single escaped quote, ''
|
||||||
|
buf.WriteByte(esc)
|
||||||
|
} else {
|
||||||
|
// The content (until a closing esc) is printed out verbatim
|
||||||
|
// except that we must un-double any double-esc escapes in
|
||||||
|
// the middle of the string.
|
||||||
|
raw := tok[1 : len(tok)-1]
|
||||||
|
for i := 0; i < len(raw); i++ {
|
||||||
|
buf.WriteByte(raw[i])
|
||||||
|
if raw[i] == esc {
|
||||||
|
i++ // skip the escaped quote
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case startsDateFormatVerb(tok[0]):
|
||||||
|
switch tok[0] {
|
||||||
|
case 'Y':
|
||||||
|
y := t.Year()
|
||||||
|
switch len(tok) {
|
||||||
|
case 2:
|
||||||
|
fmt.Fprintf(&buf, "%02d", y%100)
|
||||||
|
case 4:
|
||||||
|
fmt.Fprintf(&buf, "%04d", y)
|
||||||
|
default:
|
||||||
|
return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q: year must either be \"YY\" or \"YYYY\"", tok)
|
||||||
|
}
|
||||||
|
case 'M':
|
||||||
|
m := t.Month()
|
||||||
|
switch len(tok) {
|
||||||
|
case 1:
|
||||||
|
fmt.Fprintf(&buf, "%d", m)
|
||||||
|
case 2:
|
||||||
|
fmt.Fprintf(&buf, "%02d", m)
|
||||||
|
case 3:
|
||||||
|
buf.WriteString(m.String()[:3])
|
||||||
|
case 4:
|
||||||
|
buf.WriteString(m.String())
|
||||||
|
default:
|
||||||
|
return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q: month must be \"M\", \"MM\", \"MMM\", or \"MMMM\"", tok)
|
||||||
|
}
|
||||||
|
case 'D':
|
||||||
|
d := t.Day()
|
||||||
|
switch len(tok) {
|
||||||
|
case 1:
|
||||||
|
fmt.Fprintf(&buf, "%d", d)
|
||||||
|
case 2:
|
||||||
|
fmt.Fprintf(&buf, "%02d", d)
|
||||||
|
default:
|
||||||
|
return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q: day of month must either be \"D\" or \"DD\"", tok)
|
||||||
|
}
|
||||||
|
case 'E':
|
||||||
|
d := t.Weekday()
|
||||||
|
switch len(tok) {
|
||||||
|
case 3:
|
||||||
|
buf.WriteString(d.String()[:3])
|
||||||
|
case 4:
|
||||||
|
buf.WriteString(d.String())
|
||||||
|
default:
|
||||||
|
return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q: day of week must either be \"EEE\" or \"EEEE\"", tok)
|
||||||
|
}
|
||||||
|
case 'h':
|
||||||
|
h := t.Hour()
|
||||||
|
switch len(tok) {
|
||||||
|
case 1:
|
||||||
|
fmt.Fprintf(&buf, "%d", h)
|
||||||
|
case 2:
|
||||||
|
fmt.Fprintf(&buf, "%02d", h)
|
||||||
|
default:
|
||||||
|
return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q: 24-hour must either be \"h\" or \"hh\"", tok)
|
||||||
|
}
|
||||||
|
case 'H':
|
||||||
|
h := t.Hour() % 12
|
||||||
|
if h == 0 {
|
||||||
|
h = 12
|
||||||
|
}
|
||||||
|
switch len(tok) {
|
||||||
|
case 1:
|
||||||
|
fmt.Fprintf(&buf, "%d", h)
|
||||||
|
case 2:
|
||||||
|
fmt.Fprintf(&buf, "%02d", h)
|
||||||
|
default:
|
||||||
|
return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q: 12-hour must either be \"H\" or \"HH\"", tok)
|
||||||
|
}
|
||||||
|
case 'A', 'a':
|
||||||
|
if len(tok) != 2 {
|
||||||
|
return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q: must be \"%s%s\"", tok, tok[0:1], tok[0:1])
|
||||||
|
}
|
||||||
|
upper := tok[0] == 'A'
|
||||||
|
switch t.Hour() / 12 {
|
||||||
|
case 0:
|
||||||
|
if upper {
|
||||||
|
buf.WriteString("AM")
|
||||||
|
} else {
|
||||||
|
buf.WriteString("am")
|
||||||
|
}
|
||||||
|
case 1:
|
||||||
|
if upper {
|
||||||
|
buf.WriteString("PM")
|
||||||
|
} else {
|
||||||
|
buf.WriteString("pm")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 'm':
|
||||||
|
m := t.Minute()
|
||||||
|
switch len(tok) {
|
||||||
|
case 1:
|
||||||
|
fmt.Fprintf(&buf, "%d", m)
|
||||||
|
case 2:
|
||||||
|
fmt.Fprintf(&buf, "%02d", m)
|
||||||
|
default:
|
||||||
|
return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q: minute must either be \"m\" or \"mm\"", tok)
|
||||||
|
}
|
||||||
|
case 's':
|
||||||
|
s := t.Second()
|
||||||
|
switch len(tok) {
|
||||||
|
case 1:
|
||||||
|
fmt.Fprintf(&buf, "%d", s)
|
||||||
|
case 2:
|
||||||
|
fmt.Fprintf(&buf, "%02d", s)
|
||||||
|
default:
|
||||||
|
return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q: second must either be \"s\" or \"ss\"", tok)
|
||||||
|
}
|
||||||
|
case 'Z':
|
||||||
|
// We'll just lean on Go's own formatter for this one, since
|
||||||
|
// the necessary information is unexported.
|
||||||
|
switch len(tok) {
|
||||||
|
case 1:
|
||||||
|
buf.WriteString(t.Format("Z07:00"))
|
||||||
|
case 3:
|
||||||
|
str := t.Format("-0700")
|
||||||
|
switch str {
|
||||||
|
case "+0000":
|
||||||
|
buf.WriteString("UTC")
|
||||||
|
default:
|
||||||
|
buf.WriteString(str)
|
||||||
|
}
|
||||||
|
case 4:
|
||||||
|
buf.WriteString(t.Format("-0700"))
|
||||||
|
case 5:
|
||||||
|
buf.WriteString(t.Format("-07:00"))
|
||||||
|
default:
|
||||||
|
return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q: timezone must be Z, ZZZZ, or ZZZZZ", tok)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return cty.DynamicVal, function.NewArgErrorf(0, "invalid date format verb %q", tok)
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Any other starting character indicates a literal sequence
|
||||||
|
buf.Write(tok)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cty.StringVal(buf.String()), nil
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// FormatDate reformats a timestamp given in RFC3339 syntax into another time
|
||||||
|
// syntax defined by a given format string.
|
||||||
|
//
|
||||||
|
// The format string uses letter mnemonics to represent portions of the
|
||||||
|
// timestamp, with repetition signifying length variants of each portion.
|
||||||
|
// Single quote characters ' can be used to quote sequences of literal letters
|
||||||
|
// that should not be interpreted as formatting mnemonics.
|
||||||
|
//
|
||||||
|
// The full set of supported mnemonic sequences is listed below:
|
||||||
|
//
|
||||||
|
// YY Year modulo 100 zero-padded to two digits, like "06".
|
||||||
|
// YYYY Four (or more) digit year, like "2006".
|
||||||
|
// M Month number, like "1" for January.
|
||||||
|
// MM Month number zero-padded to two digits, like "01".
|
||||||
|
// MMM English month name abbreviated to three letters, like "Jan".
|
||||||
|
// MMMM English month name unabbreviated, like "January".
|
||||||
|
// D Day of month number, like "2".
|
||||||
|
// DD Day of month number zero-padded to two digits, like "02".
|
||||||
|
// EEE English day of week name abbreviated to three letters, like "Mon".
|
||||||
|
// EEEE English day of week name unabbreviated, like "Monday".
|
||||||
|
// h 24-hour number, like "2".
|
||||||
|
// hh 24-hour number zero-padded to two digits, like "02".
|
||||||
|
// H 12-hour number, like "2".
|
||||||
|
// HH 12-hour number zero-padded to two digits, like "02".
|
||||||
|
// AA Hour AM/PM marker in uppercase, like "AM".
|
||||||
|
// aa Hour AM/PM marker in lowercase, like "am".
|
||||||
|
// m Minute within hour, like "5".
|
||||||
|
// mm Minute within hour zero-padded to two digits, like "05".
|
||||||
|
// s Second within minute, like "9".
|
||||||
|
// ss Second within minute zero-padded to two digits, like "09".
|
||||||
|
// ZZZZ Timezone offset with just sign and digit, like "-0800".
|
||||||
|
// ZZZZZ Timezone offset with colon separating hours and minutes, like "-08:00".
|
||||||
|
// Z Like ZZZZZ but with a special case "Z" for UTC.
|
||||||
|
// ZZZ Like ZZZZ but with a special case "UTC" for UTC.
|
||||||
|
//
|
||||||
|
// The format syntax is optimized mainly for generating machine-oriented
|
||||||
|
// timestamps rather than human-oriented timestamps; the English language
|
||||||
|
// portions of the output reflect the use of English names in a number of
|
||||||
|
// machine-readable date formatting standards. For presentation to humans,
|
||||||
|
// a locale-aware time formatter (not included in this package) is a better
|
||||||
|
// choice.
|
||||||
|
//
|
||||||
|
// The format syntax is not compatible with that of any other language, but
|
||||||
|
// is optimized so that patterns for common standard date formats can be
|
||||||
|
// recognized quickly even by a reader unfamiliar with the format syntax.
|
||||||
|
func FormatDate(format cty.Value, timestamp cty.Value) (cty.Value, error) {
|
||||||
|
return FormatDateFunc.Call([]cty.Value{format, timestamp})
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseTimestamp(ts string) (time.Time, error) {
|
||||||
|
t, err := time.Parse(time.RFC3339, ts)
|
||||||
|
if err != nil {
|
||||||
|
switch err := err.(type) {
|
||||||
|
case *time.ParseError:
|
||||||
|
// If err is s time.ParseError then its string representation is not
|
||||||
|
// appropriate since it relies on details of Go's strange date format
|
||||||
|
// representation, which a caller of our functions is not expected
|
||||||
|
// to be familiar with.
|
||||||
|
//
|
||||||
|
// Therefore we do some light transformation to get a more suitable
|
||||||
|
// error that should make more sense to our callers. These are
|
||||||
|
// still not awesome error messages, but at least they refer to
|
||||||
|
// the timestamp portions by name rather than by Go's example
|
||||||
|
// values.
|
||||||
|
if err.LayoutElem == "" && err.ValueElem == "" && err.Message != "" {
|
||||||
|
// For some reason err.Message is populated with a ": " prefix
|
||||||
|
// by the time package.
|
||||||
|
return time.Time{}, fmt.Errorf("not a valid RFC3339 timestamp%s", err.Message)
|
||||||
|
}
|
||||||
|
var what string
|
||||||
|
switch err.LayoutElem {
|
||||||
|
case "2006":
|
||||||
|
what = "year"
|
||||||
|
case "01":
|
||||||
|
what = "month"
|
||||||
|
case "02":
|
||||||
|
what = "day of month"
|
||||||
|
case "15":
|
||||||
|
what = "hour"
|
||||||
|
case "04":
|
||||||
|
what = "minute"
|
||||||
|
case "05":
|
||||||
|
what = "second"
|
||||||
|
case "Z07:00":
|
||||||
|
what = "UTC offset"
|
||||||
|
case "T":
|
||||||
|
return time.Time{}, fmt.Errorf("not a valid RFC3339 timestamp: missing required time introducer 'T'")
|
||||||
|
case ":", "-":
|
||||||
|
if err.ValueElem == "" {
|
||||||
|
return time.Time{}, fmt.Errorf("not a valid RFC3339 timestamp: end of string where %q is expected", err.LayoutElem)
|
||||||
|
} else {
|
||||||
|
return time.Time{}, fmt.Errorf("not a valid RFC3339 timestamp: found %q where %q is expected", err.ValueElem, err.LayoutElem)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// Should never get here, because time.RFC3339 includes only the
|
||||||
|
// above portions, but since that might change in future we'll
|
||||||
|
// be robust here.
|
||||||
|
what = "timestamp segment"
|
||||||
|
}
|
||||||
|
if err.ValueElem == "" {
|
||||||
|
return time.Time{}, fmt.Errorf("not a valid RFC3339 timestamp: end of string before %s", what)
|
||||||
|
} else {
|
||||||
|
return time.Time{}, fmt.Errorf("not a valid RFC3339 timestamp: cannot use %q as %s", err.ValueElem, what)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return time.Time{}, err
|
||||||
|
}
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// splitDataFormat is a bufio.SplitFunc used to tokenize a date format.
|
||||||
|
func splitDateFormat(data []byte, atEOF bool) (advance int, token []byte, err error) {
|
||||||
|
if len(data) == 0 {
|
||||||
|
return 0, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const esc = '\''
|
||||||
|
|
||||||
|
switch {
|
||||||
|
|
||||||
|
case data[0] == esc:
|
||||||
|
// If we have another quote immediately after then this is a single
|
||||||
|
// escaped escape.
|
||||||
|
if len(data) > 1 && data[1] == esc {
|
||||||
|
return 2, data[:2], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Beginning of quoted sequence, so we will seek forward until we find
|
||||||
|
// the closing quote, ignoring escaped quotes along the way.
|
||||||
|
for i := 1; i < len(data); i++ {
|
||||||
|
if data[i] == esc {
|
||||||
|
if (i + 1) == len(data) {
|
||||||
|
// We need at least one more byte to decide if this is an
|
||||||
|
// escape or a terminator.
|
||||||
|
return 0, nil, nil
|
||||||
|
}
|
||||||
|
if data[i+1] == esc {
|
||||||
|
i++ // doubled-up quotes are an escape sequence
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// We've found the closing quote
|
||||||
|
return i + 1, data[:i+1], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If we fall out here then we need more bytes to find the end,
|
||||||
|
// unless we're already at the end with an unclosed quote.
|
||||||
|
if atEOF {
|
||||||
|
return len(data), data, nil
|
||||||
|
}
|
||||||
|
return 0, nil, nil
|
||||||
|
|
||||||
|
case startsDateFormatVerb(data[0]):
|
||||||
|
rep := data[0]
|
||||||
|
for i := 1; i < len(data); i++ {
|
||||||
|
if data[i] != rep {
|
||||||
|
return i, data[:i], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if atEOF {
|
||||||
|
return len(data), data, nil
|
||||||
|
}
|
||||||
|
// We need more data to decide if we've found the end
|
||||||
|
return 0, nil, nil
|
||||||
|
|
||||||
|
default:
|
||||||
|
for i := 1; i < len(data); i++ {
|
||||||
|
if data[i] == esc || startsDateFormatVerb(data[i]) {
|
||||||
|
return i, data[:i], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// We might not actually be at the end of a literal sequence,
|
||||||
|
// but that doesn't matter since we'll concat them back together
|
||||||
|
// anyway.
|
||||||
|
return len(data), data, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func startsDateFormatVerb(b byte) bool {
|
||||||
|
return (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z')
|
||||||
|
}
|
|
@ -138,7 +138,10 @@ func unmarshalList(dec *msgpack.Decoder, ety cty.Type, path cty.Path) (cty.Value
|
||||||
return cty.DynamicVal, path.NewErrorf("a list is required")
|
return cty.DynamicVal, path.NewErrorf("a list is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
if length == 0 {
|
switch {
|
||||||
|
case length < 0:
|
||||||
|
return cty.NullVal(cty.List(ety)), nil
|
||||||
|
case length == 0:
|
||||||
return cty.ListValEmpty(ety), nil
|
return cty.ListValEmpty(ety), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,7 +169,10 @@ func unmarshalSet(dec *msgpack.Decoder, ety cty.Type, path cty.Path) (cty.Value,
|
||||||
return cty.DynamicVal, path.NewErrorf("a set is required")
|
return cty.DynamicVal, path.NewErrorf("a set is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
if length == 0 {
|
switch {
|
||||||
|
case length < 0:
|
||||||
|
return cty.NullVal(cty.Set(ety)), nil
|
||||||
|
case length == 0:
|
||||||
return cty.SetValEmpty(ety), nil
|
return cty.SetValEmpty(ety), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,7 +200,10 @@ func unmarshalMap(dec *msgpack.Decoder, ety cty.Type, path cty.Path) (cty.Value,
|
||||||
return cty.DynamicVal, path.NewErrorf("a map is required")
|
return cty.DynamicVal, path.NewErrorf("a map is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
if length == 0 {
|
switch {
|
||||||
|
case length < 0:
|
||||||
|
return cty.NullVal(cty.Map(ety)), nil
|
||||||
|
case length == 0:
|
||||||
return cty.MapValEmpty(ety), nil
|
return cty.MapValEmpty(ety), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,7 +236,12 @@ func unmarshalTuple(dec *msgpack.Decoder, etys []cty.Type, path cty.Path) (cty.V
|
||||||
return cty.DynamicVal, path.NewErrorf("a tuple is required")
|
return cty.DynamicVal, path.NewErrorf("a tuple is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
if length != len(etys) {
|
switch {
|
||||||
|
case length < 0:
|
||||||
|
return cty.NullVal(cty.Tuple(etys)), nil
|
||||||
|
case length == 0:
|
||||||
|
return cty.TupleVal(nil), nil
|
||||||
|
case length != len(etys):
|
||||||
return cty.DynamicVal, path.NewErrorf("a tuple of length %d is required", len(etys))
|
return cty.DynamicVal, path.NewErrorf("a tuple of length %d is required", len(etys))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -256,7 +270,12 @@ func unmarshalObject(dec *msgpack.Decoder, atys map[string]cty.Type, path cty.Pa
|
||||||
return cty.DynamicVal, path.NewErrorf("an object is required")
|
return cty.DynamicVal, path.NewErrorf("an object is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
if length != len(atys) {
|
switch {
|
||||||
|
case length < 0:
|
||||||
|
return cty.NullVal(cty.Object(atys)), nil
|
||||||
|
case length == 0:
|
||||||
|
return cty.ObjectVal(nil), nil
|
||||||
|
case length != len(atys):
|
||||||
return cty.DynamicVal, path.NewErrorf("an object with %d attributes is required", len(atys))
|
return cty.DynamicVal, path.NewErrorf("an object with %d attributes is required", len(atys))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -293,7 +312,10 @@ func unmarshalDynamic(dec *msgpack.Decoder, path cty.Path) (cty.Value, error) {
|
||||||
return cty.DynamicVal, path.NewError(err)
|
return cty.DynamicVal, path.NewError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if length != 2 {
|
switch {
|
||||||
|
case length == -1:
|
||||||
|
return cty.NullVal(cty.DynamicPseudoType), nil
|
||||||
|
case length != 2:
|
||||||
return cty.DynamicVal, path.NewErrorf(
|
return cty.DynamicVal, path.NewErrorf(
|
||||||
"dynamic value array must have exactly two elements",
|
"dynamic value array must have exactly two elements",
|
||||||
)
|
)
|
||||||
|
|
|
@ -14,16 +14,15 @@ func (val Value) GoString() string {
|
||||||
return "cty.NilVal"
|
return "cty.NilVal"
|
||||||
}
|
}
|
||||||
|
|
||||||
if val.ty == DynamicPseudoType {
|
|
||||||
return "cty.DynamicVal"
|
|
||||||
}
|
|
||||||
|
|
||||||
if !val.IsKnown() {
|
|
||||||
return fmt.Sprintf("cty.UnknownVal(%#v)", val.ty)
|
|
||||||
}
|
|
||||||
if val.IsNull() {
|
if val.IsNull() {
|
||||||
return fmt.Sprintf("cty.NullVal(%#v)", val.ty)
|
return fmt.Sprintf("cty.NullVal(%#v)", val.ty)
|
||||||
}
|
}
|
||||||
|
if val == DynamicVal { // is unknown, so must be before the IsKnown check below
|
||||||
|
return "cty.DynamicVal"
|
||||||
|
}
|
||||||
|
if !val.IsKnown() {
|
||||||
|
return fmt.Sprintf("cty.UnknownVal(%#v)", val.ty)
|
||||||
|
}
|
||||||
|
|
||||||
// By the time we reach here we've dealt with all of the exceptions around
|
// By the time we reach here we've dealt with all of the exceptions around
|
||||||
// unknowns and nulls, so we're guaranteed that the values are the
|
// unknowns and nulls, so we're guaranteed that the values are the
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
/.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,27 +0,0 @@
|
||||||
language: go
|
|
||||||
|
|
||||||
go:
|
|
||||||
# 1.8 is tested by AppVeyor
|
|
||||||
- 1.10.x
|
|
||||||
|
|
||||||
go_import_path: go.opencensus.io
|
|
||||||
|
|
||||||
# Don't email me the results of the test runs.
|
|
||||||
notifications:
|
|
||||||
email: false
|
|
||||||
|
|
||||||
before_script:
|
|
||||||
- GO_FILES=$(find . -iname '*.go' | grep -v /vendor/) # All the .go files, excluding vendor/ if any
|
|
||||||
- PKGS=$(go list ./... | grep -v /vendor/) # All the import paths, excluding vendor/ if any
|
|
||||||
- curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh # Install latest dep release
|
|
||||||
- go get github.com/rakyll/embedmd
|
|
||||||
|
|
||||||
script:
|
|
||||||
- embedmd -d README.md # Ensure embedded code is up-to-date
|
|
||||||
- dep ensure -v
|
|
||||||
- go build ./... # Ensure dependency updates don't break build
|
|
||||||
- if [ -n "$(gofmt -s -l $GO_FILES)" ]; then echo "gofmt the following files:"; gofmt -s -l $GO_FILES; exit 1; fi
|
|
||||||
- go vet ./...
|
|
||||||
- go test -v -race $PKGS # Run all the tests with the race detector enabled
|
|
||||||
- 'if [[ $TRAVIS_GO_VERSION = 1.8* ]]; then ! golint ./... | grep -vE "(_mock|_string|\.pb)\.go:"; fi'
|
|
||||||
- go run internal/check/version.go
|
|
|
@ -1 +0,0 @@
|
||||||
Google Inc.
|
|
|
@ -1,56 +0,0 @@
|
||||||
# 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:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ go test ./...
|
|
||||||
```
|
|
||||||
|
|
||||||
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.
|
|
|
@ -1,231 +0,0 @@
|
||||||
# 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
|
|
|
@ -1,36 +0,0 @@
|
||||||
# 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
|
|
|
@ -1,202 +0,0 @@
|
||||||
|
|
||||||
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.
|
|
|
@ -1,262 +0,0 @@
|
||||||
# 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/basvanbeek/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 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
|
|
||||||
|
|
||||||
## 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(0, 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(0, 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 uses the OpenCensus' [binary propagation format](https://godoc.org/go.opencensus.io/trace/propagation).
|
|
||||||
* HTTP integrations uses 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
|
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
version: "{build}"
|
|
||||||
|
|
||||||
platform: x64
|
|
||||||
|
|
||||||
clone_folder: c:\gopath\src\go.opencensus.io
|
|
||||||
|
|
||||||
environment:
|
|
||||||
GOPATH: c:\gopath
|
|
||||||
GOVERSION: 1.8
|
|
||||||
|
|
||||||
install:
|
|
||||||
- set PATH=%GOPATH%\bin;c:\go\bin;%PATH%
|
|
||||||
- go version
|
|
||||||
- go env
|
|
||||||
|
|
||||||
build: false
|
|
||||||
deploy: false
|
|
||||||
|
|
||||||
test_script:
|
|
||||||
- cd %APPVEYOR_BUILD_FOLDER%
|
|
||||||
- gofmt -w .
|
|
||||||
- go get -v -t .\...
|
|
||||||
- go test -race -v .\...
|
|
||||||
- go vet .\...
|
|
|
@ -1,20 +0,0 @@
|
||||||
module go.opencensus.io
|
|
||||||
|
|
||||||
require (
|
|
||||||
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999
|
|
||||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973
|
|
||||||
github.com/golang/protobuf v1.2.0
|
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1
|
|
||||||
github.com/openzipkin/zipkin-go v0.1.1
|
|
||||||
github.com/prometheus/client_golang v0.8.0
|
|
||||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910
|
|
||||||
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e
|
|
||||||
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273
|
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd
|
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f
|
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e
|
|
||||||
golang.org/x/text v0.3.0
|
|
||||||
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf
|
|
||||||
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b
|
|
||||||
google.golang.org/grpc v1.14.0
|
|
||||||
)
|
|
|
@ -1,21 +0,0 @@
|
||||||
git.apache.org/thrift.git v0.0.0-20180807212849-6e67faa92827/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
|
|
||||||
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
|
|
||||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
|
||||||
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
|
|
||||||
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
|
||||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
|
||||||
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
|
||||||
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
|
||||||
golang.org/x/net v0.0.0-20180821023952-922f4815f713/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sys v0.0.0-20180821140842-3b58ed4ad339/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|
||||||
google.golang.org/api v0.0.0-20180818000503-e21acd801f91/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
|
||||||
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
|
||||||
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
|
||||||
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
|
|
@ -1,37 +0,0 @@
|
||||||
// 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"
|
|
||||||
|
|
||||||
"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))
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
// 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 '_'
|
|
||||||
}
|
|
|
@ -1,72 +0,0 @@
|
||||||
// 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"
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 read the values when decoding valuesBytes 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
|
|
||||||
}
|
|
||||||
|
|
||||||
func (vb *Values) Bytes() []byte {
|
|
||||||
return vb.Buffer[:vb.WriteIndex]
|
|
||||||
}
|
|
|
@ -1,52 +0,0 @@
|
||||||
// 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{}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
// 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.17.0"
|
|
||||||
}
|
|
|
@ -1,107 +0,0 @@
|
||||||
// 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
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
rt = &traceTransport{
|
|
||||||
base: rt,
|
|
||||||
format: format,
|
|
||||||
startOptions: trace.StartOptions{
|
|
||||||
Sampler: t.StartOptions.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)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,125 +0,0 @@
|
||||||
// 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(Host, req.URL.Host),
|
|
||||||
tag.Upsert(Path, req.URL.Path),
|
|
||||||
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 resp.Body == nil {
|
|
||||||
track.end()
|
|
||||||
} else {
|
|
||||||
track.body = resp.Body
|
|
||||||
resp.Body = track
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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
|
|
||||||
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() {
|
|
||||||
m := []stats.Measurement{
|
|
||||||
ClientLatency.M(float64(time.Since(t.start)) / float64(time.Millisecond)),
|
|
||||||
ClientResponseBytes.M(t.respSize),
|
|
||||||
}
|
|
||||||
if t.reqSize >= 0 {
|
|
||||||
m = append(m, ClientRequestBytes.M(t.reqSize))
|
|
||||||
}
|
|
||||||
ctx, _ := tag.New(t.ctx, tag.Upsert(StatusCode, strconv.Itoa(t.statusCode)))
|
|
||||||
stats.Record(ctx, m...)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tracker) Read(b []byte) (int, error) {
|
|
||||||
n, err := t.body.Read(b)
|
|
||||||
switch err {
|
|
||||||
case nil:
|
|
||||||
t.respSize += int64(n)
|
|
||||||
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()
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
// 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"
|
|
|
@ -1,123 +0,0 @@
|
||||||
// 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, reciever 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)
|
|
||||||
}
|
|
|
@ -1,51 +0,0 @@
|
||||||
// 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"
|
|
||||||
|
|
||||||
"go.opencensus.io/tag"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
|
@ -1,430 +0,0 @@
|
||||||
// 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
|
|
||||||
|
|
||||||
// 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()
|
|
||||||
var span *trace.Span
|
|
||||||
sc, ok := h.extractSpanContext(r)
|
|
||||||
if ok && !h.IsPublicEndpoint {
|
|
||||||
ctx, span = trace.StartSpanWithRemoteParent(ctx, name, sc,
|
|
||||||
trace.WithSampler(h.StartOptions.Sampler),
|
|
||||||
trace.WithSpanKind(trace.SpanKindServer))
|
|
||||||
} else {
|
|
||||||
ctx, span = trace.StartSpan(ctx, name,
|
|
||||||
trace.WithSampler(h.StartOptions.Sampler),
|
|
||||||
trace.WithSpanKind(trace.SpanKindServer),
|
|
||||||
)
|
|
||||||
if ok {
|
|
||||||
span.AddLink(trace.Link{
|
|
||||||
TraceID: sc.TraceID,
|
|
||||||
SpanID: sc.SpanID,
|
|
||||||
Type: trace.LinkTypeChild,
|
|
||||||
Attributes: nil,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
span.AddAttributes(requestAttrs(r)...)
|
|
||||||
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.URL.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))
|
|
||||||
|
|
||||||
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)
|
|
||||||
ctx, _ := tag.New(t.ctx, allTags...)
|
|
||||||
stats.Record(ctx, 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)
|
|
||||||
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}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,167 +0,0 @@
|
||||||
// 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.
|
|
||||||
|
|
||||||
// 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")
|
|
||||||
}
|
|
|
@ -1,186 +0,0 @@
|
||||||
// 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"
|
|
||||||
)
|
|
||||||
|
|
||||||
// The following client HTTP measures are supported for use in custom views.
|
|
||||||
var (
|
|
||||||
ClientRequestCount = stats.Int64("opencensus.io/http/client/request_count", "Number of HTTP requests started", stats.UnitDimensionless)
|
|
||||||
ClientRequestBytes = stats.Int64("opencensus.io/http/client/request_bytes", "HTTP request body size if set as ContentLength (uncompressed)", stats.UnitBytes)
|
|
||||||
ClientResponseBytes = stats.Int64("opencensus.io/http/client/response_bytes", "HTTP response body size (uncompressed)", stats.UnitBytes)
|
|
||||||
ClientLatency = stats.Float64("opencensus.io/http/client/latency", "End-to-end latency", 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")
|
|
||||||
)
|
|
||||||
|
|
||||||
// Default distributions used by views in this package.
|
|
||||||
var (
|
|
||||||
DefaultSizeDistribution = view.Distribution(0, 1024, 2048, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216, 67108864, 268435456, 1073741824, 4294967296)
|
|
||||||
DefaultLatencyDistribution = view.Distribution(0, 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.
|
|
||||||
// You need to register the views for data to actually be collected.
|
|
||||||
var (
|
|
||||||
ClientRequestCountView = &view.View{
|
|
||||||
Name: "opencensus.io/http/client/request_count",
|
|
||||||
Description: "Count of HTTP requests started",
|
|
||||||
Measure: ClientRequestCount,
|
|
||||||
Aggregation: view.Count(),
|
|
||||||
}
|
|
||||||
|
|
||||||
ClientRequestBytesView = &view.View{
|
|
||||||
Name: "opencensus.io/http/client/request_bytes",
|
|
||||||
Description: "Size distribution of HTTP request body",
|
|
||||||
Measure: ClientRequestBytes,
|
|
||||||
Aggregation: DefaultSizeDistribution,
|
|
||||||
}
|
|
||||||
|
|
||||||
ClientResponseBytesView = &view.View{
|
|
||||||
Name: "opencensus.io/http/client/response_bytes",
|
|
||||||
Description: "Size distribution of HTTP response body",
|
|
||||||
Measure: ClientResponseBytes,
|
|
||||||
Aggregation: DefaultSizeDistribution,
|
|
||||||
}
|
|
||||||
|
|
||||||
ClientLatencyView = &view.View{
|
|
||||||
Name: "opencensus.io/http/client/latency",
|
|
||||||
Description: "Latency distribution of HTTP requests",
|
|
||||||
Measure: ClientLatency,
|
|
||||||
Aggregation: DefaultLatencyDistribution,
|
|
||||||
}
|
|
||||||
|
|
||||||
ClientRequestCountByMethod = &view.View{
|
|
||||||
Name: "opencensus.io/http/client/request_count_by_method",
|
|
||||||
Description: "Client request count by HTTP method",
|
|
||||||
TagKeys: []tag.Key{Method},
|
|
||||||
Measure: ClientRequestCount,
|
|
||||||
Aggregation: view.Count(),
|
|
||||||
}
|
|
||||||
|
|
||||||
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: ClientLatency,
|
|
||||||
Aggregation: view.Count(),
|
|
||||||
}
|
|
||||||
|
|
||||||
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.
|
|
||||||
var DefaultClientViews = []*view.View{
|
|
||||||
ClientRequestCountView,
|
|
||||||
ClientRequestBytesView,
|
|
||||||
ClientResponseBytesView,
|
|
||||||
ClientLatencyView,
|
|
||||||
ClientRequestCountByMethod,
|
|
||||||
ClientResponseCountByStatusCode,
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultServerViews are the default server views provided by this package.
|
|
||||||
var DefaultServerViews = []*view.View{
|
|
||||||
ServerRequestCountView,
|
|
||||||
ServerRequestBytesView,
|
|
||||||
ServerResponseBytesView,
|
|
||||||
ServerLatencyView,
|
|
||||||
ServerRequestCountByMethod,
|
|
||||||
ServerResponseCountByStatusCode,
|
|
||||||
}
|
|
|
@ -1,228 +0,0 @@
|
||||||
// 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"
|
|
||||||
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.
|
|
||||||
resp.Body = &bodyTracker{rc: resp.Body, span: span}
|
|
||||||
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 {
|
|
||||||
return []trace.Attribute{
|
|
||||||
trace.StringAttribute(PathAttribute, r.URL.Path),
|
|
||||||
trace.StringAttribute(HostAttribute, r.URL.Host),
|
|
||||||
trace.StringAttribute(MethodAttribute, r.Method),
|
|
||||||
trace.StringAttribute(UserAgentAttribute, r.UserAgent()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
|
@ -1,55 +0,0 @@
|
||||||
// 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 metric to be tracked and recorded.
|
|
||||||
For example, latency, request Mb/s, and response Mb/s are measures
|
|
||||||
to collect from a server.
|
|
||||||
|
|
||||||
Each measure needs to be registered before being used. 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 for their end users to
|
|
||||||
create views and collect instrumentation data.
|
|
||||||
|
|
||||||
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. Users collect data points on the existing 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 user is not aggregating
|
|
||||||
them via views. Users don't necessarily need to conditionally enable/disable
|
|
||||||
recording to reduce cost. Recording of measurements is cheap.
|
|
||||||
|
|
||||||
Libraries can always record measurements, and end-users can later decide
|
|
||||||
on which measurements they want to collect by registering views. This allows
|
|
||||||
libraries to turn on the instrumentation by default.
|
|
||||||
*/
|
|
||||||
package stats // import "go.opencensus.io/stats"
|
|
||||||
|
|
||||||
// TODO(acetechnologist): Add a link to the language independent OpenCensus
|
|
||||||
// spec when it is available.
|
|
|
@ -1,25 +0,0 @@
|
||||||
// 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(*tag.Map, interface{})
|
|
||||||
|
|
||||||
// SubscriptionReporter reports when a view subscribed with a measure.
|
|
||||||
var SubscriptionReporter func(measure string)
|
|
|
@ -1,28 +0,0 @@
|
||||||
// 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/stats/internal"
|
|
||||||
|
|
||||||
const (
|
|
||||||
MaxNameLength = 255
|
|
||||||
)
|
|
||||||
|
|
||||||
func IsPrintable(str string) bool {
|
|
||||||
for _, r := range str {
|
|
||||||
if !(r >= ' ' && r <= '~') {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
|
@ -1,108 +0,0 @@
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
|
@ -1,54 +0,0 @@
|
||||||
// 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 {
|
|
||||||
md *measureDescriptor
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name returns the name of the measure.
|
|
||||||
func (m *Float64Measure) Name() string {
|
|
||||||
return m.md.name
|
|
||||||
}
|
|
||||||
|
|
||||||
// Description returns the description of the measure.
|
|
||||||
func (m *Float64Measure) Description() string {
|
|
||||||
return m.md.description
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unit returns the unit of the measure.
|
|
||||||
func (m *Float64Measure) Unit() string {
|
|
||||||
return m.md.unit
|
|
||||||
}
|
|
||||||
|
|
||||||
// M creates a new float64 measurement.
|
|
||||||
// Use Record to record measurements.
|
|
||||||
func (m *Float64Measure) M(v float64) Measurement {
|
|
||||||
if !m.md.subscribed() {
|
|
||||||
return Measurement{}
|
|
||||||
}
|
|
||||||
return Measurement{m: m, 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}
|
|
||||||
}
|
|
|
@ -1,54 +0,0 @@
|
||||||
// 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 {
|
|
||||||
md *measureDescriptor
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name returns the name of the measure.
|
|
||||||
func (m *Int64Measure) Name() string {
|
|
||||||
return m.md.name
|
|
||||||
}
|
|
||||||
|
|
||||||
// Description returns the description of the measure.
|
|
||||||
func (m *Int64Measure) Description() string {
|
|
||||||
return m.md.description
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unit returns the unit of the measure.
|
|
||||||
func (m *Int64Measure) Unit() string {
|
|
||||||
return m.md.unit
|
|
||||||
}
|
|
||||||
|
|
||||||
// M creates a new int64 measurement.
|
|
||||||
// Use Record to record measurements.
|
|
||||||
func (m *Int64Measure) M(v int64) Measurement {
|
|
||||||
if !m.md.subscribed() {
|
|
||||||
return Measurement{}
|
|
||||||
}
|
|
||||||
return Measurement{m: m, 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}
|
|
||||||
}
|
|
|
@ -1,52 +0,0 @@
|
||||||
// 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/stats/internal"
|
|
||||||
"go.opencensus.io/tag"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
internal.SubscriptionReporter = func(measure string) {
|
|
||||||
mu.Lock()
|
|
||||||
measures[measure].subscribe()
|
|
||||||
mu.Unlock()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Record records one or multiple measurements with the same tags at once.
|
|
||||||
// If there are any tags in the context, measurements will be tagged with them.
|
|
||||||
func Record(ctx context.Context, ms ...Measurement) {
|
|
||||||
if len(ms) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var record bool
|
|
||||||
for _, m := range ms {
|
|
||||||
if (m != Measurement{}) {
|
|
||||||
record = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !record {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if internal.DefaultRecorder != nil {
|
|
||||||
internal.DefaultRecorder(tag.FromContext(ctx), ms)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
// 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"
|
|
||||||
)
|
|
|
@ -1,120 +0,0 @@
|
||||||
// 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{}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,207 +0,0 @@
|
||||||
// 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"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
clone() AggregationData
|
|
||||||
equal(other AggregationData) bool
|
|
||||||
}
|
|
||||||
|
|
||||||
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(v float64) {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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(f float64) {
|
|
||||||
a.Value += f
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// DistributionData is the aggregated data for the
|
|
||||||
// Distribution aggregation.
|
|
||||||
//
|
|
||||||
// Most users won't directly access distribution data.
|
|
||||||
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
|
|
||||||
bounds []float64 // histogram distribution of the values
|
|
||||||
}
|
|
||||||
|
|
||||||
func newDistributionData(bounds []float64) *DistributionData {
|
|
||||||
return &DistributionData{
|
|
||||||
CountPerBucket: make([]int64, len(bounds)+1),
|
|
||||||
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 }
|
|
||||||
|
|
||||||
func (a *DistributionData) addSample(f float64) {
|
|
||||||
if f < a.Min {
|
|
||||||
a.Min = f
|
|
||||||
}
|
|
||||||
if f > a.Max {
|
|
||||||
a.Max = f
|
|
||||||
}
|
|
||||||
a.Count++
|
|
||||||
a.incrementBucketCount(f)
|
|
||||||
|
|
||||||
if a.Count == 1 {
|
|
||||||
a.Mean = f
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
oldMean := a.Mean
|
|
||||||
a.Mean = a.Mean + (f-a.Mean)/float64(a.Count)
|
|
||||||
a.SumOfSquaredDev = a.SumOfSquaredDev + (f-oldMean)*(f-a.Mean)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *DistributionData) incrementBucketCount(f float64) {
|
|
||||||
if len(a.bounds) == 0 {
|
|
||||||
a.CountPerBucket[0]++
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, b := range a.bounds {
|
|
||||||
if f < b {
|
|
||||||
a.CountPerBucket[i]++
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
a.CountPerBucket[len(a.bounds)]++
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *DistributionData) clone() AggregationData {
|
|
||||||
counts := make([]int64, len(a.CountPerBucket))
|
|
||||||
copy(counts, a.CountPerBucket)
|
|
||||||
c := *a
|
|
||||||
c.CountPerBucket = counts
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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) {
|
|
||||||
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
|
|
||||||
}
|
|
|
@ -1,85 +0,0 @@
|
||||||
// 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"
|
|
||||||
|
|
||||||
"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) {
|
|
||||||
aggregator, ok := c.signatures[s]
|
|
||||||
if !ok {
|
|
||||||
aggregator = c.a.newData()
|
|
||||||
c.signatures[s] = aggregator
|
|
||||||
}
|
|
||||||
aggregator.addSample(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
|
@ -1,46 +0,0 @@
|
||||||
// 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 over a time window.
|
|
||||||
|
|
||||||
All recorded measurements can be filtered by a list of tags.
|
|
||||||
|
|
||||||
OpenCensus provides several aggregation methods: count, distribution and sum.
|
|
||||||
Count aggregation only counts the number of measurement points. Distribution
|
|
||||||
aggregation provides statistical summary of the aggregated data. Sum distribution
|
|
||||||
sums up the measurement points. Aggregations are cumulative.
|
|
||||||
|
|
||||||
Users can dynamically create and delete views.
|
|
||||||
|
|
||||||
Libraries can export their own views and claim the view names
|
|
||||||
by registering them themselves.
|
|
||||||
|
|
||||||
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 backends. Users need to unregister the exporters once they
|
|
||||||
no longer are needed.
|
|
||||||
*/
|
|
||||||
package view // import "go.opencensus.io/stats/view"
|
|
||||||
|
|
||||||
// TODO(acetechnologist): Add a link to the language independent OpenCensus
|
|
||||||
// spec when it is available.
|
|
|
@ -1,58 +0,0 @@
|
||||||
// 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)
|
|
||||||
}
|
|
|
@ -1,183 +0,0 @@
|
||||||
// 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"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"sort"
|
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"go.opencensus.io/stats"
|
|
||||||
"go.opencensus.io/stats/internal"
|
|
||||||
"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()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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()
|
|
||||||
})
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
func newViewInternal(v *View) (*viewInternal, error) {
|
|
||||||
return &viewInternal{
|
|
||||||
view: v,
|
|
||||||
collector: &collector{make(map[string]AggregationData), v.Aggregation},
|
|
||||||
}, 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) {
|
|
||||||
if !v.isSubscribed() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
sig := string(encodeWithKeys(m, v.view.TagKeys))
|
|
||||||
v.collector.addSample(sig, val)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkViewName(name string) error {
|
|
||||||
if len(name) > internal.MaxNameLength {
|
|
||||||
return fmt.Errorf("view name cannot be larger than %v", internal.MaxNameLength)
|
|
||||||
}
|
|
||||||
if !internal.IsPrintable(name) {
|
|
||||||
return fmt.Errorf("view name needs to be an ASCII string")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,227 +0,0 @@
|
||||||
// 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"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"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
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
for _, v := range views {
|
|
||||||
if err := v.canonicalize(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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{}) {
|
|
||||||
req := &recordReq{
|
|
||||||
tm: tags,
|
|
||||||
ms: ms.([]stats.Measurement),
|
|
||||||
}
|
|
||||||
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() {
|
|
||||||
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() {
|
|
||||||
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) {
|
|
||||||
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) 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) {
|
|
||||||
for _, v := range w.views {
|
|
||||||
w.reportView(v, now)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,174 +0,0 @@
|
||||||
// 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) {
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
delete(w.views, 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) {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cmd *recordReq) handleCommand(w *worker) {
|
|
||||||
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())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
// 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{}
|
|
|
@ -1,26 +0,0 @@
|
||||||
// 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"
|
|
|
@ -1,35 +0,0 @@
|
||||||
// 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
|
|
||||||
}
|
|
|
@ -1,197 +0,0 @@
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map is a map of tags. Use New to create a context containing
|
|
||||||
// a new Map.
|
|
||||||
type Map struct {
|
|
||||||
m map[Key]string
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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, 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) {
|
|
||||||
if _, ok := m.m[k]; ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
m.m[k] = v
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Map) update(k Key, v string) {
|
|
||||||
if _, ok := m.m[k]; ok {
|
|
||||||
m.m[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Map) upsert(k Key, v string) {
|
|
||||||
m.m[k] = v
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Map) delete(k Key) {
|
|
||||||
delete(m.m, k)
|
|
||||||
}
|
|
||||||
|
|
||||||
func newMap() *Map {
|
|
||||||
return &Map{m: make(map[Key]string)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
func Insert(k Key, v string) Mutator {
|
|
||||||
return &mutator{
|
|
||||||
fn: func(m *Map) (*Map, error) {
|
|
||||||
if !checkValue(v) {
|
|
||||||
return nil, errInvalidValue
|
|
||||||
}
|
|
||||||
m.insert(k, v)
|
|
||||||
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.
|
|
||||||
func Update(k Key, v string) Mutator {
|
|
||||||
return &mutator{
|
|
||||||
fn: func(m *Map) (*Map, error) {
|
|
||||||
if !checkValue(v) {
|
|
||||||
return nil, errInvalidValue
|
|
||||||
}
|
|
||||||
m.update(k, v)
|
|
||||||
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.
|
|
||||||
func Upsert(k Key, v string) Mutator {
|
|
||||||
return &mutator{
|
|
||||||
fn: func(m *Map) (*Map, error) {
|
|
||||||
if !checkValue(v) {
|
|
||||||
return nil, errInvalidValue
|
|
||||||
}
|
|
||||||
m.upsert(k, v)
|
|
||||||
return m, nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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) {
|
|
||||||
return ctx, fmt.Errorf("key:%q value:%q: %v", k.Name(), v, errInvalidValue)
|
|
||||||
}
|
|
||||||
m.insert(k, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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)
|
|
||||||
}
|
|
|
@ -1,234 +0,0 @@
|
||||||
// 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 {
|
|
||||||
eg := &encoderGRPC{
|
|
||||||
buf: make([]byte, len(m.m)),
|
|
||||||
}
|
|
||||||
eg.writeByte(byte(tagsVersionID))
|
|
||||||
for k, v := range m.m {
|
|
||||||
eg.writeByte(byte(keyTypeString))
|
|
||||||
eg.writeStringWithVarintLen(k.name)
|
|
||||||
eg.writeBytesWithVarintLen([]byte(v))
|
|
||||||
}
|
|
||||||
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)) 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)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
// 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.
|
|
||||||
|
|
||||||
// +build go1.9
|
|
||||||
|
|
||||||
package tag
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"runtime/pprof"
|
|
||||||
)
|
|
||||||
|
|
||||||
func do(ctx context.Context, f func(ctx context.Context)) {
|
|
||||||
m := FromContext(ctx)
|
|
||||||
keyvals := make([]string, 0, 2*len(m.m))
|
|
||||||
for k, v := range m.m {
|
|
||||||
keyvals = append(keyvals, k.Name(), v)
|
|
||||||
}
|
|
||||||
pprof.Do(ctx, pprof.Labels(keyvals...), f)
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
// 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.
|
|
||||||
|
|
||||||
// +build !go1.9
|
|
||||||
|
|
||||||
package tag
|
|
||||||
|
|
||||||
import "context"
|
|
||||||
|
|
||||||
func do(ctx context.Context, f func(ctx context.Context)) {
|
|
||||||
f(ctx)
|
|
||||||
}
|
|
|
@ -1,56 +0,0 @@
|
||||||
// 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 "errors"
|
|
||||||
|
|
||||||
const (
|
|
||||||
maxKeyLength = 255
|
|
||||||
|
|
||||||
// valid are restricted to US-ASCII subset (range 0x20 (' ') to 0x7e ('~')).
|
|
||||||
validKeyValueMin = 32
|
|
||||||
validKeyValueMax = 126
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
errInvalidKeyName = errors.New("invalid key name: only ASCII characters accepted; max length must be 255 characters")
|
|
||||||
errInvalidValue = errors.New("invalid value: only ASCII characters accepted; max length must be 255 characters")
|
|
||||||
)
|
|
||||||
|
|
||||||
func checkKeyName(name string) bool {
|
|
||||||
if len(name) == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if len(name) > maxKeyLength {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return isASCII(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func isASCII(s string) bool {
|
|
||||||
for _, c := range s {
|
|
||||||
if (c < validKeyValueMin) || (c > validKeyValueMax) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkValue(v string) bool {
|
|
||||||
if len(v) > maxKeyLength {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return isASCII(v)
|
|
||||||
}
|
|
|
@ -1,114 +0,0 @@
|
||||||
// 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 trace
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
|
||||||
// TraceID is a 16-byte identifier for a set of spans.
|
|
||||||
TraceID [16]byte
|
|
||||||
|
|
||||||
// SpanID is an 8-byte identifier for a single span.
|
|
||||||
SpanID [8]byte
|
|
||||||
)
|
|
||||||
|
|
||||||
func (t TraceID) String() string {
|
|
||||||
return fmt.Sprintf("%02x", t[:])
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s SpanID) String() string {
|
|
||||||
return fmt.Sprintf("%02x", s[:])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Annotation represents a text annotation with a set of attributes and a timestamp.
|
|
||||||
type Annotation struct {
|
|
||||||
Time time.Time
|
|
||||||
Message string
|
|
||||||
Attributes map[string]interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attribute represents a key-value pair on a span, link or annotation.
|
|
||||||
// Construct with one of: BoolAttribute, Int64Attribute, or StringAttribute.
|
|
||||||
type Attribute struct {
|
|
||||||
key string
|
|
||||||
value interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// BoolAttribute returns a bool-valued attribute.
|
|
||||||
func BoolAttribute(key string, value bool) Attribute {
|
|
||||||
return Attribute{key: key, value: value}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Int64Attribute returns an int64-valued attribute.
|
|
||||||
func Int64Attribute(key string, value int64) Attribute {
|
|
||||||
return Attribute{key: key, value: value}
|
|
||||||
}
|
|
||||||
|
|
||||||
// StringAttribute returns a string-valued attribute.
|
|
||||||
func StringAttribute(key string, value string) Attribute {
|
|
||||||
return Attribute{key: key, value: value}
|
|
||||||
}
|
|
||||||
|
|
||||||
// LinkType specifies the relationship between the span that had the link
|
|
||||||
// added, and the linked span.
|
|
||||||
type LinkType int32
|
|
||||||
|
|
||||||
// LinkType values.
|
|
||||||
const (
|
|
||||||
LinkTypeUnspecified LinkType = iota // The relationship of the two spans is unknown.
|
|
||||||
LinkTypeChild // The current span is a child of the linked span.
|
|
||||||
LinkTypeParent // The current span is the parent of the linked span.
|
|
||||||
)
|
|
||||||
|
|
||||||
// Link represents a reference from one span to another span.
|
|
||||||
type Link struct {
|
|
||||||
TraceID TraceID
|
|
||||||
SpanID SpanID
|
|
||||||
Type LinkType
|
|
||||||
// Attributes is a set of attributes on the link.
|
|
||||||
Attributes map[string]interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MessageEventType specifies the type of message event.
|
|
||||||
type MessageEventType int32
|
|
||||||
|
|
||||||
// MessageEventType values.
|
|
||||||
const (
|
|
||||||
MessageEventTypeUnspecified MessageEventType = iota // Unknown event type.
|
|
||||||
MessageEventTypeSent // Indicates a sent RPC message.
|
|
||||||
MessageEventTypeRecv // Indicates a received RPC message.
|
|
||||||
)
|
|
||||||
|
|
||||||
// MessageEvent represents an event describing a message sent or received on the network.
|
|
||||||
type MessageEvent struct {
|
|
||||||
Time time.Time
|
|
||||||
EventType MessageEventType
|
|
||||||
MessageID int64
|
|
||||||
UncompressedByteSize int64
|
|
||||||
CompressedByteSize int64
|
|
||||||
}
|
|
||||||
|
|
||||||
// Status is the status of a Span.
|
|
||||||
type Status struct {
|
|
||||||
// Code is a status code. Zero indicates success.
|
|
||||||
//
|
|
||||||
// If Code will be propagated to Google APIs, it ideally should be a value from
|
|
||||||
// https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto .
|
|
||||||
Code int32
|
|
||||||
Message string
|
|
||||||
}
|
|
|
@ -1,48 +0,0 @@
|
||||||
// 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 trace
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"go.opencensus.io/trace/internal"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Config represents the global tracing configuration.
|
|
||||||
type Config struct {
|
|
||||||
// DefaultSampler is the default sampler used when creating new spans.
|
|
||||||
DefaultSampler Sampler
|
|
||||||
|
|
||||||
// IDGenerator is for internal use only.
|
|
||||||
IDGenerator internal.IDGenerator
|
|
||||||
}
|
|
||||||
|
|
||||||
var configWriteMu sync.Mutex
|
|
||||||
|
|
||||||
// ApplyConfig applies changes to the global tracing configuration.
|
|
||||||
//
|
|
||||||
// Fields not provided in the given config are going to be preserved.
|
|
||||||
func ApplyConfig(cfg Config) {
|
|
||||||
configWriteMu.Lock()
|
|
||||||
defer configWriteMu.Unlock()
|
|
||||||
c := *config.Load().(*Config)
|
|
||||||
if cfg.DefaultSampler != nil {
|
|
||||||
c.DefaultSampler = cfg.DefaultSampler
|
|
||||||
}
|
|
||||||
if cfg.IDGenerator != nil {
|
|
||||||
c.IDGenerator = cfg.IDGenerator
|
|
||||||
}
|
|
||||||
config.Store(&c)
|
|
||||||
}
|
|
|
@ -1,53 +0,0 @@
|
||||||
// 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 trace contains support for OpenCensus distributed tracing.
|
|
||||||
|
|
||||||
The following assumes a basic familiarity with OpenCensus concepts.
|
|
||||||
See http://opencensus.io
|
|
||||||
|
|
||||||
|
|
||||||
Exporting Traces
|
|
||||||
|
|
||||||
To export collected tracing data, register at least one exporter. You can use
|
|
||||||
one of the provided exporters or write your own.
|
|
||||||
|
|
||||||
trace.RegisterExporter(exporter)
|
|
||||||
|
|
||||||
By default, traces will be sampled relatively rarely. To change the sampling
|
|
||||||
frequency for your entire program, call ApplyConfig. Use a ProbabilitySampler
|
|
||||||
to sample a subset of traces, or use AlwaysSample to collect a trace on every run:
|
|
||||||
|
|
||||||
trace.ApplyConfig(trace.Config{DefaultSampler: trace.AlwaysSample()})
|
|
||||||
|
|
||||||
Be careful about using trace.AlwaysSample in a production application with
|
|
||||||
significant traffic: a new trace will be started and exported for every request.
|
|
||||||
|
|
||||||
Adding Spans to a Trace
|
|
||||||
|
|
||||||
A trace consists of a tree of spans. In Go, the current span is carried in a
|
|
||||||
context.Context.
|
|
||||||
|
|
||||||
It is common to want to capture all the activity of a function call in a span. For
|
|
||||||
this to work, the function must take a context.Context as a parameter. Add these two
|
|
||||||
lines to the top of the function:
|
|
||||||
|
|
||||||
ctx, span := trace.StartSpan(ctx, "example.com/Run")
|
|
||||||
defer span.End()
|
|
||||||
|
|
||||||
StartSpan will create a new top-level span if the context
|
|
||||||
doesn't contain another span, otherwise it will create a child span.
|
|
||||||
*/
|
|
||||||
package trace // import "go.opencensus.io/trace"
|
|
|
@ -1,90 +0,0 @@
|
||||||
// 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 trace
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Exporter is a type for functions that receive sampled trace spans.
|
|
||||||
//
|
|
||||||
// The ExportSpan method should be safe for concurrent use and should return
|
|
||||||
// quickly; if an Exporter takes a significant amount of time to process a
|
|
||||||
// SpanData, that work should be done on another goroutine.
|
|
||||||
//
|
|
||||||
// The SpanData should not be modified, but a pointer to it can be kept.
|
|
||||||
type Exporter interface {
|
|
||||||
ExportSpan(s *SpanData)
|
|
||||||
}
|
|
||||||
|
|
||||||
type exportersMap map[Exporter]struct{}
|
|
||||||
|
|
||||||
var (
|
|
||||||
exporterMu sync.Mutex
|
|
||||||
exporters atomic.Value
|
|
||||||
)
|
|
||||||
|
|
||||||
// RegisterExporter adds to the list of Exporters that will receive sampled
|
|
||||||
// trace spans.
|
|
||||||
//
|
|
||||||
// Binaries can register exporters, libraries shouldn't register exporters.
|
|
||||||
func RegisterExporter(e Exporter) {
|
|
||||||
exporterMu.Lock()
|
|
||||||
new := make(exportersMap)
|
|
||||||
if old, ok := exporters.Load().(exportersMap); ok {
|
|
||||||
for k, v := range old {
|
|
||||||
new[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
new[e] = struct{}{}
|
|
||||||
exporters.Store(new)
|
|
||||||
exporterMu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnregisterExporter removes from the list of Exporters the Exporter that was
|
|
||||||
// registered with the given name.
|
|
||||||
func UnregisterExporter(e Exporter) {
|
|
||||||
exporterMu.Lock()
|
|
||||||
new := make(exportersMap)
|
|
||||||
if old, ok := exporters.Load().(exportersMap); ok {
|
|
||||||
for k, v := range old {
|
|
||||||
new[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
delete(new, e)
|
|
||||||
exporters.Store(new)
|
|
||||||
exporterMu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// SpanData contains all the information collected by a Span.
|
|
||||||
type SpanData struct {
|
|
||||||
SpanContext
|
|
||||||
ParentSpanID SpanID
|
|
||||||
SpanKind int
|
|
||||||
Name string
|
|
||||||
StartTime time.Time
|
|
||||||
// The wall clock time of EndTime will be adjusted to always be offset
|
|
||||||
// from StartTime by the duration of the span.
|
|
||||||
EndTime time.Time
|
|
||||||
// The values of Attributes each have type string, bool, or int64.
|
|
||||||
Attributes map[string]interface{}
|
|
||||||
Annotations []Annotation
|
|
||||||
MessageEvents []MessageEvent
|
|
||||||
Status
|
|
||||||
Links []Link
|
|
||||||
HasRemoteParent bool
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
// 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 provides trace internals.
|
|
||||||
package internal
|
|
||||||
|
|
||||||
type IDGenerator interface {
|
|
||||||
NewTraceID() [16]byte
|
|
||||||
NewSpanID() [8]byte
|
|
||||||
}
|
|
|
@ -1,108 +0,0 @@
|
||||||
// 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 propagation implements the binary trace context format.
|
|
||||||
package propagation // import "go.opencensus.io/trace/propagation"
|
|
||||||
|
|
||||||
// TODO: link to external spec document.
|
|
||||||
|
|
||||||
// BinaryFormat format:
|
|
||||||
//
|
|
||||||
// Binary value: <version_id><version_format>
|
|
||||||
// version_id: 1 byte representing the version id.
|
|
||||||
//
|
|
||||||
// For version_id = 0:
|
|
||||||
//
|
|
||||||
// version_format: <field><field>
|
|
||||||
// field_format: <field_id><field_format>
|
|
||||||
//
|
|
||||||
// Fields:
|
|
||||||
//
|
|
||||||
// TraceId: (field_id = 0, len = 16, default = "0000000000000000") - 16-byte array representing the trace_id.
|
|
||||||
// SpanId: (field_id = 1, len = 8, default = "00000000") - 8-byte array representing the span_id.
|
|
||||||
// TraceOptions: (field_id = 2, len = 1, default = "0") - 1-byte array representing the trace_options.
|
|
||||||
//
|
|
||||||
// Fields MUST be encoded using the field id order (smaller to higher).
|
|
||||||
//
|
|
||||||
// Valid value example:
|
|
||||||
//
|
|
||||||
// {0, 0, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 1, 97,
|
|
||||||
// 98, 99, 100, 101, 102, 103, 104, 2, 1}
|
|
||||||
//
|
|
||||||
// version_id = 0;
|
|
||||||
// trace_id = {64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79}
|
|
||||||
// span_id = {97, 98, 99, 100, 101, 102, 103, 104};
|
|
||||||
// trace_options = {1};
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"go.opencensus.io/trace"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Binary returns the binary format representation of a SpanContext.
|
|
||||||
//
|
|
||||||
// If sc is the zero value, Binary returns nil.
|
|
||||||
func Binary(sc trace.SpanContext) []byte {
|
|
||||||
if sc == (trace.SpanContext{}) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
var b [29]byte
|
|
||||||
copy(b[2:18], sc.TraceID[:])
|
|
||||||
b[18] = 1
|
|
||||||
copy(b[19:27], sc.SpanID[:])
|
|
||||||
b[27] = 2
|
|
||||||
b[28] = uint8(sc.TraceOptions)
|
|
||||||
return b[:]
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromBinary returns the SpanContext represented by b.
|
|
||||||
//
|
|
||||||
// If b has an unsupported version ID or contains no TraceID, FromBinary
|
|
||||||
// returns with ok==false.
|
|
||||||
func FromBinary(b []byte) (sc trace.SpanContext, ok bool) {
|
|
||||||
if len(b) == 0 || b[0] != 0 {
|
|
||||||
return trace.SpanContext{}, false
|
|
||||||
}
|
|
||||||
b = b[1:]
|
|
||||||
if len(b) >= 17 && b[0] == 0 {
|
|
||||||
copy(sc.TraceID[:], b[1:17])
|
|
||||||
b = b[17:]
|
|
||||||
} else {
|
|
||||||
return trace.SpanContext{}, false
|
|
||||||
}
|
|
||||||
if len(b) >= 9 && b[0] == 1 {
|
|
||||||
copy(sc.SpanID[:], b[1:9])
|
|
||||||
b = b[9:]
|
|
||||||
}
|
|
||||||
if len(b) >= 2 && b[0] == 2 {
|
|
||||||
sc.TraceOptions = trace.TraceOptions(b[1])
|
|
||||||
}
|
|
||||||
return sc, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTTPFormat implementations propagate span contexts
|
|
||||||
// in HTTP requests.
|
|
||||||
//
|
|
||||||
// SpanContextFromRequest extracts a span context from incoming
|
|
||||||
// requests.
|
|
||||||
//
|
|
||||||
// SpanContextToRequest modifies the given request to include the given
|
|
||||||
// span context.
|
|
||||||
type HTTPFormat interface {
|
|
||||||
SpanContextFromRequest(req *http.Request) (sc trace.SpanContext, ok bool)
|
|
||||||
SpanContextToRequest(sc trace.SpanContext, req *http.Request)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(jbd): Find a more representative but short name for HTTPFormat.
|
|
|
@ -1,75 +0,0 @@
|
||||||
// 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 trace
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
)
|
|
||||||
|
|
||||||
const defaultSamplingProbability = 1e-4
|
|
||||||
|
|
||||||
// Sampler decides whether a trace should be sampled and exported.
|
|
||||||
type Sampler func(SamplingParameters) SamplingDecision
|
|
||||||
|
|
||||||
// SamplingParameters contains the values passed to a Sampler.
|
|
||||||
type SamplingParameters struct {
|
|
||||||
ParentContext SpanContext
|
|
||||||
TraceID TraceID
|
|
||||||
SpanID SpanID
|
|
||||||
Name string
|
|
||||||
HasRemoteParent bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// SamplingDecision is the value returned by a Sampler.
|
|
||||||
type SamplingDecision struct {
|
|
||||||
Sample bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// ProbabilitySampler returns a Sampler that samples a given fraction of traces.
|
|
||||||
//
|
|
||||||
// It also samples spans whose parents are sampled.
|
|
||||||
func ProbabilitySampler(fraction float64) Sampler {
|
|
||||||
if !(fraction >= 0) {
|
|
||||||
fraction = 0
|
|
||||||
} else if fraction >= 1 {
|
|
||||||
return AlwaysSample()
|
|
||||||
}
|
|
||||||
|
|
||||||
traceIDUpperBound := uint64(fraction * (1 << 63))
|
|
||||||
return Sampler(func(p SamplingParameters) SamplingDecision {
|
|
||||||
if p.ParentContext.IsSampled() {
|
|
||||||
return SamplingDecision{Sample: true}
|
|
||||||
}
|
|
||||||
x := binary.BigEndian.Uint64(p.TraceID[0:8]) >> 1
|
|
||||||
return SamplingDecision{Sample: x < traceIDUpperBound}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// AlwaysSample returns a Sampler that samples every trace.
|
|
||||||
// Be careful about using this sampler in a production application with
|
|
||||||
// significant traffic: a new trace will be started and exported for every
|
|
||||||
// request.
|
|
||||||
func AlwaysSample() Sampler {
|
|
||||||
return func(p SamplingParameters) SamplingDecision {
|
|
||||||
return SamplingDecision{Sample: true}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NeverSample returns a Sampler that samples no traces.
|
|
||||||
func NeverSample() Sampler {
|
|
||||||
return func(p SamplingParameters) SamplingDecision {
|
|
||||||
return SamplingDecision{Sample: false}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,130 +0,0 @@
|
||||||
// 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 trace
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// samplePeriod is the minimum time between accepting spans in a single bucket.
|
|
||||||
const samplePeriod = time.Second
|
|
||||||
|
|
||||||
// defaultLatencies contains the default latency bucket bounds.
|
|
||||||
// TODO: consider defaults, make configurable
|
|
||||||
var defaultLatencies = [...]time.Duration{
|
|
||||||
10 * time.Microsecond,
|
|
||||||
100 * time.Microsecond,
|
|
||||||
time.Millisecond,
|
|
||||||
10 * time.Millisecond,
|
|
||||||
100 * time.Millisecond,
|
|
||||||
time.Second,
|
|
||||||
10 * time.Second,
|
|
||||||
time.Minute,
|
|
||||||
}
|
|
||||||
|
|
||||||
// bucket is a container for a set of spans for a particular error code or latency range.
|
|
||||||
type bucket struct {
|
|
||||||
nextTime time.Time // next time we can accept a span
|
|
||||||
buffer []*SpanData // circular buffer of spans
|
|
||||||
nextIndex int // location next SpanData should be placed in buffer
|
|
||||||
overflow bool // whether the circular buffer has wrapped around
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeBucket(bufferSize int) bucket {
|
|
||||||
return bucket{
|
|
||||||
buffer: make([]*SpanData, bufferSize),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// add adds a span to the bucket, if nextTime has been reached.
|
|
||||||
func (b *bucket) add(s *SpanData) {
|
|
||||||
if s.EndTime.Before(b.nextTime) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if len(b.buffer) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
b.nextTime = s.EndTime.Add(samplePeriod)
|
|
||||||
b.buffer[b.nextIndex] = s
|
|
||||||
b.nextIndex++
|
|
||||||
if b.nextIndex == len(b.buffer) {
|
|
||||||
b.nextIndex = 0
|
|
||||||
b.overflow = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// size returns the number of spans in the bucket.
|
|
||||||
func (b *bucket) size() int {
|
|
||||||
if b.overflow {
|
|
||||||
return len(b.buffer)
|
|
||||||
}
|
|
||||||
return b.nextIndex
|
|
||||||
}
|
|
||||||
|
|
||||||
// span returns the ith span in the bucket.
|
|
||||||
func (b *bucket) span(i int) *SpanData {
|
|
||||||
if !b.overflow {
|
|
||||||
return b.buffer[i]
|
|
||||||
}
|
|
||||||
if i < len(b.buffer)-b.nextIndex {
|
|
||||||
return b.buffer[b.nextIndex+i]
|
|
||||||
}
|
|
||||||
return b.buffer[b.nextIndex+i-len(b.buffer)]
|
|
||||||
}
|
|
||||||
|
|
||||||
// resize changes the size of the bucket to n, keeping up to n existing spans.
|
|
||||||
func (b *bucket) resize(n int) {
|
|
||||||
cur := b.size()
|
|
||||||
newBuffer := make([]*SpanData, n)
|
|
||||||
if cur < n {
|
|
||||||
for i := 0; i < cur; i++ {
|
|
||||||
newBuffer[i] = b.span(i)
|
|
||||||
}
|
|
||||||
b.buffer = newBuffer
|
|
||||||
b.nextIndex = cur
|
|
||||||
b.overflow = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for i := 0; i < n; i++ {
|
|
||||||
newBuffer[i] = b.span(i + cur - n)
|
|
||||||
}
|
|
||||||
b.buffer = newBuffer
|
|
||||||
b.nextIndex = 0
|
|
||||||
b.overflow = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// latencyBucket returns the appropriate bucket number for a given latency.
|
|
||||||
func latencyBucket(latency time.Duration) int {
|
|
||||||
i := 0
|
|
||||||
for i < len(defaultLatencies) && latency >= defaultLatencies[i] {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
// latencyBucketBounds returns the lower and upper bounds for a latency bucket
|
|
||||||
// number.
|
|
||||||
//
|
|
||||||
// The lower bound is inclusive, the upper bound is exclusive (except for the
|
|
||||||
// last bucket.)
|
|
||||||
func latencyBucketBounds(index int) (lower time.Duration, upper time.Duration) {
|
|
||||||
if index == 0 {
|
|
||||||
return 0, defaultLatencies[index]
|
|
||||||
}
|
|
||||||
if index == len(defaultLatencies) {
|
|
||||||
return defaultLatencies[index-1], 1<<63 - 1
|
|
||||||
}
|
|
||||||
return defaultLatencies[index-1], defaultLatencies[index]
|
|
||||||
}
|
|
|
@ -1,306 +0,0 @@
|
||||||
// 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 trace
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"go.opencensus.io/internal"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
maxBucketSize = 100000
|
|
||||||
defaultBucketSize = 10
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ssmu sync.RWMutex // protects spanStores
|
|
||||||
spanStores = make(map[string]*spanStore)
|
|
||||||
)
|
|
||||||
|
|
||||||
// This exists purely to avoid exposing internal methods used by z-Pages externally.
|
|
||||||
type internalOnly struct{}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
//TODO(#412): remove
|
|
||||||
internal.Trace = &internalOnly{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReportActiveSpans returns the active spans for the given name.
|
|
||||||
func (i internalOnly) ReportActiveSpans(name string) []*SpanData {
|
|
||||||
s := spanStoreForName(name)
|
|
||||||
if s == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
var out []*SpanData
|
|
||||||
s.mu.Lock()
|
|
||||||
defer s.mu.Unlock()
|
|
||||||
for span := range s.active {
|
|
||||||
out = append(out, span.makeSpanData())
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReportSpansByError returns a sample of error spans.
|
|
||||||
//
|
|
||||||
// If code is nonzero, only spans with that status code are returned.
|
|
||||||
func (i internalOnly) ReportSpansByError(name string, code int32) []*SpanData {
|
|
||||||
s := spanStoreForName(name)
|
|
||||||
if s == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
var out []*SpanData
|
|
||||||
s.mu.Lock()
|
|
||||||
defer s.mu.Unlock()
|
|
||||||
if code != 0 {
|
|
||||||
if b, ok := s.errors[code]; ok {
|
|
||||||
for _, sd := range b.buffer {
|
|
||||||
if sd == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
out = append(out, sd)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for _, b := range s.errors {
|
|
||||||
for _, sd := range b.buffer {
|
|
||||||
if sd == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
out = append(out, sd)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConfigureBucketSizes sets the number of spans to keep per latency and error
|
|
||||||
// bucket for different span names.
|
|
||||||
func (i internalOnly) ConfigureBucketSizes(bcs []internal.BucketConfiguration) {
|
|
||||||
for _, bc := range bcs {
|
|
||||||
latencyBucketSize := bc.MaxRequestsSucceeded
|
|
||||||
if latencyBucketSize < 0 {
|
|
||||||
latencyBucketSize = 0
|
|
||||||
}
|
|
||||||
if latencyBucketSize > maxBucketSize {
|
|
||||||
latencyBucketSize = maxBucketSize
|
|
||||||
}
|
|
||||||
errorBucketSize := bc.MaxRequestsErrors
|
|
||||||
if errorBucketSize < 0 {
|
|
||||||
errorBucketSize = 0
|
|
||||||
}
|
|
||||||
if errorBucketSize > maxBucketSize {
|
|
||||||
errorBucketSize = maxBucketSize
|
|
||||||
}
|
|
||||||
spanStoreSetSize(bc.Name, latencyBucketSize, errorBucketSize)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReportSpansPerMethod returns a summary of what spans are being stored for each span name.
|
|
||||||
func (i internalOnly) ReportSpansPerMethod() map[string]internal.PerMethodSummary {
|
|
||||||
out := make(map[string]internal.PerMethodSummary)
|
|
||||||
ssmu.RLock()
|
|
||||||
defer ssmu.RUnlock()
|
|
||||||
for name, s := range spanStores {
|
|
||||||
s.mu.Lock()
|
|
||||||
p := internal.PerMethodSummary{
|
|
||||||
Active: len(s.active),
|
|
||||||
}
|
|
||||||
for code, b := range s.errors {
|
|
||||||
p.ErrorBuckets = append(p.ErrorBuckets, internal.ErrorBucketSummary{
|
|
||||||
ErrorCode: code,
|
|
||||||
Size: b.size(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
for i, b := range s.latency {
|
|
||||||
min, max := latencyBucketBounds(i)
|
|
||||||
p.LatencyBuckets = append(p.LatencyBuckets, internal.LatencyBucketSummary{
|
|
||||||
MinLatency: min,
|
|
||||||
MaxLatency: max,
|
|
||||||
Size: b.size(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
s.mu.Unlock()
|
|
||||||
out[name] = p
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReportSpansByLatency returns a sample of successful spans.
|
|
||||||
//
|
|
||||||
// minLatency is the minimum latency of spans to be returned.
|
|
||||||
// maxLatency, if nonzero, is the maximum latency of spans to be returned.
|
|
||||||
func (i internalOnly) ReportSpansByLatency(name string, minLatency, maxLatency time.Duration) []*SpanData {
|
|
||||||
s := spanStoreForName(name)
|
|
||||||
if s == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
var out []*SpanData
|
|
||||||
s.mu.Lock()
|
|
||||||
defer s.mu.Unlock()
|
|
||||||
for i, b := range s.latency {
|
|
||||||
min, max := latencyBucketBounds(i)
|
|
||||||
if i+1 != len(s.latency) && max <= minLatency {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if maxLatency != 0 && maxLatency < min {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for _, sd := range b.buffer {
|
|
||||||
if sd == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if minLatency != 0 || maxLatency != 0 {
|
|
||||||
d := sd.EndTime.Sub(sd.StartTime)
|
|
||||||
if d < minLatency {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if maxLatency != 0 && d > maxLatency {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out = append(out, sd)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// spanStore keeps track of spans stored for a particular span name.
|
|
||||||
//
|
|
||||||
// It contains all active spans; a sample of spans for failed requests,
|
|
||||||
// categorized by error code; and a sample of spans for successful requests,
|
|
||||||
// bucketed by latency.
|
|
||||||
type spanStore struct {
|
|
||||||
mu sync.Mutex // protects everything below.
|
|
||||||
active map[*Span]struct{}
|
|
||||||
errors map[int32]*bucket
|
|
||||||
latency []bucket
|
|
||||||
maxSpansPerErrorBucket int
|
|
||||||
}
|
|
||||||
|
|
||||||
// newSpanStore creates a span store.
|
|
||||||
func newSpanStore(name string, latencyBucketSize int, errorBucketSize int) *spanStore {
|
|
||||||
s := &spanStore{
|
|
||||||
active: make(map[*Span]struct{}),
|
|
||||||
latency: make([]bucket, len(defaultLatencies)+1),
|
|
||||||
maxSpansPerErrorBucket: errorBucketSize,
|
|
||||||
}
|
|
||||||
for i := range s.latency {
|
|
||||||
s.latency[i] = makeBucket(latencyBucketSize)
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// spanStoreForName returns the spanStore for the given name.
|
|
||||||
//
|
|
||||||
// It returns nil if it doesn't exist.
|
|
||||||
func spanStoreForName(name string) *spanStore {
|
|
||||||
var s *spanStore
|
|
||||||
ssmu.RLock()
|
|
||||||
s, _ = spanStores[name]
|
|
||||||
ssmu.RUnlock()
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// spanStoreForNameCreateIfNew returns the spanStore for the given name.
|
|
||||||
//
|
|
||||||
// It creates it if it didn't exist.
|
|
||||||
func spanStoreForNameCreateIfNew(name string) *spanStore {
|
|
||||||
ssmu.RLock()
|
|
||||||
s, ok := spanStores[name]
|
|
||||||
ssmu.RUnlock()
|
|
||||||
if ok {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
ssmu.Lock()
|
|
||||||
defer ssmu.Unlock()
|
|
||||||
s, ok = spanStores[name]
|
|
||||||
if ok {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
s = newSpanStore(name, defaultBucketSize, defaultBucketSize)
|
|
||||||
spanStores[name] = s
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// spanStoreSetSize resizes the spanStore for the given name.
|
|
||||||
//
|
|
||||||
// It creates it if it didn't exist.
|
|
||||||
func spanStoreSetSize(name string, latencyBucketSize int, errorBucketSize int) {
|
|
||||||
ssmu.RLock()
|
|
||||||
s, ok := spanStores[name]
|
|
||||||
ssmu.RUnlock()
|
|
||||||
if ok {
|
|
||||||
s.resize(latencyBucketSize, errorBucketSize)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ssmu.Lock()
|
|
||||||
defer ssmu.Unlock()
|
|
||||||
s, ok = spanStores[name]
|
|
||||||
if ok {
|
|
||||||
s.resize(latencyBucketSize, errorBucketSize)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
s = newSpanStore(name, latencyBucketSize, errorBucketSize)
|
|
||||||
spanStores[name] = s
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *spanStore) resize(latencyBucketSize int, errorBucketSize int) {
|
|
||||||
s.mu.Lock()
|
|
||||||
for i := range s.latency {
|
|
||||||
s.latency[i].resize(latencyBucketSize)
|
|
||||||
}
|
|
||||||
for _, b := range s.errors {
|
|
||||||
b.resize(errorBucketSize)
|
|
||||||
}
|
|
||||||
s.maxSpansPerErrorBucket = errorBucketSize
|
|
||||||
s.mu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// add adds a span to the active bucket of the spanStore.
|
|
||||||
func (s *spanStore) add(span *Span) {
|
|
||||||
s.mu.Lock()
|
|
||||||
s.active[span] = struct{}{}
|
|
||||||
s.mu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// finished removes a span from the active set, and adds a corresponding
|
|
||||||
// SpanData to a latency or error bucket.
|
|
||||||
func (s *spanStore) finished(span *Span, sd *SpanData) {
|
|
||||||
latency := sd.EndTime.Sub(sd.StartTime)
|
|
||||||
if latency < 0 {
|
|
||||||
latency = 0
|
|
||||||
}
|
|
||||||
code := sd.Status.Code
|
|
||||||
|
|
||||||
s.mu.Lock()
|
|
||||||
delete(s.active, span)
|
|
||||||
if code == 0 {
|
|
||||||
s.latency[latencyBucket(latency)].add(sd)
|
|
||||||
} else {
|
|
||||||
if s.errors == nil {
|
|
||||||
s.errors = make(map[int32]*bucket)
|
|
||||||
}
|
|
||||||
if b := s.errors[code]; b != nil {
|
|
||||||
b.add(sd)
|
|
||||||
} else {
|
|
||||||
b := makeBucket(s.maxSpansPerErrorBucket)
|
|
||||||
s.errors[code] = &b
|
|
||||||
b.add(sd)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s.mu.Unlock()
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
// 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 trace
|
|
||||||
|
|
||||||
// Status codes for use with Span.SetStatus. These correspond to the status
|
|
||||||
// codes used by gRPC defined here: https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto
|
|
||||||
const (
|
|
||||||
StatusCodeOK = 0
|
|
||||||
StatusCodeCancelled = 1
|
|
||||||
StatusCodeUnknown = 2
|
|
||||||
StatusCodeInvalidArgument = 3
|
|
||||||
StatusCodeDeadlineExceeded = 4
|
|
||||||
StatusCodeNotFound = 5
|
|
||||||
StatusCodeAlreadyExists = 6
|
|
||||||
StatusCodePermissionDenied = 7
|
|
||||||
StatusCodeResourceExhausted = 8
|
|
||||||
StatusCodeFailedPrecondition = 9
|
|
||||||
StatusCodeAborted = 10
|
|
||||||
StatusCodeOutOfRange = 11
|
|
||||||
StatusCodeUnimplemented = 12
|
|
||||||
StatusCodeInternal = 13
|
|
||||||
StatusCodeUnavailable = 14
|
|
||||||
StatusCodeDataLoss = 15
|
|
||||||
StatusCodeUnauthenticated = 16
|
|
||||||
)
|
|
|
@ -1,513 +0,0 @@
|
||||||
// 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 trace
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
crand "crypto/rand"
|
|
||||||
"encoding/binary"
|
|
||||||
"fmt"
|
|
||||||
"math/rand"
|
|
||||||
"sync"
|
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"go.opencensus.io/internal"
|
|
||||||
"go.opencensus.io/trace/tracestate"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Span represents a span of a trace. It has an associated SpanContext, and
|
|
||||||
// stores data accumulated while the span is active.
|
|
||||||
//
|
|
||||||
// Ideally users should interact with Spans by calling the functions in this
|
|
||||||
// package that take a Context parameter.
|
|
||||||
type Span struct {
|
|
||||||
// data contains information recorded about the span.
|
|
||||||
//
|
|
||||||
// It will be non-nil if we are exporting the span or recording events for it.
|
|
||||||
// Otherwise, data is nil, and the Span is simply a carrier for the
|
|
||||||
// SpanContext, so that the trace ID is propagated.
|
|
||||||
data *SpanData
|
|
||||||
mu sync.Mutex // protects the contents of *data (but not the pointer value.)
|
|
||||||
spanContext SpanContext
|
|
||||||
// spanStore is the spanStore this span belongs to, if any, otherwise it is nil.
|
|
||||||
*spanStore
|
|
||||||
endOnce sync.Once
|
|
||||||
|
|
||||||
executionTracerTaskEnd func() // ends the execution tracer span
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsRecordingEvents returns true if events are being recorded for this span.
|
|
||||||
// Use this check to avoid computing expensive annotations when they will never
|
|
||||||
// be used.
|
|
||||||
func (s *Span) IsRecordingEvents() bool {
|
|
||||||
if s == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return s.data != nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// TraceOptions contains options associated with a trace span.
|
|
||||||
type TraceOptions uint32
|
|
||||||
|
|
||||||
// IsSampled returns true if the span will be exported.
|
|
||||||
func (sc SpanContext) IsSampled() bool {
|
|
||||||
return sc.TraceOptions.IsSampled()
|
|
||||||
}
|
|
||||||
|
|
||||||
// setIsSampled sets the TraceOptions bit that determines whether the span will be exported.
|
|
||||||
func (sc *SpanContext) setIsSampled(sampled bool) {
|
|
||||||
if sampled {
|
|
||||||
sc.TraceOptions |= 1
|
|
||||||
} else {
|
|
||||||
sc.TraceOptions &= ^TraceOptions(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsSampled returns true if the span will be exported.
|
|
||||||
func (t TraceOptions) IsSampled() bool {
|
|
||||||
return t&1 == 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// SpanContext contains the state that must propagate across process boundaries.
|
|
||||||
//
|
|
||||||
// SpanContext is not an implementation of context.Context.
|
|
||||||
// TODO: add reference to external Census docs for SpanContext.
|
|
||||||
type SpanContext struct {
|
|
||||||
TraceID TraceID
|
|
||||||
SpanID SpanID
|
|
||||||
TraceOptions TraceOptions
|
|
||||||
Tracestate *tracestate.Tracestate
|
|
||||||
}
|
|
||||||
|
|
||||||
type contextKey struct{}
|
|
||||||
|
|
||||||
// FromContext returns the Span stored in a context, or nil if there isn't one.
|
|
||||||
func FromContext(ctx context.Context) *Span {
|
|
||||||
s, _ := ctx.Value(contextKey{}).(*Span)
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewContext returns a new context with the given Span attached.
|
|
||||||
func NewContext(parent context.Context, s *Span) context.Context {
|
|
||||||
return context.WithValue(parent, contextKey{}, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// All available span kinds. Span kind must be either one of these values.
|
|
||||||
const (
|
|
||||||
SpanKindUnspecified = iota
|
|
||||||
SpanKindServer
|
|
||||||
SpanKindClient
|
|
||||||
)
|
|
||||||
|
|
||||||
// StartOptions contains options concerning how a span is started.
|
|
||||||
type StartOptions struct {
|
|
||||||
// Sampler to consult for this Span. If provided, it is always consulted.
|
|
||||||
//
|
|
||||||
// If not provided, then the behavior differs based on whether
|
|
||||||
// the parent of this Span is remote, local, or there is no parent.
|
|
||||||
// In the case of a remote parent or no parent, the
|
|
||||||
// default sampler (see Config) will be consulted. Otherwise,
|
|
||||||
// when there is a non-remote parent, no new sampling decision will be made:
|
|
||||||
// we will preserve the sampling of the parent.
|
|
||||||
Sampler Sampler
|
|
||||||
|
|
||||||
// SpanKind represents the kind of a span. If none is set,
|
|
||||||
// SpanKindUnspecified is used.
|
|
||||||
SpanKind int
|
|
||||||
}
|
|
||||||
|
|
||||||
// StartOption apply changes to StartOptions.
|
|
||||||
type StartOption func(*StartOptions)
|
|
||||||
|
|
||||||
// WithSpanKind makes new spans to be created with the given kind.
|
|
||||||
func WithSpanKind(spanKind int) StartOption {
|
|
||||||
return func(o *StartOptions) {
|
|
||||||
o.SpanKind = spanKind
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithSampler makes new spans to be be created with a custom sampler.
|
|
||||||
// Otherwise, the global sampler is used.
|
|
||||||
func WithSampler(sampler Sampler) StartOption {
|
|
||||||
return func(o *StartOptions) {
|
|
||||||
o.Sampler = sampler
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// StartSpan starts a new child span of the current span in the context. If
|
|
||||||
// there is no span in the context, creates a new trace and span.
|
|
||||||
//
|
|
||||||
// Returned context contains the newly created span. You can use it to
|
|
||||||
// propagate the returned span in process.
|
|
||||||
func StartSpan(ctx context.Context, name string, o ...StartOption) (context.Context, *Span) {
|
|
||||||
var opts StartOptions
|
|
||||||
var parent SpanContext
|
|
||||||
if p := FromContext(ctx); p != nil {
|
|
||||||
parent = p.spanContext
|
|
||||||
}
|
|
||||||
for _, op := range o {
|
|
||||||
op(&opts)
|
|
||||||
}
|
|
||||||
span := startSpanInternal(name, parent != SpanContext{}, parent, false, opts)
|
|
||||||
|
|
||||||
ctx, end := startExecutionTracerTask(ctx, name)
|
|
||||||
span.executionTracerTaskEnd = end
|
|
||||||
return NewContext(ctx, span), span
|
|
||||||
}
|
|
||||||
|
|
||||||
// StartSpanWithRemoteParent starts a new child span of the span from the given parent.
|
|
||||||
//
|
|
||||||
// If the incoming context contains a parent, it ignores. StartSpanWithRemoteParent is
|
|
||||||
// preferred for cases where the parent is propagated via an incoming request.
|
|
||||||
//
|
|
||||||
// Returned context contains the newly created span. You can use it to
|
|
||||||
// propagate the returned span in process.
|
|
||||||
func StartSpanWithRemoteParent(ctx context.Context, name string, parent SpanContext, o ...StartOption) (context.Context, *Span) {
|
|
||||||
var opts StartOptions
|
|
||||||
for _, op := range o {
|
|
||||||
op(&opts)
|
|
||||||
}
|
|
||||||
span := startSpanInternal(name, parent != SpanContext{}, parent, true, opts)
|
|
||||||
ctx, end := startExecutionTracerTask(ctx, name)
|
|
||||||
span.executionTracerTaskEnd = end
|
|
||||||
return NewContext(ctx, span), span
|
|
||||||
}
|
|
||||||
|
|
||||||
func startSpanInternal(name string, hasParent bool, parent SpanContext, remoteParent bool, o StartOptions) *Span {
|
|
||||||
span := &Span{}
|
|
||||||
span.spanContext = parent
|
|
||||||
|
|
||||||
cfg := config.Load().(*Config)
|
|
||||||
|
|
||||||
if !hasParent {
|
|
||||||
span.spanContext.TraceID = cfg.IDGenerator.NewTraceID()
|
|
||||||
}
|
|
||||||
span.spanContext.SpanID = cfg.IDGenerator.NewSpanID()
|
|
||||||
sampler := cfg.DefaultSampler
|
|
||||||
|
|
||||||
if !hasParent || remoteParent || o.Sampler != nil {
|
|
||||||
// If this span is the child of a local span and no Sampler is set in the
|
|
||||||
// options, keep the parent's TraceOptions.
|
|
||||||
//
|
|
||||||
// Otherwise, consult the Sampler in the options if it is non-nil, otherwise
|
|
||||||
// the default sampler.
|
|
||||||
if o.Sampler != nil {
|
|
||||||
sampler = o.Sampler
|
|
||||||
}
|
|
||||||
span.spanContext.setIsSampled(sampler(SamplingParameters{
|
|
||||||
ParentContext: parent,
|
|
||||||
TraceID: span.spanContext.TraceID,
|
|
||||||
SpanID: span.spanContext.SpanID,
|
|
||||||
Name: name,
|
|
||||||
HasRemoteParent: remoteParent}).Sample)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !internal.LocalSpanStoreEnabled && !span.spanContext.IsSampled() {
|
|
||||||
return span
|
|
||||||
}
|
|
||||||
|
|
||||||
span.data = &SpanData{
|
|
||||||
SpanContext: span.spanContext,
|
|
||||||
StartTime: time.Now(),
|
|
||||||
SpanKind: o.SpanKind,
|
|
||||||
Name: name,
|
|
||||||
HasRemoteParent: remoteParent,
|
|
||||||
}
|
|
||||||
if hasParent {
|
|
||||||
span.data.ParentSpanID = parent.SpanID
|
|
||||||
}
|
|
||||||
if internal.LocalSpanStoreEnabled {
|
|
||||||
var ss *spanStore
|
|
||||||
ss = spanStoreForNameCreateIfNew(name)
|
|
||||||
if ss != nil {
|
|
||||||
span.spanStore = ss
|
|
||||||
ss.add(span)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return span
|
|
||||||
}
|
|
||||||
|
|
||||||
// End ends the span.
|
|
||||||
func (s *Span) End() {
|
|
||||||
if !s.IsRecordingEvents() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
s.endOnce.Do(func() {
|
|
||||||
if s.executionTracerTaskEnd != nil {
|
|
||||||
s.executionTracerTaskEnd()
|
|
||||||
}
|
|
||||||
exp, _ := exporters.Load().(exportersMap)
|
|
||||||
mustExport := s.spanContext.IsSampled() && len(exp) > 0
|
|
||||||
if s.spanStore != nil || mustExport {
|
|
||||||
sd := s.makeSpanData()
|
|
||||||
sd.EndTime = internal.MonotonicEndTime(sd.StartTime)
|
|
||||||
if s.spanStore != nil {
|
|
||||||
s.spanStore.finished(s, sd)
|
|
||||||
}
|
|
||||||
if mustExport {
|
|
||||||
for e := range exp {
|
|
||||||
e.ExportSpan(sd)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// makeSpanData produces a SpanData representing the current state of the Span.
|
|
||||||
// It requires that s.data is non-nil.
|
|
||||||
func (s *Span) makeSpanData() *SpanData {
|
|
||||||
var sd SpanData
|
|
||||||
s.mu.Lock()
|
|
||||||
sd = *s.data
|
|
||||||
if s.data.Attributes != nil {
|
|
||||||
sd.Attributes = make(map[string]interface{})
|
|
||||||
for k, v := range s.data.Attributes {
|
|
||||||
sd.Attributes[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s.mu.Unlock()
|
|
||||||
return &sd
|
|
||||||
}
|
|
||||||
|
|
||||||
// SpanContext returns the SpanContext of the span.
|
|
||||||
func (s *Span) SpanContext() SpanContext {
|
|
||||||
if s == nil {
|
|
||||||
return SpanContext{}
|
|
||||||
}
|
|
||||||
return s.spanContext
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetName sets the name of the span, if it is recording events.
|
|
||||||
func (s *Span) SetName(name string) {
|
|
||||||
if !s.IsRecordingEvents() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
s.mu.Lock()
|
|
||||||
s.data.Name = name
|
|
||||||
s.mu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetStatus sets the status of the span, if it is recording events.
|
|
||||||
func (s *Span) SetStatus(status Status) {
|
|
||||||
if !s.IsRecordingEvents() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
s.mu.Lock()
|
|
||||||
s.data.Status = status
|
|
||||||
s.mu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddAttributes sets attributes in the span.
|
|
||||||
//
|
|
||||||
// Existing attributes whose keys appear in the attributes parameter are overwritten.
|
|
||||||
func (s *Span) AddAttributes(attributes ...Attribute) {
|
|
||||||
if !s.IsRecordingEvents() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
s.mu.Lock()
|
|
||||||
if s.data.Attributes == nil {
|
|
||||||
s.data.Attributes = make(map[string]interface{})
|
|
||||||
}
|
|
||||||
copyAttributes(s.data.Attributes, attributes)
|
|
||||||
s.mu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// copyAttributes copies a slice of Attributes into a map.
|
|
||||||
func copyAttributes(m map[string]interface{}, attributes []Attribute) {
|
|
||||||
for _, a := range attributes {
|
|
||||||
m[a.key] = a.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Span) lazyPrintfInternal(attributes []Attribute, format string, a ...interface{}) {
|
|
||||||
now := time.Now()
|
|
||||||
msg := fmt.Sprintf(format, a...)
|
|
||||||
var m map[string]interface{}
|
|
||||||
s.mu.Lock()
|
|
||||||
if len(attributes) != 0 {
|
|
||||||
m = make(map[string]interface{})
|
|
||||||
copyAttributes(m, attributes)
|
|
||||||
}
|
|
||||||
s.data.Annotations = append(s.data.Annotations, Annotation{
|
|
||||||
Time: now,
|
|
||||||
Message: msg,
|
|
||||||
Attributes: m,
|
|
||||||
})
|
|
||||||
s.mu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Span) printStringInternal(attributes []Attribute, str string) {
|
|
||||||
now := time.Now()
|
|
||||||
var a map[string]interface{}
|
|
||||||
s.mu.Lock()
|
|
||||||
if len(attributes) != 0 {
|
|
||||||
a = make(map[string]interface{})
|
|
||||||
copyAttributes(a, attributes)
|
|
||||||
}
|
|
||||||
s.data.Annotations = append(s.data.Annotations, Annotation{
|
|
||||||
Time: now,
|
|
||||||
Message: str,
|
|
||||||
Attributes: a,
|
|
||||||
})
|
|
||||||
s.mu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Annotate adds an annotation with attributes.
|
|
||||||
// Attributes can be nil.
|
|
||||||
func (s *Span) Annotate(attributes []Attribute, str string) {
|
|
||||||
if !s.IsRecordingEvents() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
s.printStringInternal(attributes, str)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Annotatef adds an annotation with attributes.
|
|
||||||
func (s *Span) Annotatef(attributes []Attribute, format string, a ...interface{}) {
|
|
||||||
if !s.IsRecordingEvents() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
s.lazyPrintfInternal(attributes, format, a...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddMessageSendEvent adds a message send event to the span.
|
|
||||||
//
|
|
||||||
// messageID is an identifier for the message, which is recommended to be
|
|
||||||
// unique in this span and the same between the send event and the receive
|
|
||||||
// event (this allows to identify a message between the sender and receiver).
|
|
||||||
// For example, this could be a sequence id.
|
|
||||||
func (s *Span) AddMessageSendEvent(messageID, uncompressedByteSize, compressedByteSize int64) {
|
|
||||||
if !s.IsRecordingEvents() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
now := time.Now()
|
|
||||||
s.mu.Lock()
|
|
||||||
s.data.MessageEvents = append(s.data.MessageEvents, MessageEvent{
|
|
||||||
Time: now,
|
|
||||||
EventType: MessageEventTypeSent,
|
|
||||||
MessageID: messageID,
|
|
||||||
UncompressedByteSize: uncompressedByteSize,
|
|
||||||
CompressedByteSize: compressedByteSize,
|
|
||||||
})
|
|
||||||
s.mu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddMessageReceiveEvent adds a message receive event to the span.
|
|
||||||
//
|
|
||||||
// messageID is an identifier for the message, which is recommended to be
|
|
||||||
// unique in this span and the same between the send event and the receive
|
|
||||||
// event (this allows to identify a message between the sender and receiver).
|
|
||||||
// For example, this could be a sequence id.
|
|
||||||
func (s *Span) AddMessageReceiveEvent(messageID, uncompressedByteSize, compressedByteSize int64) {
|
|
||||||
if !s.IsRecordingEvents() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
now := time.Now()
|
|
||||||
s.mu.Lock()
|
|
||||||
s.data.MessageEvents = append(s.data.MessageEvents, MessageEvent{
|
|
||||||
Time: now,
|
|
||||||
EventType: MessageEventTypeRecv,
|
|
||||||
MessageID: messageID,
|
|
||||||
UncompressedByteSize: uncompressedByteSize,
|
|
||||||
CompressedByteSize: compressedByteSize,
|
|
||||||
})
|
|
||||||
s.mu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddLink adds a link to the span.
|
|
||||||
func (s *Span) AddLink(l Link) {
|
|
||||||
if !s.IsRecordingEvents() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
s.mu.Lock()
|
|
||||||
s.data.Links = append(s.data.Links, l)
|
|
||||||
s.mu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Span) String() string {
|
|
||||||
if s == nil {
|
|
||||||
return "<nil>"
|
|
||||||
}
|
|
||||||
if s.data == nil {
|
|
||||||
return fmt.Sprintf("span %s", s.spanContext.SpanID)
|
|
||||||
}
|
|
||||||
s.mu.Lock()
|
|
||||||
str := fmt.Sprintf("span %s %q", s.spanContext.SpanID, s.data.Name)
|
|
||||||
s.mu.Unlock()
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
|
|
||||||
var config atomic.Value // access atomically
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
gen := &defaultIDGenerator{}
|
|
||||||
// initialize traceID and spanID generators.
|
|
||||||
var rngSeed int64
|
|
||||||
for _, p := range []interface{}{
|
|
||||||
&rngSeed, &gen.traceIDAdd, &gen.nextSpanID, &gen.spanIDInc,
|
|
||||||
} {
|
|
||||||
binary.Read(crand.Reader, binary.LittleEndian, p)
|
|
||||||
}
|
|
||||||
gen.traceIDRand = rand.New(rand.NewSource(rngSeed))
|
|
||||||
gen.spanIDInc |= 1
|
|
||||||
|
|
||||||
config.Store(&Config{
|
|
||||||
DefaultSampler: ProbabilitySampler(defaultSamplingProbability),
|
|
||||||
IDGenerator: gen,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
type defaultIDGenerator struct {
|
|
||||||
sync.Mutex
|
|
||||||
|
|
||||||
// Please keep these as the first fields
|
|
||||||
// so that these 8 byte fields will be aligned on addresses
|
|
||||||
// divisible by 8, on both 32-bit and 64-bit machines when
|
|
||||||
// performing atomic increments and accesses.
|
|
||||||
// See:
|
|
||||||
// * https://github.com/census-instrumentation/opencensus-go/issues/587
|
|
||||||
// * https://github.com/census-instrumentation/opencensus-go/issues/865
|
|
||||||
// * https://golang.org/pkg/sync/atomic/#pkg-note-BUG
|
|
||||||
nextSpanID uint64
|
|
||||||
spanIDInc uint64
|
|
||||||
|
|
||||||
traceIDAdd [2]uint64
|
|
||||||
traceIDRand *rand.Rand
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewSpanID returns a non-zero span ID from a randomly-chosen sequence.
|
|
||||||
func (gen *defaultIDGenerator) NewSpanID() [8]byte {
|
|
||||||
var id uint64
|
|
||||||
for id == 0 {
|
|
||||||
id = atomic.AddUint64(&gen.nextSpanID, gen.spanIDInc)
|
|
||||||
}
|
|
||||||
var sid [8]byte
|
|
||||||
binary.LittleEndian.PutUint64(sid[:], id)
|
|
||||||
return sid
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewTraceID returns a non-zero trace ID from a randomly-chosen sequence.
|
|
||||||
// mu should be held while this function is called.
|
|
||||||
func (gen *defaultIDGenerator) NewTraceID() [16]byte {
|
|
||||||
var tid [16]byte
|
|
||||||
// Construct the trace ID from two outputs of traceIDRand, with a constant
|
|
||||||
// added to each half for additional entropy.
|
|
||||||
gen.Lock()
|
|
||||||
binary.LittleEndian.PutUint64(tid[0:8], gen.traceIDRand.Uint64()+gen.traceIDAdd[0])
|
|
||||||
binary.LittleEndian.PutUint64(tid[8:16], gen.traceIDRand.Uint64()+gen.traceIDAdd[1])
|
|
||||||
gen.Unlock()
|
|
||||||
return tid
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
// 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.
|
|
||||||
|
|
||||||
// +build go1.11
|
|
||||||
|
|
||||||
package trace
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
t "runtime/trace"
|
|
||||||
)
|
|
||||||
|
|
||||||
func startExecutionTracerTask(ctx context.Context, name string) (context.Context, func()) {
|
|
||||||
if !t.IsEnabled() {
|
|
||||||
// Avoid additional overhead if
|
|
||||||
// runtime/trace is not enabled.
|
|
||||||
return ctx, func() {}
|
|
||||||
}
|
|
||||||
nctx, task := t.NewTask(ctx, name)
|
|
||||||
return nctx, task.End
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
// 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.
|
|
||||||
|
|
||||||
// +build !go1.11
|
|
||||||
|
|
||||||
package trace
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
)
|
|
||||||
|
|
||||||
func startExecutionTracerTask(ctx context.Context, name string) (context.Context, func()) {
|
|
||||||
return ctx, func() {}
|
|
||||||
}
|
|
|
@ -1,145 +0,0 @@
|
||||||
// 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 tracestate
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"regexp"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
keyMaxSize = 256
|
|
||||||
valueMaxSize = 256
|
|
||||||
maxKeyValuePairs = 32
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
keyWithoutVendorFormat = `[a-z][_0-9a-z\-\*\/]{0,255}`
|
|
||||||
keyWithVendorFormat = `[a-z][_0-9a-z\-\*\/]{0,240}@[a-z][_0-9a-z\-\*\/]{0,13}`
|
|
||||||
keyFormat = `(` + keyWithoutVendorFormat + `)|(` + keyWithVendorFormat + `)`
|
|
||||||
valueFormat = `[\x20-\x2b\x2d-\x3c\x3e-\x7e]{0,255}[\x21-\x2b\x2d-\x3c\x3e-\x7e]`
|
|
||||||
)
|
|
||||||
|
|
||||||
var keyValidationRegExp = regexp.MustCompile(`^(` + keyFormat + `)$`)
|
|
||||||
var valueValidationRegExp = regexp.MustCompile(`^(` + valueFormat + `)$`)
|
|
||||||
|
|
||||||
// Tracestate represents tracing-system specific context in a list of key-value pairs. Tracestate allows different
|
|
||||||
// vendors propagate additional information and inter-operate with their legacy Id formats.
|
|
||||||
type Tracestate struct {
|
|
||||||
entries []Entry
|
|
||||||
}
|
|
||||||
|
|
||||||
// Entry represents one key-value pair in a list of key-value pair of Tracestate.
|
|
||||||
type Entry struct {
|
|
||||||
// Key is an opaque string up to 256 characters printable. It MUST begin with a lowercase letter,
|
|
||||||
// and can only contain lowercase letters a-z, digits 0-9, underscores _, dashes -, asterisks *, and
|
|
||||||
// forward slashes /.
|
|
||||||
Key string
|
|
||||||
|
|
||||||
// Value is an opaque string up to 256 characters printable ASCII RFC0020 characters (i.e., the
|
|
||||||
// range 0x20 to 0x7E) except comma , and =.
|
|
||||||
Value string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Entries returns a slice of Entry.
|
|
||||||
func (ts *Tracestate) Entries() []Entry {
|
|
||||||
if ts == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return ts.entries
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ts *Tracestate) remove(key string) *Entry {
|
|
||||||
for index, entry := range ts.entries {
|
|
||||||
if entry.Key == key {
|
|
||||||
ts.entries = append(ts.entries[:index], ts.entries[index+1:]...)
|
|
||||||
return &entry
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ts *Tracestate) add(entries []Entry) error {
|
|
||||||
for _, entry := range entries {
|
|
||||||
ts.remove(entry.Key)
|
|
||||||
}
|
|
||||||
if len(ts.entries)+len(entries) > maxKeyValuePairs {
|
|
||||||
return fmt.Errorf("adding %d key-value pairs to current %d pairs exceeds the limit of %d",
|
|
||||||
len(entries), len(ts.entries), maxKeyValuePairs)
|
|
||||||
}
|
|
||||||
ts.entries = append(entries, ts.entries...)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func isValid(entry Entry) bool {
|
|
||||||
return keyValidationRegExp.MatchString(entry.Key) &&
|
|
||||||
valueValidationRegExp.MatchString(entry.Value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func containsDuplicateKey(entries ...Entry) (string, bool) {
|
|
||||||
keyMap := make(map[string]int)
|
|
||||||
for _, entry := range entries {
|
|
||||||
if _, ok := keyMap[entry.Key]; ok {
|
|
||||||
return entry.Key, true
|
|
||||||
}
|
|
||||||
keyMap[entry.Key] = 1
|
|
||||||
}
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
func areEntriesValid(entries ...Entry) (*Entry, bool) {
|
|
||||||
for _, entry := range entries {
|
|
||||||
if !isValid(entry) {
|
|
||||||
return &entry, false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// New creates a Tracestate object from a parent and/or entries (key-value pair).
|
|
||||||
// Entries from the parent are copied if present. The entries passed to this function
|
|
||||||
// are inserted in front of those copied from the parent. If an entry copied from the
|
|
||||||
// parent contains the same key as one of the entry in entries then the entry copied
|
|
||||||
// from the parent is removed. See add func.
|
|
||||||
//
|
|
||||||
// An error is returned with nil Tracestate if
|
|
||||||
// 1. one or more entry in entries is invalid.
|
|
||||||
// 2. two or more entries in the input entries have the same key.
|
|
||||||
// 3. the number of entries combined from the parent and the input entries exceeds maxKeyValuePairs.
|
|
||||||
// (duplicate entry is counted only once).
|
|
||||||
func New(parent *Tracestate, entries ...Entry) (*Tracestate, error) {
|
|
||||||
if parent == nil && len(entries) == 0 {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
if entry, ok := areEntriesValid(entries...); !ok {
|
|
||||||
return nil, fmt.Errorf("key-value pair {%s, %s} is invalid", entry.Key, entry.Value)
|
|
||||||
}
|
|
||||||
|
|
||||||
if key, duplicate := containsDuplicateKey(entries...); duplicate {
|
|
||||||
return nil, fmt.Errorf("contains duplicate keys (%s)", key)
|
|
||||||
}
|
|
||||||
|
|
||||||
tracestate := Tracestate{}
|
|
||||||
|
|
||||||
if parent != nil && len(parent.entries) > 0 {
|
|
||||||
tracestate.entries = append([]Entry{}, parent.entries...)
|
|
||||||
}
|
|
||||||
|
|
||||||
err := tracestate.add(entries)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &tracestate, nil
|
|
||||||
}
|
|
|
@ -333,7 +333,6 @@ func ReadEntity(packets *packet.Reader) (*Entity, error) {
|
||||||
return nil, errors.StructuralError("primary key cannot be used for signatures")
|
return nil, errors.StructuralError("primary key cannot be used for signatures")
|
||||||
}
|
}
|
||||||
|
|
||||||
var current *Identity
|
|
||||||
var revocations []*packet.Signature
|
var revocations []*packet.Signature
|
||||||
EachPacket:
|
EachPacket:
|
||||||
for {
|
for {
|
||||||
|
@ -349,7 +348,7 @@ EachPacket:
|
||||||
// Make a new Identity object, that we might wind up throwing away.
|
// Make a new Identity object, that we might wind up throwing away.
|
||||||
// We'll only add it if we get a valid self-signature over this
|
// We'll only add it if we get a valid self-signature over this
|
||||||
// userID.
|
// userID.
|
||||||
current = new(Identity)
|
current := new(Identity)
|
||||||
current.Name = pkt.Id
|
current.Name = pkt.Id
|
||||||
current.UserId = pkt
|
current.UserId = pkt
|
||||||
|
|
||||||
|
@ -384,11 +383,9 @@ EachPacket:
|
||||||
// TODO: RFC4880 5.2.1 permits signatures
|
// TODO: RFC4880 5.2.1 permits signatures
|
||||||
// directly on keys (eg. to bind additional
|
// directly on keys (eg. to bind additional
|
||||||
// revocation keys).
|
// revocation keys).
|
||||||
} else if current == nil {
|
|
||||||
return nil, errors.StructuralError("signature packet found before user id packet")
|
|
||||||
} else {
|
|
||||||
current.Signatures = append(current.Signatures, pkt)
|
|
||||||
}
|
}
|
||||||
|
// Else, ignoring the signature as it does not follow anything
|
||||||
|
// we would know to attach it to.
|
||||||
case *packet.PrivateKey:
|
case *packet.PrivateKey:
|
||||||
if pkt.IsSubkey == false {
|
if pkt.IsSubkey == false {
|
||||||
packets.Unread(p)
|
packets.Unread(p)
|
||||||
|
@ -433,26 +430,45 @@ func addSubkey(e *Entity, packets *packet.Reader, pub *packet.PublicKey, priv *p
|
||||||
var subKey Subkey
|
var subKey Subkey
|
||||||
subKey.PublicKey = pub
|
subKey.PublicKey = pub
|
||||||
subKey.PrivateKey = priv
|
subKey.PrivateKey = priv
|
||||||
p, err := packets.Next()
|
|
||||||
if err == io.EOF {
|
for {
|
||||||
return io.ErrUnexpectedEOF
|
p, err := packets.Next()
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
} else if err != nil {
|
||||||
|
return errors.StructuralError("subkey signature invalid: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
sig, ok := p.(*packet.Signature)
|
||||||
|
if !ok {
|
||||||
|
packets.Unread(p)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if sig.SigType != packet.SigTypeSubkeyBinding && sig.SigType != packet.SigTypeSubkeyRevocation {
|
||||||
|
return errors.StructuralError("subkey signature with wrong type")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := e.PrimaryKey.VerifyKeySignature(subKey.PublicKey, sig); err != nil {
|
||||||
|
return errors.StructuralError("subkey signature invalid: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
switch sig.SigType {
|
||||||
|
case packet.SigTypeSubkeyRevocation:
|
||||||
|
subKey.Sig = sig
|
||||||
|
case packet.SigTypeSubkeyBinding:
|
||||||
|
if subKey.Sig == nil {
|
||||||
|
subKey.Sig = sig
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if err != nil {
|
|
||||||
return errors.StructuralError("subkey signature invalid: " + err.Error())
|
if subKey.Sig == nil {
|
||||||
}
|
|
||||||
var ok bool
|
|
||||||
subKey.Sig, ok = p.(*packet.Signature)
|
|
||||||
if !ok {
|
|
||||||
return errors.StructuralError("subkey packet not followed by signature")
|
return errors.StructuralError("subkey packet not followed by signature")
|
||||||
}
|
}
|
||||||
if subKey.Sig.SigType != packet.SigTypeSubkeyBinding && subKey.Sig.SigType != packet.SigTypeSubkeyRevocation {
|
|
||||||
return errors.StructuralError("subkey signature with wrong type")
|
|
||||||
}
|
|
||||||
err = e.PrimaryKey.VerifyKeySignature(subKey.PublicKey, subKey.Sig)
|
|
||||||
if err != nil {
|
|
||||||
return errors.StructuralError("subkey signature invalid: " + err.Error())
|
|
||||||
}
|
|
||||||
e.Subkeys = append(e.Subkeys, subKey)
|
e.Subkeys = append(e.Subkeys, subKey)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -903,8 +903,8 @@ func ParseDSAPrivateKey(der []byte) (*dsa.PrivateKey, error) {
|
||||||
// Implemented based on the documentation at
|
// Implemented based on the documentation at
|
||||||
// https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key
|
// https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key
|
||||||
func parseOpenSSHPrivateKey(key []byte) (crypto.PrivateKey, error) {
|
func parseOpenSSHPrivateKey(key []byte) (crypto.PrivateKey, error) {
|
||||||
magic := append([]byte("openssh-key-v1"), 0)
|
const magic = "openssh-key-v1\x00"
|
||||||
if !bytes.Equal(magic, key[0:len(magic)]) {
|
if len(key) < len(magic) || string(key[:len(magic)]) != magic {
|
||||||
return nil, errors.New("ssh: invalid openssh private key format")
|
return nil, errors.New("ssh: invalid openssh private key format")
|
||||||
}
|
}
|
||||||
remaining := key[len(magic):]
|
remaining := key[len(magic):]
|
||||||
|
|
|
@ -97,8 +97,16 @@ func isSpecialElement(element *Node) bool {
|
||||||
switch element.Namespace {
|
switch element.Namespace {
|
||||||
case "", "html":
|
case "", "html":
|
||||||
return isSpecialElementMap[element.Data]
|
return isSpecialElementMap[element.Data]
|
||||||
|
case "math":
|
||||||
|
switch element.Data {
|
||||||
|
case "mi", "mo", "mn", "ms", "mtext", "annotation-xml":
|
||||||
|
return true
|
||||||
|
}
|
||||||
case "svg":
|
case "svg":
|
||||||
return element.Data == "foreignObject"
|
switch element.Data {
|
||||||
|
case "foreignObject", "desc", "title":
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
@ -470,6 +470,10 @@ func (p *parser) resetInsertionMode() {
|
||||||
case a.Table:
|
case a.Table:
|
||||||
p.im = inTableIM
|
p.im = inTableIM
|
||||||
case a.Template:
|
case a.Template:
|
||||||
|
// TODO: remove this divergence from the HTML5 spec.
|
||||||
|
if n.Namespace != "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
p.im = p.templateStack.top()
|
p.im = p.templateStack.top()
|
||||||
case a.Head:
|
case a.Head:
|
||||||
// TODO: remove this divergence from the HTML5 spec.
|
// TODO: remove this divergence from the HTML5 spec.
|
||||||
|
@ -984,6 +988,14 @@ func inBodyIM(p *parser) bool {
|
||||||
p.acknowledgeSelfClosingTag()
|
p.acknowledgeSelfClosingTag()
|
||||||
p.popUntil(buttonScope, a.P)
|
p.popUntil(buttonScope, a.P)
|
||||||
p.parseImpliedToken(StartTagToken, a.Form, a.Form.String())
|
p.parseImpliedToken(StartTagToken, a.Form, a.Form.String())
|
||||||
|
if p.form == nil {
|
||||||
|
// NOTE: The 'isindex' element has been removed,
|
||||||
|
// and the 'template' element has not been designed to be
|
||||||
|
// collaborative with the index element.
|
||||||
|
//
|
||||||
|
// Ignore the token.
|
||||||
|
return true
|
||||||
|
}
|
||||||
if action != "" {
|
if action != "" {
|
||||||
p.form.Attr = []Attribute{{Key: "action", Val: action}}
|
p.form.Attr = []Attribute{{Key: "action", Val: action}}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,15 +4,16 @@ Go is an open source project.
|
||||||
|
|
||||||
It is the work of hundreds of contributors. We appreciate your help!
|
It is the work of hundreds of contributors. We appreciate your help!
|
||||||
|
|
||||||
|
|
||||||
## Filing issues
|
## Filing issues
|
||||||
|
|
||||||
When [filing an issue](https://github.com/golang/oauth2/issues), make sure to answer these five questions:
|
When [filing an issue](https://github.com/golang/oauth2/issues), make sure to answer these five questions:
|
||||||
|
|
||||||
1. What version of Go are you using (`go version`)?
|
1. What version of Go are you using (`go version`)?
|
||||||
2. What operating system and processor architecture are you using?
|
2. What operating system and processor architecture are you using?
|
||||||
3. What did you do?
|
3. What did you do?
|
||||||
4. What did you expect to see?
|
4. What did you expect to see?
|
||||||
5. What did you see instead?
|
5. What did you see instead?
|
||||||
|
|
||||||
General questions should go to the [golang-nuts mailing list](https://groups.google.com/group/golang-nuts) instead of the issue tracker.
|
General questions should go to the [golang-nuts mailing list](https://groups.google.com/group/golang-nuts) instead of the issue tracker.
|
||||||
The gophers there will answer or ask you to file an issue if you've tripped over a bug.
|
The gophers there will answer or ask you to file an issue if you've tripped over a bug.
|
||||||
|
@ -22,5 +23,9 @@ The gophers there will answer or ask you to file an issue if you've tripped over
|
||||||
Please read the [Contribution Guidelines](https://golang.org/doc/contribute.html)
|
Please read the [Contribution Guidelines](https://golang.org/doc/contribute.html)
|
||||||
before sending patches.
|
before sending patches.
|
||||||
|
|
||||||
|
**We do not accept GitHub pull requests**
|
||||||
|
(we use [Gerrit](https://code.google.com/p/gerrit/) instead for code review).
|
||||||
|
|
||||||
Unless otherwise noted, the Go source files are distributed under
|
Unless otherwise noted, the Go source files are distributed under
|
||||||
the BSD-style license found in the LICENSE file.
|
the BSD-style license found in the LICENSE file.
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build appengine
|
||||||
|
|
||||||
|
// App Engine hooks.
|
||||||
|
|
||||||
|
package oauth2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
"golang.org/x/oauth2/internal"
|
||||||
|
"google.golang.org/appengine/urlfetch"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
internal.RegisterContextClientFunc(contextClientAppEngine)
|
||||||
|
}
|
||||||
|
|
||||||
|
func contextClientAppEngine(ctx context.Context) (*http.Client, error) {
|
||||||
|
return urlfetch.Client(ctx), nil
|
||||||
|
}
|
|
@ -18,6 +18,20 @@ import (
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// DefaultCredentials holds "Application Default Credentials".
|
||||||
|
// For more details, see:
|
||||||
|
// https://developers.google.com/accounts/docs/application-default-credentials
|
||||||
|
type DefaultCredentials struct {
|
||||||
|
ProjectID string // may be empty
|
||||||
|
TokenSource oauth2.TokenSource
|
||||||
|
|
||||||
|
// JSON contains the raw bytes from a JSON credentials file.
|
||||||
|
// This field may be nil if authentication is provided by the
|
||||||
|
// environment and not with a credentials file, e.g. when code is
|
||||||
|
// running on Google Cloud Platform.
|
||||||
|
JSON []byte
|
||||||
|
}
|
||||||
|
|
||||||
// DefaultClient returns an HTTP Client that uses the
|
// DefaultClient returns an HTTP Client that uses the
|
||||||
// DefaultTokenSource to obtain authentication credentials.
|
// DefaultTokenSource to obtain authentication credentials.
|
||||||
func DefaultClient(ctx context.Context, scope ...string) (*http.Client, error) {
|
func DefaultClient(ctx context.Context, scope ...string) (*http.Client, error) {
|
||||||
|
@ -39,12 +53,25 @@ func DefaultTokenSource(ctx context.Context, scope ...string) (oauth2.TokenSourc
|
||||||
return creds.TokenSource, nil
|
return creds.TokenSource, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Common implementation for FindDefaultCredentials.
|
// FindDefaultCredentials searches for "Application Default Credentials".
|
||||||
func findDefaultCredentials(ctx context.Context, scopes []string) (*DefaultCredentials, error) {
|
//
|
||||||
|
// It looks for credentials in the following places,
|
||||||
|
// preferring the first location found:
|
||||||
|
//
|
||||||
|
// 1. A JSON file whose path is specified by the
|
||||||
|
// GOOGLE_APPLICATION_CREDENTIALS environment variable.
|
||||||
|
// 2. A JSON file in a location known to the gcloud command-line tool.
|
||||||
|
// On Windows, this is %APPDATA%/gcloud/application_default_credentials.json.
|
||||||
|
// On other systems, $HOME/.config/gcloud/application_default_credentials.json.
|
||||||
|
// 3. On Google App Engine it uses the appengine.AccessToken function.
|
||||||
|
// 4. On Google Compute Engine and Google App Engine Managed VMs, it fetches
|
||||||
|
// credentials from the metadata server.
|
||||||
|
// (In this final case any provided scopes are ignored.)
|
||||||
|
func FindDefaultCredentials(ctx context.Context, scope ...string) (*DefaultCredentials, error) {
|
||||||
// First, try the environment variable.
|
// First, try the environment variable.
|
||||||
const envVar = "GOOGLE_APPLICATION_CREDENTIALS"
|
const envVar = "GOOGLE_APPLICATION_CREDENTIALS"
|
||||||
if filename := os.Getenv(envVar); filename != "" {
|
if filename := os.Getenv(envVar); filename != "" {
|
||||||
creds, err := readCredentialsFile(ctx, filename, scopes)
|
creds, err := readCredentialsFile(ctx, filename, scope)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("google: error getting credentials using %v environment variable: %v", envVar, err)
|
return nil, fmt.Errorf("google: error getting credentials using %v environment variable: %v", envVar, err)
|
||||||
}
|
}
|
||||||
|
@ -53,7 +80,7 @@ func findDefaultCredentials(ctx context.Context, scopes []string) (*DefaultCrede
|
||||||
|
|
||||||
// Second, try a well-known file.
|
// Second, try a well-known file.
|
||||||
filename := wellKnownFile()
|
filename := wellKnownFile()
|
||||||
if creds, err := readCredentialsFile(ctx, filename, scopes); err == nil {
|
if creds, err := readCredentialsFile(ctx, filename, scope); err == nil {
|
||||||
return creds, nil
|
return creds, nil
|
||||||
} else if !os.IsNotExist(err) {
|
} else if !os.IsNotExist(err) {
|
||||||
return nil, fmt.Errorf("google: error getting credentials using well-known file (%v): %v", filename, err)
|
return nil, fmt.Errorf("google: error getting credentials using well-known file (%v): %v", filename, err)
|
||||||
|
@ -63,7 +90,7 @@ func findDefaultCredentials(ctx context.Context, scopes []string) (*DefaultCrede
|
||||||
if appengineTokenFunc != nil && !appengineFlex {
|
if appengineTokenFunc != nil && !appengineFlex {
|
||||||
return &DefaultCredentials{
|
return &DefaultCredentials{
|
||||||
ProjectID: appengineAppIDFunc(ctx),
|
ProjectID: appengineAppIDFunc(ctx),
|
||||||
TokenSource: AppEngineTokenSource(ctx, scopes...),
|
TokenSource: AppEngineTokenSource(ctx, scope...),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,23 +108,6 @@ func findDefaultCredentials(ctx context.Context, scopes []string) (*DefaultCrede
|
||||||
return nil, fmt.Errorf("google: could not find default credentials. See %v for more information.", url)
|
return nil, fmt.Errorf("google: could not find default credentials. See %v for more information.", url)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Common implementation for CredentialsFromJSON.
|
|
||||||
func credentialsFromJSON(ctx context.Context, jsonData []byte, scopes []string) (*DefaultCredentials, error) {
|
|
||||||
var f credentialsFile
|
|
||||||
if err := json.Unmarshal(jsonData, &f); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ts, err := f.tokenSource(ctx, append([]string(nil), scopes...))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &DefaultCredentials{
|
|
||||||
ProjectID: f.ProjectID,
|
|
||||||
TokenSource: ts,
|
|
||||||
JSON: jsonData,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func wellKnownFile() string {
|
func wellKnownFile() string {
|
||||||
const f = "application_default_credentials.json"
|
const f = "application_default_credentials.json"
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
|
@ -111,5 +121,17 @@ func readCredentialsFile(ctx context.Context, filename string, scopes []string)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return CredentialsFromJSON(ctx, b, scopes...)
|
var f credentialsFile
|
||||||
|
if err := json.Unmarshal(b, &f); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ts, err := f.tokenSource(ctx, append([]string(nil), scopes...))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &DefaultCredentials{
|
||||||
|
ProjectID: f.ProjectID,
|
||||||
|
TokenSource: ts,
|
||||||
|
JSON: b,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,42 +0,0 @@
|
||||||
// Copyright 2018 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// +build go1.9
|
|
||||||
|
|
||||||
// Package google provides support for making OAuth2 authorized and authenticated
|
|
||||||
// HTTP requests to Google APIs. It supports the Web server flow, client-side
|
|
||||||
// credentials, service accounts, Google Compute Engine service accounts, and Google
|
|
||||||
// App Engine service accounts.
|
|
||||||
//
|
|
||||||
// A brief overview of the package follows. For more information, please read
|
|
||||||
// https://developers.google.com/accounts/docs/OAuth2
|
|
||||||
// and
|
|
||||||
// https://developers.google.com/accounts/docs/application-default-credentials.
|
|
||||||
//
|
|
||||||
// OAuth2 Configs
|
|
||||||
//
|
|
||||||
// Two functions in this package return golang.org/x/oauth2.Config values from Google credential
|
|
||||||
// data. Google supports two JSON formats for OAuth2 credentials: one is handled by ConfigFromJSON,
|
|
||||||
// the other by JWTConfigFromJSON. The returned Config can be used to obtain a TokenSource or
|
|
||||||
// create an http.Client.
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// Credentials
|
|
||||||
//
|
|
||||||
// The Credentials type represents Google credentials, including Application Default
|
|
||||||
// Credentials.
|
|
||||||
//
|
|
||||||
// Use FindDefaultCredentials to obtain Application Default Credentials.
|
|
||||||
// FindDefaultCredentials looks in some well-known places for a credentials file, and
|
|
||||||
// will call AppEngineTokenSource or ComputeTokenSource as needed.
|
|
||||||
//
|
|
||||||
// DefaultClient and DefaultTokenSource are convenience methods. They first call FindDefaultCredentials,
|
|
||||||
// then use the credentials to construct an http.Client or an oauth2.TokenSource.
|
|
||||||
//
|
|
||||||
// Use CredentialsFromJSON to obtain credentials from either of the two JSON formats
|
|
||||||
// described in OAuth2 Configs, above. The TokenSource in the returned value is the
|
|
||||||
// same as the one obtained from the oauth2.Config returned from ConfigFromJSON or
|
|
||||||
// JWTConfigFromJSON, but the Credentials may contain additional information
|
|
||||||
// that is useful is some circumstances.
|
|
||||||
package google // import "golang.org/x/oauth2/google"
|
|
|
@ -1,43 +0,0 @@
|
||||||
// Copyright 2018 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// +build !go1.9
|
|
||||||
|
|
||||||
// Package google provides support for making OAuth2 authorized and authenticated
|
|
||||||
// HTTP requests to Google APIs. It supports the Web server flow, client-side
|
|
||||||
// credentials, service accounts, Google Compute Engine service accounts, and Google
|
|
||||||
// App Engine service accounts.
|
|
||||||
//
|
|
||||||
// A brief overview of the package follows. For more information, please read
|
|
||||||
// https://developers.google.com/accounts/docs/OAuth2
|
|
||||||
// and
|
|
||||||
// https://developers.google.com/accounts/docs/application-default-credentials.
|
|
||||||
//
|
|
||||||
// OAuth2 Configs
|
|
||||||
//
|
|
||||||
// Two functions in this package return golang.org/x/oauth2.Config values from Google credential
|
|
||||||
// data. Google supports two JSON formats for OAuth2 credentials: one is handled by ConfigFromJSON,
|
|
||||||
// the other by JWTConfigFromJSON. The returned Config can be used to obtain a TokenSource or
|
|
||||||
// create an http.Client.
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// Credentials
|
|
||||||
//
|
|
||||||
// The DefaultCredentials type represents Google Application Default Credentials, as
|
|
||||||
// well as other forms of credential.
|
|
||||||
//
|
|
||||||
// Use FindDefaultCredentials to obtain Application Default Credentials.
|
|
||||||
// FindDefaultCredentials looks in some well-known places for a credentials file, and
|
|
||||||
// will call AppEngineTokenSource or ComputeTokenSource as needed.
|
|
||||||
//
|
|
||||||
// DefaultClient and DefaultTokenSource are convenience methods. They first call FindDefaultCredentials,
|
|
||||||
// then use the credentials to construct an http.Client or an oauth2.TokenSource.
|
|
||||||
//
|
|
||||||
// Use CredentialsFromJSON to obtain credentials from either of the two JSON
|
|
||||||
// formats described in OAuth2 Configs, above. (The DefaultCredentials returned may
|
|
||||||
// not be "Application Default Credentials".) The TokenSource in the returned value
|
|
||||||
// is the same as the one obtained from the oauth2.Config returned from
|
|
||||||
// ConfigFromJSON or JWTConfigFromJSON, but the DefaultCredentials may contain
|
|
||||||
// additional information that is useful is some circumstances.
|
|
||||||
package google // import "golang.org/x/oauth2/google"
|
|
|
@ -1,57 +0,0 @@
|
||||||
// Copyright 2018 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// +build go1.9
|
|
||||||
|
|
||||||
package google
|
|
||||||
|
|
||||||
import (
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
"golang.org/x/oauth2"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Credentials holds Google credentials, including "Application Default Credentials".
|
|
||||||
// For more details, see:
|
|
||||||
// https://developers.google.com/accounts/docs/application-default-credentials
|
|
||||||
type Credentials struct {
|
|
||||||
ProjectID string // may be empty
|
|
||||||
TokenSource oauth2.TokenSource
|
|
||||||
|
|
||||||
// JSON contains the raw bytes from a JSON credentials file.
|
|
||||||
// This field may be nil if authentication is provided by the
|
|
||||||
// environment and not with a credentials file, e.g. when code is
|
|
||||||
// running on Google Cloud Platform.
|
|
||||||
JSON []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultCredentials is the old name of Credentials.
|
|
||||||
//
|
|
||||||
// Deprecated: use Credentials instead.
|
|
||||||
type DefaultCredentials = Credentials
|
|
||||||
|
|
||||||
// FindDefaultCredentials searches for "Application Default Credentials".
|
|
||||||
//
|
|
||||||
// It looks for credentials in the following places,
|
|
||||||
// preferring the first location found:
|
|
||||||
//
|
|
||||||
// 1. A JSON file whose path is specified by the
|
|
||||||
// GOOGLE_APPLICATION_CREDENTIALS environment variable.
|
|
||||||
// 2. A JSON file in a location known to the gcloud command-line tool.
|
|
||||||
// On Windows, this is %APPDATA%/gcloud/application_default_credentials.json.
|
|
||||||
// On other systems, $HOME/.config/gcloud/application_default_credentials.json.
|
|
||||||
// 3. On Google App Engine it uses the appengine.AccessToken function.
|
|
||||||
// 4. On Google Compute Engine and Google App Engine Managed VMs, it fetches
|
|
||||||
// credentials from the metadata server.
|
|
||||||
// (In this final case any provided scopes are ignored.)
|
|
||||||
func FindDefaultCredentials(ctx context.Context, scopes ...string) (*Credentials, error) {
|
|
||||||
return findDefaultCredentials(ctx, scopes)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CredentialsFromJSON obtains Google credentials from a JSON value. The JSON can
|
|
||||||
// represent either a Google Developers Console client_credentials.json file (as in
|
|
||||||
// ConfigFromJSON) or a Google Developers service account key file (as in
|
|
||||||
// JWTConfigFromJSON).
|
|
||||||
func CredentialsFromJSON(ctx context.Context, jsonData []byte, scopes ...string) (*Credentials, error) {
|
|
||||||
return credentialsFromJSON(ctx, jsonData, scopes)
|
|
||||||
}
|
|
|
@ -2,7 +2,17 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package google
|
// Package google provides support for making OAuth2 authorized and
|
||||||
|
// authenticated HTTP requests to Google APIs.
|
||||||
|
// It supports the Web server flow, client-side credentials, service accounts,
|
||||||
|
// Google Compute Engine service accounts, and Google App Engine service
|
||||||
|
// accounts.
|
||||||
|
//
|
||||||
|
// For more information, please read
|
||||||
|
// https://developers.google.com/accounts/docs/OAuth2
|
||||||
|
// and
|
||||||
|
// https://developers.google.com/accounts/docs/application-default-credentials.
|
||||||
|
package google // import "golang.org/x/oauth2/google"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
|
@ -1,54 +0,0 @@
|
||||||
// Copyright 2018 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// +build !go1.9
|
|
||||||
|
|
||||||
package google
|
|
||||||
|
|
||||||
import (
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
"golang.org/x/oauth2"
|
|
||||||
)
|
|
||||||
|
|
||||||
// DefaultCredentials holds Google credentials, including "Application Default Credentials".
|
|
||||||
// For more details, see:
|
|
||||||
// https://developers.google.com/accounts/docs/application-default-credentials
|
|
||||||
type DefaultCredentials struct {
|
|
||||||
ProjectID string // may be empty
|
|
||||||
TokenSource oauth2.TokenSource
|
|
||||||
|
|
||||||
// JSON contains the raw bytes from a JSON credentials file.
|
|
||||||
// This field may be nil if authentication is provided by the
|
|
||||||
// environment and not with a credentials file, e.g. when code is
|
|
||||||
// running on Google Cloud Platform.
|
|
||||||
JSON []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// FindDefaultCredentials searches for "Application Default Credentials".
|
|
||||||
//
|
|
||||||
// It looks for credentials in the following places,
|
|
||||||
// preferring the first location found:
|
|
||||||
//
|
|
||||||
// 1. A JSON file whose path is specified by the
|
|
||||||
// GOOGLE_APPLICATION_CREDENTIALS environment variable.
|
|
||||||
// 2. A JSON file in a location known to the gcloud command-line tool.
|
|
||||||
// On Windows, this is %APPDATA%/gcloud/application_default_credentials.json.
|
|
||||||
// On other systems, $HOME/.config/gcloud/application_default_credentials.json.
|
|
||||||
// 3. On Google App Engine it uses the appengine.AccessToken function.
|
|
||||||
// 4. On Google Compute Engine and Google App Engine Managed VMs, it fetches
|
|
||||||
// credentials from the metadata server.
|
|
||||||
// (In this final case any provided scopes are ignored.)
|
|
||||||
func FindDefaultCredentials(ctx context.Context, scopes ...string) (*DefaultCredentials, error) {
|
|
||||||
return findDefaultCredentials(ctx, scopes)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CredentialsFromJSON obtains Google credentials from a JSON value. The JSON can
|
|
||||||
// represent either a Google Developers Console client_credentials.json file (as in
|
|
||||||
// ConfigFromJSON) or a Google Developers service account key file (as in
|
|
||||||
// JWTConfigFromJSON).
|
|
||||||
//
|
|
||||||
// Note: despite the name, the returned credentials may not be Application Default Credentials.
|
|
||||||
func CredentialsFromJSON(ctx context.Context, jsonData []byte, scopes ...string) (*DefaultCredentials, error) {
|
|
||||||
return credentialsFromJSON(ctx, jsonData, scopes)
|
|
||||||
}
|
|
|
@ -5,11 +5,9 @@
|
||||||
package google
|
package google
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/user"
|
"os/user"
|
||||||
|
@ -20,6 +18,7 @@ import (
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
|
"golang.org/x/oauth2/internal"
|
||||||
)
|
)
|
||||||
|
|
||||||
type sdkCredentials struct {
|
type sdkCredentials struct {
|
||||||
|
@ -77,7 +76,7 @@ func NewSDKConfig(account string) (*SDKConfig, error) {
|
||||||
return nil, fmt.Errorf("oauth2/google: failed to load SDK properties: %v", err)
|
return nil, fmt.Errorf("oauth2/google: failed to load SDK properties: %v", err)
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
ini, err := parseINI(f)
|
ini, err := internal.ParseINI(f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("oauth2/google: failed to parse SDK properties %q: %v", propertiesPath, err)
|
return nil, fmt.Errorf("oauth2/google: failed to parse SDK properties %q: %v", propertiesPath, err)
|
||||||
}
|
}
|
||||||
|
@ -147,34 +146,6 @@ func (c *SDKConfig) Scopes() []string {
|
||||||
return c.conf.Scopes
|
return c.conf.Scopes
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseINI(ini io.Reader) (map[string]map[string]string, error) {
|
|
||||||
result := map[string]map[string]string{
|
|
||||||
"": {}, // root section
|
|
||||||
}
|
|
||||||
scanner := bufio.NewScanner(ini)
|
|
||||||
currentSection := ""
|
|
||||||
for scanner.Scan() {
|
|
||||||
line := strings.TrimSpace(scanner.Text())
|
|
||||||
if strings.HasPrefix(line, ";") {
|
|
||||||
// comment.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if strings.HasPrefix(line, "[") && strings.HasSuffix(line, "]") {
|
|
||||||
currentSection = strings.TrimSpace(line[1 : len(line)-1])
|
|
||||||
result[currentSection] = map[string]string{}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
parts := strings.SplitN(line, "=", 2)
|
|
||||||
if len(parts) == 2 && parts[0] != "" {
|
|
||||||
result[currentSection][strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := scanner.Err(); err != nil {
|
|
||||||
return nil, fmt.Errorf("error scanning ini: %v", err)
|
|
||||||
}
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// sdkConfigPath tries to guess where the gcloud config is located.
|
// sdkConfigPath tries to guess where the gcloud config is located.
|
||||||
// It can be overridden during tests.
|
// It can be overridden during tests.
|
||||||
var sdkConfigPath = func() (string, error) {
|
var sdkConfigPath = func() (string, error) {
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
// Copyright 2018 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// +build appengine
|
|
||||||
|
|
||||||
package internal
|
|
||||||
|
|
||||||
import "google.golang.org/appengine/urlfetch"
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
appengineClientHook = urlfetch.Client
|
|
||||||
}
|
|
|
@ -5,11 +5,14 @@
|
||||||
package internal
|
package internal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ParseKey converts the binary contents of a private key file
|
// ParseKey converts the binary contents of a private key file
|
||||||
|
@ -35,3 +38,38 @@ func ParseKey(key []byte) (*rsa.PrivateKey, error) {
|
||||||
}
|
}
|
||||||
return parsed, nil
|
return parsed, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ParseINI(ini io.Reader) (map[string]map[string]string, error) {
|
||||||
|
result := map[string]map[string]string{
|
||||||
|
"": {}, // root section
|
||||||
|
}
|
||||||
|
scanner := bufio.NewScanner(ini)
|
||||||
|
currentSection := ""
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := strings.TrimSpace(scanner.Text())
|
||||||
|
if strings.HasPrefix(line, ";") {
|
||||||
|
// comment.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(line, "[") && strings.HasSuffix(line, "]") {
|
||||||
|
currentSection = strings.TrimSpace(line[1 : len(line)-1])
|
||||||
|
result[currentSection] = map[string]string{}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
parts := strings.SplitN(line, "=", 2)
|
||||||
|
if len(parts) == 2 && parts[0] != "" {
|
||||||
|
result[currentSection][strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
return nil, fmt.Errorf("error scanning ini: %v", err)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CondVal(v string) []string {
|
||||||
|
if v == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return []string{v}
|
||||||
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ package internal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
@ -21,7 +20,7 @@ import (
|
||||||
"golang.org/x/net/context/ctxhttp"
|
"golang.org/x/net/context/ctxhttp"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Token represents the credentials used to authorize
|
// Token represents the crendentials used to authorize
|
||||||
// the requests to access protected resources on the OAuth 2.0
|
// the requests to access protected resources on the OAuth 2.0
|
||||||
// provider's backend.
|
// provider's backend.
|
||||||
//
|
//
|
||||||
|
@ -101,15 +100,12 @@ var brokenAuthHeaderProviders = []string{
|
||||||
"https://api.pushbullet.com/",
|
"https://api.pushbullet.com/",
|
||||||
"https://api.soundcloud.com/",
|
"https://api.soundcloud.com/",
|
||||||
"https://api.twitch.tv/",
|
"https://api.twitch.tv/",
|
||||||
"https://id.twitch.tv/",
|
|
||||||
"https://app.box.com/",
|
"https://app.box.com/",
|
||||||
"https://api.box.com/",
|
|
||||||
"https://connect.stripe.com/",
|
"https://connect.stripe.com/",
|
||||||
"https://login.mailchimp.com/",
|
"https://graph.facebook.com", // see https://github.com/golang/oauth2/issues/214
|
||||||
"https://login.microsoftonline.com/",
|
"https://login.microsoftonline.com/",
|
||||||
"https://login.salesforce.com/",
|
"https://login.salesforce.com/",
|
||||||
"https://login.windows.net",
|
"https://login.windows.net",
|
||||||
"https://login.live.com/",
|
|
||||||
"https://oauth.sandbox.trainingpeaks.com/",
|
"https://oauth.sandbox.trainingpeaks.com/",
|
||||||
"https://oauth.trainingpeaks.com/",
|
"https://oauth.trainingpeaks.com/",
|
||||||
"https://oauth.vk.com/",
|
"https://oauth.vk.com/",
|
||||||
|
@ -126,17 +122,10 @@ var brokenAuthHeaderProviders = []string{
|
||||||
"https://api.patreon.com/",
|
"https://api.patreon.com/",
|
||||||
"https://sandbox.codeswholesale.com/oauth/token",
|
"https://sandbox.codeswholesale.com/oauth/token",
|
||||||
"https://api.sipgate.com/v1/authorization/oauth",
|
"https://api.sipgate.com/v1/authorization/oauth",
|
||||||
"https://api.medium.com/v1/tokens",
|
|
||||||
"https://log.finalsurge.com/oauth/token",
|
|
||||||
"https://multisport.todaysplan.com.au/rest/oauth/access_token",
|
|
||||||
"https://whats.todaysplan.com.au/rest/oauth/access_token",
|
|
||||||
"https://stackoverflow.com/oauth/access_token",
|
|
||||||
"https://account.health.nokia.com",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// brokenAuthHeaderDomains lists broken providers that issue dynamic endpoints.
|
// brokenAuthHeaderDomains lists broken providers that issue dynamic endpoints.
|
||||||
var brokenAuthHeaderDomains = []string{
|
var brokenAuthHeaderDomains = []string{
|
||||||
".auth0.com",
|
|
||||||
".force.com",
|
".force.com",
|
||||||
".myshopify.com",
|
".myshopify.com",
|
||||||
".okta.com",
|
".okta.com",
|
||||||
|
@ -179,6 +168,10 @@ func providerAuthHeaderWorks(tokenURL string) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func RetrieveToken(ctx context.Context, clientID, clientSecret, tokenURL string, v url.Values) (*Token, error) {
|
func RetrieveToken(ctx context.Context, clientID, clientSecret, tokenURL string, v url.Values) (*Token, error) {
|
||||||
|
hc, err := ContextClient(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
bustedAuth := !providerAuthHeaderWorks(tokenURL)
|
bustedAuth := !providerAuthHeaderWorks(tokenURL)
|
||||||
if bustedAuth {
|
if bustedAuth {
|
||||||
if clientID != "" {
|
if clientID != "" {
|
||||||
|
@ -196,7 +189,7 @@ func RetrieveToken(ctx context.Context, clientID, clientSecret, tokenURL string,
|
||||||
if !bustedAuth {
|
if !bustedAuth {
|
||||||
req.SetBasicAuth(url.QueryEscape(clientID), url.QueryEscape(clientSecret))
|
req.SetBasicAuth(url.QueryEscape(clientID), url.QueryEscape(clientSecret))
|
||||||
}
|
}
|
||||||
r, err := ctxhttp.Do(ctx, ContextClient(ctx), req)
|
r, err := ctxhttp.Do(ctx, hc, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -206,10 +199,7 @@ func RetrieveToken(ctx context.Context, clientID, clientSecret, tokenURL string,
|
||||||
return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)
|
return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)
|
||||||
}
|
}
|
||||||
if code := r.StatusCode; code < 200 || code > 299 {
|
if code := r.StatusCode; code < 200 || code > 299 {
|
||||||
return nil, &RetrieveError{
|
return nil, fmt.Errorf("oauth2: cannot fetch token: %v\nResponse: %s", r.Status, body)
|
||||||
Response: r,
|
|
||||||
Body: body,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var token *Token
|
var token *Token
|
||||||
|
@ -256,17 +246,5 @@ func RetrieveToken(ctx context.Context, clientID, clientSecret, tokenURL string,
|
||||||
if token.RefreshToken == "" {
|
if token.RefreshToken == "" {
|
||||||
token.RefreshToken = v.Get("refresh_token")
|
token.RefreshToken = v.Get("refresh_token")
|
||||||
}
|
}
|
||||||
if token.AccessToken == "" {
|
|
||||||
return token, errors.New("oauth2: server response missing access_token")
|
|
||||||
}
|
|
||||||
return token, nil
|
return token, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type RetrieveError struct {
|
|
||||||
Response *http.Response
|
|
||||||
Body []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *RetrieveError) Error() string {
|
|
||||||
return fmt.Sprintf("oauth2: cannot fetch token: %v\nResponse: %s", r.Response.Status, r.Body)
|
|
||||||
}
|
|
||||||
|
|
|
@ -19,16 +19,50 @@ var HTTPClient ContextKey
|
||||||
// because nobody else can create a ContextKey, being unexported.
|
// because nobody else can create a ContextKey, being unexported.
|
||||||
type ContextKey struct{}
|
type ContextKey struct{}
|
||||||
|
|
||||||
var appengineClientHook func(context.Context) *http.Client
|
// ContextClientFunc is a func which tries to return an *http.Client
|
||||||
|
// given a Context value. If it returns an error, the search stops
|
||||||
|
// with that error. If it returns (nil, nil), the search continues
|
||||||
|
// down the list of registered funcs.
|
||||||
|
type ContextClientFunc func(context.Context) (*http.Client, error)
|
||||||
|
|
||||||
func ContextClient(ctx context.Context) *http.Client {
|
var contextClientFuncs []ContextClientFunc
|
||||||
|
|
||||||
|
func RegisterContextClientFunc(fn ContextClientFunc) {
|
||||||
|
contextClientFuncs = append(contextClientFuncs, fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ContextClient(ctx context.Context) (*http.Client, error) {
|
||||||
if ctx != nil {
|
if ctx != nil {
|
||||||
if hc, ok := ctx.Value(HTTPClient).(*http.Client); ok {
|
if hc, ok := ctx.Value(HTTPClient).(*http.Client); ok {
|
||||||
return hc
|
return hc, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if appengineClientHook != nil {
|
for _, fn := range contextClientFuncs {
|
||||||
return appengineClientHook(ctx)
|
c, err := fn(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if c != nil {
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return http.DefaultClient
|
return http.DefaultClient, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ContextTransport(ctx context.Context) http.RoundTripper {
|
||||||
|
hc, err := ContextClient(ctx)
|
||||||
|
// This is a rare error case (somebody using nil on App Engine).
|
||||||
|
if err != nil {
|
||||||
|
return ErrorTransport{err}
|
||||||
|
}
|
||||||
|
return hc.Transport
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrorTransport returns the specified error on RoundTrip.
|
||||||
|
// This RoundTripper should be used in rare error cases where
|
||||||
|
// error handling can be postponed to response handling time.
|
||||||
|
type ErrorTransport struct{ Err error }
|
||||||
|
|
||||||
|
func (t ErrorTransport) RoundTrip(*http.Request) (*http.Response, error) {
|
||||||
|
return nil, t.Err
|
||||||
}
|
}
|
||||||
|
|
|
@ -124,10 +124,7 @@ func (js jwtSource) Token() (*oauth2.Token, error) {
|
||||||
return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)
|
return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)
|
||||||
}
|
}
|
||||||
if c := resp.StatusCode; c < 200 || c > 299 {
|
if c := resp.StatusCode; c < 200 || c > 299 {
|
||||||
return nil, &oauth2.RetrieveError{
|
return nil, fmt.Errorf("oauth2: cannot fetch token: %v\nResponse: %s", resp.Status, body)
|
||||||
Response: resp,
|
|
||||||
Body: body,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// tokenRes is the JSON response body.
|
// tokenRes is the JSON response body.
|
||||||
var tokenRes struct {
|
var tokenRes struct {
|
||||||
|
|
|
@ -3,8 +3,7 @@
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// Package oauth2 provides support for making
|
// Package oauth2 provides support for making
|
||||||
// OAuth2 authorized and authenticated HTTP requests,
|
// OAuth2 authorized and authenticated HTTP requests.
|
||||||
// as specified in RFC 6749.
|
|
||||||
// It can additionally grant authorization with Bearer JWT.
|
// It can additionally grant authorization with Bearer JWT.
|
||||||
package oauth2 // import "golang.org/x/oauth2"
|
package oauth2 // import "golang.org/x/oauth2"
|
||||||
|
|
||||||
|
@ -118,30 +117,21 @@ func SetAuthURLParam(key, value string) AuthCodeOption {
|
||||||
// that asks for permissions for the required scopes explicitly.
|
// that asks for permissions for the required scopes explicitly.
|
||||||
//
|
//
|
||||||
// State is a token to protect the user from CSRF attacks. You must
|
// State is a token to protect the user from CSRF attacks. You must
|
||||||
// always provide a non-empty string and validate that it matches the
|
// always provide a non-zero string and validate that it matches the
|
||||||
// the state query parameter on your redirect callback.
|
// the state query parameter on your redirect callback.
|
||||||
// See http://tools.ietf.org/html/rfc6749#section-10.12 for more info.
|
// See http://tools.ietf.org/html/rfc6749#section-10.12 for more info.
|
||||||
//
|
//
|
||||||
// Opts may include AccessTypeOnline or AccessTypeOffline, as well
|
// Opts may include AccessTypeOnline or AccessTypeOffline, as well
|
||||||
// as ApprovalForce.
|
// as ApprovalForce.
|
||||||
// It can also be used to pass the PKCE challange.
|
|
||||||
// See https://www.oauth.com/oauth2-servers/pkce/ for more info.
|
|
||||||
func (c *Config) AuthCodeURL(state string, opts ...AuthCodeOption) string {
|
func (c *Config) AuthCodeURL(state string, opts ...AuthCodeOption) string {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
buf.WriteString(c.Endpoint.AuthURL)
|
buf.WriteString(c.Endpoint.AuthURL)
|
||||||
v := url.Values{
|
v := url.Values{
|
||||||
"response_type": {"code"},
|
"response_type": {"code"},
|
||||||
"client_id": {c.ClientID},
|
"client_id": {c.ClientID},
|
||||||
}
|
"redirect_uri": internal.CondVal(c.RedirectURL),
|
||||||
if c.RedirectURL != "" {
|
"scope": internal.CondVal(strings.Join(c.Scopes, " ")),
|
||||||
v.Set("redirect_uri", c.RedirectURL)
|
"state": internal.CondVal(state),
|
||||||
}
|
|
||||||
if len(c.Scopes) > 0 {
|
|
||||||
v.Set("scope", strings.Join(c.Scopes, " "))
|
|
||||||
}
|
|
||||||
if state != "" {
|
|
||||||
// TODO(light): Docs say never to omit state; don't allow empty.
|
|
||||||
v.Set("state", state)
|
|
||||||
}
|
}
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
opt.setValue(v)
|
opt.setValue(v)
|
||||||
|
@ -167,15 +157,12 @@ func (c *Config) AuthCodeURL(state string, opts ...AuthCodeOption) string {
|
||||||
// The HTTP client to use is derived from the context.
|
// The HTTP client to use is derived from the context.
|
||||||
// If nil, http.DefaultClient is used.
|
// If nil, http.DefaultClient is used.
|
||||||
func (c *Config) PasswordCredentialsToken(ctx context.Context, username, password string) (*Token, error) {
|
func (c *Config) PasswordCredentialsToken(ctx context.Context, username, password string) (*Token, error) {
|
||||||
v := url.Values{
|
return retrieveToken(ctx, c, url.Values{
|
||||||
"grant_type": {"password"},
|
"grant_type": {"password"},
|
||||||
"username": {username},
|
"username": {username},
|
||||||
"password": {password},
|
"password": {password},
|
||||||
}
|
"scope": internal.CondVal(strings.Join(c.Scopes, " ")),
|
||||||
if len(c.Scopes) > 0 {
|
})
|
||||||
v.Set("scope", strings.Join(c.Scopes, " "))
|
|
||||||
}
|
|
||||||
return retrieveToken(ctx, c, v)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exchange converts an authorization code into a token.
|
// Exchange converts an authorization code into a token.
|
||||||
|
@ -188,21 +175,12 @@ func (c *Config) PasswordCredentialsToken(ctx context.Context, username, passwor
|
||||||
//
|
//
|
||||||
// The code will be in the *http.Request.FormValue("code"). Before
|
// The code will be in the *http.Request.FormValue("code"). Before
|
||||||
// calling Exchange, be sure to validate FormValue("state").
|
// calling Exchange, be sure to validate FormValue("state").
|
||||||
//
|
func (c *Config) Exchange(ctx context.Context, code string) (*Token, error) {
|
||||||
// Opts may include the PKCE verifier code if previously used in AuthCodeURL.
|
return retrieveToken(ctx, c, url.Values{
|
||||||
// See https://www.oauth.com/oauth2-servers/pkce/ for more info.
|
"grant_type": {"authorization_code"},
|
||||||
func (c *Config) Exchange(ctx context.Context, code string, opts ...AuthCodeOption) (*Token, error) {
|
"code": {code},
|
||||||
v := url.Values{
|
"redirect_uri": internal.CondVal(c.RedirectURL),
|
||||||
"grant_type": {"authorization_code"},
|
})
|
||||||
"code": {code},
|
|
||||||
}
|
|
||||||
if c.RedirectURL != "" {
|
|
||||||
v.Set("redirect_uri", c.RedirectURL)
|
|
||||||
}
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt.setValue(v)
|
|
||||||
}
|
|
||||||
return retrieveToken(ctx, c, v)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Client returns an HTTP client using the provided token.
|
// Client returns an HTTP client using the provided token.
|
||||||
|
@ -322,11 +300,15 @@ var HTTPClient internal.ContextKey
|
||||||
// packages.
|
// packages.
|
||||||
func NewClient(ctx context.Context, src TokenSource) *http.Client {
|
func NewClient(ctx context.Context, src TokenSource) *http.Client {
|
||||||
if src == nil {
|
if src == nil {
|
||||||
return internal.ContextClient(ctx)
|
c, err := internal.ContextClient(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return &http.Client{Transport: internal.ErrorTransport{Err: err}}
|
||||||
|
}
|
||||||
|
return c
|
||||||
}
|
}
|
||||||
return &http.Client{
|
return &http.Client{
|
||||||
Transport: &Transport{
|
Transport: &Transport{
|
||||||
Base: internal.ContextClient(ctx).Transport,
|
Base: internal.ContextTransport(ctx),
|
||||||
Source: ReuseTokenSource(nil, src),
|
Source: ReuseTokenSource(nil, src),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
package oauth2
|
package oauth2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -21,7 +20,7 @@ import (
|
||||||
// expirations due to client-server time mismatches.
|
// expirations due to client-server time mismatches.
|
||||||
const expiryDelta = 10 * time.Second
|
const expiryDelta = 10 * time.Second
|
||||||
|
|
||||||
// Token represents the credentials used to authorize
|
// Token represents the crendentials used to authorize
|
||||||
// the requests to access protected resources on the OAuth 2.0
|
// the requests to access protected resources on the OAuth 2.0
|
||||||
// provider's backend.
|
// provider's backend.
|
||||||
//
|
//
|
||||||
|
@ -124,7 +123,7 @@ func (t *Token) expired() bool {
|
||||||
if t.Expiry.IsZero() {
|
if t.Expiry.IsZero() {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return t.Expiry.Round(0).Add(-expiryDelta).Before(time.Now())
|
return t.Expiry.Add(-expiryDelta).Before(time.Now())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Valid reports whether t is non-nil, has an AccessToken, and is not expired.
|
// Valid reports whether t is non-nil, has an AccessToken, and is not expired.
|
||||||
|
@ -153,23 +152,7 @@ func tokenFromInternal(t *internal.Token) *Token {
|
||||||
func retrieveToken(ctx context.Context, c *Config, v url.Values) (*Token, error) {
|
func retrieveToken(ctx context.Context, c *Config, v url.Values) (*Token, error) {
|
||||||
tk, err := internal.RetrieveToken(ctx, c.ClientID, c.ClientSecret, c.Endpoint.TokenURL, v)
|
tk, err := internal.RetrieveToken(ctx, c.ClientID, c.ClientSecret, c.Endpoint.TokenURL, v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if rErr, ok := err.(*internal.RetrieveError); ok {
|
|
||||||
return nil, (*RetrieveError)(rErr)
|
|
||||||
}
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return tokenFromInternal(tk), nil
|
return tokenFromInternal(tk), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RetrieveError is the error returned when the token endpoint returns a
|
|
||||||
// non-2XX HTTP status code.
|
|
||||||
type RetrieveError struct {
|
|
||||||
Response *http.Response
|
|
||||||
// Body is the body that was consumed by reading Response.Body.
|
|
||||||
// It may be truncated.
|
|
||||||
Body []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *RetrieveError) Error() string {
|
|
||||||
return fmt.Sprintf("oauth2: cannot fetch token: %v\nResponse: %s", r.Response.Status, r.Body)
|
|
||||||
}
|
|
||||||
|
|
|
@ -31,17 +31,9 @@ type Transport struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// RoundTrip authorizes and authenticates the request with an
|
// RoundTrip authorizes and authenticates the request with an
|
||||||
// access token from Transport's Source.
|
// access token. If no token exists or token is expired,
|
||||||
|
// tries to refresh/fetch a new token.
|
||||||
func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
|
func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||||
reqBodyClosed := false
|
|
||||||
if req.Body != nil {
|
|
||||||
defer func() {
|
|
||||||
if !reqBodyClosed {
|
|
||||||
req.Body.Close()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
if t.Source == nil {
|
if t.Source == nil {
|
||||||
return nil, errors.New("oauth2: Transport's Source is nil")
|
return nil, errors.New("oauth2: Transport's Source is nil")
|
||||||
}
|
}
|
||||||
|
@ -54,10 +46,6 @@ func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||||
token.SetAuthHeader(req2)
|
token.SetAuthHeader(req2)
|
||||||
t.setModReq(req, req2)
|
t.setModReq(req, req2)
|
||||||
res, err := t.base().RoundTrip(req2)
|
res, err := t.base().RoundTrip(req2)
|
||||||
|
|
||||||
// req.Body is assumed to have been closed by the base RoundTripper.
|
|
||||||
reqBodyClosed = true
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.setModReq(req, nil)
|
t.setModReq(req, nil)
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -6,17 +6,7 @@
|
||||||
// +build ppc
|
// +build ppc
|
||||||
|
|
||||||
// Functions to access/create device major and minor numbers matching the
|
// Functions to access/create device major and minor numbers matching the
|
||||||
// encoding used by the Linux kernel and glibc.
|
// encoding used by AIX.
|
||||||
//
|
|
||||||
// The information below is extracted and adapted from bits/sysmacros.h in the
|
|
||||||
// glibc sources:
|
|
||||||
//
|
|
||||||
// dev_t in glibc is 64-bit, with 32-bit major and minor numbers. glibc's
|
|
||||||
// default encoding is MMMM Mmmm mmmM MMmm, where M is a hex digit of the major
|
|
||||||
// number and m is a hex digit of the minor number. This is backward compatible
|
|
||||||
// with legacy systems where dev_t is 16 bits wide, encoded as MMmm. It is also
|
|
||||||
// backward compatible with the Linux kernel, which for some architectures uses
|
|
||||||
// 32-bit dev_t, encoded as mmmM MMmm.
|
|
||||||
|
|
||||||
package unix
|
package unix
|
||||||
|
|
||||||
|
|
|
@ -6,17 +6,7 @@
|
||||||
// +build ppc64
|
// +build ppc64
|
||||||
|
|
||||||
// Functions to access/create device major and minor numbers matching the
|
// Functions to access/create device major and minor numbers matching the
|
||||||
// encoding used by the Linux kernel and glibc.
|
// encoding used AIX.
|
||||||
//
|
|
||||||
// The information below is extracted and adapted from bits/sysmacros.h in the
|
|
||||||
// glibc sources:
|
|
||||||
//
|
|
||||||
// dev_t in glibc is 64-bit, with 32-bit major and minor numbers. glibc's
|
|
||||||
// default encoding is MMMM Mmmm mmmM MMmm, where M is a hex digit of the major
|
|
||||||
// number and m is a hex digit of the minor number. This is backward compatible
|
|
||||||
// with legacy systems where dev_t is 16 bits wide, encoded as MMmm. It is also
|
|
||||||
// backward compatible with the Linux kernel, which for some architectures uses
|
|
||||||
// 32-bit dev_t, encoded as mmmM MMmm.
|
|
||||||
|
|
||||||
package unix
|
package unix
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
|
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
|
||||||
|
|
||||||
package unix
|
package unix
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,7 @@ includes_AIX='
|
||||||
#include <sys/stropts.h>
|
#include <sys/stropts.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <sys/poll.h>
|
#include <sys/poll.h>
|
||||||
|
#include <sys/termio.h>
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
@ -86,6 +87,7 @@ includes_DragonFly='
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/sysctl.h>
|
#include <sys/sysctl.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
|
#include <sys/mount.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <net/bpf.h>
|
#include <net/bpf.h>
|
||||||
|
@ -193,6 +195,7 @@ struct ltchars {
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
#include <linux/keyctl.h>
|
#include <linux/keyctl.h>
|
||||||
#include <linux/magic.h>
|
#include <linux/magic.h>
|
||||||
|
#include <linux/memfd.h>
|
||||||
#include <linux/netfilter/nfnetlink.h>
|
#include <linux/netfilter/nfnetlink.h>
|
||||||
#include <linux/netlink.h>
|
#include <linux/netlink.h>
|
||||||
#include <linux/net_namespace.h>
|
#include <linux/net_namespace.h>
|
||||||
|
@ -214,6 +217,7 @@ struct ltchars {
|
||||||
#include <linux/watchdog.h>
|
#include <linux/watchdog.h>
|
||||||
#include <linux/hdreg.h>
|
#include <linux/hdreg.h>
|
||||||
#include <linux/rtc.h>
|
#include <linux/rtc.h>
|
||||||
|
#include <linux/if_xdp.h>
|
||||||
#include <mtd/ubi-user.h>
|
#include <mtd/ubi-user.h>
|
||||||
#include <net/route.h>
|
#include <net/route.h>
|
||||||
#include <asm/termbits.h>
|
#include <asm/termbits.h>
|
||||||
|
@ -244,6 +248,16 @@ struct ltchars {
|
||||||
#define FS_KEY_DESC_PREFIX "fscrypt:"
|
#define FS_KEY_DESC_PREFIX "fscrypt:"
|
||||||
#define FS_KEY_DESC_PREFIX_SIZE 8
|
#define FS_KEY_DESC_PREFIX_SIZE 8
|
||||||
#define FS_MAX_KEY_SIZE 64
|
#define FS_MAX_KEY_SIZE 64
|
||||||
|
|
||||||
|
// XDP socket constants do not appear to be picked up otherwise.
|
||||||
|
// Copied from samples/bpf/xdpsock_user.c.
|
||||||
|
#ifndef SOL_XDP
|
||||||
|
#define SOL_XDP 283
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef AF_XDP
|
||||||
|
#define AF_XDP 44
|
||||||
|
#endif
|
||||||
'
|
'
|
||||||
|
|
||||||
includes_NetBSD='
|
includes_NetBSD='
|
||||||
|
@ -252,6 +266,7 @@ includes_NetBSD='
|
||||||
#include <sys/event.h>
|
#include <sys/event.h>
|
||||||
#include <sys/extattr.h>
|
#include <sys/extattr.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
|
#include <sys/mount.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/sockio.h>
|
#include <sys/sockio.h>
|
||||||
#include <sys/sysctl.h>
|
#include <sys/sysctl.h>
|
||||||
|
@ -277,6 +292,7 @@ includes_OpenBSD='
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
#include <sys/event.h>
|
#include <sys/event.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
|
#include <sys/mount.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/sockio.h>
|
#include <sys/sockio.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
@ -378,6 +394,7 @@ ccflags="$@"
|
||||||
$2 ~ /^EXTATTR_NAMESPACE_NAMES/ ||
|
$2 ~ /^EXTATTR_NAMESPACE_NAMES/ ||
|
||||||
$2 ~ /^EXTATTR_NAMESPACE_[A-Z]+_STRING/ {next}
|
$2 ~ /^EXTATTR_NAMESPACE_[A-Z]+_STRING/ {next}
|
||||||
|
|
||||||
|
$2 !~ /^ECCAPBITS/ &&
|
||||||
$2 !~ /^ETH_/ &&
|
$2 !~ /^ETH_/ &&
|
||||||
$2 !~ /^EPROC_/ &&
|
$2 !~ /^EPROC_/ &&
|
||||||
$2 !~ /^EQUIV_/ &&
|
$2 !~ /^EQUIV_/ &&
|
||||||
|
@ -413,7 +430,7 @@ ccflags="$@"
|
||||||
$2 ~ /^TC[IO](ON|OFF)$/ ||
|
$2 ~ /^TC[IO](ON|OFF)$/ ||
|
||||||
$2 ~ /^IN_/ ||
|
$2 ~ /^IN_/ ||
|
||||||
$2 ~ /^LOCK_(SH|EX|NB|UN)$/ ||
|
$2 ~ /^LOCK_(SH|EX|NB|UN)$/ ||
|
||||||
$2 ~ /^(AF|SOCK|SO|SOL|IPPROTO|IP|IPV6|ICMP6|TCP|EVFILT|NOTE|EV|SHUT|PROT|MAP|T?PACKET|MSG|SCM|MCL|DT|MADV|PR)_/ ||
|
$2 ~ /^(AF|SOCK|SO|SOL|IPPROTO|IP|IPV6|ICMP6|TCP|EVFILT|NOTE|EV|SHUT|PROT|MAP|MFD|T?PACKET|MSG|SCM|MCL|DT|MADV|PR)_/ ||
|
||||||
$2 ~ /^TP_STATUS_/ ||
|
$2 ~ /^TP_STATUS_/ ||
|
||||||
$2 ~ /^FALLOC_/ ||
|
$2 ~ /^FALLOC_/ ||
|
||||||
$2 == "ICMPV6_FILTER" ||
|
$2 == "ICMPV6_FILTER" ||
|
||||||
|
@ -424,6 +441,7 @@ ccflags="$@"
|
||||||
$2 ~ /^KERN_(HOSTNAME|OS(RELEASE|TYPE)|VERSION)$/ ||
|
$2 ~ /^KERN_(HOSTNAME|OS(RELEASE|TYPE)|VERSION)$/ ||
|
||||||
$2 ~ /^HW_MACHINE$/ ||
|
$2 ~ /^HW_MACHINE$/ ||
|
||||||
$2 ~ /^SYSCTL_VERS/ ||
|
$2 ~ /^SYSCTL_VERS/ ||
|
||||||
|
$2 !~ "MNT_BITS" &&
|
||||||
$2 ~ /^(MS|MNT|UMOUNT)_/ ||
|
$2 ~ /^(MS|MNT|UMOUNT)_/ ||
|
||||||
$2 ~ /^TUN(SET|GET|ATTACH|DETACH)/ ||
|
$2 ~ /^TUN(SET|GET|ATTACH|DETACH)/ ||
|
||||||
$2 ~ /^(O|F|E?FD|NAME|S|PTRACE|PT)_/ ||
|
$2 ~ /^(O|F|E?FD|NAME|S|PTRACE|PT)_/ ||
|
||||||
|
@ -474,6 +492,7 @@ ccflags="$@"
|
||||||
$2 ~ /^FSOPT_/ ||
|
$2 ~ /^FSOPT_/ ||
|
||||||
$2 ~ /^WDIOC_/ ||
|
$2 ~ /^WDIOC_/ ||
|
||||||
$2 ~ /^NFN/ ||
|
$2 ~ /^NFN/ ||
|
||||||
|
$2 ~ /^XDP_/ ||
|
||||||
$2 ~ /^(HDIO|WIN|SMART)_/ ||
|
$2 ~ /^(HDIO|WIN|SMART)_/ ||
|
||||||
$2 !~ "WMESGLEN" &&
|
$2 !~ "WMESGLEN" &&
|
||||||
$2 ~ /^W[A-Z0-9]+$/ ||
|
$2 ~ /^W[A-Z0-9]+$/ ||
|
||||||
|
|
|
@ -345,11 +345,11 @@ func IoctlSetInt(fd int, req uint, value int) error {
|
||||||
return ioctl(fd, req, uintptr(value))
|
return ioctl(fd, req, uintptr(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
func IoctlSetWinsize(fd int, req uint, value *Winsize) error {
|
func ioctlSetWinsize(fd int, req uint, value *Winsize) error {
|
||||||
return ioctl(fd, req, uintptr(unsafe.Pointer(value)))
|
return ioctl(fd, req, uintptr(unsafe.Pointer(value)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func IoctlSetTermios(fd int, req uint, value *Termios) error {
|
func ioctlSetTermios(fd int, req uint, value *Termios) error {
|
||||||
return ioctl(fd, req, uintptr(unsafe.Pointer(value)))
|
return ioctl(fd, req, uintptr(unsafe.Pointer(value)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -419,8 +419,10 @@ func Flock(fd int, how int) (err error) {
|
||||||
//sysnb Getsid(pid int) (sid int, err error)
|
//sysnb Getsid(pid int) (sid int, err error)
|
||||||
//sysnb Kill(pid int, sig syscall.Signal) (err error)
|
//sysnb Kill(pid int, sig syscall.Signal) (err error)
|
||||||
//sys Klogctl(typ int, buf []byte) (n int, err error) = syslog
|
//sys Klogctl(typ int, buf []byte) (n int, err error) = syslog
|
||||||
|
//sys Mkdir(dirfd int, path string, mode uint32) (err error)
|
||||||
//sys Mkdirat(dirfd int, path string, mode uint32) (err error)
|
//sys Mkdirat(dirfd int, path string, mode uint32) (err error)
|
||||||
//sys Mkfifo(path string, mode uint32) (err error)
|
//sys Mkfifo(path string, mode uint32) (err error)
|
||||||
|
//sys Mknod(path string, mode uint32, dev int) (err error)
|
||||||
//sys Mknodat(dirfd int, path string, mode uint32, dev int) (err error)
|
//sys Mknodat(dirfd int, path string, mode uint32, dev int) (err error)
|
||||||
//sys Nanosleep(time *Timespec, leftover *Timespec) (err error)
|
//sys Nanosleep(time *Timespec, leftover *Timespec) (err error)
|
||||||
//sys Open(path string, mode int, perm uint32) (fd int, err error) = open64
|
//sys Open(path string, mode int, perm uint32) (fd int, err error) = open64
|
||||||
|
|
|
@ -692,6 +692,24 @@ func (sa *SockaddrVM) sockaddr() (unsafe.Pointer, _Socklen, error) {
|
||||||
return unsafe.Pointer(&sa.raw), SizeofSockaddrVM, nil
|
return unsafe.Pointer(&sa.raw), SizeofSockaddrVM, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SockaddrXDP struct {
|
||||||
|
Flags uint16
|
||||||
|
Ifindex uint32
|
||||||
|
QueueID uint32
|
||||||
|
SharedUmemFD uint32
|
||||||
|
raw RawSockaddrXDP
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sa *SockaddrXDP) sockaddr() (unsafe.Pointer, _Socklen, error) {
|
||||||
|
sa.raw.Family = AF_XDP
|
||||||
|
sa.raw.Flags = sa.Flags
|
||||||
|
sa.raw.Ifindex = sa.Ifindex
|
||||||
|
sa.raw.Queue_id = sa.QueueID
|
||||||
|
sa.raw.Shared_umem_fd = sa.SharedUmemFD
|
||||||
|
|
||||||
|
return unsafe.Pointer(&sa.raw), SizeofSockaddrXDP, nil
|
||||||
|
}
|
||||||
|
|
||||||
func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) {
|
func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) {
|
||||||
switch rsa.Addr.Family {
|
switch rsa.Addr.Family {
|
||||||
case AF_NETLINK:
|
case AF_NETLINK:
|
||||||
|
@ -793,6 +811,15 @@ func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) {
|
||||||
}
|
}
|
||||||
return sa, nil
|
return sa, nil
|
||||||
}
|
}
|
||||||
|
case AF_XDP:
|
||||||
|
pp := (*RawSockaddrXDP)(unsafe.Pointer(rsa))
|
||||||
|
sa := &SockaddrXDP{
|
||||||
|
Flags: pp.Flags,
|
||||||
|
Ifindex: pp.Ifindex,
|
||||||
|
QueueID: pp.Queue_id,
|
||||||
|
SharedUmemFD: pp.Shared_umem_fd,
|
||||||
|
}
|
||||||
|
return sa, nil
|
||||||
}
|
}
|
||||||
return nil, EAFNOSUPPORT
|
return nil, EAFNOSUPPORT
|
||||||
}
|
}
|
||||||
|
@ -1320,6 +1347,7 @@ func Getpgrp() (pid int) {
|
||||||
//sys Llistxattr(path string, dest []byte) (sz int, err error)
|
//sys Llistxattr(path string, dest []byte) (sz int, err error)
|
||||||
//sys Lremovexattr(path string, attr string) (err error)
|
//sys Lremovexattr(path string, attr string) (err error)
|
||||||
//sys Lsetxattr(path string, attr string, data []byte, flags int) (err error)
|
//sys Lsetxattr(path string, attr string, data []byte, flags int) (err error)
|
||||||
|
//sys MemfdCreate(name string, flags int) (fd int, err error)
|
||||||
//sys Mkdirat(dirfd int, path string, mode uint32) (err error)
|
//sys Mkdirat(dirfd int, path string, mode uint32) (err error)
|
||||||
//sys Mknodat(dirfd int, path string, mode uint32, dev int) (err error)
|
//sys Mknodat(dirfd int, path string, mode uint32, dev int) (err error)
|
||||||
//sys Nanosleep(time *Timespec, leftover *Timespec) (err error)
|
//sys Nanosleep(time *Timespec, leftover *Timespec) (err error)
|
||||||
|
|
|
@ -22,6 +22,11 @@ package unix
|
||||||
#include <utime.h>
|
#include <utime.h>
|
||||||
#include <sys/utsname.h>
|
#include <sys/utsname.h>
|
||||||
#include <sys/poll.h>
|
#include <sys/poll.h>
|
||||||
|
#include <sys/resource.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/statfs.h>
|
||||||
|
#include <sys/termio.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
|
|
||||||
|
@ -33,7 +38,6 @@ package unix
|
||||||
|
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <gcrypt.h>
|
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
sizeofPtr = sizeof(void*),
|
sizeofPtr = sizeof(void*),
|
||||||
|
@ -224,6 +228,9 @@ type Flock_t C.struct_flock64
|
||||||
|
|
||||||
// Statfs
|
// Statfs
|
||||||
|
|
||||||
|
type Fsid_t C.struct_fsid_t
|
||||||
|
type Fsid64_t C.struct_fsid64_t
|
||||||
|
|
||||||
type Statfs_t C.struct_statfs
|
type Statfs_t C.struct_statfs
|
||||||
|
|
||||||
const RNDGETENTCNT = 0x80045200
|
const RNDGETENTCNT = 0x80045200
|
||||||
|
|
|
@ -867,6 +867,9 @@ const (
|
||||||
TAB2 = 0x800
|
TAB2 = 0x800
|
||||||
TAB3 = 0xc00
|
TAB3 = 0xc00
|
||||||
TABDLY = 0xc00
|
TABDLY = 0xc00
|
||||||
|
TCFLSH = 0x540c
|
||||||
|
TCGETA = 0x5405
|
||||||
|
TCGETS = 0x5401
|
||||||
TCIFLUSH = 0x0
|
TCIFLUSH = 0x0
|
||||||
TCIOFF = 0x2
|
TCIOFF = 0x2
|
||||||
TCIOFLUSH = 0x2
|
TCIOFLUSH = 0x2
|
||||||
|
@ -915,6 +918,15 @@ const (
|
||||||
TCP_TIMESTAMP_OPTLEN = 0xc
|
TCP_TIMESTAMP_OPTLEN = 0xc
|
||||||
TCP_UNSETPRIV = 0x28
|
TCP_UNSETPRIV = 0x28
|
||||||
TCSAFLUSH = 0x2
|
TCSAFLUSH = 0x2
|
||||||
|
TCSBRK = 0x5409
|
||||||
|
TCSETA = 0x5406
|
||||||
|
TCSETAF = 0x5408
|
||||||
|
TCSETAW = 0x5407
|
||||||
|
TCSETS = 0x5402
|
||||||
|
TCSETSF = 0x5404
|
||||||
|
TCSETSW = 0x5403
|
||||||
|
TCXONC = 0x540b
|
||||||
|
TIOC = 0x5400
|
||||||
TIOCCBRK = 0x2000747a
|
TIOCCBRK = 0x2000747a
|
||||||
TIOCCDTR = 0x20007478
|
TIOCCDTR = 0x20007478
|
||||||
TIOCCONS = 0x80047462
|
TIOCCONS = 0x80047462
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue