enable storage driver of gcs and oss (#12180)

Add go build tags for gcs and oss, otherwise these drivers cannot be registered and the error "StorageDriver is not regsited: GCS" will raise on registryctl launch under the setting of GCS storage.

These build tags are designed in the distribution, just refer to https://github.com/docker/distribution/blob/release/2.7/registry/storage/driver/gcs/gcs.go#L13

Pin the google cloud API to a old version is because distribution depends on it, otherwise go mode will use v0.17.0 that go-migrate is using as the dependency version, but this version will break the compile process with following error:
harbor/pkg/mod/google.golang.org/cloud@v0.0.0-20151119220103-975617b05ea8/storage/acl.go:65:16: invalid type assertion: v.(map[string]<inter>) (non-interface type *storage.ObjectAccessControl on left)
that's bacause another dependency google.golang.org/cloud requires the pinned version of google.golang.org/api.

The pinned package should be removed once https://github.com/docker/distribution/pull/3019 is merged, and distribution ships their v2.8.0

Signed-off-by: wang yan <wangyan@vmware.com>
This commit is contained in:
Wang Yan 2020-06-09 00:57:27 +08:00 committed by GitHub
parent 6a30fda874
commit c993103e01
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
126 changed files with 4055 additions and 21066 deletions

View File

@ -83,4 +83,5 @@ require (
replace (
github.com/Azure/go-autorest => github.com/Azure/go-autorest v13.3.3+incompatible
github.com/goharbor/harbor => ../
google.golang.org/api => google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff
)

View File

@ -816,7 +816,6 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@ -903,7 +902,6 @@ golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
@ -939,17 +937,8 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0=
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ=
google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff h1:mk5zS3XLqVUzdF/CQCZ5ERujSF/8JFo+Wpkp/5I93NA=
google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.17.0 h1:0q95w+VuFtv4PAx4PZVQdBMmYbaCHbnfKaEiDIcVyag=
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=

View File

@ -1,191 +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:
You must give any other recipients of the Work or Derivative Works a copy of
this License; and
You must cause any modified files to carry prominent notices stating that You
changed the files; and
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
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.

View File

@ -1,133 +0,0 @@
/*
Copyright 2013 Google Inc.
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 lru implements an LRU cache.
package lru
import "container/list"
// Cache is an LRU cache. It is not safe for concurrent access.
type Cache struct {
// MaxEntries is the maximum number of cache entries before
// an item is evicted. Zero means no limit.
MaxEntries int
// OnEvicted optionally specifies a callback function to be
// executed when an entry is purged from the cache.
OnEvicted func(key Key, value interface{})
ll *list.List
cache map[interface{}]*list.Element
}
// A Key may be any value that is comparable. See http://golang.org/ref/spec#Comparison_operators
type Key interface{}
type entry struct {
key Key
value interface{}
}
// New creates a new Cache.
// If maxEntries is zero, the cache has no limit and it's assumed
// that eviction is done by the caller.
func New(maxEntries int) *Cache {
return &Cache{
MaxEntries: maxEntries,
ll: list.New(),
cache: make(map[interface{}]*list.Element),
}
}
// Add adds a value to the cache.
func (c *Cache) Add(key Key, value interface{}) {
if c.cache == nil {
c.cache = make(map[interface{}]*list.Element)
c.ll = list.New()
}
if ee, ok := c.cache[key]; ok {
c.ll.MoveToFront(ee)
ee.Value.(*entry).value = value
return
}
ele := c.ll.PushFront(&entry{key, value})
c.cache[key] = ele
if c.MaxEntries != 0 && c.ll.Len() > c.MaxEntries {
c.RemoveOldest()
}
}
// Get looks up a key's value from the cache.
func (c *Cache) Get(key Key) (value interface{}, ok bool) {
if c.cache == nil {
return
}
if ele, hit := c.cache[key]; hit {
c.ll.MoveToFront(ele)
return ele.Value.(*entry).value, true
}
return
}
// Remove removes the provided key from the cache.
func (c *Cache) Remove(key Key) {
if c.cache == nil {
return
}
if ele, hit := c.cache[key]; hit {
c.removeElement(ele)
}
}
// RemoveOldest removes the oldest item from the cache.
func (c *Cache) RemoveOldest() {
if c.cache == nil {
return
}
ele := c.ll.Back()
if ele != nil {
c.removeElement(ele)
}
}
func (c *Cache) removeElement(e *list.Element) {
c.ll.Remove(e)
kv := e.Value.(*entry)
delete(c.cache, kv.key)
if c.OnEvicted != nil {
c.OnEvicted(kv.key, kv.value)
}
}
// Len returns the number of items in the cache.
func (c *Cache) Len() int {
if c.cache == nil {
return 0
}
return c.ll.Len()
}
// Clear purges all stored items from the cache.
func (c *Cache) Clear() {
if c.OnEvicted != nil {
for _, e := range c.cache {
kv := e.Value.(*entry)
c.OnEvicted(kv.key, kv.value)
}
}
c.ll = nil
c.cache = nil
}

View File

@ -1,27 +0,0 @@
Copyright 2016, Google Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -1,161 +0,0 @@
// Copyright 2016, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package gax
import (
"math/rand"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// CallOption is an option used by Invoke to control behaviors of RPC calls.
// CallOption works by modifying relevant fields of CallSettings.
type CallOption interface {
// Resolve applies the option by modifying cs.
Resolve(cs *CallSettings)
}
// Retryer is used by Invoke to determine retry behavior.
type Retryer interface {
// Retry reports whether a request should be retriedand how long to pause before retrying
// if the previous attempt returned with err. Invoke never calls Retry with nil error.
Retry(err error) (pause time.Duration, shouldRetry bool)
}
type retryerOption func() Retryer
func (o retryerOption) Resolve(s *CallSettings) {
s.Retry = o
}
// WithRetry sets CallSettings.Retry to fn.
func WithRetry(fn func() Retryer) CallOption {
return retryerOption(fn)
}
// OnCodes returns a Retryer that retries if and only if
// the previous attempt returns a GRPC error whose error code is stored in cc.
// Pause times between retries are specified by bo.
//
// bo is only used for its parameters; each Retryer has its own copy.
func OnCodes(cc []codes.Code, bo Backoff) Retryer {
return &boRetryer{
backoff: bo,
codes: append([]codes.Code(nil), cc...),
}
}
type boRetryer struct {
backoff Backoff
codes []codes.Code
}
func (r *boRetryer) Retry(err error) (time.Duration, bool) {
st, ok := status.FromError(err)
if !ok {
return 0, false
}
c := st.Code()
for _, rc := range r.codes {
if c == rc {
return r.backoff.Pause(), true
}
}
return 0, false
}
// Backoff implements exponential backoff.
// The wait time between retries is a random value between 0 and the "retry envelope".
// The envelope starts at Initial and increases by the factor of Multiplier every retry,
// but is capped at Max.
type Backoff struct {
// Initial is the initial value of the retry envelope, defaults to 1 second.
Initial time.Duration
// Max is the maximum value of the retry envelope, defaults to 30 seconds.
Max time.Duration
// Multiplier is the factor by which the retry envelope increases.
// It should be greater than 1 and defaults to 2.
Multiplier float64
// cur is the current retry envelope
cur time.Duration
}
// Pause returns the next time.Duration that the caller should use to backoff.
func (bo *Backoff) Pause() time.Duration {
if bo.Initial == 0 {
bo.Initial = time.Second
}
if bo.cur == 0 {
bo.cur = bo.Initial
}
if bo.Max == 0 {
bo.Max = 30 * time.Second
}
if bo.Multiplier < 1 {
bo.Multiplier = 2
}
// Select a duration between 1ns and the current max. It might seem
// counterintuitive to have so much jitter, but
// https://www.awsarchitectureblog.com/2015/03/backoff.html argues that
// that is the best strategy.
d := time.Duration(1 + rand.Int63n(int64(bo.cur)))
bo.cur = time.Duration(float64(bo.cur) * bo.Multiplier)
if bo.cur > bo.Max {
bo.cur = bo.Max
}
return d
}
type grpcOpt []grpc.CallOption
func (o grpcOpt) Resolve(s *CallSettings) {
s.GRPC = o
}
// WithGRPCOptions allows passing gRPC call options during client creation.
func WithGRPCOptions(opt ...grpc.CallOption) CallOption {
return grpcOpt(append([]grpc.CallOption(nil), opt...))
}
// CallSettings allow fine-grained control over how calls are made.
type CallSettings struct {
// Retry returns a Retryer to be used to control retry logic of a method call.
// If Retry is nil or the returned Retryer is nil, the call will not be retried.
Retry func() Retryer
// CallOptions to be forwarded to GRPC.
GRPC []grpc.CallOption
}

View File

@ -1,39 +0,0 @@
// Copyright 2016, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Package gax contains a set of modules which aid the development of APIs
// for clients and servers based on gRPC and Google API conventions.
//
// Application code will rarely need to use this library directly.
// However, code generated automatically from API definition files can use it
// to simplify code generation and to provide more convenient and idiomatic API surfaces.
package gax
// Version specifies the gax-go version being used.
const Version = "2.0.4"

View File

@ -1,3 +0,0 @@
module github.com/googleapis/gax-go/v2
require google.golang.org/grpc v1.19.0

View File

@ -1,25 +0,0 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d h1:g9qWBGx4puODJTMVyoPrpoxPFgVGd+z1DZwjfRu4d0I=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522 h1:Ve1ORMCxvRmSXBwJK+t3Oy+V2vRW2OetUQBq4rJIkZE=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/grpc v1.19.0 h1:cfg4PD8YEdSFnm7qLV4++93WcmhH2nIUhMjhdCvl3j8=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@ -1,53 +0,0 @@
// Copyright 2018, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package gax
import "bytes"
// XGoogHeader is for use by the Google Cloud Libraries only.
//
// XGoogHeader formats key-value pairs.
// The resulting string is suitable for x-goog-api-client header.
func XGoogHeader(keyval ...string) string {
if len(keyval) == 0 {
return ""
}
if len(keyval)%2 != 0 {
panic("gax.Header: odd argument count")
}
var buf bytes.Buffer
for i := 0; i < len(keyval); i += 2 {
buf.WriteByte(' ')
buf.WriteString(keyval[i])
buf.WriteByte('/')
buf.WriteString(keyval[i+1])
}
return buf.String()[1:]
}

View File

@ -1,99 +0,0 @@
// Copyright 2016, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package gax
import (
"context"
"strings"
"time"
)
// APICall is a user defined call stub.
type APICall func(context.Context, CallSettings) error
// Invoke calls the given APICall,
// performing retries as specified by opts, if any.
func Invoke(ctx context.Context, call APICall, opts ...CallOption) error {
var settings CallSettings
for _, opt := range opts {
opt.Resolve(&settings)
}
return invoke(ctx, call, settings, Sleep)
}
// Sleep is similar to time.Sleep, but it can be interrupted by ctx.Done() closing.
// If interrupted, Sleep returns ctx.Err().
func Sleep(ctx context.Context, d time.Duration) error {
t := time.NewTimer(d)
select {
case <-ctx.Done():
t.Stop()
return ctx.Err()
case <-t.C:
return nil
}
}
type sleeper func(ctx context.Context, d time.Duration) error
// invoke implements Invoke, taking an additional sleeper argument for testing.
func invoke(ctx context.Context, call APICall, settings CallSettings, sp sleeper) error {
var retryer Retryer
for {
err := call(ctx, settings)
if err == nil {
return nil
}
if settings.Retry == nil {
return err
}
// Never retry permanent certificate errors. (e.x. if ca-certificates
// are not installed). We should only make very few, targeted
// exceptions: many (other) status=Unavailable should be retried, such
// as if there's a network hiccup, or the internet goes out for a
// minute. This is also why here we are doing string parsing instead of
// simply making Unavailable a non-retried code elsewhere.
if strings.Contains(err.Error(), "x509: certificate signed by unknown authority") {
return err
}
if retryer == nil {
if r := settings.Retry(); r != nil {
retryer = r
} else {
return err
}
}
if d, ok := retryer.Retry(err); !ok {
return err
} else if err = sp(ctx, d); err != nil {
return err
}
}
}

View File

@ -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/

View File

@ -1,17 +0,0 @@
language: go
go_import_path: go.opencensus.io
go:
- 1.11.x
env:
global:
GO111MODULE=on
before_script:
- make install-tools
script:
- make travis-ci
- go run internal/check/version.go # TODO move this to makefile

View File

@ -1 +0,0 @@
Google Inc.

View File

@ -1,63 +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:
```
$ make install-tools # Only first time.
$ make
```
Checkout a new branch, make modifications and push the branch to your fork:
```
$ git checkout -b feature
# edit files
$ git commit
$ git push fork feature
```
Open a pull request against the main opencensus-go repo.
## General Notes
This project uses Appveyor and Travis for CI.
The dependencies are managed with `go mod` if you work with the sources under your
`$GOPATH` you need to set the environment variable `GO111MODULE=on`.

202
src/vendor/go.opencensus.io/LICENSE generated vendored
View File

@ -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.

96
src/vendor/go.opencensus.io/Makefile generated vendored
View File

@ -1,96 +0,0 @@
# TODO: Fix this on windows.
ALL_SRC := $(shell find . -name '*.go' \
-not -path './vendor/*' \
-not -path '*/gen-go/*' \
-type f | sort)
ALL_PKGS := $(shell go list $(sort $(dir $(ALL_SRC))))
GOTEST_OPT?=-v -race -timeout 30s
GOTEST_OPT_WITH_COVERAGE = $(GOTEST_OPT) -coverprofile=coverage.txt -covermode=atomic
GOTEST=go test
GOFMT=gofmt
GOLINT=golint
GOVET=go vet
EMBEDMD=embedmd
# TODO decide if we need to change these names.
TRACE_ID_LINT_EXCEPTION="type name will be used as trace.TraceID by other packages"
TRACE_OPTION_LINT_EXCEPTION="type name will be used as trace.TraceOptions by other packages"
README_FILES := $(shell find . -name '*README.md' | sort | tr '\n' ' ')
.DEFAULT_GOAL := fmt-lint-vet-embedmd-test
.PHONY: fmt-lint-vet-embedmd-test
fmt-lint-vet-embedmd-test: fmt lint vet embedmd test
# TODO enable test-with-coverage in tavis
.PHONY: travis-ci
travis-ci: fmt lint vet embedmd test test-386
all-pkgs:
@echo $(ALL_PKGS) | tr ' ' '\n' | sort
all-srcs:
@echo $(ALL_SRC) | tr ' ' '\n' | sort
.PHONY: test
test:
$(GOTEST) $(GOTEST_OPT) $(ALL_PKGS)
.PHONY: test-386
test-386:
GOARCH=386 $(GOTEST) -v -timeout 30s $(ALL_PKGS)
.PHONY: test-with-coverage
test-with-coverage:
$(GOTEST) $(GOTEST_OPT_WITH_COVERAGE) $(ALL_PKGS)
.PHONY: fmt
fmt:
@FMTOUT=`$(GOFMT) -s -l $(ALL_SRC) 2>&1`; \
if [ "$$FMTOUT" ]; then \
echo "$(GOFMT) FAILED => gofmt the following files:\n"; \
echo "$$FMTOUT\n"; \
exit 1; \
else \
echo "Fmt finished successfully"; \
fi
.PHONY: lint
lint:
@LINTOUT=`$(GOLINT) $(ALL_PKGS) | grep -v $(TRACE_ID_LINT_EXCEPTION) | grep -v $(TRACE_OPTION_LINT_EXCEPTION) 2>&1`; \
if [ "$$LINTOUT" ]; then \
echo "$(GOLINT) FAILED => clean the following lint errors:\n"; \
echo "$$LINTOUT\n"; \
exit 1; \
else \
echo "Lint finished successfully"; \
fi
.PHONY: vet
vet:
# TODO: Understand why go vet downloads "github.com/google/go-cmp v0.2.0"
@VETOUT=`$(GOVET) ./... | grep -v "go: downloading" 2>&1`; \
if [ "$$VETOUT" ]; then \
echo "$(GOVET) FAILED => go vet the following files:\n"; \
echo "$$VETOUT\n"; \
exit 1; \
else \
echo "Vet finished successfully"; \
fi
.PHONY: embedmd
embedmd:
@EMBEDMDOUT=`$(EMBEDMD) -d $(README_FILES) 2>&1`; \
if [ "$$EMBEDMDOUT" ]; then \
echo "$(EMBEDMD) FAILED => embedmd the following files:\n"; \
echo "$$EMBEDMDOUT\n"; \
exit 1; \
else \
echo "Embedmd finished successfully"; \
fi
.PHONY: install-tools
install-tools:
go get -u golang.org/x/tools/cmd/cover
go get -u golang.org/x/lint/golint
go get -u github.com/rakyll/embedmd

267
src/vendor/go.opencensus.io/README.md generated vendored
View File

@ -1,267 +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.
#### OpenCensus and OpenTracing have merged to form OpenTelemetry, which serves as the next major version of OpenCensus and OpenTracing. OpenTelemetry will offer backwards compatibility with existing OpenCensus integrations, and we will continue to make security patches to existing OpenCensus libraries for two years. Read more about the merger [here](https://medium.com/opentracing/a-roadmap-to-convergence-b074e5815289).
## Installation
```
$ go get -u go.opencensus.io
```
The API of this project is still evolving, see: [Deprecation Policy](#deprecation-policy).
The use of vendoring or a dependency management tool is recommended.
## Prerequisites
OpenCensus Go libraries require Go 1.8 or later.
## Getting Started
The easiest way to get started using OpenCensus in your application is to use an existing
integration with your RPC framework:
* [net/http](https://godoc.org/go.opencensus.io/plugin/ochttp)
* [gRPC](https://godoc.org/go.opencensus.io/plugin/ocgrpc)
* [database/sql](https://godoc.org/github.com/opencensus-integrations/ocsql)
* [Go kit](https://godoc.org/github.com/go-kit/kit/tracing/opencensus)
* [Groupcache](https://godoc.org/github.com/orijtech/groupcache)
* [Caddy webserver](https://godoc.org/github.com/orijtech/caddy)
* [MongoDB](https://godoc.org/github.com/orijtech/mongo-go-driver)
* [Redis gomodule/redigo](https://godoc.org/github.com/orijtech/redigo)
* [Redis goredis/redis](https://godoc.org/github.com/orijtech/redis)
* [Memcache](https://godoc.org/github.com/orijtech/gomemcache)
If you're using a framework not listed here, you could either implement your own middleware for your
framework or use [custom stats](#stats) and [spans](#spans) directly in your application.
## Exporters
OpenCensus can export instrumentation data to various backends.
OpenCensus has exporter implementations for the following, users
can implement their own exporters by implementing the exporter interfaces
([stats](https://godoc.org/go.opencensus.io/stats/view#Exporter),
[trace](https://godoc.org/go.opencensus.io/trace#Exporter)):
* [Prometheus][exporter-prom] for stats
* [OpenZipkin][exporter-zipkin] for traces
* [Stackdriver][exporter-stackdriver] Monitoring for stats and Trace for traces
* [Jaeger][exporter-jaeger] for traces
* [AWS X-Ray][exporter-xray] for traces
* [Datadog][exporter-datadog] for stats and traces
* [Graphite][exporter-graphite] for stats
* [Honeycomb][exporter-honeycomb] for traces
* [New Relic][exporter-newrelic] for stats and traces
## Overview
![OpenCensus Overview](https://i.imgur.com/cf4ElHE.jpg)
In a microservices environment, a user request may go through
multiple services until there is a response. OpenCensus allows
you to instrument your services and collect diagnostics data all
through your services end-to-end.
## Tags
Tags represent propagated key-value pairs. They are propagated using `context.Context`
in the same process or can be encoded to be transmitted on the wire. Usually, this will
be handled by an integration plugin, e.g. `ocgrpc.ServerHandler` and `ocgrpc.ClientHandler`
for gRPC.
Package `tag` allows adding or modifying tags in the current context.
[embedmd]:# (internal/readme/tags.go new)
```go
ctx, err := tag.New(ctx,
tag.Insert(osKey, "macOS-10.12.5"),
tag.Upsert(userIDKey, "cde36753ed"),
)
if err != nil {
log.Fatal(err)
}
```
## Stats
OpenCensus is a low-overhead framework even if instrumentation is always enabled.
In order to be so, it is optimized to make recording of data points fast
and separate from the data aggregation.
OpenCensus stats collection happens in two stages:
* Definition of measures and recording of data points
* Definition of views and aggregation of the recorded data
### Recording
Measurements are data points associated with a measure.
Recording implicitly tags the set of Measurements with the tags from the
provided context:
[embedmd]:# (internal/readme/stats.go record)
```go
stats.Record(ctx, videoSize.M(102478))
```
### Views
Views are how Measures are aggregated. You can think of them as queries over the
set of recorded data points (measurements).
Views have two parts: the tags to group by and the aggregation type used.
Currently three types of aggregations are supported:
* CountAggregation is used to count the number of times a sample was recorded.
* DistributionAggregation is used to provide a histogram of the values of the samples.
* SumAggregation is used to sum up all sample values.
[embedmd]:# (internal/readme/stats.go aggs)
```go
distAgg := view.Distribution(1<<32, 2<<32, 3<<32)
countAgg := view.Count()
sumAgg := view.Sum()
```
Here we create a view with the DistributionAggregation over our measure.
[embedmd]:# (internal/readme/stats.go view)
```go
if err := view.Register(&view.View{
Name: "example.com/video_size_distribution",
Description: "distribution of processed video size over time",
Measure: videoSize,
Aggregation: view.Distribution(1<<32, 2<<32, 3<<32),
}); err != nil {
log.Fatalf("Failed to register view: %v", err)
}
```
Register begins collecting data for the view. Registered views' data will be
exported via the registered exporters.
## Traces
A distributed trace tracks the progression of a single user request as
it is handled by the services and processes that make up an application.
Each step is called a span in the trace. Spans include metadata about the step,
including especially the time spent in the step, called the spans latency.
Below you see a trace and several spans underneath it.
![Traces and spans](https://i.imgur.com/7hZwRVj.png)
### Spans
Span is the unit step in a trace. Each span has a name, latency, status and
additional metadata.
Below we are starting a span for a cache read and ending it
when we are done:
[embedmd]:# (internal/readme/trace.go startend)
```go
ctx, span := trace.StartSpan(ctx, "cache.Get")
defer span.End()
// Do work to get from cache.
```
### Propagation
Spans can have parents or can be root spans if they don't have any parents.
The current span is propagated in-process and across the network to allow associating
new child spans with the parent.
In the same process, `context.Context` is used to propagate spans.
`trace.StartSpan` creates a new span as a root if the current context
doesn't contain a span. Or, it creates a child of the span that is
already in current context. The returned context can be used to keep
propagating the newly created span in the current context.
[embedmd]:# (internal/readme/trace.go startend)
```go
ctx, span := trace.StartSpan(ctx, "cache.Get")
defer span.End()
// Do work to get from cache.
```
Across the network, OpenCensus provides different propagation
methods for different protocols.
* gRPC integrations use the OpenCensus' [binary propagation format](https://godoc.org/go.opencensus.io/trace/propagation).
* HTTP integrations use Zipkin's [B3](https://github.com/openzipkin/b3-propagation)
by default but can be configured to use a custom propagation method by setting another
[propagation.HTTPFormat](https://godoc.org/go.opencensus.io/trace/propagation#HTTPFormat).
## Execution Tracer
With Go 1.11, OpenCensus Go will support integration with the Go execution tracer.
See [Debugging Latency in Go](https://medium.com/observability/debugging-latency-in-go-1-11-9f97a7910d68)
for an example of their mutual use.
## Profiles
OpenCensus tags can be applied as profiler labels
for users who are on Go 1.9 and above.
[embedmd]:# (internal/readme/tags.go profiler)
```go
ctx, err = tag.New(ctx,
tag.Insert(osKey, "macOS-10.12.5"),
tag.Insert(userIDKey, "fff0989878"),
)
if err != nil {
log.Fatal(err)
}
tag.Do(ctx, func(ctx context.Context) {
// Do work.
// When profiling is on, samples will be
// recorded with the key/values from the tag map.
})
```
A screenshot of the CPU profile from the program above:
![CPU profile](https://i.imgur.com/jBKjlkw.png)
## Deprecation Policy
Before version 1.0.0, the following deprecation policy will be observed:
No backwards-incompatible changes will be made except for the removal of symbols that have
been marked as *Deprecated* for at least one minor release (e.g. 0.9.0 to 0.10.0). A release
removing the *Deprecated* functionality will be made no sooner than 28 days after the first
release in which the functionality was marked *Deprecated*.
[travis-image]: https://travis-ci.org/census-instrumentation/opencensus-go.svg?branch=master
[travis-url]: https://travis-ci.org/census-instrumentation/opencensus-go
[appveyor-image]: https://ci.appveyor.com/api/projects/status/vgtt29ps1783ig38?svg=true
[appveyor-url]: https://ci.appveyor.com/project/opencensusgoteam/opencensus-go/branch/master
[godoc-image]: https://godoc.org/go.opencensus.io?status.svg
[godoc-url]: https://godoc.org/go.opencensus.io
[gitter-image]: https://badges.gitter.im/census-instrumentation/lobby.svg
[gitter-url]: https://gitter.im/census-instrumentation/lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
[new-ex]: https://godoc.org/go.opencensus.io/tag#example-NewMap
[new-replace-ex]: https://godoc.org/go.opencensus.io/tag#example-NewMap--Replace
[exporter-prom]: https://godoc.org/contrib.go.opencensus.io/exporter/prometheus
[exporter-stackdriver]: https://godoc.org/contrib.go.opencensus.io/exporter/stackdriver
[exporter-zipkin]: https://godoc.org/contrib.go.opencensus.io/exporter/zipkin
[exporter-jaeger]: https://godoc.org/contrib.go.opencensus.io/exporter/jaeger
[exporter-xray]: https://github.com/census-ecosystem/opencensus-go-exporter-aws
[exporter-datadog]: https://github.com/DataDog/opencensus-go-exporter-datadog
[exporter-graphite]: https://github.com/census-ecosystem/opencensus-go-exporter-graphite
[exporter-honeycomb]: https://github.com/honeycombio/opencensus-exporter
[exporter-newrelic]: https://github.com/newrelic/newrelic-opencensus-exporter-go

View File

@ -1,24 +0,0 @@
version: "{build}"
platform: x64
clone_folder: c:\gopath\src\go.opencensus.io
environment:
GOPATH: 'c:\gopath'
GO111MODULE: 'on'
CGO_ENABLED: '0' # See: https://github.com/appveyor/ci/issues/2613
stack: go 1.11
before_test:
- go version
- go env
build: false
deploy: false
test_script:
- cd %APPVEYOR_BUILD_FOLDER%
- go build -v .\...
- go test -v .\... # No -race because cgo is disabled

15
src/vendor/go.opencensus.io/go.mod generated vendored
View File

@ -1,15 +0,0 @@
module go.opencensus.io
require (
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6
github.com/golang/protobuf v1.3.1
github.com/google/go-cmp v0.3.0
github.com/stretchr/testify v1.4.0
golang.org/x/net v0.0.0-20190620200207-3b0461eec859
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd // indirect
golang.org/x/text v0.3.2 // indirect
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb // indirect
google.golang.org/grpc v1.20.1
)
go 1.13

74
src/vendor/go.opencensus.io/go.sum generated vendored
View File

@ -1,74 +0,0 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3 h1:XQyxROzUlZH+WIQwySDgnISgOivlhjIEwaQaJEJrrN0=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTmV7VDcZyvRZ+QQXkXTZQ=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 h1:bjcUS9ztw9kFmmIxJInhon/0Is3p+EHBKNgquIzo1OI=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd h1:r7DufRZuZbWB7j439YfAzP8RPDa9unLkpwQKUYbIMPI=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd h1:/e+gpKk9r3dJobndpTytxS2gOy6m5uvpg+ISQoEcusQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb h1:i1Ppqkc3WQXikh8bXiwHqAN5Rv3/qDCcRk0/Otx73BY=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/grpc v1.19.0 h1:cfg4PD8YEdSFnm7qLV4++93WcmhH2nIUhMjhdCvl3j8=
google.golang.org/grpc v1.19.0 h1:cfg4PD8YEdSFnm7qLV4++93WcmhH2nIUhMjhdCvl3j8=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1 h1:Hz2g2wirWK7H0qIIhGIqRGTuMwTE8HEKFnDZZ7lm9NU=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@ -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"
opencensus "go.opencensus.io"
)
// UserAgent is the user agent to be added to the outgoing
// requests from the exporters.
var UserAgent = fmt.Sprintf("opencensus-go/%s", opencensus.Version())
// MonotonicEndTime returns the end time at present
// but offset from start, monotonically.
//
// The monotonic clock is used in subtractions hence
// the duration since start added back to start gives
// end as a monotonic time.
// See https://golang.org/pkg/time/#hdr-Monotonic_Clocks
func MonotonicEndTime(start time.Time) time.Time {
return start.Add(time.Since(start))
}

View File

@ -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 '_'
}

View File

@ -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 tagencoding contains the tag encoding
// used interally by the stats collector.
package tagencoding // import "go.opencensus.io/internal/tagencoding"
// Values represent the encoded buffer for the values.
type Values struct {
Buffer []byte
WriteIndex int
ReadIndex int
}
func (vb *Values) growIfRequired(expected int) {
if len(vb.Buffer)-vb.WriteIndex < expected {
tmp := make([]byte, 2*(len(vb.Buffer)+1)+expected)
copy(tmp, vb.Buffer)
vb.Buffer = tmp
}
}
// WriteValue is the helper method to encode Values from map[Key][]byte.
func (vb *Values) WriteValue(v []byte) {
length := len(v) & 0xff
vb.growIfRequired(1 + length)
// writing length of v
vb.Buffer[vb.WriteIndex] = byte(length)
vb.WriteIndex++
if length == 0 {
// No value was encoded for this key
return
}
// writing v
copy(vb.Buffer[vb.WriteIndex:], v[:length])
vb.WriteIndex += length
}
// ReadValue is the helper method to decode Values to a map[Key][]byte.
func (vb *Values) ReadValue() []byte {
// read length of v
length := int(vb.Buffer[vb.ReadIndex])
vb.ReadIndex++
if length == 0 {
// No value was encoded for this key
return nil
}
// read value of v
v := make([]byte, length)
endIdx := vb.ReadIndex + length
copy(v, vb.Buffer[vb.ReadIndex:endIdx])
vb.ReadIndex = endIdx
return v
}
// Bytes returns a reference to already written bytes in the Buffer.
func (vb *Values) Bytes() []byte {
return vb.Buffer[:vb.WriteIndex]
}

View File

@ -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 internal
import (
"time"
)
// Trace allows internal access to some trace functionality.
// TODO(#412): remove this
var Trace interface{}
// LocalSpanStoreEnabled true if the local span store is enabled.
var LocalSpanStoreEnabled bool
// BucketConfiguration stores the number of samples to store for span buckets
// for successful and failed spans for a particular span name.
type BucketConfiguration struct {
Name string
MaxRequestsSucceeded int
MaxRequestsErrors int
}
// PerMethodSummary is a summary of the spans stored for a single span name.
type PerMethodSummary struct {
Active int
LatencyBuckets []LatencyBucketSummary
ErrorBuckets []ErrorBucketSummary
}
// LatencyBucketSummary is a summary of a latency bucket.
type LatencyBucketSummary struct {
MinLatency, MaxLatency time.Duration
Size int
}
// ErrorBucketSummary is a summary of an error bucket.
type ErrorBucketSummary struct {
ErrorCode int32
Size int
}

View File

@ -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 metricdata contains the metrics data model.
//
// This is an EXPERIMENTAL package, and may change in arbitrary ways without
// notice.
package metricdata // import "go.opencensus.io/metric/metricdata"

View File

@ -1,38 +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 metricdata
import (
"time"
)
// Exemplars keys.
const (
AttachmentKeySpanContext = "SpanContext"
)
// Exemplar is an example data point associated with each bucket of a
// distribution type aggregation.
//
// Their purpose is to provide an example of the kind of thing
// (request, RPC, trace span, etc.) that resulted in that measurement.
type Exemplar struct {
Value float64 // the value that was recorded
Timestamp time.Time // the time the value was recorded
Attachments Attachments // attachments (if any)
}
// Attachments is a map of extra values associated with a recorded data point.
type Attachments map[string]interface{}

View File

@ -1,35 +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 metricdata
// LabelKey represents key of a label. It has optional
// description attribute.
type LabelKey struct {
Key string
Description string
}
// LabelValue represents the value of a label.
// The zero value represents a missing label value, which may be treated
// differently to an empty string value by some back ends.
type LabelValue struct {
Value string // string value of the label
Present bool // flag that indicated whether a value is present or not
}
// NewLabelValue creates a new non-nil LabelValue that represents the given string.
func NewLabelValue(val string) LabelValue {
return LabelValue{Value: val, Present: true}
}

View File

@ -1,46 +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 metricdata
import (
"time"
"go.opencensus.io/resource"
)
// Descriptor holds metadata about a metric.
type Descriptor struct {
Name string // full name of the metric
Description string // human-readable description
Unit Unit // units for the measure
Type Type // type of measure
LabelKeys []LabelKey // label keys
}
// Metric represents a quantity measured against a resource with different
// label value combinations.
type Metric struct {
Descriptor Descriptor // metric descriptor
Resource *resource.Resource // resource against which this was measured
TimeSeries []*TimeSeries // one time series for each combination of label values
}
// TimeSeries is a sequence of points associated with a combination of label
// values.
type TimeSeries struct {
LabelValues []LabelValue // label values, same order as keys in the metric descriptor
Points []Point // points sequence
StartTime time.Time // time we started recording this time series
}

View File

@ -1,193 +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 metricdata
import (
"time"
)
// Point is a single data point of a time series.
type Point struct {
// Time is the point in time that this point represents in a time series.
Time time.Time
// Value is the value of this point. Prefer using ReadValue to switching on
// the value type, since new value types might be added.
Value interface{}
}
//go:generate stringer -type ValueType
// NewFloat64Point creates a new Point holding a float64 value.
func NewFloat64Point(t time.Time, val float64) Point {
return Point{
Value: val,
Time: t,
}
}
// NewInt64Point creates a new Point holding an int64 value.
func NewInt64Point(t time.Time, val int64) Point {
return Point{
Value: val,
Time: t,
}
}
// NewDistributionPoint creates a new Point holding a Distribution value.
func NewDistributionPoint(t time.Time, val *Distribution) Point {
return Point{
Value: val,
Time: t,
}
}
// NewSummaryPoint creates a new Point holding a Summary value.
func NewSummaryPoint(t time.Time, val *Summary) Point {
return Point{
Value: val,
Time: t,
}
}
// ValueVisitor allows reading the value of a point.
type ValueVisitor interface {
VisitFloat64Value(float64)
VisitInt64Value(int64)
VisitDistributionValue(*Distribution)
VisitSummaryValue(*Summary)
}
// ReadValue accepts a ValueVisitor and calls the appropriate method with the
// value of this point.
// Consumers of Point should use this in preference to switching on the type
// of the value directly, since new value types may be added.
func (p Point) ReadValue(vv ValueVisitor) {
switch v := p.Value.(type) {
case int64:
vv.VisitInt64Value(v)
case float64:
vv.VisitFloat64Value(v)
case *Distribution:
vv.VisitDistributionValue(v)
case *Summary:
vv.VisitSummaryValue(v)
default:
panic("unexpected value type")
}
}
// Distribution contains summary statistics for a population of values. It
// optionally contains a histogram representing the distribution of those
// values across a set of buckets.
type Distribution struct {
// Count is the number of values in the population. Must be non-negative. This value
// must equal the sum of the values in bucket_counts if a histogram is
// provided.
Count int64
// Sum is the sum of the values in the population. If count is zero then this field
// must be zero.
Sum float64
// SumOfSquaredDeviation is the sum of squared deviations from the mean of the values in the
// population. For values x_i this is:
//
// Sum[i=1..n]((x_i - mean)^2)
//
// Knuth, "The Art of Computer Programming", Vol. 2, page 323, 3rd edition
// describes Welford's method for accumulating this sum in one pass.
//
// If count is zero then this field must be zero.
SumOfSquaredDeviation float64
// BucketOptions describes the bounds of the histogram buckets in this
// distribution.
//
// A Distribution may optionally contain a histogram of the values in the
// population.
//
// If nil, there is no associated histogram.
BucketOptions *BucketOptions
// Bucket If the distribution does not have a histogram, then omit this field.
// If there is a histogram, then the sum of the values in the Bucket counts
// must equal the value in the count field of the distribution.
Buckets []Bucket
}
// BucketOptions describes the bounds of the histogram buckets in this
// distribution.
type BucketOptions struct {
// Bounds specifies a set of bucket upper bounds.
// This defines len(bounds) + 1 (= N) buckets. The boundaries for bucket
// index i are:
//
// [0, Bounds[i]) for i == 0
// [Bounds[i-1], Bounds[i]) for 0 < i < N-1
// [Bounds[i-1], +infinity) for i == N-1
Bounds []float64
}
// Bucket represents a single bucket (value range) in a distribution.
type Bucket struct {
// Count is the number of values in each bucket of the histogram, as described in
// bucket_bounds.
Count int64
// Exemplar associated with this bucket (if any).
Exemplar *Exemplar
}
// Summary is a representation of percentiles.
type Summary struct {
// Count is the cumulative count (if available).
Count int64
// Sum is the cumulative sum of values (if available).
Sum float64
// HasCountAndSum is true if Count and Sum are available.
HasCountAndSum bool
// Snapshot represents percentiles calculated over an arbitrary time window.
// The values in this struct can be reset at arbitrary unknown times, with
// the requirement that all of them are reset at the same time.
Snapshot Snapshot
}
// Snapshot represents percentiles over an arbitrary time.
// The values in this struct can be reset at arbitrary unknown times, with
// the requirement that all of them are reset at the same time.
type Snapshot struct {
// Count is the number of values in the snapshot. Optional since some systems don't
// expose this. Set to 0 if not available.
Count int64
// Sum is the sum of values in the snapshot. Optional since some systems don't
// expose this. If count is 0 then this field must be zero.
Sum float64
// Percentiles is a map from percentile (range (0-100.0]) to the value of
// the percentile.
Percentiles map[float64]float64
}
//go:generate stringer -type Type
// Type is the overall type of metric, including its value type and whether it
// represents a cumulative total (since the start time) or if it represents a
// gauge value.
type Type int
// Metric types.
const (
TypeGaugeInt64 Type = iota
TypeGaugeFloat64
TypeGaugeDistribution
TypeCumulativeInt64
TypeCumulativeFloat64
TypeCumulativeDistribution
TypeSummary
)

View File

@ -1,16 +0,0 @@
// Code generated by "stringer -type Type"; DO NOT EDIT.
package metricdata
import "strconv"
const _Type_name = "TypeGaugeInt64TypeGaugeFloat64TypeGaugeDistributionTypeCumulativeInt64TypeCumulativeFloat64TypeCumulativeDistributionTypeSummary"
var _Type_index = [...]uint8{0, 14, 30, 51, 70, 91, 117, 128}
func (i Type) String() string {
if i < 0 || i >= Type(len(_Type_index)-1) {
return "Type(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _Type_name[_Type_index[i]:_Type_index[i+1]]
}

View File

@ -1,27 +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 metricdata
// Unit is a string encoded according to the case-sensitive abbreviations from the
// Unified Code for Units of Measure: http://unitsofmeasure.org/ucum.html
type Unit string
// Predefined units. To record against a unit not represented here, create your
// own Unit type constant from a string.
const (
UnitDimensionless Unit = "1"
UnitBytes Unit = "By"
UnitMilliseconds Unit = "ms"
)

View File

@ -1,78 +0,0 @@
// Copyright 2019, OpenCensus Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package metricproducer
import (
"sync"
)
// Manager maintains a list of active producers. Producers can register
// with the manager to allow readers to read all metrics provided by them.
// Readers can retrieve all producers registered with the manager,
// read metrics from the producers and export them.
type Manager struct {
mu sync.RWMutex
producers map[Producer]struct{}
}
var prodMgr *Manager
var once sync.Once
// GlobalManager is a single instance of producer manager
// that is used by all producers and all readers.
func GlobalManager() *Manager {
once.Do(func() {
prodMgr = &Manager{}
prodMgr.producers = make(map[Producer]struct{})
})
return prodMgr
}
// AddProducer adds the producer to the Manager if it is not already present.
func (pm *Manager) AddProducer(producer Producer) {
if producer == nil {
return
}
pm.mu.Lock()
defer pm.mu.Unlock()
pm.producers[producer] = struct{}{}
}
// DeleteProducer deletes the producer from the Manager if it is present.
func (pm *Manager) DeleteProducer(producer Producer) {
if producer == nil {
return
}
pm.mu.Lock()
defer pm.mu.Unlock()
delete(pm.producers, producer)
}
// GetAll returns a slice of all producer currently registered with
// the Manager. For each call it generates a new slice. The slice
// should not be cached as registration may change at any time. It is
// typically called periodically by exporter to read metrics from
// the producers.
func (pm *Manager) GetAll() []Producer {
pm.mu.Lock()
defer pm.mu.Unlock()
producers := make([]Producer, len(pm.producers))
i := 0
for producer := range pm.producers {
producers[i] = producer
i++
}
return producers
}

View File

@ -1,28 +0,0 @@
// Copyright 2019, OpenCensus Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package metricproducer
import (
"go.opencensus.io/metric/metricdata"
)
// Producer is a source of metrics.
type Producer interface {
// Read should return the current values of all metrics supported by this
// metric provider.
// The returned metrics should be unique for each combination of name and
// resource.
Read() []*metricdata.Metric
}

View File

@ -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.23.0"
}

View File

@ -1,117 +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
// GetStartOptions allows to set start options per request. If set,
// StartOptions is going to be ignored.
GetStartOptions func(*http.Request) trace.StartOptions
// NameFromRequest holds the function to use for generating the span name
// from the information found in the outgoing HTTP Request. By default the
// name equals the URL Path.
FormatSpanName func(*http.Request) string
// NewClientTrace may be set to a function allowing the current *trace.Span
// to be annotated with HTTP request event information emitted by the
// httptrace package.
NewClientTrace func(*http.Request, *trace.Span) *httptrace.ClientTrace
// TODO: Implement tag propagation for HTTP.
}
// RoundTrip implements http.RoundTripper, delegating to Base and recording stats and traces for the request.
func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
rt := t.base()
if isHealthEndpoint(req.URL.Path) {
return rt.RoundTrip(req)
}
// TODO: remove excessive nesting of http.RoundTrippers here.
format := t.Propagation
if format == nil {
format = defaultFormat
}
spanNameFormatter := t.FormatSpanName
if spanNameFormatter == nil {
spanNameFormatter = spanNameFromURL
}
startOpts := t.StartOptions
if t.GetStartOptions != nil {
startOpts = t.GetStartOptions(req)
}
rt = &traceTransport{
base: rt,
format: format,
startOptions: trace.StartOptions{
Sampler: startOpts.Sampler,
SpanKind: trace.SpanKindClient,
},
formatSpanName: spanNameFormatter,
newClientTrace: t.NewClientTrace,
}
rt = statsTransport{base: rt}
return rt.RoundTrip(req)
}
func (t *Transport) base() http.RoundTripper {
if t.Base != nil {
return t.Base
}
return http.DefaultTransport
}
// CancelRequest cancels an in-flight request by closing its connection.
func (t *Transport) CancelRequest(req *http.Request) {
type canceler interface {
CancelRequest(*http.Request)
}
if cr, ok := t.base().(canceler); ok {
cr.CancelRequest(req)
}
}

View File

@ -1,143 +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(KeyClientHost, req.Host),
tag.Upsert(Host, req.Host),
tag.Upsert(KeyClientPath, req.URL.Path),
tag.Upsert(Path, req.URL.Path),
tag.Upsert(KeyClientMethod, req.Method),
tag.Upsert(Method, req.Method))
req = req.WithContext(ctx)
track := &tracker{
start: time.Now(),
ctx: ctx,
}
if req.Body == nil {
// TODO: Handle cases where ContentLength is not set.
track.reqSize = -1
} else if req.ContentLength > 0 {
track.reqSize = req.ContentLength
}
stats.Record(ctx, ClientRequestCount.M(1))
// Perform request.
resp, err := t.base.RoundTrip(req)
if err != nil {
track.statusCode = http.StatusInternalServerError
track.end()
} else {
track.statusCode = resp.StatusCode
if req.Method != "HEAD" {
track.respContentLength = resp.ContentLength
}
if resp.Body == nil {
track.end()
} else {
track.body = resp.Body
resp.Body = wrappedBody(track, resp.Body)
}
}
return resp, err
}
// CancelRequest cancels an in-flight request by closing its connection.
func (t statsTransport) CancelRequest(req *http.Request) {
type canceler interface {
CancelRequest(*http.Request)
}
if cr, ok := t.base.(canceler); ok {
cr.CancelRequest(req)
}
}
type tracker struct {
ctx context.Context
respSize int64
respContentLength int64
reqSize int64
start time.Time
body io.ReadCloser
statusCode int
endOnce sync.Once
}
var _ io.ReadCloser = (*tracker)(nil)
func (t *tracker) end() {
t.endOnce.Do(func() {
latencyMs := float64(time.Since(t.start)) / float64(time.Millisecond)
respSize := t.respSize
if t.respSize == 0 && t.respContentLength > 0 {
respSize = t.respContentLength
}
m := []stats.Measurement{
ClientSentBytes.M(t.reqSize),
ClientReceivedBytes.M(respSize),
ClientRoundtripLatency.M(latencyMs),
ClientLatency.M(latencyMs),
ClientResponseBytes.M(t.respSize),
}
if t.reqSize >= 0 {
m = append(m, ClientRequestBytes.M(t.reqSize))
}
stats.RecordWithTags(t.ctx, []tag.Mutator{
tag.Upsert(StatusCode, strconv.Itoa(t.statusCode)),
tag.Upsert(KeyClientStatus, strconv.Itoa(t.statusCode)),
}, m...)
})
}
func (t *tracker) Read(b []byte) (int, error) {
n, err := t.body.Read(b)
t.respSize += int64(n)
switch err {
case nil:
return n, nil
case io.EOF:
t.end()
}
return n, err
}
func (t *tracker) Close() error {
// Invoking endSpan on Close will help catch the cases
// in which a read returned a non-nil error, we set the
// span status but didn't end the span.
t.end()
return t.body.Close()
}

View File

@ -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"

View File

@ -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, receiver of the outgoing spans should use client-side
// span created by OpenCensus as the parent.
type HTTPFormat struct{}
var _ propagation.HTTPFormat = (*HTTPFormat)(nil)
// SpanContextFromRequest extracts a B3 span context from incoming requests.
func (f *HTTPFormat) SpanContextFromRequest(req *http.Request) (sc trace.SpanContext, ok bool) {
tid, ok := ParseTraceID(req.Header.Get(TraceIDHeader))
if !ok {
return trace.SpanContext{}, false
}
sid, ok := ParseSpanID(req.Header.Get(SpanIDHeader))
if !ok {
return trace.SpanContext{}, false
}
sampled, _ := ParseSampled(req.Header.Get(SampledHeader))
return trace.SpanContext{
TraceID: tid,
SpanID: sid,
TraceOptions: sampled,
}, true
}
// ParseTraceID parses the value of the X-B3-TraceId header.
func ParseTraceID(tid string) (trace.TraceID, bool) {
if tid == "" {
return trace.TraceID{}, false
}
b, err := hex.DecodeString(tid)
if err != nil {
return trace.TraceID{}, false
}
var traceID trace.TraceID
if len(b) <= 8 {
// The lower 64-bits.
start := 8 + (8 - len(b))
copy(traceID[start:], b)
} else {
start := 16 - len(b)
copy(traceID[start:], b)
}
return traceID, true
}
// ParseSpanID parses the value of the X-B3-SpanId or X-B3-ParentSpanId headers.
func ParseSpanID(sid string) (spanID trace.SpanID, ok bool) {
if sid == "" {
return trace.SpanID{}, false
}
b, err := hex.DecodeString(sid)
if err != nil {
return trace.SpanID{}, false
}
start := 8 - len(b)
copy(spanID[start:], b)
return spanID, true
}
// ParseSampled parses the value of the X-B3-Sampled header.
func ParseSampled(sampled string) (trace.TraceOptions, bool) {
switch sampled {
case "true", "1":
return trace.TraceOptions(1), true
default:
return trace.TraceOptions(0), false
}
}
// SpanContextToRequest modifies the given request to include B3 headers.
func (f *HTTPFormat) SpanContextToRequest(sc trace.SpanContext, req *http.Request) {
req.Header.Set(TraceIDHeader, hex.EncodeToString(sc.TraceID[:]))
req.Header.Set(SpanIDHeader, hex.EncodeToString(sc.SpanID[:]))
var sampled string
if sc.IsSampled() {
sampled = "1"
} else {
sampled = "0"
}
req.Header.Set(SampledHeader, sampled)
}

View File

@ -1,61 +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"
"net/http"
"go.opencensus.io/tag"
)
// SetRoute sets the http_server_route tag to the given value.
// It's useful when an HTTP framework does not support the http.Handler interface
// and using WithRouteTag is not an option, but provides a way to hook into the request flow.
func SetRoute(ctx context.Context, route string) {
if a, ok := ctx.Value(addedTagsKey{}).(*addedTags); ok {
a.t = append(a.t, tag.Upsert(KeyServerRoute, route))
}
}
// WithRouteTag returns an http.Handler that records stats with the
// http_server_route tag set to the given value.
func WithRouteTag(handler http.Handler, route string) http.Handler {
return taggedHandlerFunc(func(w http.ResponseWriter, r *http.Request) []tag.Mutator {
addRoute := []tag.Mutator{tag.Upsert(KeyServerRoute, route)}
ctx, _ := tag.New(r.Context(), addRoute...)
r = r.WithContext(ctx)
handler.ServeHTTP(w, r)
return addRoute
})
}
// taggedHandlerFunc is a http.Handler that returns tags describing the
// processing of the request. These tags will be recorded along with the
// measures in this package at the end of the request.
type taggedHandlerFunc func(w http.ResponseWriter, r *http.Request) []tag.Mutator
func (h taggedHandlerFunc) ServeHTTP(w http.ResponseWriter, r *http.Request) {
tags := h(w, r)
if a, ok := r.Context().Value(addedTagsKey{}).(*addedTags); ok {
a.t = append(a.t, tags...)
}
}
type addedTagsKey struct{}
type addedTags struct {
t []tag.Mutator
}

View File

@ -1,453 +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
// GetStartOptions allows to set start options per request. If set,
// StartOptions is going to be ignored.
GetStartOptions func(*http.Request) trace.StartOptions
// IsPublicEndpoint should be set to true for publicly accessible HTTP(S)
// servers. If true, any trace metadata set on the incoming request will
// be added as a linked trace instead of being added as a parent of the
// current trace.
IsPublicEndpoint bool
// FormatSpanName holds the function to use for generating the span name
// from the information found in the incoming HTTP Request. By default the
// name equals the URL Path.
FormatSpanName func(*http.Request) string
// IsHealthEndpoint holds the function to use for determining if the
// incoming HTTP request should be considered a health check. This is in
// addition to the private isHealthEndpoint func which may also indicate
// tracing should be skipped.
IsHealthEndpoint func(*http.Request) bool
}
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 h.IsHealthEndpoint != nil && h.IsHealthEndpoint(r) || isHealthEndpoint(r.URL.Path) {
return r, func() {}
}
var name string
if h.FormatSpanName == nil {
name = spanNameFromURL(r)
} else {
name = h.FormatSpanName(r)
}
ctx := r.Context()
startOpts := h.StartOptions
if h.GetStartOptions != nil {
startOpts = h.GetStartOptions(r)
}
var span *trace.Span
sc, ok := h.extractSpanContext(r)
if ok && !h.IsPublicEndpoint {
ctx, span = trace.StartSpanWithRemoteParent(ctx, name, sc,
trace.WithSampler(startOpts.Sampler),
trace.WithSpanKind(trace.SpanKindServer))
} else {
ctx, span = trace.StartSpan(ctx, name,
trace.WithSampler(startOpts.Sampler),
trace.WithSpanKind(trace.SpanKindServer),
)
if ok {
span.AddLink(trace.Link{
TraceID: sc.TraceID,
SpanID: sc.SpanID,
Type: trace.LinkTypeParent,
Attributes: nil,
})
}
}
span.AddAttributes(requestAttrs(r)...)
if r.Body == nil {
// TODO: Handle cases where ContentLength is not set.
} else if r.ContentLength > 0 {
span.AddMessageReceiveEvent(0, /* TODO: messageID */
r.ContentLength, -1)
}
return r.WithContext(ctx), span.End
}
func (h *Handler) extractSpanContext(r *http.Request) (trace.SpanContext, bool) {
if h.Propagation == nil {
return defaultFormat.SpanContextFromRequest(r)
}
return h.Propagation.SpanContextFromRequest(r)
}
func (h *Handler) startStats(w http.ResponseWriter, r *http.Request) (http.ResponseWriter, func(tags *addedTags)) {
ctx, _ := tag.New(r.Context(),
tag.Upsert(Host, r.Host),
tag.Upsert(Path, r.URL.Path),
tag.Upsert(Method, r.Method))
track := &trackingResponseWriter{
start: time.Now(),
ctx: ctx,
writer: w,
}
if r.Body == nil {
// TODO: Handle cases where ContentLength is not set.
track.reqSize = -1
} else if r.ContentLength > 0 {
track.reqSize = r.ContentLength
}
stats.Record(ctx, ServerRequestCount.M(1))
return track.wrappedResponseWriter(), track.end
}
type trackingResponseWriter struct {
ctx context.Context
reqSize int64
respSize int64
start time.Time
statusCode int
statusLine string
endOnce sync.Once
writer http.ResponseWriter
}
// Compile time assertion for ResponseWriter interface
var _ http.ResponseWriter = (*trackingResponseWriter)(nil)
func (t *trackingResponseWriter) end(tags *addedTags) {
t.endOnce.Do(func() {
if t.statusCode == 0 {
t.statusCode = 200
}
span := trace.FromContext(t.ctx)
span.SetStatus(TraceStatus(t.statusCode, t.statusLine))
span.AddAttributes(trace.Int64Attribute(StatusCodeAttribute, int64(t.statusCode)))
m := []stats.Measurement{
ServerLatency.M(float64(time.Since(t.start)) / float64(time.Millisecond)),
ServerResponseBytes.M(t.respSize),
}
if t.reqSize >= 0 {
m = append(m, ServerRequestBytes.M(t.reqSize))
}
allTags := make([]tag.Mutator, len(tags.t)+1)
allTags[0] = tag.Upsert(StatusCode, strconv.Itoa(t.statusCode))
copy(allTags[1:], tags.t)
stats.RecordWithTags(t.ctx, allTags, m...)
})
}
func (t *trackingResponseWriter) Header() http.Header {
return t.writer.Header()
}
func (t *trackingResponseWriter) Write(data []byte) (int, error) {
n, err := t.writer.Write(data)
t.respSize += int64(n)
// Add message event for request bytes sent.
span := trace.FromContext(t.ctx)
span.AddMessageSendEvent(0 /* TODO: messageID */, int64(n), -1)
return n, err
}
func (t *trackingResponseWriter) WriteHeader(statusCode int) {
t.writer.WriteHeader(statusCode)
t.statusCode = statusCode
t.statusLine = http.StatusText(t.statusCode)
}
// wrappedResponseWriter returns a wrapped version of the original
// ResponseWriter and only implements the same combination of additional
// interfaces as the original.
// This implementation is based on https://github.com/felixge/httpsnoop.
func (t *trackingResponseWriter) wrappedResponseWriter() http.ResponseWriter {
var (
hj, i0 = t.writer.(http.Hijacker)
cn, i1 = t.writer.(http.CloseNotifier)
pu, i2 = t.writer.(http.Pusher)
fl, i3 = t.writer.(http.Flusher)
rf, i4 = t.writer.(io.ReaderFrom)
)
switch {
case !i0 && !i1 && !i2 && !i3 && !i4:
return struct {
http.ResponseWriter
}{t}
case !i0 && !i1 && !i2 && !i3 && i4:
return struct {
http.ResponseWriter
io.ReaderFrom
}{t, rf}
case !i0 && !i1 && !i2 && i3 && !i4:
return struct {
http.ResponseWriter
http.Flusher
}{t, fl}
case !i0 && !i1 && !i2 && i3 && i4:
return struct {
http.ResponseWriter
http.Flusher
io.ReaderFrom
}{t, fl, rf}
case !i0 && !i1 && i2 && !i3 && !i4:
return struct {
http.ResponseWriter
http.Pusher
}{t, pu}
case !i0 && !i1 && i2 && !i3 && i4:
return struct {
http.ResponseWriter
http.Pusher
io.ReaderFrom
}{t, pu, rf}
case !i0 && !i1 && i2 && i3 && !i4:
return struct {
http.ResponseWriter
http.Pusher
http.Flusher
}{t, pu, fl}
case !i0 && !i1 && i2 && i3 && i4:
return struct {
http.ResponseWriter
http.Pusher
http.Flusher
io.ReaderFrom
}{t, pu, fl, rf}
case !i0 && i1 && !i2 && !i3 && !i4:
return struct {
http.ResponseWriter
http.CloseNotifier
}{t, cn}
case !i0 && i1 && !i2 && !i3 && i4:
return struct {
http.ResponseWriter
http.CloseNotifier
io.ReaderFrom
}{t, cn, rf}
case !i0 && i1 && !i2 && i3 && !i4:
return struct {
http.ResponseWriter
http.CloseNotifier
http.Flusher
}{t, cn, fl}
case !i0 && i1 && !i2 && i3 && i4:
return struct {
http.ResponseWriter
http.CloseNotifier
http.Flusher
io.ReaderFrom
}{t, cn, fl, rf}
case !i0 && i1 && i2 && !i3 && !i4:
return struct {
http.ResponseWriter
http.CloseNotifier
http.Pusher
}{t, cn, pu}
case !i0 && i1 && i2 && !i3 && i4:
return struct {
http.ResponseWriter
http.CloseNotifier
http.Pusher
io.ReaderFrom
}{t, cn, pu, rf}
case !i0 && i1 && i2 && i3 && !i4:
return struct {
http.ResponseWriter
http.CloseNotifier
http.Pusher
http.Flusher
}{t, cn, pu, fl}
case !i0 && i1 && i2 && i3 && i4:
return struct {
http.ResponseWriter
http.CloseNotifier
http.Pusher
http.Flusher
io.ReaderFrom
}{t, cn, pu, fl, rf}
case i0 && !i1 && !i2 && !i3 && !i4:
return struct {
http.ResponseWriter
http.Hijacker
}{t, hj}
case i0 && !i1 && !i2 && !i3 && i4:
return struct {
http.ResponseWriter
http.Hijacker
io.ReaderFrom
}{t, hj, rf}
case i0 && !i1 && !i2 && i3 && !i4:
return struct {
http.ResponseWriter
http.Hijacker
http.Flusher
}{t, hj, fl}
case i0 && !i1 && !i2 && i3 && i4:
return struct {
http.ResponseWriter
http.Hijacker
http.Flusher
io.ReaderFrom
}{t, hj, fl, rf}
case i0 && !i1 && i2 && !i3 && !i4:
return struct {
http.ResponseWriter
http.Hijacker
http.Pusher
}{t, hj, pu}
case i0 && !i1 && i2 && !i3 && i4:
return struct {
http.ResponseWriter
http.Hijacker
http.Pusher
io.ReaderFrom
}{t, hj, pu, rf}
case i0 && !i1 && i2 && i3 && !i4:
return struct {
http.ResponseWriter
http.Hijacker
http.Pusher
http.Flusher
}{t, hj, pu, fl}
case i0 && !i1 && i2 && i3 && i4:
return struct {
http.ResponseWriter
http.Hijacker
http.Pusher
http.Flusher
io.ReaderFrom
}{t, hj, pu, fl, rf}
case i0 && i1 && !i2 && !i3 && !i4:
return struct {
http.ResponseWriter
http.Hijacker
http.CloseNotifier
}{t, hj, cn}
case i0 && i1 && !i2 && !i3 && i4:
return struct {
http.ResponseWriter
http.Hijacker
http.CloseNotifier
io.ReaderFrom
}{t, hj, cn, rf}
case i0 && i1 && !i2 && i3 && !i4:
return struct {
http.ResponseWriter
http.Hijacker
http.CloseNotifier
http.Flusher
}{t, hj, cn, fl}
case i0 && i1 && !i2 && i3 && i4:
return struct {
http.ResponseWriter
http.Hijacker
http.CloseNotifier
http.Flusher
io.ReaderFrom
}{t, hj, cn, fl, rf}
case i0 && i1 && i2 && !i3 && !i4:
return struct {
http.ResponseWriter
http.Hijacker
http.CloseNotifier
http.Pusher
}{t, hj, cn, pu}
case i0 && i1 && i2 && !i3 && i4:
return struct {
http.ResponseWriter
http.Hijacker
http.CloseNotifier
http.Pusher
io.ReaderFrom
}{t, hj, cn, pu, rf}
case i0 && i1 && i2 && i3 && !i4:
return struct {
http.ResponseWriter
http.Hijacker
http.CloseNotifier
http.Pusher
http.Flusher
}{t, hj, cn, pu, fl}
case i0 && i1 && i2 && i3 && i4:
return struct {
http.ResponseWriter
http.Hijacker
http.CloseNotifier
http.Pusher
http.Flusher
io.ReaderFrom
}{t, hj, cn, pu, fl, rf}
default:
return struct {
http.ResponseWriter
}{t}
}
}

View File

@ -1,169 +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.
// NewSpanAnnotator returns a httptrace.ClientTrace which annotates
// all emitted httptrace events on the provided Span.
// Deprecated: Use NewSpanAnnotatingClientTrace instead
func NewSpanAnnotator(r *http.Request, s *trace.Span) *httptrace.ClientTrace {
return NewSpanAnnotatingClientTrace(r, s)
}
// NewSpanAnnotatingClientTrace returns a httptrace.ClientTrace which annotates
// all emitted httptrace events on the provided Span.
func NewSpanAnnotatingClientTrace(_ *http.Request, s *trace.Span) *httptrace.ClientTrace {
sa := spanAnnotator{sp: s}
return &httptrace.ClientTrace{
GetConn: sa.getConn,
GotConn: sa.gotConn,
PutIdleConn: sa.putIdleConn,
GotFirstResponseByte: sa.gotFirstResponseByte,
Got100Continue: sa.got100Continue,
DNSStart: sa.dnsStart,
DNSDone: sa.dnsDone,
ConnectStart: sa.connectStart,
ConnectDone: sa.connectDone,
TLSHandshakeStart: sa.tlsHandshakeStart,
TLSHandshakeDone: sa.tlsHandshakeDone,
WroteHeaders: sa.wroteHeaders,
Wait100Continue: sa.wait100Continue,
WroteRequest: sa.wroteRequest,
}
}
func (s spanAnnotator) getConn(hostPort string) {
attrs := []trace.Attribute{
trace.StringAttribute("httptrace.get_connection.host_port", hostPort),
}
s.sp.Annotate(attrs, "GetConn")
}
func (s spanAnnotator) gotConn(info httptrace.GotConnInfo) {
attrs := []trace.Attribute{
trace.BoolAttribute("httptrace.got_connection.reused", info.Reused),
trace.BoolAttribute("httptrace.got_connection.was_idle", info.WasIdle),
}
if info.WasIdle {
attrs = append(attrs,
trace.StringAttribute("httptrace.got_connection.idle_time", info.IdleTime.String()))
}
s.sp.Annotate(attrs, "GotConn")
}
// PutIdleConn implements a httptrace.ClientTrace hook
func (s spanAnnotator) putIdleConn(err error) {
var attrs []trace.Attribute
if err != nil {
attrs = append(attrs,
trace.StringAttribute("httptrace.put_idle_connection.error", err.Error()))
}
s.sp.Annotate(attrs, "PutIdleConn")
}
func (s spanAnnotator) gotFirstResponseByte() {
s.sp.Annotate(nil, "GotFirstResponseByte")
}
func (s spanAnnotator) got100Continue() {
s.sp.Annotate(nil, "Got100Continue")
}
func (s spanAnnotator) dnsStart(info httptrace.DNSStartInfo) {
attrs := []trace.Attribute{
trace.StringAttribute("httptrace.dns_start.host", info.Host),
}
s.sp.Annotate(attrs, "DNSStart")
}
func (s spanAnnotator) dnsDone(info httptrace.DNSDoneInfo) {
var addrs []string
for _, addr := range info.Addrs {
addrs = append(addrs, addr.String())
}
attrs := []trace.Attribute{
trace.StringAttribute("httptrace.dns_done.addrs", strings.Join(addrs, " , ")),
}
if info.Err != nil {
attrs = append(attrs,
trace.StringAttribute("httptrace.dns_done.error", info.Err.Error()))
}
s.sp.Annotate(attrs, "DNSDone")
}
func (s spanAnnotator) connectStart(network, addr string) {
attrs := []trace.Attribute{
trace.StringAttribute("httptrace.connect_start.network", network),
trace.StringAttribute("httptrace.connect_start.addr", addr),
}
s.sp.Annotate(attrs, "ConnectStart")
}
func (s spanAnnotator) connectDone(network, addr string, err error) {
attrs := []trace.Attribute{
trace.StringAttribute("httptrace.connect_done.network", network),
trace.StringAttribute("httptrace.connect_done.addr", addr),
}
if err != nil {
attrs = append(attrs,
trace.StringAttribute("httptrace.connect_done.error", err.Error()))
}
s.sp.Annotate(attrs, "ConnectDone")
}
func (s spanAnnotator) tlsHandshakeStart() {
s.sp.Annotate(nil, "TLSHandshakeStart")
}
func (s spanAnnotator) tlsHandshakeDone(_ tls.ConnectionState, err error) {
var attrs []trace.Attribute
if err != nil {
attrs = append(attrs,
trace.StringAttribute("httptrace.tls_handshake_done.error", err.Error()))
}
s.sp.Annotate(attrs, "TLSHandshakeDone")
}
func (s spanAnnotator) wroteHeaders() {
s.sp.Annotate(nil, "WroteHeaders")
}
func (s spanAnnotator) wait100Continue() {
s.sp.Annotate(nil, "Wait100Continue")
}
func (s spanAnnotator) wroteRequest(info httptrace.WroteRequestInfo) {
var attrs []trace.Attribute
if info.Err != nil {
attrs = append(attrs,
trace.StringAttribute("httptrace.wrote_request.error", info.Err.Error()))
}
s.sp.Annotate(attrs, "WroteRequest")
}

View File

@ -1,292 +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"
)
// Deprecated: client HTTP measures.
var (
// Deprecated: Use a Count aggregation over one of the other client measures to achieve the same effect.
ClientRequestCount = stats.Int64(
"opencensus.io/http/client/request_count",
"Number of HTTP requests started",
stats.UnitDimensionless)
// Deprecated: Use ClientSentBytes.
ClientRequestBytes = stats.Int64(
"opencensus.io/http/client/request_bytes",
"HTTP request body size if set as ContentLength (uncompressed)",
stats.UnitBytes)
// Deprecated: Use ClientReceivedBytes.
ClientResponseBytes = stats.Int64(
"opencensus.io/http/client/response_bytes",
"HTTP response body size (uncompressed)",
stats.UnitBytes)
// Deprecated: Use ClientRoundtripLatency.
ClientLatency = stats.Float64(
"opencensus.io/http/client/latency",
"End-to-end latency",
stats.UnitMilliseconds)
)
// The following client HTTP measures are supported for use in custom views.
var (
ClientSentBytes = stats.Int64(
"opencensus.io/http/client/sent_bytes",
"Total bytes sent in request body (not including headers)",
stats.UnitBytes,
)
ClientReceivedBytes = stats.Int64(
"opencensus.io/http/client/received_bytes",
"Total bytes received in response bodies (not including headers but including error responses with bodies)",
stats.UnitBytes,
)
ClientRoundtripLatency = stats.Float64(
"opencensus.io/http/client/roundtrip_latency",
"Time between first byte of request headers sent to last byte of response received, or terminal error",
stats.UnitMilliseconds,
)
)
// The following server HTTP measures are supported for use in custom views:
var (
ServerRequestCount = stats.Int64(
"opencensus.io/http/server/request_count",
"Number of HTTP requests started",
stats.UnitDimensionless)
ServerRequestBytes = stats.Int64(
"opencensus.io/http/server/request_bytes",
"HTTP request body size if set as ContentLength (uncompressed)",
stats.UnitBytes)
ServerResponseBytes = stats.Int64(
"opencensus.io/http/server/response_bytes",
"HTTP response body size (uncompressed)",
stats.UnitBytes)
ServerLatency = stats.Float64(
"opencensus.io/http/server/latency",
"End-to-end latency",
stats.UnitMilliseconds)
)
// The following tags are applied to stats recorded by this package. Host, Path
// and Method are applied to all measures. StatusCode is not applied to
// ClientRequestCount or ServerRequestCount, since it is recorded before the status is known.
var (
// Host is the value of the HTTP Host header.
//
// The value of this tag can be controlled by the HTTP client, so you need
// to watch out for potentially generating high-cardinality labels in your
// metrics backend if you use this tag in views.
Host = tag.MustNewKey("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.MustNewKey("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.MustNewKey("http.path")
// Method is the HTTP method of the request, capitalized (GET, POST, etc.).
Method = tag.MustNewKey("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.MustNewKey("http_server_route")
)
// Client tag keys.
var (
// KeyClientMethod is the HTTP method, capitalized (i.e. GET, POST, PUT, DELETE, etc.).
KeyClientMethod = tag.MustNewKey("http_client_method")
// KeyClientPath is the URL path (not including query string).
KeyClientPath = tag.MustNewKey("http_client_path")
// KeyClientStatus is the HTTP status code as an integer (e.g. 200, 404, 500.), or "error" if no response status line was received.
KeyClientStatus = tag.MustNewKey("http_client_status")
// KeyClientHost is the value of the request Host header.
KeyClientHost = tag.MustNewKey("http_client_host")
)
// Default distributions used by views in this package.
var (
DefaultSizeDistribution = view.Distribution(1024, 2048, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216, 67108864, 268435456, 1073741824, 4294967296)
DefaultLatencyDistribution = view.Distribution(1, 2, 3, 4, 5, 6, 8, 10, 13, 16, 20, 25, 30, 40, 50, 65, 80, 100, 130, 160, 200, 250, 300, 400, 500, 650, 800, 1000, 2000, 5000, 10000, 20000, 50000, 100000)
)
// Package ochttp provides some convenience views for client measures.
// You still need to register these views for data to actually be collected.
var (
ClientSentBytesDistribution = &view.View{
Name: "opencensus.io/http/client/sent_bytes",
Measure: ClientSentBytes,
Aggregation: DefaultSizeDistribution,
Description: "Total bytes sent in request body (not including headers), by HTTP method and response status",
TagKeys: []tag.Key{KeyClientMethod, KeyClientStatus},
}
ClientReceivedBytesDistribution = &view.View{
Name: "opencensus.io/http/client/received_bytes",
Measure: ClientReceivedBytes,
Aggregation: DefaultSizeDistribution,
Description: "Total bytes received in response bodies (not including headers but including error responses with bodies), by HTTP method and response status",
TagKeys: []tag.Key{KeyClientMethod, KeyClientStatus},
}
ClientRoundtripLatencyDistribution = &view.View{
Name: "opencensus.io/http/client/roundtrip_latency",
Measure: ClientRoundtripLatency,
Aggregation: DefaultLatencyDistribution,
Description: "End-to-end latency, by HTTP method and response status",
TagKeys: []tag.Key{KeyClientMethod, KeyClientStatus},
}
ClientCompletedCount = &view.View{
Name: "opencensus.io/http/client/completed_count",
Measure: ClientRoundtripLatency,
Aggregation: view.Count(),
Description: "Count of completed requests, by HTTP method and response status",
TagKeys: []tag.Key{KeyClientMethod, KeyClientStatus},
}
)
// Deprecated: Old client Views.
var (
// Deprecated: No direct replacement, but see ClientCompletedCount.
ClientRequestCountView = &view.View{
Name: "opencensus.io/http/client/request_count",
Description: "Count of HTTP requests started",
Measure: ClientRequestCount,
Aggregation: view.Count(),
}
// Deprecated: Use ClientSentBytesDistribution.
ClientRequestBytesView = &view.View{
Name: "opencensus.io/http/client/request_bytes",
Description: "Size distribution of HTTP request body",
Measure: ClientSentBytes,
Aggregation: DefaultSizeDistribution,
}
// Deprecated: Use ClientReceivedBytesDistribution instead.
ClientResponseBytesView = &view.View{
Name: "opencensus.io/http/client/response_bytes",
Description: "Size distribution of HTTP response body",
Measure: ClientReceivedBytes,
Aggregation: DefaultSizeDistribution,
}
// Deprecated: Use ClientRoundtripLatencyDistribution instead.
ClientLatencyView = &view.View{
Name: "opencensus.io/http/client/latency",
Description: "Latency distribution of HTTP requests",
Measure: ClientRoundtripLatency,
Aggregation: DefaultLatencyDistribution,
}
// Deprecated: Use ClientCompletedCount instead.
ClientRequestCountByMethod = &view.View{
Name: "opencensus.io/http/client/request_count_by_method",
Description: "Client request count by HTTP method",
TagKeys: []tag.Key{Method},
Measure: ClientSentBytes,
Aggregation: view.Count(),
}
// Deprecated: Use ClientCompletedCount instead.
ClientResponseCountByStatusCode = &view.View{
Name: "opencensus.io/http/client/response_count_by_status_code",
Description: "Client response count by status code",
TagKeys: []tag.Key{StatusCode},
Measure: ClientRoundtripLatency,
Aggregation: view.Count(),
}
)
// Package ochttp provides some convenience views for server measures.
// You still need to register these views for data to actually be collected.
var (
ServerRequestCountView = &view.View{
Name: "opencensus.io/http/server/request_count",
Description: "Count of HTTP requests started",
Measure: ServerRequestCount,
Aggregation: view.Count(),
}
ServerRequestBytesView = &view.View{
Name: "opencensus.io/http/server/request_bytes",
Description: "Size distribution of HTTP request body",
Measure: ServerRequestBytes,
Aggregation: DefaultSizeDistribution,
}
ServerResponseBytesView = &view.View{
Name: "opencensus.io/http/server/response_bytes",
Description: "Size distribution of HTTP response body",
Measure: ServerResponseBytes,
Aggregation: DefaultSizeDistribution,
}
ServerLatencyView = &view.View{
Name: "opencensus.io/http/server/latency",
Description: "Latency distribution of HTTP requests",
Measure: ServerLatency,
Aggregation: DefaultLatencyDistribution,
}
ServerRequestCountByMethod = &view.View{
Name: "opencensus.io/http/server/request_count_by_method",
Description: "Server request count by HTTP method",
TagKeys: []tag.Key{Method},
Measure: ServerRequestCount,
Aggregation: view.Count(),
}
ServerResponseCountByStatusCode = &view.View{
Name: "opencensus.io/http/server/response_count_by_status_code",
Description: "Server response count by status code",
TagKeys: []tag.Key{StatusCode},
Measure: ServerLatency,
Aggregation: view.Count(),
}
)
// DefaultClientViews are the default client views provided by this package.
// Deprecated: No replacement. Register the views you would like individually.
var DefaultClientViews = []*view.View{
ClientRequestCountView,
ClientRequestBytesView,
ClientResponseBytesView,
ClientLatencyView,
ClientRequestCountByMethod,
ClientResponseCountByStatusCode,
}
// DefaultServerViews are the default server views provided by this package.
// Deprecated: No replacement. Register the views you would like individually.
var DefaultServerViews = []*view.View{
ServerRequestCountView,
ServerRequestBytesView,
ServerResponseBytesView,
ServerLatencyView,
ServerRequestCountByMethod,
ServerResponseCountByStatusCode,
}

View File

@ -1,244 +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"
URLAttribute = "http.url"
UserAgentAttribute = "http.user_agent"
StatusCodeAttribute = "http.status_code"
)
type traceTransport struct {
base http.RoundTripper
startOptions trace.StartOptions
format propagation.HTTPFormat
formatSpanName func(*http.Request) string
newClientTrace func(*http.Request, *trace.Span) *httptrace.ClientTrace
}
// TODO(jbd): Add message events for request and response size.
// RoundTrip creates a trace.Span and inserts it into the outgoing request's headers.
// The created span can follow a parent span, if a parent is presented in
// the request's context.
func (t *traceTransport) RoundTrip(req *http.Request) (*http.Response, error) {
name := t.formatSpanName(req)
// TODO(jbd): Discuss whether we want to prefix
// outgoing requests with Sent.
ctx, span := trace.StartSpan(req.Context(), name,
trace.WithSampler(t.startOptions.Sampler),
trace.WithSpanKind(trace.SpanKindClient))
if t.newClientTrace != nil {
req = req.WithContext(httptrace.WithClientTrace(ctx, t.newClientTrace(req, span)))
} else {
req = req.WithContext(ctx)
}
if t.format != nil {
// SpanContextToRequest will modify its Request argument, which is
// contrary to the contract for http.RoundTripper, so we need to
// pass it a copy of the Request.
// However, the Request struct itself was already copied by
// the WithContext calls above and so we just need to copy the header.
header := make(http.Header)
for k, v := range req.Header {
header[k] = v
}
req.Header = header
t.format.SpanContextToRequest(span.SpanContext(), req)
}
span.AddAttributes(requestAttrs(req)...)
resp, err := t.base.RoundTrip(req)
if err != nil {
span.SetStatus(trace.Status{Code: trace.StatusCodeUnknown, Message: err.Error()})
span.End()
return resp, err
}
span.AddAttributes(responseAttrs(resp)...)
span.SetStatus(TraceStatus(resp.StatusCode, resp.Status))
// span.End() will be invoked after
// a read from resp.Body returns io.EOF or when
// resp.Body.Close() is invoked.
bt := &bodyTracker{rc: resp.Body, span: span}
resp.Body = wrappedBody(bt, resp.Body)
return resp, err
}
// bodyTracker wraps a response.Body and invokes
// trace.EndSpan on encountering io.EOF on reading
// the body of the original response.
type bodyTracker struct {
rc io.ReadCloser
span *trace.Span
}
var _ io.ReadCloser = (*bodyTracker)(nil)
func (bt *bodyTracker) Read(b []byte) (int, error) {
n, err := bt.rc.Read(b)
switch err {
case nil:
return n, nil
case io.EOF:
bt.span.End()
default:
// For all other errors, set the span status
bt.span.SetStatus(trace.Status{
// Code 2 is the error code for Internal server error.
Code: 2,
Message: err.Error(),
})
}
return n, err
}
func (bt *bodyTracker) Close() error {
// Invoking endSpan on Close will help catch the cases
// in which a read returned a non-nil error, we set the
// span status but didn't end the span.
bt.span.End()
return bt.rc.Close()
}
// CancelRequest cancels an in-flight request by closing its connection.
func (t *traceTransport) CancelRequest(req *http.Request) {
type canceler interface {
CancelRequest(*http.Request)
}
if cr, ok := t.base.(canceler); ok {
cr.CancelRequest(req)
}
}
func spanNameFromURL(req *http.Request) string {
return req.URL.Path
}
func requestAttrs(r *http.Request) []trace.Attribute {
userAgent := r.UserAgent()
attrs := make([]trace.Attribute, 0, 5)
attrs = append(attrs,
trace.StringAttribute(PathAttribute, r.URL.Path),
trace.StringAttribute(URLAttribute, r.URL.String()),
trace.StringAttribute(HostAttribute, r.Host),
trace.StringAttribute(MethodAttribute, r.Method),
)
if userAgent != "" {
attrs = append(attrs, trace.StringAttribute(UserAgentAttribute, userAgent))
}
return attrs
}
func responseAttrs(resp *http.Response) []trace.Attribute {
return []trace.Attribute{
trace.Int64Attribute(StatusCodeAttribute, int64(resp.StatusCode)),
}
}
// TraceStatus is a utility to convert the HTTP status code to a trace.Status that
// represents the outcome as closely as possible.
func TraceStatus(httpStatusCode int, statusLine string) trace.Status {
var code int32
if httpStatusCode < 200 || httpStatusCode >= 400 {
code = trace.StatusCodeUnknown
}
switch httpStatusCode {
case 499:
code = trace.StatusCodeCancelled
case http.StatusBadRequest:
code = trace.StatusCodeInvalidArgument
case http.StatusUnprocessableEntity:
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
case http.StatusConflict:
code = trace.StatusCodeAlreadyExists
}
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
}

View File

@ -1,44 +0,0 @@
// Copyright 2019, OpenCensus Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ochttp
import (
"io"
)
// wrappedBody returns a wrapped version of the original
// Body and only implements the same combination of additional
// interfaces as the original.
func wrappedBody(wrapper io.ReadCloser, body io.ReadCloser) io.ReadCloser {
var (
wr, i0 = body.(io.Writer)
)
switch {
case !i0:
return struct {
io.ReadCloser
}{wrapper}
case i0:
return struct {
io.ReadCloser
io.Writer
}{wrapper, wr}
default:
return struct {
io.ReadCloser
}{wrapper}
}
}

View File

@ -1,164 +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 resource provides functionality for resource, which capture
// identifying information about the entities for which signals are exported.
package resource
import (
"context"
"fmt"
"os"
"regexp"
"sort"
"strconv"
"strings"
)
// Environment variables used by FromEnv to decode a resource.
const (
EnvVarType = "OC_RESOURCE_TYPE"
EnvVarLabels = "OC_RESOURCE_LABELS"
)
// Resource describes an entity about which identifying information and metadata is exposed.
// For example, a type "k8s.io/container" may hold labels describing the pod name and namespace.
type Resource struct {
Type string
Labels map[string]string
}
// EncodeLabels encodes a labels map to a string as provided via the OC_RESOURCE_LABELS environment variable.
func EncodeLabels(labels map[string]string) string {
sortedKeys := make([]string, 0, len(labels))
for k := range labels {
sortedKeys = append(sortedKeys, k)
}
sort.Strings(sortedKeys)
s := ""
for i, k := range sortedKeys {
if i > 0 {
s += ","
}
s += k + "=" + strconv.Quote(labels[k])
}
return s
}
var labelRegex = regexp.MustCompile(`^\s*([[:ascii:]]{1,256}?)=("[[:ascii:]]{0,256}?")\s*,`)
// DecodeLabels decodes a serialized label map as used in the OC_RESOURCE_LABELS variable.
// A list of labels of the form `<key1>="<value1>",<key2>="<value2>",...` is accepted.
// Domain names and paths are accepted as label keys.
// Most users will want to use FromEnv instead.
func DecodeLabels(s string) (map[string]string, error) {
m := map[string]string{}
// Ensure a trailing comma, which allows us to keep the regex simpler
s = strings.TrimRight(strings.TrimSpace(s), ",") + ","
for len(s) > 0 {
match := labelRegex.FindStringSubmatch(s)
if len(match) == 0 {
return nil, fmt.Errorf("invalid label formatting, remainder: %s", s)
}
v := match[2]
if v == "" {
v = match[3]
} else {
var err error
if v, err = strconv.Unquote(v); err != nil {
return nil, fmt.Errorf("invalid label formatting, remainder: %s, err: %s", s, err)
}
}
m[match[1]] = v
s = s[len(match[0]):]
}
return m, nil
}
// FromEnv is a detector that loads resource information from the OC_RESOURCE_TYPE
// and OC_RESOURCE_labelS environment variables.
func FromEnv(context.Context) (*Resource, error) {
res := &Resource{
Type: strings.TrimSpace(os.Getenv(EnvVarType)),
}
labels := strings.TrimSpace(os.Getenv(EnvVarLabels))
if labels == "" {
return res, nil
}
var err error
if res.Labels, err = DecodeLabels(labels); err != nil {
return nil, err
}
return res, nil
}
var _ Detector = FromEnv
// merge resource information from b into a. In case of a collision, a takes precedence.
func merge(a, b *Resource) *Resource {
if a == nil {
return b
}
if b == nil {
return a
}
res := &Resource{
Type: a.Type,
Labels: map[string]string{},
}
if res.Type == "" {
res.Type = b.Type
}
for k, v := range b.Labels {
res.Labels[k] = v
}
// Labels from resource a overwrite labels from resource b.
for k, v := range a.Labels {
res.Labels[k] = v
}
return res
}
// Detector attempts to detect resource information.
// If the detector cannot find resource information, the returned resource is nil but no
// error is returned.
// An error is only returned on unexpected failures.
type Detector func(context.Context) (*Resource, error)
// MultiDetector returns a Detector that calls all input detectors in order and
// merges each result with the previous one. In case a type of label key is already set,
// the first set value is takes precedence.
// It returns on the first error that a sub-detector encounters.
func MultiDetector(detectors ...Detector) Detector {
return func(ctx context.Context) (*Resource, error) {
return detectAll(ctx, detectors...)
}
}
// detectall calls all input detectors sequentially an merges each result with the previous one.
// It returns on the first error that a sub-detector encounters.
func detectAll(ctx context.Context, detectors ...Detector) (*Resource, error) {
var res *Resource
for _, d := range detectors {
r, err := d(ctx)
if err != nil {
return nil, err
}
res = merge(res, r)
}
return res, nil
}

View File

@ -1,69 +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 data point to be tracked and recorded.
For example, latency, request Mb/s, and response Mb/s are measures
to collect from a server.
Measure constructors such as Int64 and Float64 automatically
register the measure by the given name. Each registered measure needs
to be unique by name. Measures also have a description and a unit.
Libraries can define and export measures. Application authors can then
create views and collect and break down measures by the tags they are
interested in.
Recording measurements
Measurement is a data point to be collected for a measure. For example,
for a latency (ms) measure, 100 is a measurement that represents a 100ms
latency event. Measurements are created from measures with
the current context. Tags from the current context are recorded with the
measurements if they are any.
Recorded measurements are dropped immediately if no views are registered for them.
There is usually no need to conditionally enable and disable
recording to reduce cost. Recording of measurements is cheap.
Libraries can always record measurements, and applications can later decide
on which measurements they want to collect by registering views. This allows
libraries to turn on the instrumentation by default.
Exemplars
For a given recorded measurement, the associated exemplar is a diagnostic map
that gives more information about the measurement.
When aggregated using a Distribution aggregation, an exemplar is kept for each
bucket in the Distribution. This allows you to easily find an example of a
measurement that fell into each bucket.
For example, if you also use the OpenCensus trace package and you
record a measurement with a context that contains a sampled trace span,
then the trace span will be added to the exemplar associated with the measurement.
When exported to a supporting back end, you should be able to easily navigate
to example traces that fell into each bucket in the Distribution.
*/
package stats // import "go.opencensus.io/stats"

View File

@ -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(tags *tag.Map, measurement interface{}, attachments map[string]interface{})
// SubscriptionReporter reports when a view subscribed with a measure.
var SubscriptionReporter func(measure string)

View File

@ -1,109 +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
desc *measureDescriptor
}
// Value returns the value of the Measurement as a float64.
func (m Measurement) Value() float64 {
return m.v
}
// Measure returns the Measure from which this Measurement was created.
func (m Measurement) Measure() Measure {
return m.m
}

View File

@ -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
// Float64Measure is a measure for float64 values.
type Float64Measure struct {
desc *measureDescriptor
}
// M creates a new float64 measurement.
// Use Record to record measurements.
func (m *Float64Measure) M(v float64) Measurement {
return Measurement{
m: m,
desc: m.desc,
v: v,
}
}
// Float64 creates a new measure for float64 values.
//
// See the documentation for interface Measure for more guidance on the
// parameters of this function.
func Float64(name, description, unit string) *Float64Measure {
mi := registerMeasureHandle(name, description, unit)
return &Float64Measure{mi}
}
// Name returns the name of the measure.
func (m *Float64Measure) Name() string {
return m.desc.name
}
// Description returns the description of the measure.
func (m *Float64Measure) Description() string {
return m.desc.description
}
// Unit returns the unit of the measure.
func (m *Float64Measure) Unit() string {
return m.desc.unit
}

View File

@ -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
// Int64Measure is a measure for int64 values.
type Int64Measure struct {
desc *measureDescriptor
}
// M creates a new int64 measurement.
// Use Record to record measurements.
func (m *Int64Measure) M(v int64) Measurement {
return Measurement{
m: m,
desc: m.desc,
v: float64(v),
}
}
// Int64 creates a new measure for int64 values.
//
// See the documentation for interface Measure for more guidance on the
// parameters of this function.
func Int64(name, description, unit string) *Int64Measure {
mi := registerMeasureHandle(name, description, unit)
return &Int64Measure{mi}
}
// Name returns the name of the measure.
func (m *Int64Measure) Name() string {
return m.desc.name
}
// Description returns the description of the measure.
func (m *Int64Measure) Description() string {
return m.desc.description
}
// Unit returns the unit of the measure.
func (m *Int64Measure) Unit() string {
return m.desc.unit
}

View File

@ -1,117 +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/metric/metricdata"
"go.opencensus.io/stats/internal"
"go.opencensus.io/tag"
)
func init() {
internal.SubscriptionReporter = func(measure string) {
mu.Lock()
measures[measure].subscribe()
mu.Unlock()
}
}
type recordOptions struct {
attachments metricdata.Attachments
mutators []tag.Mutator
measurements []Measurement
}
// WithAttachments applies provided exemplar attachments.
func WithAttachments(attachments metricdata.Attachments) Options {
return func(ro *recordOptions) {
ro.attachments = attachments
}
}
// WithTags applies provided tag mutators.
func WithTags(mutators ...tag.Mutator) Options {
return func(ro *recordOptions) {
ro.mutators = mutators
}
}
// WithMeasurements applies provided measurements.
func WithMeasurements(measurements ...Measurement) Options {
return func(ro *recordOptions) {
ro.measurements = measurements
}
}
// Options apply changes to recordOptions.
type Options func(*recordOptions)
func createRecordOption(ros ...Options) *recordOptions {
o := &recordOptions{}
for _, ro := range ros {
ro(o)
}
return o
}
// Record records one or multiple measurements with the same context at once.
// If there are any tags in the context, measurements will be tagged with them.
func Record(ctx context.Context, ms ...Measurement) {
RecordWithOptions(ctx, WithMeasurements(ms...))
}
// RecordWithTags records one or multiple measurements at once.
//
// Measurements will be tagged with the tags in the context mutated by the mutators.
// RecordWithTags is useful if you want to record with tag mutations but don't want
// to propagate the mutations in the context.
func RecordWithTags(ctx context.Context, mutators []tag.Mutator, ms ...Measurement) error {
return RecordWithOptions(ctx, WithTags(mutators...), WithMeasurements(ms...))
}
// RecordWithOptions records measurements from the given options (if any) against context
// and tags and attachments in the options (if any).
// If there are any tags in the context, measurements will be tagged with them.
func RecordWithOptions(ctx context.Context, ros ...Options) error {
o := createRecordOption(ros...)
if len(o.measurements) == 0 {
return nil
}
recorder := internal.DefaultRecorder
if recorder == nil {
return nil
}
record := false
for _, m := range o.measurements {
if m.desc.subscribed() {
record = true
break
}
}
if !record {
return nil
}
if len(o.mutators) > 0 {
var err error
if ctx, err = tag.New(ctx, o.mutators...); err != nil {
return err
}
}
recorder(tag.FromContext(ctx), o.measurements, o.attachments)
return nil
}

View File

@ -1,26 +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"
UnitSeconds = "s"
)

View File

@ -1,121 +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.
//
// A 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 {
agg := &Aggregation{
Type: AggTypeDistribution,
Buckets: bounds,
}
agg.newData = func() AggregationData {
return newDistributionData(agg)
}
return agg
}
// 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{}
},
}
}

View File

@ -1,293 +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"
"time"
"go.opencensus.io/metric/metricdata"
)
// AggregationData represents an aggregated value from a collection.
// They are reported on the view data during exporting.
// Mosts users won't directly access aggregration data.
type AggregationData interface {
isAggregationData() bool
addSample(v float64, attachments map[string]interface{}, t time.Time)
clone() AggregationData
equal(other AggregationData) bool
toPoint(t metricdata.Type, time time.Time) metricdata.Point
}
const epsilon = 1e-9
// CountData is the aggregated data for the Count aggregation.
// A count aggregation processes data and counts the recordings.
//
// Most users won't directly access count data.
type CountData struct {
Value int64
}
func (a *CountData) isAggregationData() bool { return true }
func (a *CountData) addSample(_ float64, _ map[string]interface{}, _ time.Time) {
a.Value = a.Value + 1
}
func (a *CountData) clone() AggregationData {
return &CountData{Value: a.Value}
}
func (a *CountData) equal(other AggregationData) bool {
a2, ok := other.(*CountData)
if !ok {
return false
}
return a.Value == a2.Value
}
func (a *CountData) toPoint(metricType metricdata.Type, t time.Time) metricdata.Point {
switch metricType {
case metricdata.TypeCumulativeInt64:
return metricdata.NewInt64Point(t, a.Value)
default:
panic("unsupported metricdata.Type")
}
}
// SumData is the aggregated data for the Sum aggregation.
// A sum aggregation processes data and sums up the recordings.
//
// Most users won't directly access sum data.
type SumData struct {
Value float64
}
func (a *SumData) isAggregationData() bool { return true }
func (a *SumData) addSample(v float64, _ map[string]interface{}, _ time.Time) {
a.Value += v
}
func (a *SumData) clone() AggregationData {
return &SumData{Value: a.Value}
}
func (a *SumData) equal(other AggregationData) bool {
a2, ok := other.(*SumData)
if !ok {
return false
}
return math.Pow(a.Value-a2.Value, 2) < epsilon
}
func (a *SumData) toPoint(metricType metricdata.Type, t time.Time) metricdata.Point {
switch metricType {
case metricdata.TypeCumulativeInt64:
return metricdata.NewInt64Point(t, int64(a.Value))
case metricdata.TypeCumulativeFloat64:
return metricdata.NewFloat64Point(t, a.Value)
default:
panic("unsupported metricdata.Type")
}
}
// DistributionData is the aggregated data for the
// Distribution aggregation.
//
// Most users won't directly access distribution data.
//
// For a distribution with N bounds, the associated DistributionData will have
// N+1 buckets.
type DistributionData struct {
Count int64 // number of data points aggregated
Min float64 // minimum value in the distribution
Max float64 // max value in the distribution
Mean float64 // mean of the distribution
SumOfSquaredDev float64 // sum of the squared deviation from the mean
CountPerBucket []int64 // number of occurrences per bucket
// ExemplarsPerBucket is slice the same length as CountPerBucket containing
// an exemplar for the associated bucket, or nil.
ExemplarsPerBucket []*metricdata.Exemplar
bounds []float64 // histogram distribution of the values
}
func newDistributionData(agg *Aggregation) *DistributionData {
bucketCount := len(agg.Buckets) + 1
return &DistributionData{
CountPerBucket: make([]int64, bucketCount),
ExemplarsPerBucket: make([]*metricdata.Exemplar, bucketCount),
bounds: agg.Buckets,
Min: math.MaxFloat64,
Max: math.SmallestNonzeroFloat64,
}
}
// Sum returns the sum of all samples collected.
func (a *DistributionData) Sum() float64 { return a.Mean * float64(a.Count) }
func (a *DistributionData) variance() float64 {
if a.Count <= 1 {
return 0
}
return a.SumOfSquaredDev / float64(a.Count-1)
}
func (a *DistributionData) isAggregationData() bool { return true }
// TODO(songy23): support exemplar attachments.
func (a *DistributionData) addSample(v float64, attachments map[string]interface{}, t time.Time) {
if v < a.Min {
a.Min = v
}
if v > a.Max {
a.Max = v
}
a.Count++
a.addToBucket(v, attachments, t)
if a.Count == 1 {
a.Mean = v
return
}
oldMean := a.Mean
a.Mean = a.Mean + (v-a.Mean)/float64(a.Count)
a.SumOfSquaredDev = a.SumOfSquaredDev + (v-oldMean)*(v-a.Mean)
}
func (a *DistributionData) addToBucket(v float64, attachments map[string]interface{}, t time.Time) {
var count *int64
var i int
var b float64
for i, b = range a.bounds {
if v < b {
count = &a.CountPerBucket[i]
break
}
}
if count == nil { // Last bucket.
i = len(a.bounds)
count = &a.CountPerBucket[i]
}
*count++
if exemplar := getExemplar(v, attachments, t); exemplar != nil {
a.ExemplarsPerBucket[i] = exemplar
}
}
func getExemplar(v float64, attachments map[string]interface{}, t time.Time) *metricdata.Exemplar {
if len(attachments) == 0 {
return nil
}
return &metricdata.Exemplar{
Value: v,
Timestamp: t,
Attachments: attachments,
}
}
func (a *DistributionData) clone() AggregationData {
c := *a
c.CountPerBucket = append([]int64(nil), a.CountPerBucket...)
c.ExemplarsPerBucket = append([]*metricdata.Exemplar(nil), a.ExemplarsPerBucket...)
return &c
}
func (a *DistributionData) equal(other AggregationData) bool {
a2, ok := other.(*DistributionData)
if !ok {
return false
}
if a2 == nil {
return false
}
if len(a.CountPerBucket) != len(a2.CountPerBucket) {
return false
}
for i := range a.CountPerBucket {
if a.CountPerBucket[i] != a2.CountPerBucket[i] {
return false
}
}
return a.Count == a2.Count && a.Min == a2.Min && a.Max == a2.Max && math.Pow(a.Mean-a2.Mean, 2) < epsilon && math.Pow(a.variance()-a2.variance(), 2) < epsilon
}
func (a *DistributionData) toPoint(metricType metricdata.Type, t time.Time) metricdata.Point {
switch metricType {
case metricdata.TypeCumulativeDistribution:
buckets := []metricdata.Bucket{}
for i := 0; i < len(a.CountPerBucket); i++ {
buckets = append(buckets, metricdata.Bucket{
Count: a.CountPerBucket[i],
Exemplar: a.ExemplarsPerBucket[i],
})
}
bucketOptions := &metricdata.BucketOptions{Bounds: a.bounds}
val := &metricdata.Distribution{
Count: a.Count,
Sum: a.Sum(),
SumOfSquaredDeviation: a.SumOfSquaredDev,
BucketOptions: bucketOptions,
Buckets: buckets,
}
return metricdata.NewDistributionPoint(t, val)
default:
// TODO: [rghetia] when we have a use case for TypeGaugeDistribution.
panic("unsupported metricdata.Type")
}
}
// LastValueData returns the last value recorded for LastValue aggregation.
type LastValueData struct {
Value float64
}
func (l *LastValueData) isAggregationData() bool {
return true
}
func (l *LastValueData) addSample(v float64, _ map[string]interface{}, _ time.Time) {
l.Value = v
}
func (l *LastValueData) clone() AggregationData {
return &LastValueData{l.Value}
}
func (l *LastValueData) equal(other AggregationData) bool {
a2, ok := other.(*LastValueData)
if !ok {
return false
}
return l.Value == a2.Value
}
func (l *LastValueData) toPoint(metricType metricdata.Type, t time.Time) metricdata.Point {
switch metricType {
case metricdata.TypeGaugeInt64:
return metricdata.NewInt64Point(t, int64(l.Value))
case metricdata.TypeGaugeFloat64:
return metricdata.NewFloat64Point(t, l.Value)
default:
panic("unsupported metricdata.Type")
}
}

View File

@ -1,86 +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"
"time"
"go.opencensus.io/internal/tagencoding"
"go.opencensus.io/tag"
)
type collector struct {
// signatures holds the aggregations values for each unique tag signature
// (values for all keys) to its aggregator.
signatures map[string]AggregationData
// Aggregation is the description of the aggregation to perform for this
// view.
a *Aggregation
}
func (c *collector) addSample(s string, v float64, attachments map[string]interface{}, t time.Time) {
aggregator, ok := c.signatures[s]
if !ok {
aggregator = c.a.newData()
c.signatures[s] = aggregator
}
aggregator.addSample(v, attachments, t)
}
// collectRows returns a snapshot of the collected Row values.
func (c *collector) collectedRows(keys []tag.Key) []*Row {
rows := make([]*Row, 0, len(c.signatures))
for sig, aggregator := range c.signatures {
tags := decodeTags([]byte(sig), keys)
row := &Row{Tags: tags, Data: aggregator.clone()}
rows = append(rows, row)
}
return rows
}
func (c *collector) clearRows() {
c.signatures = make(map[string]AggregationData)
}
// encodeWithKeys encodes the map by using values
// only associated with the keys provided.
func encodeWithKeys(m *tag.Map, keys []tag.Key) []byte {
vb := &tagencoding.Values{
Buffer: make([]byte, len(keys)),
}
for _, k := range keys {
v, _ := m.Value(k)
vb.WriteValue([]byte(v))
}
return vb.Bytes()
}
// decodeTags decodes tags from the buffer and
// orders them by the keys.
func decodeTags(buf []byte, keys []tag.Key) []tag.Tag {
vb := &tagencoding.Values{Buffer: buf}
var tags []tag.Tag
for _, k := range keys {
v := vb.ReadValue()
if v != nil {
tags = append(tags, tag.Tag{Key: k, Value: string(v)})
}
}
vb.ReadIndex = 0
sort.Slice(tags, func(i, j int) bool { return tags[i].Key.Name() < tags[j].Key.Name() })
return tags
}

View File

@ -1,47 +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.
//
// All recorded measurements can be grouped by a list of tags.
//
// OpenCensus provides several aggregation methods: Count, Distribution and Sum.
//
// Count only counts the number of measurement points recorded.
// Distribution provides statistical summary of the aggregated data by counting
// how many recorded measurements fall into each bucket.
// Sum adds up the measurement values.
// LastValue just keeps track of the most recently recorded measurement value.
// All aggregations are cumulative.
//
// Views can be registered and unregistered at any time during program execution.
//
// Libraries can define views but it is recommended that in most cases registering
// views be left up to applications.
//
// Exporting
//
// Collected and aggregated data can be exported to a metric collection
// backend by registering its exporter.
//
// Multiple exporters can be registered to upload the data to various
// different back ends.
package view // import "go.opencensus.io/stats/view"
// TODO(acetechnologist): Add a link to the language independent OpenCensus
// spec when it is available.

View File

@ -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)
}

View File

@ -1,221 +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"
"errors"
"fmt"
"reflect"
"sort"
"sync/atomic"
"time"
"go.opencensus.io/metric/metricdata"
"go.opencensus.io/stats"
"go.opencensus.io/tag"
)
// View allows users to aggregate the recorded stats.Measurements.
// Views need to be passed to the Register function 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 to apply to the set of Measurements.
Aggregation *Aggregation
}
// WithName returns a copy of the View with a new name. This is useful for
// renaming views to cope with limitations placed on metric names by various
// backends.
func (v *View) WithName(name string) *View {
vNew := *v
vNew.Name = name
return &vNew
}
// same compares two views and returns true if they represent the same aggregation.
func (v *View) same(other *View) bool {
if v == other {
return true
}
if v == nil {
return false
}
return reflect.DeepEqual(v.Aggregation, other.Aggregation) &&
v.Measure.Name() == other.Measure.Name()
}
// ErrNegativeBucketBounds error returned if histogram contains negative bounds.
//
// Deprecated: this should not be public.
var ErrNegativeBucketBounds = errors.New("negative bucket bounds not supported")
// canonicalize canonicalizes v by setting explicit
// defaults for Name and Description and sorting the TagKeys
func (v *View) canonicalize() error {
if v.Measure == nil {
return fmt.Errorf("cannot register view %q: measure not set", v.Name)
}
if v.Aggregation == nil {
return fmt.Errorf("cannot register view %q: aggregation not set", v.Name)
}
if v.Name == "" {
v.Name = v.Measure.Name()
}
if v.Description == "" {
v.Description = v.Measure.Description()
}
if err := checkViewName(v.Name); err != nil {
return err
}
sort.Slice(v.TagKeys, func(i, j int) bool {
return v.TagKeys[i].Name() < v.TagKeys[j].Name()
})
sort.Float64s(v.Aggregation.Buckets)
for _, b := range v.Aggregation.Buckets {
if b < 0 {
return ErrNegativeBucketBounds
}
}
// drop 0 bucket silently.
v.Aggregation.Buckets = dropZeroBounds(v.Aggregation.Buckets...)
return nil
}
func dropZeroBounds(bounds ...float64) []float64 {
for i, bound := range bounds {
if bound > 0 {
return bounds[i:]
}
}
return []float64{}
}
// viewInternal is the internal representation of a View.
type viewInternal struct {
view *View // view is the canonicalized View definition associated with this view.
subscribed uint32 // 1 if someone is subscribed and data need to be exported, use atomic to access
collector *collector
metricDescriptor *metricdata.Descriptor
}
func newViewInternal(v *View) (*viewInternal, error) {
return &viewInternal{
view: v,
collector: &collector{make(map[string]AggregationData), v.Aggregation},
metricDescriptor: viewToMetricDescriptor(v),
}, nil
}
func (v *viewInternal) subscribe() {
atomic.StoreUint32(&v.subscribed, 1)
}
func (v *viewInternal) unsubscribe() {
atomic.StoreUint32(&v.subscribed, 0)
}
// isSubscribed returns true if the view is exporting
// data by subscription.
func (v *viewInternal) isSubscribed() bool {
return atomic.LoadUint32(&v.subscribed) == 1
}
func (v *viewInternal) clearRows() {
v.collector.clearRows()
}
func (v *viewInternal) collectedRows() []*Row {
return v.collector.collectedRows(v.view.TagKeys)
}
func (v *viewInternal) addSample(m *tag.Map, val float64, attachments map[string]interface{}, t time.Time) {
if !v.isSubscribed() {
return
}
sig := string(encodeWithKeys(m, v.view.TagKeys))
v.collector.addSample(sig, val, attachments, t)
}
// A Data is a set of rows about usage of the single measure associated
// with the given view. Each row is specific to a unique set of tags.
type Data struct {
View *View
Start, End time.Time
Rows []*Row
}
// Row is the collected value for a specific set of key value pairs a.k.a tags.
type Row struct {
Tags []tag.Tag
Data AggregationData
}
func (r *Row) String() string {
var buffer bytes.Buffer
buffer.WriteString("{ ")
buffer.WriteString("{ ")
for _, t := range r.Tags {
buffer.WriteString(fmt.Sprintf("{%v %v}", t.Key.Name(), t.Value))
}
buffer.WriteString(" }")
buffer.WriteString(fmt.Sprintf("%v", r.Data))
buffer.WriteString(" }")
return buffer.String()
}
// Equal returns true if both rows are equal. Tags are expected to be ordered
// by the key name. Even if both rows have the same tags but the tags appear in
// different orders it will return false.
func (r *Row) Equal(other *Row) bool {
if r == other {
return true
}
return reflect.DeepEqual(r.Tags, other.Tags) && r.Data.equal(other.Data)
}
const maxNameLength = 255
// Returns true if the given string contains only printable characters.
func isPrintable(str string) bool {
for _, r := range str {
if !(r >= ' ' && r <= '~') {
return false
}
}
return true
}
func checkViewName(name string) error {
if len(name) > maxNameLength {
return fmt.Errorf("view name cannot be larger than %v", maxNameLength)
}
if !isPrintable(name) {
return fmt.Errorf("view name needs to be an ASCII string")
}
return nil
}

View File

@ -1,149 +0,0 @@
// Copyright 2019, OpenCensus Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package view
import (
"time"
"go.opencensus.io/metric/metricdata"
"go.opencensus.io/stats"
)
func getUnit(unit string) metricdata.Unit {
switch unit {
case "1":
return metricdata.UnitDimensionless
case "ms":
return metricdata.UnitMilliseconds
case "By":
return metricdata.UnitBytes
}
return metricdata.UnitDimensionless
}
func getType(v *View) metricdata.Type {
m := v.Measure
agg := v.Aggregation
switch agg.Type {
case AggTypeSum:
switch m.(type) {
case *stats.Int64Measure:
return metricdata.TypeCumulativeInt64
case *stats.Float64Measure:
return metricdata.TypeCumulativeFloat64
default:
panic("unexpected measure type")
}
case AggTypeDistribution:
return metricdata.TypeCumulativeDistribution
case AggTypeLastValue:
switch m.(type) {
case *stats.Int64Measure:
return metricdata.TypeGaugeInt64
case *stats.Float64Measure:
return metricdata.TypeGaugeFloat64
default:
panic("unexpected measure type")
}
case AggTypeCount:
switch m.(type) {
case *stats.Int64Measure:
return metricdata.TypeCumulativeInt64
case *stats.Float64Measure:
return metricdata.TypeCumulativeInt64
default:
panic("unexpected measure type")
}
default:
panic("unexpected aggregation type")
}
}
func getLabelKeys(v *View) []metricdata.LabelKey {
labelKeys := []metricdata.LabelKey{}
for _, k := range v.TagKeys {
labelKeys = append(labelKeys, metricdata.LabelKey{Key: k.Name()})
}
return labelKeys
}
func viewToMetricDescriptor(v *View) *metricdata.Descriptor {
return &metricdata.Descriptor{
Name: v.Name,
Description: v.Description,
Unit: convertUnit(v),
Type: getType(v),
LabelKeys: getLabelKeys(v),
}
}
func convertUnit(v *View) metricdata.Unit {
switch v.Aggregation.Type {
case AggTypeCount:
return metricdata.UnitDimensionless
default:
return getUnit(v.Measure.Unit())
}
}
func toLabelValues(row *Row, expectedKeys []metricdata.LabelKey) []metricdata.LabelValue {
labelValues := []metricdata.LabelValue{}
tagMap := make(map[string]string)
for _, tag := range row.Tags {
tagMap[tag.Key.Name()] = tag.Value
}
for _, key := range expectedKeys {
if val, ok := tagMap[key.Key]; ok {
labelValues = append(labelValues, metricdata.NewLabelValue(val))
} else {
labelValues = append(labelValues, metricdata.LabelValue{})
}
}
return labelValues
}
func rowToTimeseries(v *viewInternal, row *Row, now time.Time, startTime time.Time) *metricdata.TimeSeries {
return &metricdata.TimeSeries{
Points: []metricdata.Point{row.Data.toPoint(v.metricDescriptor.Type, now)},
LabelValues: toLabelValues(row, v.metricDescriptor.LabelKeys),
StartTime: startTime,
}
}
func viewToMetric(v *viewInternal, now time.Time, startTime time.Time) *metricdata.Metric {
if v.metricDescriptor.Type == metricdata.TypeGaugeInt64 ||
v.metricDescriptor.Type == metricdata.TypeGaugeFloat64 {
startTime = time.Time{}
}
rows := v.collectedRows()
if len(rows) == 0 {
return nil
}
ts := []*metricdata.TimeSeries{}
for _, row := range rows {
ts = append(ts, rowToTimeseries(v, row, now, startTime))
}
m := &metricdata.Metric{
Descriptor: *v.metricDescriptor,
TimeSeries: ts,
}
return m
}

View File

@ -1,281 +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"
"sync"
"time"
"go.opencensus.io/metric/metricdata"
"go.opencensus.io/metric/metricproducer"
"go.opencensus.io/stats"
"go.opencensus.io/stats/internal"
"go.opencensus.io/tag"
)
func init() {
defaultWorker = newWorker()
go defaultWorker.start()
internal.DefaultRecorder = record
}
type measureRef struct {
measure string
views map[*viewInternal]struct{}
}
type worker struct {
measures map[string]*measureRef
views map[string]*viewInternal
startTimes map[*viewInternal]time.Time
timer *time.Ticker
c chan command
quit, done chan bool
mu sync.RWMutex
}
var defaultWorker *worker
var defaultReportingDuration = 10 * time.Second
// Find returns a registered view associated with this name.
// If no registered view is found, nil is returned.
func Find(name string) (v *View) {
req := &getViewByNameReq{
name: name,
c: make(chan *getViewByNameResp),
}
defaultWorker.c <- req
resp := <-req.c
return resp.v
}
// Register begins collecting data for the given views.
// Once a view is registered, it reports data to the registered exporters.
func Register(views ...*View) error {
req := &registerViewReq{
views: views,
err: make(chan error),
}
defaultWorker.c <- req
return <-req.err
}
// Unregister the given views. Data will not longer be exported for these views
// after Unregister returns.
// It is not necessary to unregister from views you expect to collect for the
// duration of your program execution.
func Unregister(views ...*View) {
names := make([]string, len(views))
for i := range views {
names[i] = views[i].Name
}
req := &unregisterFromViewReq{
views: names,
done: make(chan struct{}),
}
defaultWorker.c <- req
<-req.done
}
// RetrieveData gets a snapshot of the data collected for the the view registered
// with the given name. It is intended for testing only.
func RetrieveData(viewName string) ([]*Row, error) {
req := &retrieveDataReq{
now: time.Now(),
v: viewName,
c: make(chan *retrieveDataResp),
}
defaultWorker.c <- req
resp := <-req.c
return resp.rows, resp.err
}
func record(tags *tag.Map, ms interface{}, attachments map[string]interface{}) {
req := &recordReq{
tm: tags,
ms: ms.([]stats.Measurement),
attachments: attachments,
t: time.Now(),
}
defaultWorker.c <- req
}
// SetReportingPeriod sets the interval between reporting aggregated views in
// the program. If duration is less than or equal to zero, it enables the
// default behavior.
//
// Note: each exporter makes different promises about what the lowest supported
// duration is. For example, the Stackdriver exporter recommends a value no
// lower than 1 minute. Consult each exporter per your needs.
func SetReportingPeriod(d time.Duration) {
// TODO(acetechnologist): ensure that the duration d is more than a certain
// value. e.g. 1s
req := &setReportingPeriodReq{
d: d,
c: make(chan bool),
}
defaultWorker.c <- req
<-req.c // don't return until the timer is set to the new duration.
}
func newWorker() *worker {
return &worker{
measures: make(map[string]*measureRef),
views: make(map[string]*viewInternal),
startTimes: make(map[*viewInternal]time.Time),
timer: time.NewTicker(defaultReportingDuration),
c: make(chan command, 1024),
quit: make(chan bool),
done: make(chan bool),
}
}
func (w *worker) start() {
prodMgr := metricproducer.GlobalManager()
prodMgr.AddProducer(w)
for {
select {
case cmd := <-w.c:
cmd.handleCommand(w)
case <-w.timer.C:
w.reportUsage(time.Now())
case <-w.quit:
w.timer.Stop()
close(w.c)
w.done <- true
return
}
}
}
func (w *worker) stop() {
prodMgr := metricproducer.GlobalManager()
prodMgr.DeleteProducer(w)
w.quit <- true
<-w.done
}
func (w *worker) getMeasureRef(name string) *measureRef {
if mr, ok := w.measures[name]; ok {
return mr
}
mr := &measureRef{
measure: name,
views: make(map[*viewInternal]struct{}),
}
w.measures[name] = mr
return mr
}
func (w *worker) tryRegisterView(v *View) (*viewInternal, error) {
w.mu.Lock()
defer w.mu.Unlock()
vi, err := newViewInternal(v)
if err != nil {
return nil, err
}
if x, ok := w.views[vi.view.Name]; ok {
if !x.view.same(vi.view) {
return nil, fmt.Errorf("cannot register view %q; a different view with the same name is already registered", v.Name)
}
// the view is already registered so there is nothing to do and the
// command is considered successful.
return x, nil
}
w.views[vi.view.Name] = vi
ref := w.getMeasureRef(vi.view.Measure.Name())
ref.views[vi] = struct{}{}
return vi, nil
}
func (w *worker) unregisterView(viewName string) {
w.mu.Lock()
defer w.mu.Unlock()
delete(w.views, viewName)
}
func (w *worker) reportView(v *viewInternal, now time.Time) {
if !v.isSubscribed() {
return
}
rows := v.collectedRows()
_, ok := w.startTimes[v]
if !ok {
w.startTimes[v] = now
}
viewData := &Data{
View: v.view,
Start: w.startTimes[v],
End: time.Now(),
Rows: rows,
}
exportersMu.Lock()
for e := range exporters {
e.ExportView(viewData)
}
exportersMu.Unlock()
}
func (w *worker) reportUsage(now time.Time) {
w.mu.Lock()
defer w.mu.Unlock()
for _, v := range w.views {
w.reportView(v, now)
}
}
func (w *worker) toMetric(v *viewInternal, now time.Time) *metricdata.Metric {
if !v.isSubscribed() {
return nil
}
_, ok := w.startTimes[v]
if !ok {
w.startTimes[v] = now
}
var startTime time.Time
if v.metricDescriptor.Type == metricdata.TypeGaugeInt64 ||
v.metricDescriptor.Type == metricdata.TypeGaugeFloat64 {
startTime = time.Time{}
} else {
startTime = w.startTimes[v]
}
return viewToMetric(v, now, startTime)
}
// Read reads all view data and returns them as metrics.
// It is typically invoked by metric reader to export stats in metric format.
func (w *worker) Read() []*metricdata.Metric {
w.mu.Lock()
defer w.mu.Unlock()
now := time.Now()
metrics := make([]*metricdata.Metric, 0, len(w.views))
for _, v := range w.views {
metric := w.toMetric(v, now)
if metric != nil {
metrics = append(metrics, metric)
}
}
return metrics
}

View File

@ -1,186 +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) {
for _, v := range cmd.views {
if err := v.canonicalize(); err != nil {
cmd.err <- err
return
}
}
var errstr []string
for _, view := range cmd.views {
vi, err := w.tryRegisterView(view)
if err != nil {
errstr = append(errstr, fmt.Sprintf("%s: %v", view.Name, err))
continue
}
internal.SubscriptionReporter(view.Measure.Name())
vi.subscribe()
}
if len(errstr) > 0 {
cmd.err <- errors.New(strings.Join(errstr, "\n"))
} else {
cmd.err <- nil
}
}
// unregisterFromViewReq is the command to unregister to a view. Has no
// impact on the data collection for client that are pulling data from the
// library.
type unregisterFromViewReq struct {
views []string
done chan struct{}
}
func (cmd *unregisterFromViewReq) handleCommand(w *worker) {
for _, name := range cmd.views {
vi, ok := w.views[name]
if !ok {
continue
}
// Report pending data for this view before removing it.
w.reportView(vi, time.Now())
vi.unsubscribe()
if !vi.isSubscribed() {
// this was the last subscription and view is not collecting anymore.
// The collected data can be cleared.
vi.clearRows()
}
w.unregisterView(name)
}
cmd.done <- struct{}{}
}
// retrieveDataReq is the command to retrieve data for a view.
type retrieveDataReq struct {
now time.Time
v string
c chan *retrieveDataResp
}
type retrieveDataResp struct {
rows []*Row
err error
}
func (cmd *retrieveDataReq) handleCommand(w *worker) {
w.mu.Lock()
defer w.mu.Unlock()
vi, ok := w.views[cmd.v]
if !ok {
cmd.c <- &retrieveDataResp{
nil,
fmt.Errorf("cannot retrieve data; view %q is not registered", cmd.v),
}
return
}
if !vi.isSubscribed() {
cmd.c <- &retrieveDataResp{
nil,
fmt.Errorf("cannot retrieve data; view %q has no subscriptions or collection is not forcibly started", cmd.v),
}
return
}
cmd.c <- &retrieveDataResp{
vi.collectedRows(),
nil,
}
}
// recordReq is the command to record data related to multiple measures
// at once.
type recordReq struct {
tm *tag.Map
ms []stats.Measurement
attachments map[string]interface{}
t time.Time
}
func (cmd *recordReq) handleCommand(w *worker) {
w.mu.Lock()
defer w.mu.Unlock()
for _, m := range cmd.ms {
if (m == stats.Measurement{}) { // not registered
continue
}
ref := w.getMeasureRef(m.Measure().Name())
for v := range ref.views {
v.addSample(cmd.tm, m.Value(), cmd.attachments, time.Now())
}
}
}
// setReportingPeriodReq is the command to modify the duration between
// reporting the collected data to the registered clients.
type setReportingPeriodReq struct {
d time.Duration
c chan bool
}
func (cmd *setReportingPeriodReq) handleCommand(w *worker) {
w.timer.Stop()
if cmd.d <= 0 {
w.timer = time.NewTicker(defaultReportingDuration)
} else {
w.timer = time.NewTicker(cmd.d)
}
cmd.c <- true
}

View File

@ -1,43 +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{}

View File

@ -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"

View File

@ -1,44 +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 more than once 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
}
// MustNewKey returns a key with the given name, and panics if name is an invalid key name.
func MustNewKey(name string) Key {
k, err := NewKey(name)
if err != nil {
panic(err)
}
return k
}
// Name returns the name of the key.
func (k Key) Name() string {
return k.name
}

View File

@ -1,229 +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
}
type tagContent struct {
value string
m metadatas
}
// Map is a map of tags. Use New to create a context containing
// a new Map.
type Map struct {
m map[Key]tagContent
}
// Value returns the value for the key if a value for the key exists.
func (m *Map) Value(k Key) (string, bool) {
if m == nil {
return "", false
}
v, ok := m.m[k]
return v.value, ok
}
func (m *Map) String() string {
if m == nil {
return "nil"
}
keys := make([]Key, 0, len(m.m))
for k := range m.m {
keys = append(keys, k)
}
sort.Slice(keys, func(i, j int) bool { return keys[i].Name() < keys[j].Name() })
var buffer bytes.Buffer
buffer.WriteString("{ ")
for _, k := range keys {
buffer.WriteString(fmt.Sprintf("{%v %v}", k.name, m.m[k]))
}
buffer.WriteString(" }")
return buffer.String()
}
func (m *Map) insert(k Key, v string, md metadatas) {
if _, ok := m.m[k]; ok {
return
}
m.m[k] = tagContent{value: v, m: md}
}
func (m *Map) update(k Key, v string, md metadatas) {
if _, ok := m.m[k]; ok {
m.m[k] = tagContent{value: v, m: md}
}
}
func (m *Map) upsert(k Key, v string, md metadatas) {
m.m[k] = tagContent{value: v, m: md}
}
func (m *Map) delete(k Key) {
delete(m.m, k)
}
func newMap() *Map {
return &Map{m: make(map[Key]tagContent)}
}
// Mutator modifies a tag map.
type Mutator interface {
Mutate(t *Map) (*Map, error)
}
// Insert returns a mutator that inserts a
// value associated with k. If k already exists in the tag map,
// mutator doesn't update the value.
// Metadata applies metadata to the tag. It is optional.
// Metadatas are applied in the order in which it is provided.
// If more than one metadata updates the same attribute then
// the update from the last metadata prevails.
func Insert(k Key, v string, mds ...Metadata) Mutator {
return &mutator{
fn: func(m *Map) (*Map, error) {
if !checkValue(v) {
return nil, errInvalidValue
}
m.insert(k, v, createMetadatas(mds...))
return m, nil
},
}
}
// Update returns a mutator that updates the
// value of the tag associated with k with v. If k doesn't
// exists in the tag map, the mutator doesn't insert the value.
// Metadata applies metadata to the tag. It is optional.
// Metadatas are applied in the order in which it is provided.
// If more than one metadata updates the same attribute then
// the update from the last metadata prevails.
func Update(k Key, v string, mds ...Metadata) Mutator {
return &mutator{
fn: func(m *Map) (*Map, error) {
if !checkValue(v) {
return nil, errInvalidValue
}
m.update(k, v, createMetadatas(mds...))
return m, nil
},
}
}
// Upsert returns a mutator that upserts the
// value of the tag associated with k with v. It inserts the
// value if k doesn't exist already. It mutates the value
// if k already exists.
// Metadata applies metadata to the tag. It is optional.
// Metadatas are applied in the order in which it is provided.
// If more than one metadata updates the same attribute then
// the update from the last metadata prevails.
func Upsert(k Key, v string, mds ...Metadata) Mutator {
return &mutator{
fn: func(m *Map) (*Map, error) {
if !checkValue(v) {
return nil, errInvalidValue
}
m.upsert(k, v, createMetadatas(mds...))
return m, nil
},
}
}
func createMetadatas(mds ...Metadata) metadatas {
var metas metadatas
if len(mds) > 0 {
for _, md := range mds {
if md != nil {
md(&metas)
}
}
} else {
WithTTL(TTLUnlimitedPropagation)(&metas)
}
return metas
}
// Delete returns a mutator that deletes
// the value associated with k.
func Delete(k Key) Mutator {
return &mutator{
fn: func(m *Map) (*Map, error) {
m.delete(k)
return m, nil
},
}
}
// New returns a new context that contains a tag map
// originated from the incoming context and modified
// with the provided mutators.
func New(ctx context.Context, mutator ...Mutator) (context.Context, error) {
m := newMap()
orig := FromContext(ctx)
if orig != nil {
for k, v := range orig.m {
if !checkKeyName(k.Name()) {
return ctx, fmt.Errorf("key:%q: %v", k, errInvalidKeyName)
}
if !checkValue(v.value) {
return ctx, fmt.Errorf("key:%q value:%q: %v", k.Name(), v, errInvalidValue)
}
m.insert(k, v.value, v.m)
}
}
var err error
for _, mod := range mutator {
m, err = mod.Mutate(m)
if err != nil {
return ctx, err
}
}
return NewContext(ctx, m), nil
}
// Do is similar to pprof.Do: a convenience for installing the tags
// from the context as Go profiler labels. This allows you to
// correlated runtime profiling with stats.
//
// It converts the key/values from the given map to Go profiler labels
// and calls pprof.Do.
//
// Do is going to do nothing if your Go version is below 1.9.
func Do(ctx context.Context, f func(ctx context.Context)) {
do(ctx, f)
}
type mutator struct {
fn func(t *Map) (*Map, error)
}
func (m *mutator) Mutate(t *Map) (*Map, error) {
return m.fn(t)
}

View File

@ -1,239 +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 {
if m == nil {
return nil
}
eg := &encoderGRPC{
buf: make([]byte, len(m.m)),
}
eg.writeByte(tagsVersionID)
for k, v := range m.m {
if v.m.ttl.ttl == valueTTLUnlimitedPropagation {
eg.writeByte(byte(keyTypeString))
eg.writeStringWithVarintLen(k.name)
eg.writeBytesWithVarintLen([]byte(v.value))
}
}
return eg.bytes()
}
// Decode decodes the given []byte into a tag map.
func Decode(bytes []byte) (*Map, error) {
ts := newMap()
err := DecodeEach(bytes, ts.upsert)
if err != nil {
// no partial failures
return nil, err
}
return ts, nil
}
// DecodeEach decodes the given serialized tag map, calling handler for each
// tag key and value decoded.
func DecodeEach(bytes []byte, fn func(key Key, val string, md metadatas)) error {
eg := &encoderGRPC{
buf: bytes,
}
if len(eg.buf) == 0 {
return nil
}
version := eg.readByte()
if version > tagsVersionID {
return fmt.Errorf("cannot decode: unsupported version: %q; supports only up to: %q", version, tagsVersionID)
}
for !eg.readEnded() {
typ := keyType(eg.readByte())
if typ != keyTypeString {
return fmt.Errorf("cannot decode: invalid key type: %q", typ)
}
k, err := eg.readBytesWithVarintLen()
if err != nil {
return err
}
v, err := eg.readBytesWithVarintLen()
if err != nil {
return err
}
key, err := NewKey(string(k))
if err != nil {
return err
}
val := string(v)
if !checkValue(val) {
return errInvalidValue
}
fn(key, val, createMetadatas(WithTTL(TTLUnlimitedPropagation)))
if err != nil {
return err
}
}
return nil
}

View File

@ -1,52 +0,0 @@
// Copyright 2019, OpenCensus Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package tag
const (
// valueTTLNoPropagation prevents tag from propagating.
valueTTLNoPropagation = 0
// valueTTLUnlimitedPropagation allows tag to propagate without any limits on number of hops.
valueTTLUnlimitedPropagation = -1
)
// TTL is metadata that specifies number of hops a tag can propagate.
// Details about TTL metadata is specified at https://github.com/census-instrumentation/opencensus-specs/blob/master/tags/TagMap.md#tagmetadata
type TTL struct {
ttl int
}
var (
// TTLUnlimitedPropagation is TTL metadata that allows tag to propagate without any limits on number of hops.
TTLUnlimitedPropagation = TTL{ttl: valueTTLUnlimitedPropagation}
// TTLNoPropagation is TTL metadata that prevents tag from propagating.
TTLNoPropagation = TTL{ttl: valueTTLNoPropagation}
)
type metadatas struct {
ttl TTL
}
// Metadata applies metadatas specified by the function.
type Metadata func(*metadatas)
// WithTTL applies metadata with provided ttl.
func WithTTL(ttl TTL) Metadata {
return func(m *metadatas) {
m.ttl = ttl
}
}

View File

@ -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.value)
}
pprof.Do(ctx, pprof.Labels(keyvals...), f)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -1,119 +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}
}
// Float64Attribute returns a float64-valued attribute.
func Float64Attribute(key string, value float64) 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 linked span is a child of the current span.
LinkTypeParent // The linked span is the parent of the current 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
}

View File

@ -1,86 +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
// MaxAnnotationEventsPerSpan is max number of annotation events per span
MaxAnnotationEventsPerSpan int
// MaxMessageEventsPerSpan is max number of message events per span
MaxMessageEventsPerSpan int
// MaxAnnotationEventsPerSpan is max number of attributes per span
MaxAttributesPerSpan int
// MaxLinksPerSpan is max number of links per span
MaxLinksPerSpan int
}
var configWriteMu sync.Mutex
const (
// DefaultMaxAnnotationEventsPerSpan is default max number of annotation events per span
DefaultMaxAnnotationEventsPerSpan = 32
// DefaultMaxMessageEventsPerSpan is default max number of message events per span
DefaultMaxMessageEventsPerSpan = 128
// DefaultMaxAttributesPerSpan is default max number of attributes per span
DefaultMaxAttributesPerSpan = 32
// DefaultMaxLinksPerSpan is default max number of links per span
DefaultMaxLinksPerSpan = 32
)
// 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
}
if cfg.MaxAnnotationEventsPerSpan > 0 {
c.MaxAnnotationEventsPerSpan = cfg.MaxAnnotationEventsPerSpan
}
if cfg.MaxMessageEventsPerSpan > 0 {
c.MaxMessageEventsPerSpan = cfg.MaxMessageEventsPerSpan
}
if cfg.MaxAttributesPerSpan > 0 {
c.MaxAttributesPerSpan = cfg.MaxAttributesPerSpan
}
if cfg.MaxLinksPerSpan > 0 {
c.MaxLinksPerSpan = cfg.MaxLinksPerSpan
}
config.Store(&c)
}

View File

@ -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"

View File

@ -1,38 +0,0 @@
// Copyright 2019, OpenCensus Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package trace
type evictedQueue struct {
queue []interface{}
capacity int
droppedCount int
}
func newEvictedQueue(capacity int) *evictedQueue {
eq := &evictedQueue{
capacity: capacity,
queue: make([]interface{}, 0),
}
return eq
}
func (eq *evictedQueue) add(value interface{}) {
if len(eq.queue) == eq.capacity {
eq.queue = eq.queue[1:]
eq.droppedCount++
}
eq.queue = append(eq.queue, value)
}

View File

@ -1,97 +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
DroppedAttributeCount int
DroppedAnnotationCount int
DroppedMessageEventCount int
DroppedLinkCount int
// ChildSpanCount holds the number of child span created for this span.
ChildSpanCount int
}

View File

@ -1,22 +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
// IDGenerator allows custom generators for TraceId and SpanId.
type IDGenerator interface {
NewTraceID() [16]byte
NewSpanID() [8]byte
}

View File

@ -1,61 +0,0 @@
// Copyright 2019, OpenCensus Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package trace
import (
"github.com/golang/groupcache/lru"
)
// A simple lru.Cache wrapper that tracks the keys of the current contents and
// the cumulative number of evicted items.
type lruMap struct {
cacheKeys map[lru.Key]bool
cache *lru.Cache
droppedCount int
}
func newLruMap(size int) *lruMap {
lm := &lruMap{
cacheKeys: make(map[lru.Key]bool),
cache: lru.New(size),
droppedCount: 0,
}
lm.cache.OnEvicted = func(key lru.Key, value interface{}) {
delete(lm.cacheKeys, key)
lm.droppedCount++
}
return lm
}
func (lm lruMap) len() int {
return lm.cache.Len()
}
func (lm lruMap) keys() []interface{} {
keys := []interface{}{}
for k := range lm.cacheKeys {
keys = append(keys, k)
}
return keys
}
func (lm *lruMap) add(key, value interface{}) {
lm.cacheKeys[lru.Key(key)] = true
lm.cache.Add(lru.Key(key), value)
}
func (lm *lruMap) get(key interface{}) (interface{}, bool) {
return lm.cache.Get(key)
}

View File

@ -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.

View File

@ -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}
}
}

View File

@ -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]
}

View File

@ -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()
}

View File

@ -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
)

View File

@ -1,598 +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
// lruAttributes are capped at configured limit. When the capacity is reached an oldest entry
// is removed to create room for a new entry.
lruAttributes *lruMap
// annotations are stored in FIFO queue capped by configured limit.
annotations *evictedQueue
// messageEvents are stored in FIFO queue capped by configured limit.
messageEvents *evictedQueue
// links are stored in FIFO queue capped by configured limit.
links *evictedQueue
// 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 {
p.addChild()
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,
}
span.lruAttributes = newLruMap(cfg.MaxAttributesPerSpan)
span.annotations = newEvictedQueue(cfg.MaxAnnotationEventsPerSpan)
span.messageEvents = newEvictedQueue(cfg.MaxMessageEventsPerSpan)
span.links = newEvictedQueue(cfg.MaxLinksPerSpan)
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 == nil {
return
}
if s.executionTracerTaskEnd != nil {
s.executionTracerTaskEnd()
}
if !s.IsRecordingEvents() {
return
}
s.endOnce.Do(func() {
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.lruAttributes.len() > 0 {
sd.Attributes = s.lruAttributesToAttributeMap()
sd.DroppedAttributeCount = s.lruAttributes.droppedCount
}
if len(s.annotations.queue) > 0 {
sd.Annotations = s.interfaceArrayToAnnotationArray()
sd.DroppedAnnotationCount = s.annotations.droppedCount
}
if len(s.messageEvents.queue) > 0 {
sd.MessageEvents = s.interfaceArrayToMessageEventArray()
sd.DroppedMessageEventCount = s.messageEvents.droppedCount
}
if len(s.links.queue) > 0 {
sd.Links = s.interfaceArrayToLinksArray()
sd.DroppedLinkCount = s.links.droppedCount
}
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()
}
func (s *Span) interfaceArrayToLinksArray() []Link {
linksArr := make([]Link, 0)
for _, value := range s.links.queue {
linksArr = append(linksArr, value.(Link))
}
return linksArr
}
func (s *Span) interfaceArrayToMessageEventArray() []MessageEvent {
messageEventArr := make([]MessageEvent, 0)
for _, value := range s.messageEvents.queue {
messageEventArr = append(messageEventArr, value.(MessageEvent))
}
return messageEventArr
}
func (s *Span) interfaceArrayToAnnotationArray() []Annotation {
annotationArr := make([]Annotation, 0)
for _, value := range s.annotations.queue {
annotationArr = append(annotationArr, value.(Annotation))
}
return annotationArr
}
func (s *Span) lruAttributesToAttributeMap() map[string]interface{} {
attributes := make(map[string]interface{})
for _, key := range s.lruAttributes.keys() {
value, ok := s.lruAttributes.get(key)
if ok {
keyStr := key.(string)
attributes[keyStr] = value
}
}
return attributes
}
func (s *Span) copyToCappedAttributes(attributes []Attribute) {
for _, a := range attributes {
s.lruAttributes.add(a.key, a.value)
}
}
func (s *Span) addChild() {
if !s.IsRecordingEvents() {
return
}
s.mu.Lock()
s.data.ChildSpanCount++
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()
s.copyToCappedAttributes(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.annotations.add(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.annotations.add(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.messageEvents.add(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.messageEvents.add(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.links.add(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,
MaxAttributesPerSpan: DefaultMaxAttributesPerSpan,
MaxAnnotationEventsPerSpan: DefaultMaxAnnotationEventsPerSpan,
MaxMessageEventsPerSpan: DefaultMaxMessageEventsPerSpan,
MaxLinksPerSpan: DefaultMaxLinksPerSpan,
})
}
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
}

View File

@ -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
}

View File

@ -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() {}
}

View File

@ -1,147 +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 implements support for the Tracestate header of the
// W3C TraceContext propagation format.
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
}

View File

@ -8,4 +8,3 @@
# Please keep the list sorted.
Google Inc.
LightStep Inc.

View File

@ -44,13 +44,7 @@ Ivan Krasin <krasin@golang.org>
Jason Hall <jasonhall@google.com>
Johan Euphrosine <proppy@google.com>
Kostik Shtoyk <kostik@google.com>
Kunpei Sakai <namusyaka@gmail.com>
Matthew Dolan <dolan@lightstep.com>
Matthew Whisenhunt <matt.whisenhunt@gmail.com>
Michael McGreevy <mcgreevy@golang.org>
Nick Craig-Wood <nickcw@gmail.com>
Robbie Trencheny <me@robbiet.us>
Ross Light <light@google.com>
Sarah Adams <shadams@google.com>
Scott Van Woudenberg <scottvw@google.com>
Takashi Matsuo <tmatsuo@google.com>

46
src/vendor/google.golang.org/api/gensupport/backoff.go generated vendored Normal file
View File

@ -0,0 +1,46 @@
// Copyright 2016 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.
package gensupport
import (
"math/rand"
"time"
)
type BackoffStrategy interface {
// Pause returns the duration of the next pause and true if the operation should be
// retried, or false if no further retries should be attempted.
Pause() (time.Duration, bool)
// Reset restores the strategy to its initial state.
Reset()
}
// ExponentialBackoff performs exponential backoff as per https://en.wikipedia.org/wiki/Exponential_backoff.
// The initial pause time is given by Base.
// Once the total pause time exceeds Max, Pause will indicate no further retries.
type ExponentialBackoff struct {
Base time.Duration
Max time.Duration
total time.Duration
n uint
}
func (eb *ExponentialBackoff) Pause() (time.Duration, bool) {
if eb.total > eb.Max {
return 0, false
}
// The next pause is selected from randomly from [0, 2^n * Base).
d := time.Duration(rand.Int63n((1 << eb.n) * int64(eb.Base)))
eb.total += d
eb.n++
return d, true
}
func (eb *ExponentialBackoff) Reset() {
eb.n = 0
eb.total = 0
}

View File

@ -11,9 +11,8 @@ import (
"google.golang.org/api/googleapi"
)
// MediaBuffer buffers data from an io.Reader to support uploading media in
// retryable chunks. It should be created with NewMediaBuffer.
type MediaBuffer struct {
// ResumableBuffer buffers data from an io.Reader to support uploading media in retryable chunks.
type ResumableBuffer struct {
media io.Reader
chunk []byte // The current chunk which is pending upload. The capacity is the chunk size.
@ -23,43 +22,42 @@ type MediaBuffer struct {
off int64
}
// NewMediaBuffer initializes a MediaBuffer.
func NewMediaBuffer(media io.Reader, chunkSize int) *MediaBuffer {
return &MediaBuffer{media: media, chunk: make([]byte, 0, chunkSize)}
func NewResumableBuffer(media io.Reader, chunkSize int) *ResumableBuffer {
return &ResumableBuffer{media: media, chunk: make([]byte, 0, chunkSize)}
}
// Chunk returns the current buffered chunk, the offset in the underlying media
// from which the chunk is drawn, and the size of the chunk.
// Successive calls to Chunk return the same chunk between calls to Next.
func (mb *MediaBuffer) Chunk() (chunk io.Reader, off int64, size int, err error) {
func (rb *ResumableBuffer) Chunk() (chunk io.Reader, off int64, size int, err error) {
// There may already be data in chunk if Next has not been called since the previous call to Chunk.
if mb.err == nil && len(mb.chunk) == 0 {
mb.err = mb.loadChunk()
if rb.err == nil && len(rb.chunk) == 0 {
rb.err = rb.loadChunk()
}
return bytes.NewReader(mb.chunk), mb.off, len(mb.chunk), mb.err
return bytes.NewReader(rb.chunk), rb.off, len(rb.chunk), rb.err
}
// loadChunk will read from media into chunk, up to the capacity of chunk.
func (mb *MediaBuffer) loadChunk() error {
bufSize := cap(mb.chunk)
mb.chunk = mb.chunk[:bufSize]
func (rb *ResumableBuffer) loadChunk() error {
bufSize := cap(rb.chunk)
rb.chunk = rb.chunk[:bufSize]
read := 0
var err error
for err == nil && read < bufSize {
var n int
n, err = mb.media.Read(mb.chunk[read:])
n, err = rb.media.Read(rb.chunk[read:])
read += n
}
mb.chunk = mb.chunk[:read]
rb.chunk = rb.chunk[:read]
return err
}
// Next advances to the next chunk, which will be returned by the next call to Chunk.
// Calls to Next without a corresponding prior call to Chunk will have no effect.
func (mb *MediaBuffer) Next() {
mb.off += int64(len(mb.chunk))
mb.chunk = mb.chunk[0:0]
func (rb *ResumableBuffer) Next() {
rb.off += int64(len(rb.chunk))
rb.chunk = rb.chunk[0:0]
}
type readerTyper struct {

View File

@ -12,43 +12,29 @@ import (
)
// MarshalJSON returns a JSON encoding of schema containing only selected fields.
// A field is selected if any of the following is true:
// * it has a non-empty value
// * its field name is present in forceSendFields and it is not a nil pointer or nil interface
// * its field name is present in nullFields.
// A field is selected if:
// * it has a non-empty value, or
// * its field name is present in forceSendFields, and
// * it is not a nil pointer or nil interface.
// The JSON key for each selected field is taken from the field's json: struct tag.
func MarshalJSON(schema interface{}, forceSendFields, nullFields []string) ([]byte, error) {
if len(forceSendFields) == 0 && len(nullFields) == 0 {
func MarshalJSON(schema interface{}, forceSendFields []string) ([]byte, error) {
if len(forceSendFields) == 0 {
return json.Marshal(schema)
}
mustInclude := make(map[string]bool)
mustInclude := make(map[string]struct{})
for _, f := range forceSendFields {
mustInclude[f] = true
}
useNull := make(map[string]bool)
useNullMaps := make(map[string]map[string]bool)
for _, nf := range nullFields {
parts := strings.SplitN(nf, ".", 2)
field := parts[0]
if len(parts) == 1 {
useNull[field] = true
} else {
if useNullMaps[field] == nil {
useNullMaps[field] = map[string]bool{}
}
useNullMaps[field][parts[1]] = true
}
mustInclude[f] = struct{}{}
}
dataMap, err := schemaToMap(schema, mustInclude, useNull, useNullMaps)
dataMap, err := schemaToMap(schema, mustInclude)
if err != nil {
return nil, err
}
return json.Marshal(dataMap)
}
func schemaToMap(schema interface{}, mustInclude, useNull map[string]bool, useNullMaps map[string]map[string]bool) (map[string]interface{}, error) {
func schemaToMap(schema interface{}, mustInclude map[string]struct{}) (map[string]interface{}, error) {
m := make(map[string]interface{})
s := reflect.ValueOf(schema)
st := s.Type()
@ -68,36 +54,10 @@ func schemaToMap(schema interface{}, mustInclude, useNull map[string]bool, useNu
v := s.Field(i)
f := st.Field(i)
if useNull[f.Name] {
if !isEmptyValue(v) {
return nil, fmt.Errorf("field %q in NullFields has non-empty value", f.Name)
}
m[tag.apiName] = nil
continue
}
if !includeField(v, f, mustInclude) {
continue
}
// If map fields are explicitly set to null, use a map[string]interface{}.
if f.Type.Kind() == reflect.Map && useNullMaps[f.Name] != nil {
ms, ok := v.Interface().(map[string]string)
if !ok {
return nil, fmt.Errorf("field %q has keys in NullFields but is not a map[string]string", f.Name)
}
mi := map[string]interface{}{}
for k, v := range ms {
mi[k] = v
}
for k := range useNullMaps[f.Name] {
mi[k] = nil
}
m[tag.apiName] = mi
continue
}
// nil maps are treated as empty maps.
if f.Type.Kind() == reflect.Map && v.IsNil() {
m[tag.apiName] = map[string]string{}
@ -167,7 +127,7 @@ func parseJSONTag(val string) (jsonTag, error) {
}
// Reports whether the struct field "f" with value "v" should be included in JSON output.
func includeField(v reflect.Value, f reflect.StructField, mustInclude map[string]bool) bool {
func includeField(v reflect.Value, f reflect.StructField, mustInclude map[string]struct{}) bool {
// The regular JSON encoding of a nil pointer is "null", which means "delete this field".
// Therefore, we could enable field deletion by honoring pointer fields' presence in the mustInclude set.
// However, many fields are not pointers, so there would be no way to delete these fields.
@ -184,7 +144,8 @@ func includeField(v reflect.Value, f reflect.StructField, mustInclude map[string
return false
}
return mustInclude[f.Name] || !isEmptyValue(v)
_, ok := mustInclude[f.Name]
return ok || !isEmptyValue(v)
}
// isEmptyValue reports whether v is the empty value for its type. This

200
src/vendor/google.golang.org/api/gensupport/media.go generated vendored Normal file
View File

@ -0,0 +1,200 @@
// Copyright 2016 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.
package gensupport
import (
"fmt"
"io"
"io/ioutil"
"mime/multipart"
"net/http"
"net/textproto"
"google.golang.org/api/googleapi"
)
const sniffBuffSize = 512
func newContentSniffer(r io.Reader) *contentSniffer {
return &contentSniffer{r: r}
}
// contentSniffer wraps a Reader, and reports the content type determined by sniffing up to 512 bytes from the Reader.
type contentSniffer struct {
r io.Reader
start []byte // buffer for the sniffed bytes.
err error // set to any error encountered while reading bytes to be sniffed.
ctype string // set on first sniff.
sniffed bool // set to true on first sniff.
}
func (cs *contentSniffer) Read(p []byte) (n int, err error) {
// Ensure that the content type is sniffed before any data is consumed from Reader.
_, _ = cs.ContentType()
if len(cs.start) > 0 {
n := copy(p, cs.start)
cs.start = cs.start[n:]
return n, nil
}
// We may have read some bytes into start while sniffing, even if the read ended in an error.
// We should first return those bytes, then the error.
if cs.err != nil {
return 0, cs.err
}
// Now we have handled all bytes that were buffered while sniffing. Now just delegate to the underlying reader.
return cs.r.Read(p)
}
// ContentType returns the sniffed content type, and whether the content type was succesfully sniffed.
func (cs *contentSniffer) ContentType() (string, bool) {
if cs.sniffed {
return cs.ctype, cs.ctype != ""
}
cs.sniffed = true
// If ReadAll hits EOF, it returns err==nil.
cs.start, cs.err = ioutil.ReadAll(io.LimitReader(cs.r, sniffBuffSize))
// Don't try to detect the content type based on possibly incomplete data.
if cs.err != nil {
return "", false
}
cs.ctype = http.DetectContentType(cs.start)
return cs.ctype, true
}
// DetermineContentType determines the content type of the supplied reader.
// If the content type is already known, it can be specified via ctype.
// Otherwise, the content of media will be sniffed to determine the content type.
// If media implements googleapi.ContentTyper (deprecated), this will be used
// instead of sniffing the content.
// After calling DetectContentType the caller must not perform further reads on
// media, but rather read from the Reader that is returned.
func DetermineContentType(media io.Reader, ctype string) (io.Reader, string) {
// Note: callers could avoid calling DetectContentType if ctype != "",
// but doing the check inside this function reduces the amount of
// generated code.
if ctype != "" {
return media, ctype
}
// For backwards compatability, allow clients to set content
// type by providing a ContentTyper for media.
if typer, ok := media.(googleapi.ContentTyper); ok {
return media, typer.ContentType()
}
sniffer := newContentSniffer(media)
if ctype, ok := sniffer.ContentType(); ok {
return sniffer, ctype
}
// If content type could not be sniffed, reads from sniffer will eventually fail with an error.
return sniffer, ""
}
type typeReader struct {
io.Reader
typ string
}
// multipartReader combines the contents of multiple readers to creat a multipart/related HTTP body.
// Close must be called if reads from the multipartReader are abandoned before reaching EOF.
type multipartReader struct {
pr *io.PipeReader
pipeOpen bool
ctype string
}
func newMultipartReader(parts []typeReader) *multipartReader {
mp := &multipartReader{pipeOpen: true}
var pw *io.PipeWriter
mp.pr, pw = io.Pipe()
mpw := multipart.NewWriter(pw)
mp.ctype = "multipart/related; boundary=" + mpw.Boundary()
go func() {
for _, part := range parts {
w, err := mpw.CreatePart(typeHeader(part.typ))
if err != nil {
mpw.Close()
pw.CloseWithError(fmt.Errorf("googleapi: CreatePart failed: %v", err))
return
}
_, err = io.Copy(w, part.Reader)
if err != nil {
mpw.Close()
pw.CloseWithError(fmt.Errorf("googleapi: Copy failed: %v", err))
return
}
}
mpw.Close()
pw.Close()
}()
return mp
}
func (mp *multipartReader) Read(data []byte) (n int, err error) {
return mp.pr.Read(data)
}
func (mp *multipartReader) Close() error {
if !mp.pipeOpen {
return nil
}
mp.pipeOpen = false
return mp.pr.Close()
}
// CombineBodyMedia combines a json body with media content to create a multipart/related HTTP body.
// It returns a ReadCloser containing the combined body, and the overall "multipart/related" content type, with random boundary.
//
// The caller must call Close on the returned ReadCloser if reads are abandoned before reaching EOF.
func CombineBodyMedia(body io.Reader, bodyContentType string, media io.Reader, mediaContentType string) (io.ReadCloser, string) {
mp := newMultipartReader([]typeReader{
{body, bodyContentType},
{media, mediaContentType},
})
return mp, mp.ctype
}
func typeHeader(contentType string) textproto.MIMEHeader {
h := make(textproto.MIMEHeader)
if contentType != "" {
h.Set("Content-Type", contentType)
}
return h
}
// PrepareUpload determines whether the data in the supplied reader should be
// uploaded in a single request, or in sequential chunks.
// chunkSize is the size of the chunk that media should be split into.
// If chunkSize is non-zero and the contents of media do not fit in a single
// chunk (or there is an error reading media), then media will be returned as a
// ResumableBuffer. Otherwise, media will be returned as a Reader.
//
// After PrepareUpload has been called, media should no longer be used: the
// media content should be accessed via one of the return values.
func PrepareUpload(media io.Reader, chunkSize int) (io.Reader,
*ResumableBuffer) {
if chunkSize == 0 { // do not chunk
return media, nil
}
rb := NewResumableBuffer(media, chunkSize)
rdr, _, _, err := rb.Chunk()
if err == io.EOF { // we can upload this in a single request
return rdr, nil
}
// err might be a non-EOF error. If it is, the next call to rb.Chunk will
// return the same error. Returning a ResumableBuffer ensures that this error
// will be handled at some point.
return nil, rb
}

View File

@ -43,7 +43,6 @@ func (u URLParams) Encode() string {
return url.Values(u).Encode()
}
// SetOptions sets the URL params and any additional call options.
func SetOptions(u URLParams, opts ...googleapi.CallOption) {
for _, o := range opts {
u.Set(o.Get())

View File

@ -5,32 +5,21 @@
package gensupport
import (
"context"
"errors"
"fmt"
"io"
"net/http"
"sync"
"time"
gax "github.com/googleapis/gax-go/v2"
)
// Backoff is an interface around gax.Backoff's Pause method, allowing tests to provide their
// own implementation.
type Backoff interface {
Pause() time.Duration
}
// These are declared as global variables so that tests can overwrite them.
var (
retryDeadline = 32 * time.Second
backoff = func() Backoff {
return &gax.Backoff{Initial: 100 * time.Millisecond}
}
"golang.org/x/net/context"
"golang.org/x/net/context/ctxhttp"
)
const (
// statusResumeIncomplete is the code returned by the Google uploader
// when the transfer is not yet complete.
statusResumeIncomplete = 308
// statusTooManyRequests is returned by the storage API if the
// per-project limits have been temporarily exceeded. The request
// should be retried.
@ -46,7 +35,7 @@ type ResumableUpload struct {
URI string
UserAgent string // User-Agent for header of the request
// Media is the object being uploaded.
Media *MediaBuffer
Media *ResumableBuffer
// MediaType defines the media type, e.g. "image/jpeg".
MediaType string
@ -55,6 +44,9 @@ type ResumableUpload struct {
// Callback is an optional function that will be periodically called with the cumulative number of bytes uploaded.
Callback func(int64)
// If not specified, a default exponential backoff strategy will be used.
Backoff BackoffStrategy
}
// Progress returns the number of bytes uploaded at this point.
@ -88,23 +80,8 @@ func (rx *ResumableUpload) doUploadRequest(ctx context.Context, data io.Reader,
req.Header.Set("Content-Range", contentRange)
req.Header.Set("Content-Type", rx.MediaType)
req.Header.Set("User-Agent", rx.UserAgent)
return ctxhttp.Do(ctx, rx.Client, req)
// Google's upload endpoint uses status code 308 for a
// different purpose than the "308 Permanent Redirect"
// since-standardized in RFC 7238. Because of the conflict in
// semantics, Google added this new request header which
// causes it to not use "308" and instead reply with 200 OK
// and sets the upload-specific "X-HTTP-Status-Code-Override:
// 308" response header.
req.Header.Set("X-GUploader-No-308", "yes")
return SendRequest(ctx, rx.Client, req)
}
func statusResumeIncomplete(resp *http.Response) bool {
// This is how the server signals "status resume incomplete"
// when X-GUploader-No-308 is set to "yes":
return resp != nil && resp.Header.Get("X-Http-Status-Code-Override") == "308"
}
// reportProgress calls a user-supplied callback to report upload progress.
@ -135,107 +112,87 @@ func (rx *ResumableUpload) transferChunk(ctx context.Context) (*http.Response, e
return res, err
}
// We sent "X-GUploader-No-308: yes" (see comment elsewhere in
// this file), so we don't expect to get a 308.
if res.StatusCode == 308 {
return nil, errors.New("unexpected 308 response status code")
}
if res.StatusCode == http.StatusOK {
if res.StatusCode == statusResumeIncomplete || res.StatusCode == http.StatusOK {
rx.reportProgress(off, off+int64(size))
}
if statusResumeIncomplete(res) {
if res.StatusCode == statusResumeIncomplete {
rx.Media.Next()
}
return res, nil
}
func contextDone(ctx context.Context) bool {
select {
case <-ctx.Done():
return true
default:
return false
}
}
// Upload starts the process of a resumable upload with a cancellable context.
// It retries using the provided back off strategy until cancelled or the
// strategy indicates to stop retrying.
// It is called from the auto-generated API code and is not visible to the user.
// Before sending an HTTP request, Upload calls any registered hook functions,
// and calls the returned functions after the request returns (see send.go).
// rx is private to the auto-generated API code.
// Exactly one of resp or err will be nil. If resp is non-nil, the caller must call resp.Body.Close.
func (rx *ResumableUpload) Upload(ctx context.Context) (resp *http.Response, err error) {
var shouldRetry = func(status int, err error) bool {
if 500 <= status && status <= 599 {
return true
}
if status == statusTooManyRequests {
return true
}
if err == io.ErrUnexpectedEOF {
return true
}
if err, ok := err.(interface{ Temporary() bool }); ok {
return err.Temporary()
}
return false
var pause time.Duration
backoff := rx.Backoff
if backoff == nil {
backoff = DefaultBackoffStrategy()
}
// There are a couple of cases where it's possible for err and resp to both
// be non-nil. However, we expose a simpler contract to our callers: exactly
// one of resp and err will be non-nil. This means that any response body
// must be closed here before returning a non-nil error.
var prepareReturn = func(resp *http.Response, err error) (*http.Response, error) {
for {
// Ensure that we return in the case of cancelled context, even if pause is 0.
if contextDone(ctx) {
return nil, ctx.Err()
}
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-time.After(pause):
}
resp, err = rx.transferChunk(ctx)
var status int
if resp != nil {
status = resp.StatusCode
}
// Check if we should retry the request.
if shouldRetry(status, err) {
var retry bool
pause, retry = backoff.Pause()
if retry {
if resp != nil && resp.Body != nil {
resp.Body.Close()
}
continue
}
}
// If the chunk was uploaded successfully, but there's still
// more to go, upload the next chunk without any delay.
if status == statusResumeIncomplete {
pause = 0
backoff.Reset()
resp.Body.Close()
continue
}
// It's possible for err and resp to both be non-nil here, but we expose a simpler
// contract to our callers: exactly one of resp and err will be non-nil. This means
// that any response body must be closed here before returning a non-nil error.
if err != nil {
if resp != nil && resp.Body != nil {
resp.Body.Close()
}
return nil, err
}
return resp, nil
}
// Send all chunks.
for {
var pause time.Duration
// Each chunk gets its own initialized-at-zero retry.
bo := backoff()
quitAfter := time.After(retryDeadline)
// Retry loop for a single chunk.
for {
select {
case <-ctx.Done():
if err == nil {
err = ctx.Err()
}
return prepareReturn(resp, err)
case <-time.After(pause):
case <-quitAfter:
return prepareReturn(resp, err)
}
resp, err = rx.transferChunk(ctx)
var status int
if resp != nil {
status = resp.StatusCode
}
// Check if we should retry the request.
if !shouldRetry(status, err) {
break
}
pause = bo.Pause()
if resp != nil && resp.Body != nil {
resp.Body.Close()
}
}
// If the chunk was uploaded successfully, but there's still
// more to go, upload the next chunk without any delay.
if statusResumeIncomplete(resp) {
resp.Body.Close()
continue
}
return prepareReturn(resp, err)
}
}

77
src/vendor/google.golang.org/api/gensupport/retry.go generated vendored Normal file
View File

@ -0,0 +1,77 @@
package gensupport
import (
"io"
"net"
"net/http"
"time"
"golang.org/x/net/context"
)
// Retry invokes the given function, retrying it multiple times if the connection failed or
// the HTTP status response indicates the request should be attempted again. ctx may be nil.
func Retry(ctx context.Context, f func() (*http.Response, error), backoff BackoffStrategy) (*http.Response, error) {
for {
resp, err := f()
var status int
if resp != nil {
status = resp.StatusCode
}
// Return if we shouldn't retry.
pause, retry := backoff.Pause()
if !shouldRetry(status, err) || !retry {
return resp, err
}
// Ensure the response body is closed, if any.
if resp != nil && resp.Body != nil {
resp.Body.Close()
}
// Pause, but still listen to ctx.Done if context is not nil.
var done <-chan struct{}
if ctx != nil {
done = ctx.Done()
}
select {
case <-done:
return nil, ctx.Err()
case <-time.After(pause):
}
}
}
// DefaultBackoffStrategy returns a default strategy to use for retrying failed upload requests.
func DefaultBackoffStrategy() BackoffStrategy {
return &ExponentialBackoff{
Base: 250 * time.Millisecond,
Max: 16 * time.Second,
}
}
// shouldRetry returns true if the HTTP response / error indicates that the
// request should be attempted again.
func shouldRetry(status int, err error) bool {
// Retry for 5xx response codes.
if 500 <= status && status < 600 {
return true
}
// Retry on statusTooManyRequests{
if status == statusTooManyRequests {
return true
}
// Retry on unexpected EOFs and temporary network errors.
if err == io.ErrUnexpectedEOF {
return true
}
if err, ok := err.(net.Error); ok {
return err.Temporary()
}
return false
}

View File

@ -1,4 +1,4 @@
// Copyright 2011 Google LLC. All rights reserved.
// Copyright 2011 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
@ -16,7 +16,7 @@ import (
"net/url"
"strings"
"google.golang.org/api/internal/third_party/uritemplates"
"google.golang.org/api/googleapi/internal/uritemplates"
)
// ContentTyper is an interface for Readers which know (or would like
@ -37,28 +37,24 @@ type SizeReaderAt interface {
// ServerResponse is embedded in each Do response and
// provides the HTTP status code and header sent by the server.
type ServerResponse struct {
// HTTPStatusCode is the server's response status code. When using a
// resource method's Do call, this will always be in the 2xx range.
// HTTPStatusCode is the server's response status code.
// When using a resource method's Do call, this will always be in the 2xx range.
HTTPStatusCode int
// Header contains the response header fields from the server.
Header http.Header
}
const (
// Version defines the gax version being used. This is typically sent
// in an HTTP header to services.
Version = "0.5"
// UserAgent is the header string used to identify this package.
UserAgent = "google-api-go-client/" + Version
// DefaultUploadChunkSize is the default chunk size to use for resumable
// uploads if not specified by the user.
DefaultUploadChunkSize = 16 * 1024 * 1024
// The default chunk size to use for resumable uplods if not specified by the user.
DefaultUploadChunkSize = 8 * 1024 * 1024
// MinUploadChunkSize is the minimum chunk size that can be used for
// resumable uploads. All user-specified chunk sizes must be multiple of
// this value.
// The minimum chunk size that can be used for resumable uploads. All
// user-specified chunk sizes must be multiple of this value.
MinUploadChunkSize = 256 * 1024
)
@ -153,25 +149,21 @@ func IsNotModified(err error) bool {
// CheckMediaResponse returns an error (of type *Error) if the response
// status code is not 2xx. Unlike CheckResponse it does not assume the
// body is a JSON error document.
// It is the caller's responsibility to close res.Body.
func CheckMediaResponse(res *http.Response) error {
if res.StatusCode >= 200 && res.StatusCode <= 299 {
return nil
}
slurp, _ := ioutil.ReadAll(io.LimitReader(res.Body, 1<<20))
res.Body.Close()
return &Error{
Code: res.StatusCode,
Body: string(slurp),
}
}
// MarshalStyle defines whether to marshal JSON with a {"data": ...} wrapper.
type MarshalStyle bool
// WithDataWrapper marshals JSON with a {"data": ...} wrapper.
var WithDataWrapper = MarshalStyle(true)
// WithoutDataWrapper marshals JSON without a {"data": ...} wrapper.
var WithoutDataWrapper = MarshalStyle(false)
func (wrap MarshalStyle) JSONReader(v interface{}) (io.Reader, error) {
@ -189,12 +181,37 @@ func (wrap MarshalStyle) JSONReader(v interface{}) (io.Reader, error) {
return buf, nil
}
// endingWithErrorReader from r until it returns an error. If the
// final error from r is io.EOF and e is non-nil, e is used instead.
type endingWithErrorReader struct {
r io.Reader
e error
}
func (er endingWithErrorReader) Read(p []byte) (n int, err error) {
n, err = er.r.Read(p)
if err == io.EOF && er.e != nil {
err = er.e
}
return
}
// countingWriter counts the number of bytes it receives to write, but
// discards them.
type countingWriter struct {
n *int64
}
func (w countingWriter) Write(p []byte) (int, error) {
*w.n += int64(len(p))
return len(p), nil
}
// ProgressUpdater is a function that is called upon every progress update of a resumable upload.
// This is the only part of a resumable upload (from googleapi) that is usable by the developer.
// The remaining usable pieces of resumable uploads is exposed in each auto-generated API.
type ProgressUpdater func(current, total int64)
// MediaOption defines the interface for setting media options.
type MediaOption interface {
setOptions(o *MediaOptions)
}
@ -251,47 +268,51 @@ func ProcessMediaOptions(opts []MediaOption) *MediaOptions {
return mo
}
// ResolveRelative resolves relatives such as "http://www.golang.org/" and
// "topics/myproject/mytopic" into a single string, such as
// "http://www.golang.org/topics/myproject/mytopic". It strips all parent
// references (e.g. ../..) as well as anything after the host
// (e.g. /bar/gaz gets stripped out of foo.com/bar/gaz).
//
// ResolveRelative panics if either basestr or relstr is not able to be parsed.
func ResolveRelative(basestr, relstr string) string {
u, err := url.Parse(basestr)
if err != nil {
panic(fmt.Sprintf("failed to parse %q", basestr))
}
afterColonPath := ""
if i := strings.IndexRune(relstr, ':'); i > 0 {
afterColonPath = relstr[i+1:]
relstr = relstr[:i]
}
rel, err := url.Parse(relstr)
if err != nil {
panic(fmt.Sprintf("failed to parse %q", relstr))
}
u, _ := url.Parse(basestr)
rel, _ := url.Parse(relstr)
u = u.ResolveReference(rel)
us := u.String()
if afterColonPath != "" {
us = fmt.Sprintf("%s:%s", us, afterColonPath)
}
us = strings.Replace(us, "%7B", "{", -1)
us = strings.Replace(us, "%7D", "}", -1)
us = strings.Replace(us, "%2A", "*", -1)
return us
}
// has4860Fix is whether this Go environment contains the fix for
// http://golang.org/issue/4860
var has4860Fix bool
// init initializes has4860Fix by checking the behavior of the net/http package.
func init() {
r := http.Request{
URL: &url.URL{
Scheme: "http",
Opaque: "//opaque",
},
}
b := &bytes.Buffer{}
r.Write(b)
has4860Fix = bytes.HasPrefix(b.Bytes(), []byte("GET http"))
}
// SetOpaque sets u.Opaque from u.Path such that HTTP requests to it
// don't alter any hex-escaped characters in u.Path.
func SetOpaque(u *url.URL) {
u.Opaque = "//" + u.Host + u.Path
if !has4860Fix {
u.Opaque = u.Scheme + ":" + u.Opaque
}
}
// Expand subsitutes any {encoded} strings in the URL passed in using
// the map supplied.
//
// This calls SetOpaque to avoid encoding of the parameters in the URL path.
func Expand(u *url.URL, expansions map[string]string) {
escaped, unescaped, err := uritemplates.Expand(u.Path, expansions)
expanded, err := uritemplates.Expand(u.Path, expansions)
if err == nil {
u.Path = unescaped
u.RawPath = escaped
u.Path = expanded
SetOpaque(u)
}
}
@ -339,7 +360,7 @@ func ConvertVariant(v map[string]interface{}, dst interface{}) bool {
}
// A Field names a field to be retrieved with a partial response.
// https://cloud.google.com/storage/docs/json_api/v1/how-tos/performance
// See https://developers.google.com/gdata/docs/2.0/basics#PartialResponse
//
// Partial responses can dramatically reduce the amount of data that must be sent to your application.
// In order to request partial responses, you can specify the full list of fields
@ -356,6 +377,9 @@ func ConvertVariant(v map[string]interface{}, dst interface{}) bool {
//
// svc.Events.List().Fields("nextPageToken", "items(id,updated)").Do()
//
// More information about field formatting can be found here:
// https://developers.google.com/+/api/#fields-syntax
//
// Another way to find field names is through the Google API explorer:
// https://developers.google.com/apis-explorer/#p/
type Field string
@ -397,12 +421,4 @@ type userIP string
func (i userIP) Get() (string, string) { return "userIp", string(i) }
// Trace returns a CallOption that enables diagnostic tracing for a call.
// traceToken is an ID supplied by Google support.
func Trace(traceToken string) CallOption { return traceTok(traceToken) }
type traceTok string
func (t traceTok) Get() (string, string) { return "trace", "token:" + string(t) }
// TODO: Fields too

View File

@ -0,0 +1,18 @@
Copyright (c) 2013 Joshua Tacoma
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -34,37 +34,11 @@ func pctEncode(src []byte) []byte {
return dst
}
// pairWriter is a convenience struct which allows escaped and unescaped
// versions of the template to be written in parallel.
type pairWriter struct {
escaped, unescaped bytes.Buffer
}
// Write writes the provided string directly without any escaping.
func (w *pairWriter) Write(s string) {
w.escaped.WriteString(s)
w.unescaped.WriteString(s)
}
// Escape writes the provided string, escaping the string for the
// escaped output.
func (w *pairWriter) Escape(s string, allowReserved bool) {
w.unescaped.WriteString(s)
func escape(s string, allowReserved bool) string {
if allowReserved {
w.escaped.Write(reserved.ReplaceAllFunc([]byte(s), pctEncode))
} else {
w.escaped.Write(unreserved.ReplaceAllFunc([]byte(s), pctEncode))
return string(reserved.ReplaceAllFunc([]byte(s), pctEncode))
}
}
// Escaped returns the escaped string.
func (w *pairWriter) Escaped() string {
return w.escaped.String()
}
// Unescaped returns the unescaped string.
func (w *pairWriter) Unescaped() string {
return w.unescaped.String()
return string(unreserved.ReplaceAllFunc([]byte(s), pctEncode))
}
// A uriTemplate is a parsed representation of a URI template.
@ -191,25 +165,23 @@ func parseTerm(term string) (result templateTerm, err error) {
err = errors.New("not a valid name: " + result.name)
}
if result.explode && result.truncate > 0 {
err = errors.New("both explode and prefix modifiers on same term")
err = errors.New("both explode and prefix modifers on same term")
}
return result, err
}
// Expand expands a URI template with a set of values to produce the
// resultant URI. Two forms of the result are returned: one with all the
// elements escaped, and one with the elements unescaped.
func (t *uriTemplate) Expand(values map[string]string) (escaped, unescaped string) {
var w pairWriter
// Expand expands a URI template with a set of values to produce a string.
func (t *uriTemplate) Expand(values map[string]string) string {
var buf bytes.Buffer
for _, p := range t.parts {
p.expand(&w, values)
p.expand(&buf, values)
}
return w.Escaped(), w.Unescaped()
return buf.String()
}
func (tp *templatePart) expand(w *pairWriter, values map[string]string) {
func (tp *templatePart) expand(buf *bytes.Buffer, values map[string]string) {
if len(tp.raw) > 0 {
w.Write(tp.raw)
buf.WriteString(tp.raw)
return
}
var first = true
@ -219,30 +191,30 @@ func (tp *templatePart) expand(w *pairWriter, values map[string]string) {
continue
}
if first {
w.Write(tp.first)
buf.WriteString(tp.first)
first = false
} else {
w.Write(tp.sep)
buf.WriteString(tp.sep)
}
tp.expandString(w, term, value)
tp.expandString(buf, term, value)
}
}
func (tp *templatePart) expandName(w *pairWriter, name string, empty bool) {
func (tp *templatePart) expandName(buf *bytes.Buffer, name string, empty bool) {
if tp.named {
w.Write(name)
buf.WriteString(name)
if empty {
w.Write(tp.ifemp)
buf.WriteString(tp.ifemp)
} else {
w.Write("=")
buf.WriteString("=")
}
}
}
func (tp *templatePart) expandString(w *pairWriter, t templateTerm, s string) {
func (tp *templatePart) expandString(buf *bytes.Buffer, t templateTerm, s string) {
if len(s) > t.truncate && t.truncate > 0 {
s = s[:t.truncate]
}
tp.expandName(w, t.name, len(s) == 0)
w.Escape(s, tp.allowReserved)
tp.expandName(buf, t.name, len(s) == 0)
buf.WriteString(escape(s, tp.allowReserved))
}

Some files were not shown because too many files have changed in this diff Show More