fix: panic - putting the same entry twice not supported
This commit is contained in:
parent
766f27d8f3
commit
665bc8452c
7
go.mod
7
go.mod
|
@ -25,13 +25,12 @@ require (
|
|||
github.com/hashicorp/go-version v1.2.0
|
||||
github.com/imdario/mergo v0.3.12
|
||||
github.com/ipfs/go-cid v0.3.2
|
||||
github.com/ipfs/go-log v1.0.5 // indirect
|
||||
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a
|
||||
github.com/keighl/metabolize v0.0.0-20150915210303-97ab655d4034
|
||||
github.com/kilic/bls12-381 v0.0.0-20200607163746-32e1441c8a9f
|
||||
github.com/lib/pq v1.10.4
|
||||
github.com/libp2p/go-libp2p v0.25.1
|
||||
github.com/libp2p/go-libp2p-pubsub v0.9.0
|
||||
github.com/libp2p/go-libp2p-pubsub v0.9.1
|
||||
github.com/lucasb-eyer/go-colorful v1.0.3
|
||||
github.com/mat/besticon v0.0.0-20210314201728-1579f269edb7
|
||||
github.com/multiformats/go-multiaddr v0.8.0
|
||||
|
@ -78,7 +77,7 @@ require (
|
|||
github.com/ipfs/go-log/v2 v2.5.1
|
||||
github.com/ladydascalie/currency v1.6.0
|
||||
github.com/meirf/gopart v0.0.0-20180520194036-37e9492a85a8
|
||||
github.com/waku-org/go-waku v0.5.2-0.20230223234511-57f6110074d0
|
||||
github.com/waku-org/go-waku v0.5.2-0.20230224151428-d6c87f346b72
|
||||
github.com/yeqown/go-qrcode/v2 v2.2.1
|
||||
github.com/yeqown/go-qrcode/writer/standard v1.2.1
|
||||
)
|
||||
|
@ -123,7 +122,6 @@ require (
|
|||
github.com/dustin/go-humanize v1.0.0 // indirect
|
||||
github.com/edsrzf/mmap-go v1.0.0 // indirect
|
||||
github.com/elastic/gosigar v0.14.2 // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 // indirect
|
||||
github.com/flynn/noise v1.0.0 // indirect
|
||||
github.com/francoispqt/gojay v1.2.13 // indirect
|
||||
|
@ -192,7 +190,6 @@ require (
|
|||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.5.1 // indirect
|
||||
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 // indirect
|
||||
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
|
||||
github.com/pion/datachannel v1.5.2 // indirect
|
||||
github.com/pion/dtls/v2 v2.1.2 // indirect
|
||||
|
|
16
go.sum
16
go.sum
|
@ -721,8 +721,6 @@ github.com/elliotchance/orderedmap v1.3.0/go.mod h1:8hdSl6jmveQw8ScByd3AaNHNk51R
|
|||
github.com/elliotchance/orderedmap v1.4.0/go.mod h1:wsDwEaX5jEoyhbs7x93zk2H/qv0zwuhg4inXhDkYqys=
|
||||
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
||||
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
|
@ -1168,9 +1166,6 @@ github.com/ipfs/go-cid v0.3.2 h1:OGgOd+JCFM+y1DjWPmVH+2/4POtpDzwcr7VgnB7mZXc=
|
|||
github.com/ipfs/go-cid v0.3.2/go.mod h1:gQ8pKqT/sUxGY+tIwy1RPpAojYu7jAyCp5Tz1svoupw=
|
||||
github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk=
|
||||
github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps=
|
||||
github.com/ipfs/go-log v1.0.5 h1:2dOuUCB1Z7uoczMWgAyDck5JLb72zHzrMnGnCNNbvY8=
|
||||
github.com/ipfs/go-log v1.0.5/go.mod h1:j0b8ZoR+7+R99LD9jZ6+AJsrzkPbSXbZfGakb5JPtIo=
|
||||
github.com/ipfs/go-log/v2 v2.1.3/go.mod h1:/8d0SH3Su5Ooc31QlL1WysJhvyOTDCjcCZ9Axpmri6g=
|
||||
github.com/ipfs/go-log/v2 v2.5.1 h1:1XdUzF7048prq4aBjDQQ4SL5RxftpRGdXhNRwKSAlcY=
|
||||
github.com/ipfs/go-log/v2 v2.5.1/go.mod h1:prSpmC1Gpllc9UYWxDiZDreBYw7zp4Iqp1kOLU9U5UI=
|
||||
github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA=
|
||||
|
@ -1351,8 +1346,8 @@ github.com/libp2p/go-libp2p v0.25.1 h1:YK+YDCHpYyTvitKWVxa5PfElgIpOONU01X5UcLEwJ
|
|||
github.com/libp2p/go-libp2p v0.25.1/go.mod h1:xnK9/1d9+jeQCVvi/f1g12KqtVi/jP/SijtKV1hML3g=
|
||||
github.com/libp2p/go-libp2p-asn-util v0.2.0 h1:rg3+Os8jbnO5DxkC7K/Utdi+DkY3q/d1/1q+8WeNAsw=
|
||||
github.com/libp2p/go-libp2p-asn-util v0.2.0/go.mod h1:WoaWxbHKBymSN41hWSq/lGKJEca7TNm58+gGJi2WsLI=
|
||||
github.com/libp2p/go-libp2p-pubsub v0.9.0 h1:mcLb4WzwhUG4OKb0rp1/bYMd/DYhvMyzJheQH3LMd1s=
|
||||
github.com/libp2p/go-libp2p-pubsub v0.9.0/go.mod h1:OEsj0Cc/BpkqikXRTrVspWU/Hx7bMZwHP+6vNMd+c7I=
|
||||
github.com/libp2p/go-libp2p-pubsub v0.9.1 h1:A6LBg9BaoLf3NwRz+E974sAxTVcbUZYg95IhK2BZz9g=
|
||||
github.com/libp2p/go-libp2p-pubsub v0.9.1/go.mod h1:RYA7aM9jIic5VV47WXu4GkcRxRhrdElWf8xtyli+Dzc=
|
||||
github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA=
|
||||
github.com/libp2p/go-maddr-filter v0.1.0/go.mod h1:VzZhTXkMucEGGEOSKddrwGiOv0tUhgnKqNEmIAz/bPU=
|
||||
github.com/libp2p/go-mplex v0.7.0 h1:BDhFZdlk5tbr0oyFq/xv/NPGfjbnrsDam1EvutpBDbY=
|
||||
|
@ -1657,8 +1652,6 @@ github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKw
|
|||
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/opentracing/opentracing-go v1.0.3-0.20180606204148-bd9c31933947/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
|
||||
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
|
||||
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=
|
||||
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
|
||||
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
||||
|
@ -2105,8 +2098,8 @@ github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1
|
|||
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||
github.com/waku-org/go-discover v0.0.0-20221209174356-61c833f34d98 h1:xwY0kW5XZFimdqfZb9cZwT1S3VJP9j3AE6bdNd9boXM=
|
||||
github.com/waku-org/go-discover v0.0.0-20221209174356-61c833f34d98/go.mod h1:eBHgM6T4EG0RZzxpxKy+rGz/6Dw2Nd8DWxS0lm9ESDw=
|
||||
github.com/waku-org/go-waku v0.5.2-0.20230223234511-57f6110074d0 h1:WfPpenX+U8pzYrT14Ei1thbU6aGSbf4AM048EF75nX8=
|
||||
github.com/waku-org/go-waku v0.5.2-0.20230223234511-57f6110074d0/go.mod h1:hupqK7cKFAmY+r/EW7LLaPnMLsPrm0fW1OuR6KrO8a8=
|
||||
github.com/waku-org/go-waku v0.5.2-0.20230224151428-d6c87f346b72 h1:0W0A9n895veIh0tFw2TmgW4PmI0d84WM6eriP6K80Dg=
|
||||
github.com/waku-org/go-waku v0.5.2-0.20230224151428-d6c87f346b72/go.mod h1:0e0itkseairW1Uz9thxhb0OPRuPfC6qagJrOgZAV9PA=
|
||||
github.com/waku-org/go-zerokit-rln v0.1.7-wakuorg h1:2vVIBCtBih2w1K9ll8YnToTDZvbxcgbsClsPlJS/kkg=
|
||||
github.com/waku-org/go-zerokit-rln v0.1.7-wakuorg/go.mod h1:GlyaVeEWNEBxVJrWC6jFTvb4LNb9d9qnjdS6EiWVUvk=
|
||||
github.com/wealdtech/go-ens/v3 v3.5.0 h1:Huc9GxBgiGweCOGTYomvsg07K2QggAqZpZ5SuiZdC8o=
|
||||
|
@ -2235,7 +2228,6 @@ go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9E
|
|||
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||
go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
|
||||
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
|
||||
go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=
|
||||
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
Copyright (c) 2015, Emir Pasic
|
||||
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.
|
||||
|
||||
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 HOLDER 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.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
AVL Tree:
|
||||
|
||||
Copyright (c) 2017 Benjamin Scher Purcell <benjapurcell@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
@ -1,36 +0,0 @@
|
|||
// Copyright (c) 2015, Emir Pasic. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package containers provides core interfaces and functions for data structures.
|
||||
//
|
||||
// Container is the base interface for all data structures to implement.
|
||||
//
|
||||
// Iterators provide stateful iterators.
|
||||
//
|
||||
// Enumerable provides Ruby inspired (each, select, map, find, any?, etc.) container functions.
|
||||
//
|
||||
// Serialization provides serializers (marshalers) and deserializers (unmarshalers).
|
||||
package containers
|
||||
|
||||
import "github.com/emirpasic/gods/utils"
|
||||
|
||||
// Container is base interface that all data structures implement.
|
||||
type Container interface {
|
||||
Empty() bool
|
||||
Size() int
|
||||
Clear()
|
||||
Values() []interface{}
|
||||
String() string
|
||||
}
|
||||
|
||||
// GetSortedValues returns sorted container's elements with respect to the passed comparator.
|
||||
// Does not affect the ordering of elements within the container.
|
||||
func GetSortedValues(container Container, comparator utils.Comparator) []interface{} {
|
||||
values := container.Values()
|
||||
if len(values) < 2 {
|
||||
return values
|
||||
}
|
||||
utils.Sort(values, comparator)
|
||||
return values
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
// Copyright (c) 2015, Emir Pasic. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package containers
|
||||
|
||||
// EnumerableWithIndex provides functions for ordered containers whose values can be fetched by an index.
|
||||
type EnumerableWithIndex interface {
|
||||
// Each calls the given function once for each element, passing that element's index and value.
|
||||
Each(func(index int, value interface{}))
|
||||
|
||||
// Map invokes the given function once for each element and returns a
|
||||
// container containing the values returned by the given function.
|
||||
// Map(func(index int, value interface{}) interface{}) Container
|
||||
|
||||
// Select returns a new container containing all elements for which the given function returns a true value.
|
||||
// Select(func(index int, value interface{}) bool) Container
|
||||
|
||||
// Any passes each element of the container to the given function and
|
||||
// returns true if the function ever returns true for any element.
|
||||
Any(func(index int, value interface{}) bool) bool
|
||||
|
||||
// All passes each element of the container to the given function and
|
||||
// returns true if the function returns true for all elements.
|
||||
All(func(index int, value interface{}) bool) bool
|
||||
|
||||
// Find passes each element of the container to the given function and returns
|
||||
// the first (index,value) for which the function is true or -1,nil otherwise
|
||||
// if no element matches the criteria.
|
||||
Find(func(index int, value interface{}) bool) (int, interface{})
|
||||
}
|
||||
|
||||
// EnumerableWithKey provides functions for ordered containers whose values whose elements are key/value pairs.
|
||||
type EnumerableWithKey interface {
|
||||
// Each calls the given function once for each element, passing that element's key and value.
|
||||
Each(func(key interface{}, value interface{}))
|
||||
|
||||
// Map invokes the given function once for each element and returns a container
|
||||
// containing the values returned by the given function as key/value pairs.
|
||||
// Map(func(key interface{}, value interface{}) (interface{}, interface{})) Container
|
||||
|
||||
// Select returns a new container containing all elements for which the given function returns a true value.
|
||||
// Select(func(key interface{}, value interface{}) bool) Container
|
||||
|
||||
// Any passes each element of the container to the given function and
|
||||
// returns true if the function ever returns true for any element.
|
||||
Any(func(key interface{}, value interface{}) bool) bool
|
||||
|
||||
// All passes each element of the container to the given function and
|
||||
// returns true if the function returns true for all elements.
|
||||
All(func(key interface{}, value interface{}) bool) bool
|
||||
|
||||
// Find passes each element of the container to the given function and returns
|
||||
// the first (key,value) for which the function is true or nil,nil otherwise if no element
|
||||
// matches the criteria.
|
||||
Find(func(key interface{}, value interface{}) bool) (interface{}, interface{})
|
||||
}
|
|
@ -1,133 +0,0 @@
|
|||
// Copyright (c) 2015, Emir Pasic. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package containers
|
||||
|
||||
// IteratorWithIndex is stateful iterator for ordered containers whose values can be fetched by an index.
|
||||
type IteratorWithIndex interface {
|
||||
// Next moves the iterator to the next element and returns true if there was a next element in the container.
|
||||
// If Next() returns true, then next element's index and value can be retrieved by Index() and Value().
|
||||
// If Next() was called for the first time, then it will point the iterator to the first element if it exists.
|
||||
// Modifies the state of the iterator.
|
||||
Next() bool
|
||||
|
||||
// Value returns the current element's value.
|
||||
// Does not modify the state of the iterator.
|
||||
Value() interface{}
|
||||
|
||||
// Index returns the current element's index.
|
||||
// Does not modify the state of the iterator.
|
||||
Index() int
|
||||
|
||||
// Begin resets the iterator to its initial state (one-before-first)
|
||||
// Call Next() to fetch the first element if any.
|
||||
Begin()
|
||||
|
||||
// First moves the iterator to the first element and returns true if there was a first element in the container.
|
||||
// If First() returns true, then first element's index and value can be retrieved by Index() and Value().
|
||||
// Modifies the state of the iterator.
|
||||
First() bool
|
||||
|
||||
// NextTo moves the iterator to the next element from current position that satisfies the condition given by the
|
||||
// passed function, and returns true if there was a next element in the container.
|
||||
// If NextTo() returns true, then next element's index and value can be retrieved by Index() and Value().
|
||||
// Modifies the state of the iterator.
|
||||
NextTo(func(index int, value interface{}) bool) bool
|
||||
}
|
||||
|
||||
// IteratorWithKey is a stateful iterator for ordered containers whose elements are key value pairs.
|
||||
type IteratorWithKey interface {
|
||||
// Next moves the iterator to the next element and returns true if there was a next element in the container.
|
||||
// If Next() returns true, then next element's key and value can be retrieved by Key() and Value().
|
||||
// If Next() was called for the first time, then it will point the iterator to the first element if it exists.
|
||||
// Modifies the state of the iterator.
|
||||
Next() bool
|
||||
|
||||
// Value returns the current element's value.
|
||||
// Does not modify the state of the iterator.
|
||||
Value() interface{}
|
||||
|
||||
// Key returns the current element's key.
|
||||
// Does not modify the state of the iterator.
|
||||
Key() interface{}
|
||||
|
||||
// Begin resets the iterator to its initial state (one-before-first)
|
||||
// Call Next() to fetch the first element if any.
|
||||
Begin()
|
||||
|
||||
// First moves the iterator to the first element and returns true if there was a first element in the container.
|
||||
// If First() returns true, then first element's key and value can be retrieved by Key() and Value().
|
||||
// Modifies the state of the iterator.
|
||||
First() bool
|
||||
|
||||
// NextTo moves the iterator to the next element from current position that satisfies the condition given by the
|
||||
// passed function, and returns true if there was a next element in the container.
|
||||
// If NextTo() returns true, then next element's key and value can be retrieved by Key() and Value().
|
||||
// Modifies the state of the iterator.
|
||||
NextTo(func(key interface{}, value interface{}) bool) bool
|
||||
}
|
||||
|
||||
// ReverseIteratorWithIndex is stateful iterator for ordered containers whose values can be fetched by an index.
|
||||
//
|
||||
// Essentially it is the same as IteratorWithIndex, but provides additional:
|
||||
//
|
||||
// Prev() function to enable traversal in reverse
|
||||
//
|
||||
// Last() function to move the iterator to the last element.
|
||||
//
|
||||
// End() function to move the iterator past the last element (one-past-the-end).
|
||||
type ReverseIteratorWithIndex interface {
|
||||
// Prev moves the iterator to the previous element and returns true if there was a previous element in the container.
|
||||
// If Prev() returns true, then previous element's index and value can be retrieved by Index() and Value().
|
||||
// Modifies the state of the iterator.
|
||||
Prev() bool
|
||||
|
||||
// End moves the iterator past the last element (one-past-the-end).
|
||||
// Call Prev() to fetch the last element if any.
|
||||
End()
|
||||
|
||||
// Last moves the iterator to the last element and returns true if there was a last element in the container.
|
||||
// If Last() returns true, then last element's index and value can be retrieved by Index() and Value().
|
||||
// Modifies the state of the iterator.
|
||||
Last() bool
|
||||
|
||||
// PrevTo moves the iterator to the previous element from current position that satisfies the condition given by the
|
||||
// passed function, and returns true if there was a next element in the container.
|
||||
// If PrevTo() returns true, then next element's index and value can be retrieved by Index() and Value().
|
||||
// Modifies the state of the iterator.
|
||||
PrevTo(func(index int, value interface{}) bool) bool
|
||||
|
||||
IteratorWithIndex
|
||||
}
|
||||
|
||||
// ReverseIteratorWithKey is a stateful iterator for ordered containers whose elements are key value pairs.
|
||||
//
|
||||
// Essentially it is the same as IteratorWithKey, but provides additional:
|
||||
//
|
||||
// Prev() function to enable traversal in reverse
|
||||
//
|
||||
// Last() function to move the iterator to the last element.
|
||||
type ReverseIteratorWithKey interface {
|
||||
// Prev moves the iterator to the previous element and returns true if there was a previous element in the container.
|
||||
// If Prev() returns true, then previous element's key and value can be retrieved by Key() and Value().
|
||||
// Modifies the state of the iterator.
|
||||
Prev() bool
|
||||
|
||||
// End moves the iterator past the last element (one-past-the-end).
|
||||
// Call Prev() to fetch the last element if any.
|
||||
End()
|
||||
|
||||
// Last moves the iterator to the last element and returns true if there was a last element in the container.
|
||||
// If Last() returns true, then last element's key and value can be retrieved by Key() and Value().
|
||||
// Modifies the state of the iterator.
|
||||
Last() bool
|
||||
|
||||
// PrevTo moves the iterator to the previous element from current position that satisfies the condition given by the
|
||||
// passed function, and returns true if there was a next element in the container.
|
||||
// If PrevTo() returns true, then next element's key and value can be retrieved by Key() and Value().
|
||||
// Modifies the state of the iterator.
|
||||
PrevTo(func(key interface{}, value interface{}) bool) bool
|
||||
|
||||
IteratorWithKey
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
// Copyright (c) 2015, Emir Pasic. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package containers
|
||||
|
||||
// JSONSerializer provides JSON serialization
|
||||
type JSONSerializer interface {
|
||||
// ToJSON outputs the JSON representation of containers's elements.
|
||||
ToJSON() ([]byte, error)
|
||||
// MarshalJSON @implements json.Marshaler
|
||||
MarshalJSON() ([]byte, error)
|
||||
}
|
||||
|
||||
// JSONDeserializer provides JSON deserialization
|
||||
type JSONDeserializer interface {
|
||||
// FromJSON populates containers's elements from the input JSON representation.
|
||||
FromJSON([]byte) error
|
||||
// UnmarshalJSON @implements json.Unmarshaler
|
||||
UnmarshalJSON([]byte) error
|
||||
}
|
|
@ -1,344 +0,0 @@
|
|||
// Copyright (c) 2015, Emir Pasic. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package doublylinkedlist implements the doubly-linked list.
|
||||
//
|
||||
// Structure is not thread safe.
|
||||
//
|
||||
// Reference: https://en.wikipedia.org/wiki/List_%28abstract_data_type%29
|
||||
package doublylinkedlist
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/emirpasic/gods/lists"
|
||||
"github.com/emirpasic/gods/utils"
|
||||
)
|
||||
|
||||
// Assert List implementation
|
||||
var _ lists.List = (*List)(nil)
|
||||
|
||||
// List holds the elements, where each element points to the next and previous element
|
||||
type List struct {
|
||||
first *element
|
||||
last *element
|
||||
size int
|
||||
}
|
||||
|
||||
type element struct {
|
||||
value interface{}
|
||||
prev *element
|
||||
next *element
|
||||
}
|
||||
|
||||
// New instantiates a new list and adds the passed values, if any, to the list
|
||||
func New(values ...interface{}) *List {
|
||||
list := &List{}
|
||||
if len(values) > 0 {
|
||||
list.Add(values...)
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
// Add appends a value (one or more) at the end of the list (same as Append())
|
||||
func (list *List) Add(values ...interface{}) {
|
||||
for _, value := range values {
|
||||
newElement := &element{value: value, prev: list.last}
|
||||
if list.size == 0 {
|
||||
list.first = newElement
|
||||
list.last = newElement
|
||||
} else {
|
||||
list.last.next = newElement
|
||||
list.last = newElement
|
||||
}
|
||||
list.size++
|
||||
}
|
||||
}
|
||||
|
||||
// Append appends a value (one or more) at the end of the list (same as Add())
|
||||
func (list *List) Append(values ...interface{}) {
|
||||
list.Add(values...)
|
||||
}
|
||||
|
||||
// Prepend prepends a values (or more)
|
||||
func (list *List) Prepend(values ...interface{}) {
|
||||
// in reverse to keep passed order i.e. ["c","d"] -> Prepend(["a","b"]) -> ["a","b","c",d"]
|
||||
for v := len(values) - 1; v >= 0; v-- {
|
||||
newElement := &element{value: values[v], next: list.first}
|
||||
if list.size == 0 {
|
||||
list.first = newElement
|
||||
list.last = newElement
|
||||
} else {
|
||||
list.first.prev = newElement
|
||||
list.first = newElement
|
||||
}
|
||||
list.size++
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns the element at index.
|
||||
// Second return parameter is true if index is within bounds of the array and array is not empty, otherwise false.
|
||||
func (list *List) Get(index int) (interface{}, bool) {
|
||||
|
||||
if !list.withinRange(index) {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// determine traveral direction, last to first or first to last
|
||||
if list.size-index < index {
|
||||
element := list.last
|
||||
for e := list.size - 1; e != index; e, element = e-1, element.prev {
|
||||
}
|
||||
return element.value, true
|
||||
}
|
||||
element := list.first
|
||||
for e := 0; e != index; e, element = e+1, element.next {
|
||||
}
|
||||
return element.value, true
|
||||
}
|
||||
|
||||
// Remove removes the element at the given index from the list.
|
||||
func (list *List) Remove(index int) {
|
||||
|
||||
if !list.withinRange(index) {
|
||||
return
|
||||
}
|
||||
|
||||
if list.size == 1 {
|
||||
list.Clear()
|
||||
return
|
||||
}
|
||||
|
||||
var element *element
|
||||
// determine traversal direction, last to first or first to last
|
||||
if list.size-index < index {
|
||||
element = list.last
|
||||
for e := list.size - 1; e != index; e, element = e-1, element.prev {
|
||||
}
|
||||
} else {
|
||||
element = list.first
|
||||
for e := 0; e != index; e, element = e+1, element.next {
|
||||
}
|
||||
}
|
||||
|
||||
if element == list.first {
|
||||
list.first = element.next
|
||||
}
|
||||
if element == list.last {
|
||||
list.last = element.prev
|
||||
}
|
||||
if element.prev != nil {
|
||||
element.prev.next = element.next
|
||||
}
|
||||
if element.next != nil {
|
||||
element.next.prev = element.prev
|
||||
}
|
||||
|
||||
element = nil
|
||||
|
||||
list.size--
|
||||
}
|
||||
|
||||
// Contains check if values (one or more) are present in the set.
|
||||
// All values have to be present in the set for the method to return true.
|
||||
// Performance time complexity of n^2.
|
||||
// Returns true if no arguments are passed at all, i.e. set is always super-set of empty set.
|
||||
func (list *List) Contains(values ...interface{}) bool {
|
||||
|
||||
if len(values) == 0 {
|
||||
return true
|
||||
}
|
||||
if list.size == 0 {
|
||||
return false
|
||||
}
|
||||
for _, value := range values {
|
||||
found := false
|
||||
for element := list.first; element != nil; element = element.next {
|
||||
if element.value == value {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Values returns all elements in the list.
|
||||
func (list *List) Values() []interface{} {
|
||||
values := make([]interface{}, list.size, list.size)
|
||||
for e, element := 0, list.first; element != nil; e, element = e+1, element.next {
|
||||
values[e] = element.value
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
||||
//IndexOf returns index of provided element
|
||||
func (list *List) IndexOf(value interface{}) int {
|
||||
if list.size == 0 {
|
||||
return -1
|
||||
}
|
||||
for index, element := range list.Values() {
|
||||
if element == value {
|
||||
return index
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// Empty returns true if list does not contain any elements.
|
||||
func (list *List) Empty() bool {
|
||||
return list.size == 0
|
||||
}
|
||||
|
||||
// Size returns number of elements within the list.
|
||||
func (list *List) Size() int {
|
||||
return list.size
|
||||
}
|
||||
|
||||
// Clear removes all elements from the list.
|
||||
func (list *List) Clear() {
|
||||
list.size = 0
|
||||
list.first = nil
|
||||
list.last = nil
|
||||
}
|
||||
|
||||
// Sort sorts values (in-place) using.
|
||||
func (list *List) Sort(comparator utils.Comparator) {
|
||||
|
||||
if list.size < 2 {
|
||||
return
|
||||
}
|
||||
|
||||
values := list.Values()
|
||||
utils.Sort(values, comparator)
|
||||
|
||||
list.Clear()
|
||||
|
||||
list.Add(values...)
|
||||
|
||||
}
|
||||
|
||||
// Swap swaps values of two elements at the given indices.
|
||||
func (list *List) Swap(i, j int) {
|
||||
if list.withinRange(i) && list.withinRange(j) && i != j {
|
||||
var element1, element2 *element
|
||||
for e, currentElement := 0, list.first; element1 == nil || element2 == nil; e, currentElement = e+1, currentElement.next {
|
||||
switch e {
|
||||
case i:
|
||||
element1 = currentElement
|
||||
case j:
|
||||
element2 = currentElement
|
||||
}
|
||||
}
|
||||
element1.value, element2.value = element2.value, element1.value
|
||||
}
|
||||
}
|
||||
|
||||
// Insert inserts values at specified index position shifting the value at that position (if any) and any subsequent elements to the right.
|
||||
// Does not do anything if position is negative or bigger than list's size
|
||||
// Note: position equal to list's size is valid, i.e. append.
|
||||
func (list *List) Insert(index int, values ...interface{}) {
|
||||
|
||||
if !list.withinRange(index) {
|
||||
// Append
|
||||
if index == list.size {
|
||||
list.Add(values...)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
list.size += len(values)
|
||||
|
||||
var beforeElement *element
|
||||
var foundElement *element
|
||||
// determine traversal direction, last to first or first to last
|
||||
if list.size-index < index {
|
||||
foundElement = list.last
|
||||
for e := list.size - 1; e != index; e, foundElement = e-1, foundElement.prev {
|
||||
beforeElement = foundElement.prev
|
||||
}
|
||||
} else {
|
||||
foundElement = list.first
|
||||
for e := 0; e != index; e, foundElement = e+1, foundElement.next {
|
||||
beforeElement = foundElement
|
||||
}
|
||||
}
|
||||
|
||||
if foundElement == list.first {
|
||||
oldNextElement := list.first
|
||||
for i, value := range values {
|
||||
newElement := &element{value: value}
|
||||
if i == 0 {
|
||||
list.first = newElement
|
||||
} else {
|
||||
newElement.prev = beforeElement
|
||||
beforeElement.next = newElement
|
||||
}
|
||||
beforeElement = newElement
|
||||
}
|
||||
oldNextElement.prev = beforeElement
|
||||
beforeElement.next = oldNextElement
|
||||
} else {
|
||||
oldNextElement := beforeElement.next
|
||||
for _, value := range values {
|
||||
newElement := &element{value: value}
|
||||
newElement.prev = beforeElement
|
||||
beforeElement.next = newElement
|
||||
beforeElement = newElement
|
||||
}
|
||||
oldNextElement.prev = beforeElement
|
||||
beforeElement.next = oldNextElement
|
||||
}
|
||||
}
|
||||
|
||||
// Set value at specified index position
|
||||
// Does not do anything if position is negative or bigger than list's size
|
||||
// Note: position equal to list's size is valid, i.e. append.
|
||||
func (list *List) Set(index int, value interface{}) {
|
||||
|
||||
if !list.withinRange(index) {
|
||||
// Append
|
||||
if index == list.size {
|
||||
list.Add(value)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var foundElement *element
|
||||
// determine traversal direction, last to first or first to last
|
||||
if list.size-index < index {
|
||||
foundElement = list.last
|
||||
for e := list.size - 1; e != index; {
|
||||
fmt.Println("Set last", index, value, foundElement, foundElement.prev)
|
||||
e, foundElement = e-1, foundElement.prev
|
||||
}
|
||||
} else {
|
||||
foundElement = list.first
|
||||
for e := 0; e != index; {
|
||||
e, foundElement = e+1, foundElement.next
|
||||
}
|
||||
}
|
||||
|
||||
foundElement.value = value
|
||||
}
|
||||
|
||||
// String returns a string representation of container
|
||||
func (list *List) String() string {
|
||||
str := "DoublyLinkedList\n"
|
||||
values := []string{}
|
||||
for element := list.first; element != nil; element = element.next {
|
||||
values = append(values, fmt.Sprintf("%v", element.value))
|
||||
}
|
||||
str += strings.Join(values, ", ")
|
||||
return str
|
||||
}
|
||||
|
||||
// Check that the index is within bounds of the list
|
||||
func (list *List) withinRange(index int) bool {
|
||||
return index >= 0 && index < list.size
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
// Copyright (c) 2015, Emir Pasic. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package doublylinkedlist
|
||||
|
||||
import "github.com/emirpasic/gods/containers"
|
||||
|
||||
// Assert Enumerable implementation
|
||||
var _ containers.EnumerableWithIndex = (*List)(nil)
|
||||
|
||||
// Each calls the given function once for each element, passing that element's index and value.
|
||||
func (list *List) Each(f func(index int, value interface{})) {
|
||||
iterator := list.Iterator()
|
||||
for iterator.Next() {
|
||||
f(iterator.Index(), iterator.Value())
|
||||
}
|
||||
}
|
||||
|
||||
// Map invokes the given function once for each element and returns a
|
||||
// container containing the values returned by the given function.
|
||||
func (list *List) Map(f func(index int, value interface{}) interface{}) *List {
|
||||
newList := &List{}
|
||||
iterator := list.Iterator()
|
||||
for iterator.Next() {
|
||||
newList.Add(f(iterator.Index(), iterator.Value()))
|
||||
}
|
||||
return newList
|
||||
}
|
||||
|
||||
// Select returns a new container containing all elements for which the given function returns a true value.
|
||||
func (list *List) Select(f func(index int, value interface{}) bool) *List {
|
||||
newList := &List{}
|
||||
iterator := list.Iterator()
|
||||
for iterator.Next() {
|
||||
if f(iterator.Index(), iterator.Value()) {
|
||||
newList.Add(iterator.Value())
|
||||
}
|
||||
}
|
||||
return newList
|
||||
}
|
||||
|
||||
// Any passes each element of the container to the given function and
|
||||
// returns true if the function ever returns true for any element.
|
||||
func (list *List) Any(f func(index int, value interface{}) bool) bool {
|
||||
iterator := list.Iterator()
|
||||
for iterator.Next() {
|
||||
if f(iterator.Index(), iterator.Value()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// All passes each element of the container to the given function and
|
||||
// returns true if the function returns true for all elements.
|
||||
func (list *List) All(f func(index int, value interface{}) bool) bool {
|
||||
iterator := list.Iterator()
|
||||
for iterator.Next() {
|
||||
if !f(iterator.Index(), iterator.Value()) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Find passes each element of the container to the given function and returns
|
||||
// the first (index,value) for which the function is true or -1,nil otherwise
|
||||
// if no element matches the criteria.
|
||||
func (list *List) Find(f func(index int, value interface{}) bool) (index int, value interface{}) {
|
||||
iterator := list.Iterator()
|
||||
for iterator.Next() {
|
||||
if f(iterator.Index(), iterator.Value()) {
|
||||
return iterator.Index(), iterator.Value()
|
||||
}
|
||||
}
|
||||
return -1, nil
|
||||
}
|
|
@ -1,131 +0,0 @@
|
|||
// Copyright (c) 2015, Emir Pasic. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package doublylinkedlist
|
||||
|
||||
import "github.com/emirpasic/gods/containers"
|
||||
|
||||
// Assert Iterator implementation
|
||||
var _ containers.ReverseIteratorWithIndex = (*Iterator)(nil)
|
||||
|
||||
// Iterator holding the iterator's state
|
||||
type Iterator struct {
|
||||
list *List
|
||||
index int
|
||||
element *element
|
||||
}
|
||||
|
||||
// Iterator returns a stateful iterator whose values can be fetched by an index.
|
||||
func (list *List) Iterator() Iterator {
|
||||
return Iterator{list: list, index: -1, element: nil}
|
||||
}
|
||||
|
||||
// Next moves the iterator to the next element and returns true if there was a next element in the container.
|
||||
// If Next() returns true, then next element's index and value can be retrieved by Index() and Value().
|
||||
// If Next() was called for the first time, then it will point the iterator to the first element if it exists.
|
||||
// Modifies the state of the iterator.
|
||||
func (iterator *Iterator) Next() bool {
|
||||
if iterator.index < iterator.list.size {
|
||||
iterator.index++
|
||||
}
|
||||
if !iterator.list.withinRange(iterator.index) {
|
||||
iterator.element = nil
|
||||
return false
|
||||
}
|
||||
if iterator.index != 0 {
|
||||
iterator.element = iterator.element.next
|
||||
} else {
|
||||
iterator.element = iterator.list.first
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Prev moves the iterator to the previous element and returns true if there was a previous element in the container.
|
||||
// If Prev() returns true, then previous element's index and value can be retrieved by Index() and Value().
|
||||
// Modifies the state of the iterator.
|
||||
func (iterator *Iterator) Prev() bool {
|
||||
if iterator.index >= 0 {
|
||||
iterator.index--
|
||||
}
|
||||
if !iterator.list.withinRange(iterator.index) {
|
||||
iterator.element = nil
|
||||
return false
|
||||
}
|
||||
if iterator.index == iterator.list.size-1 {
|
||||
iterator.element = iterator.list.last
|
||||
} else {
|
||||
iterator.element = iterator.element.prev
|
||||
}
|
||||
return iterator.list.withinRange(iterator.index)
|
||||
}
|
||||
|
||||
// Value returns the current element's value.
|
||||
// Does not modify the state of the iterator.
|
||||
func (iterator *Iterator) Value() interface{} {
|
||||
return iterator.element.value
|
||||
}
|
||||
|
||||
// Index returns the current element's index.
|
||||
// Does not modify the state of the iterator.
|
||||
func (iterator *Iterator) Index() int {
|
||||
return iterator.index
|
||||
}
|
||||
|
||||
// Begin resets the iterator to its initial state (one-before-first)
|
||||
// Call Next() to fetch the first element if any.
|
||||
func (iterator *Iterator) Begin() {
|
||||
iterator.index = -1
|
||||
iterator.element = nil
|
||||
}
|
||||
|
||||
// End moves the iterator past the last element (one-past-the-end).
|
||||
// Call Prev() to fetch the last element if any.
|
||||
func (iterator *Iterator) End() {
|
||||
iterator.index = iterator.list.size
|
||||
iterator.element = iterator.list.last
|
||||
}
|
||||
|
||||
// First moves the iterator to the first element and returns true if there was a first element in the container.
|
||||
// If First() returns true, then first element's index and value can be retrieved by Index() and Value().
|
||||
// Modifies the state of the iterator.
|
||||
func (iterator *Iterator) First() bool {
|
||||
iterator.Begin()
|
||||
return iterator.Next()
|
||||
}
|
||||
|
||||
// Last moves the iterator to the last element and returns true if there was a last element in the container.
|
||||
// If Last() returns true, then last element's index and value can be retrieved by Index() and Value().
|
||||
// Modifies the state of the iterator.
|
||||
func (iterator *Iterator) Last() bool {
|
||||
iterator.End()
|
||||
return iterator.Prev()
|
||||
}
|
||||
|
||||
// NextTo moves the iterator to the next element from current position that satisfies the condition given by the
|
||||
// passed function, and returns true if there was a next element in the container.
|
||||
// If NextTo() returns true, then next element's index and value can be retrieved by Index() and Value().
|
||||
// Modifies the state of the iterator.
|
||||
func (iterator *Iterator) NextTo(f func(index int, value interface{}) bool) bool {
|
||||
for iterator.Next() {
|
||||
index, value := iterator.Index(), iterator.Value()
|
||||
if f(index, value) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// PrevTo moves the iterator to the previous element from current position that satisfies the condition given by the
|
||||
// passed function, and returns true if there was a next element in the container.
|
||||
// If PrevTo() returns true, then next element's index and value can be retrieved by Index() and Value().
|
||||
// Modifies the state of the iterator.
|
||||
func (iterator *Iterator) PrevTo(f func(index int, value interface{}) bool) bool {
|
||||
for iterator.Prev() {
|
||||
index, value := iterator.Index(), iterator.Value()
|
||||
if f(index, value) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
// Copyright (c) 2015, Emir Pasic. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package doublylinkedlist
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/emirpasic/gods/containers"
|
||||
)
|
||||
|
||||
// Assert Serialization implementation
|
||||
var _ containers.JSONSerializer = (*List)(nil)
|
||||
var _ containers.JSONDeserializer = (*List)(nil)
|
||||
|
||||
// ToJSON outputs the JSON representation of list's elements.
|
||||
func (list *List) ToJSON() ([]byte, error) {
|
||||
return json.Marshal(list.Values())
|
||||
}
|
||||
|
||||
// FromJSON populates list's elements from the input JSON representation.
|
||||
func (list *List) FromJSON(data []byte) error {
|
||||
elements := []interface{}{}
|
||||
err := json.Unmarshal(data, &elements)
|
||||
if err == nil {
|
||||
list.Clear()
|
||||
list.Add(elements...)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// UnmarshalJSON @implements json.Unmarshaler
|
||||
func (list *List) UnmarshalJSON(bytes []byte) error {
|
||||
return list.FromJSON(bytes)
|
||||
}
|
||||
|
||||
// MarshalJSON @implements json.Marshaler
|
||||
func (list *List) MarshalJSON() ([]byte, error) {
|
||||
return list.ToJSON()
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
// Copyright (c) 2015, Emir Pasic. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package lists provides an abstract List interface.
|
||||
//
|
||||
// In computer science, a list or sequence is an abstract data type that represents an ordered sequence of values, where the same value may occur more than once. An instance of a list is a computer representation of the mathematical concept of a finite sequence; the (potentially) infinite analog of a list is a stream. Lists are a basic example of containers, as they contain other values. If the same value occurs multiple times, each occurrence is considered a distinct item.
|
||||
//
|
||||
// Reference: https://en.wikipedia.org/wiki/List_%28abstract_data_type%29
|
||||
package lists
|
||||
|
||||
import (
|
||||
"github.com/emirpasic/gods/containers"
|
||||
"github.com/emirpasic/gods/utils"
|
||||
)
|
||||
|
||||
// List interface that all lists implement
|
||||
type List interface {
|
||||
Get(index int) (interface{}, bool)
|
||||
Remove(index int)
|
||||
Add(values ...interface{})
|
||||
Contains(values ...interface{}) bool
|
||||
Sort(comparator utils.Comparator)
|
||||
Swap(index1, index2 int)
|
||||
Insert(index int, values ...interface{})
|
||||
Set(index int, value interface{})
|
||||
|
||||
containers.Container
|
||||
// Empty() bool
|
||||
// Size() int
|
||||
// Clear()
|
||||
// Values() []interface{}
|
||||
// String() string
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
// Copyright (c) 2015, Emir Pasic. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package linkedhashmap
|
||||
|
||||
import "github.com/emirpasic/gods/containers"
|
||||
|
||||
// Assert Enumerable implementation
|
||||
var _ containers.EnumerableWithKey = (*Map)(nil)
|
||||
|
||||
// Each calls the given function once for each element, passing that element's key and value.
|
||||
func (m *Map) Each(f func(key interface{}, value interface{})) {
|
||||
iterator := m.Iterator()
|
||||
for iterator.Next() {
|
||||
f(iterator.Key(), iterator.Value())
|
||||
}
|
||||
}
|
||||
|
||||
// Map invokes the given function once for each element and returns a container
|
||||
// containing the values returned by the given function as key/value pairs.
|
||||
func (m *Map) Map(f func(key1 interface{}, value1 interface{}) (interface{}, interface{})) *Map {
|
||||
newMap := New()
|
||||
iterator := m.Iterator()
|
||||
for iterator.Next() {
|
||||
key2, value2 := f(iterator.Key(), iterator.Value())
|
||||
newMap.Put(key2, value2)
|
||||
}
|
||||
return newMap
|
||||
}
|
||||
|
||||
// Select returns a new container containing all elements for which the given function returns a true value.
|
||||
func (m *Map) Select(f func(key interface{}, value interface{}) bool) *Map {
|
||||
newMap := New()
|
||||
iterator := m.Iterator()
|
||||
for iterator.Next() {
|
||||
if f(iterator.Key(), iterator.Value()) {
|
||||
newMap.Put(iterator.Key(), iterator.Value())
|
||||
}
|
||||
}
|
||||
return newMap
|
||||
}
|
||||
|
||||
// Any passes each element of the container to the given function and
|
||||
// returns true if the function ever returns true for any element.
|
||||
func (m *Map) Any(f func(key interface{}, value interface{}) bool) bool {
|
||||
iterator := m.Iterator()
|
||||
for iterator.Next() {
|
||||
if f(iterator.Key(), iterator.Value()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// All passes each element of the container to the given function and
|
||||
// returns true if the function returns true for all elements.
|
||||
func (m *Map) All(f func(key interface{}, value interface{}) bool) bool {
|
||||
iterator := m.Iterator()
|
||||
for iterator.Next() {
|
||||
if !f(iterator.Key(), iterator.Value()) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Find passes each element of the container to the given function and returns
|
||||
// the first (key,value) for which the function is true or nil,nil otherwise if no element
|
||||
// matches the criteria.
|
||||
func (m *Map) Find(f func(key interface{}, value interface{}) bool) (interface{}, interface{}) {
|
||||
iterator := m.Iterator()
|
||||
for iterator.Next() {
|
||||
if f(iterator.Key(), iterator.Value()) {
|
||||
return iterator.Key(), iterator.Value()
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
|
@ -1,108 +0,0 @@
|
|||
// Copyright (c) 2015, Emir Pasic. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package linkedhashmap
|
||||
|
||||
import (
|
||||
"github.com/emirpasic/gods/containers"
|
||||
"github.com/emirpasic/gods/lists/doublylinkedlist"
|
||||
)
|
||||
|
||||
// Assert Iterator implementation
|
||||
var _ containers.ReverseIteratorWithKey = (*Iterator)(nil)
|
||||
|
||||
// Iterator holding the iterator's state
|
||||
type Iterator struct {
|
||||
iterator doublylinkedlist.Iterator
|
||||
table map[interface{}]interface{}
|
||||
}
|
||||
|
||||
// Iterator returns a stateful iterator whose elements are key/value pairs.
|
||||
func (m *Map) Iterator() Iterator {
|
||||
return Iterator{
|
||||
iterator: m.ordering.Iterator(),
|
||||
table: m.table}
|
||||
}
|
||||
|
||||
// Next moves the iterator to the next element and returns true if there was a next element in the container.
|
||||
// If Next() returns true, then next element's key and value can be retrieved by Key() and Value().
|
||||
// If Next() was called for the first time, then it will point the iterator to the first element if it exists.
|
||||
// Modifies the state of the iterator.
|
||||
func (iterator *Iterator) Next() bool {
|
||||
return iterator.iterator.Next()
|
||||
}
|
||||
|
||||
// Prev moves the iterator to the previous element and returns true if there was a previous element in the container.
|
||||
// If Prev() returns true, then previous element's key and value can be retrieved by Key() and Value().
|
||||
// Modifies the state of the iterator.
|
||||
func (iterator *Iterator) Prev() bool {
|
||||
return iterator.iterator.Prev()
|
||||
}
|
||||
|
||||
// Value returns the current element's value.
|
||||
// Does not modify the state of the iterator.
|
||||
func (iterator *Iterator) Value() interface{} {
|
||||
key := iterator.iterator.Value()
|
||||
return iterator.table[key]
|
||||
}
|
||||
|
||||
// Key returns the current element's key.
|
||||
// Does not modify the state of the iterator.
|
||||
func (iterator *Iterator) Key() interface{} {
|
||||
return iterator.iterator.Value()
|
||||
}
|
||||
|
||||
// Begin resets the iterator to its initial state (one-before-first)
|
||||
// Call Next() to fetch the first element if any.
|
||||
func (iterator *Iterator) Begin() {
|
||||
iterator.iterator.Begin()
|
||||
}
|
||||
|
||||
// End moves the iterator past the last element (one-past-the-end).
|
||||
// Call Prev() to fetch the last element if any.
|
||||
func (iterator *Iterator) End() {
|
||||
iterator.iterator.End()
|
||||
}
|
||||
|
||||
// First moves the iterator to the first element and returns true if there was a first element in the container.
|
||||
// If First() returns true, then first element's key and value can be retrieved by Key() and Value().
|
||||
// Modifies the state of the iterator
|
||||
func (iterator *Iterator) First() bool {
|
||||
return iterator.iterator.First()
|
||||
}
|
||||
|
||||
// Last moves the iterator to the last element and returns true if there was a last element in the container.
|
||||
// If Last() returns true, then last element's key and value can be retrieved by Key() and Value().
|
||||
// Modifies the state of the iterator.
|
||||
func (iterator *Iterator) Last() bool {
|
||||
return iterator.iterator.Last()
|
||||
}
|
||||
|
||||
// NextTo moves the iterator to the next element from current position that satisfies the condition given by the
|
||||
// passed function, and returns true if there was a next element in the container.
|
||||
// If NextTo() returns true, then next element's key and value can be retrieved by Key() and Value().
|
||||
// Modifies the state of the iterator.
|
||||
func (iterator *Iterator) NextTo(f func(key interface{}, value interface{}) bool) bool {
|
||||
for iterator.Next() {
|
||||
key, value := iterator.Key(), iterator.Value()
|
||||
if f(key, value) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// PrevTo moves the iterator to the previous element from current position that satisfies the condition given by the
|
||||
// passed function, and returns true if there was a next element in the container.
|
||||
// If PrevTo() returns true, then next element's key and value can be retrieved by Key() and Value().
|
||||
// Modifies the state of the iterator.
|
||||
func (iterator *Iterator) PrevTo(f func(key interface{}, value interface{}) bool) bool {
|
||||
for iterator.Prev() {
|
||||
key, value := iterator.Key(), iterator.Value()
|
||||
if f(key, value) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
|
@ -1,108 +0,0 @@
|
|||
// Copyright (c) 2015, Emir Pasic. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package linkedhashmap is a map that preserves insertion-order.
|
||||
//
|
||||
// It is backed by a hash table to store values and doubly-linked list to store ordering.
|
||||
//
|
||||
// Structure is not thread safe.
|
||||
//
|
||||
// Reference: http://en.wikipedia.org/wiki/Associative_array
|
||||
package linkedhashmap
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/emirpasic/gods/lists/doublylinkedlist"
|
||||
"github.com/emirpasic/gods/maps"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Assert Map implementation
|
||||
var _ maps.Map = (*Map)(nil)
|
||||
|
||||
// Map holds the elements in a regular hash table, and uses doubly-linked list to store key ordering.
|
||||
type Map struct {
|
||||
table map[interface{}]interface{}
|
||||
ordering *doublylinkedlist.List
|
||||
}
|
||||
|
||||
// New instantiates a linked-hash-map.
|
||||
func New() *Map {
|
||||
return &Map{
|
||||
table: make(map[interface{}]interface{}),
|
||||
ordering: doublylinkedlist.New(),
|
||||
}
|
||||
}
|
||||
|
||||
// Put inserts key-value pair into the map.
|
||||
// Key should adhere to the comparator's type assertion, otherwise method panics.
|
||||
func (m *Map) Put(key interface{}, value interface{}) {
|
||||
if _, contains := m.table[key]; !contains {
|
||||
m.ordering.Append(key)
|
||||
}
|
||||
m.table[key] = value
|
||||
}
|
||||
|
||||
// Get searches the element in the map by key and returns its value or nil if key is not found in tree.
|
||||
// Second return parameter is true if key was found, otherwise false.
|
||||
// Key should adhere to the comparator's type assertion, otherwise method panics.
|
||||
func (m *Map) Get(key interface{}) (value interface{}, found bool) {
|
||||
value = m.table[key]
|
||||
found = value != nil
|
||||
return
|
||||
}
|
||||
|
||||
// Remove removes the element from the map by key.
|
||||
// Key should adhere to the comparator's type assertion, otherwise method panics.
|
||||
func (m *Map) Remove(key interface{}) {
|
||||
if _, contains := m.table[key]; contains {
|
||||
delete(m.table, key)
|
||||
index := m.ordering.IndexOf(key)
|
||||
m.ordering.Remove(index)
|
||||
}
|
||||
}
|
||||
|
||||
// Empty returns true if map does not contain any elements
|
||||
func (m *Map) Empty() bool {
|
||||
return m.Size() == 0
|
||||
}
|
||||
|
||||
// Size returns number of elements in the map.
|
||||
func (m *Map) Size() int {
|
||||
return m.ordering.Size()
|
||||
}
|
||||
|
||||
// Keys returns all keys in-order
|
||||
func (m *Map) Keys() []interface{} {
|
||||
return m.ordering.Values()
|
||||
}
|
||||
|
||||
// Values returns all values in-order based on the key.
|
||||
func (m *Map) Values() []interface{} {
|
||||
values := make([]interface{}, m.Size())
|
||||
count := 0
|
||||
it := m.Iterator()
|
||||
for it.Next() {
|
||||
values[count] = it.Value()
|
||||
count++
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
||||
// Clear removes all elements from the map.
|
||||
func (m *Map) Clear() {
|
||||
m.table = make(map[interface{}]interface{})
|
||||
m.ordering.Clear()
|
||||
}
|
||||
|
||||
// String returns a string representation of container
|
||||
func (m *Map) String() string {
|
||||
str := "LinkedHashMap\nmap["
|
||||
it := m.Iterator()
|
||||
for it.Next() {
|
||||
str += fmt.Sprintf("%v:%v ", it.Key(), it.Value())
|
||||
}
|
||||
return strings.TrimRight(str, " ") + "]"
|
||||
|
||||
}
|
|
@ -1,112 +0,0 @@
|
|||
// Copyright (c) 2015, Emir Pasic. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package linkedhashmap
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"github.com/emirpasic/gods/containers"
|
||||
"github.com/emirpasic/gods/utils"
|
||||
)
|
||||
|
||||
// Assert Serialization implementation
|
||||
var _ containers.JSONSerializer = (*Map)(nil)
|
||||
var _ containers.JSONDeserializer = (*Map)(nil)
|
||||
|
||||
// ToJSON outputs the JSON representation of map.
|
||||
func (m *Map) ToJSON() ([]byte, error) {
|
||||
var b []byte
|
||||
buf := bytes.NewBuffer(b)
|
||||
|
||||
buf.WriteRune('{')
|
||||
|
||||
it := m.Iterator()
|
||||
lastIndex := m.Size() - 1
|
||||
index := 0
|
||||
|
||||
for it.Next() {
|
||||
km, err := json.Marshal(it.Key())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buf.Write(km)
|
||||
|
||||
buf.WriteRune(':')
|
||||
|
||||
vm, err := json.Marshal(it.Value())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buf.Write(vm)
|
||||
|
||||
if index != lastIndex {
|
||||
buf.WriteRune(',')
|
||||
}
|
||||
|
||||
index++
|
||||
}
|
||||
|
||||
buf.WriteRune('}')
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// FromJSON populates map from the input JSON representation.
|
||||
//func (m *Map) FromJSON(data []byte) error {
|
||||
// elements := make(map[string]interface{})
|
||||
// err := json.Unmarshal(data, &elements)
|
||||
// if err == nil {
|
||||
// m.Clear()
|
||||
// for key, value := range elements {
|
||||
// m.Put(key, value)
|
||||
// }
|
||||
// }
|
||||
// return err
|
||||
//}
|
||||
|
||||
// FromJSON populates map from the input JSON representation.
|
||||
func (m *Map) FromJSON(data []byte) error {
|
||||
elements := make(map[string]interface{})
|
||||
err := json.Unmarshal(data, &elements)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
index := make(map[string]int)
|
||||
var keys []interface{}
|
||||
for key := range elements {
|
||||
keys = append(keys, key)
|
||||
esc, _ := json.Marshal(key)
|
||||
index[key] = bytes.Index(data, esc)
|
||||
}
|
||||
|
||||
byIndex := func(a, b interface{}) int {
|
||||
key1 := a.(string)
|
||||
key2 := b.(string)
|
||||
index1 := index[key1]
|
||||
index2 := index[key2]
|
||||
return index1 - index2
|
||||
}
|
||||
|
||||
utils.Sort(keys, byIndex)
|
||||
|
||||
m.Clear()
|
||||
|
||||
for _, key := range keys {
|
||||
m.Put(key, elements[key.(string)])
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON @implements json.Unmarshaler
|
||||
func (m *Map) UnmarshalJSON(bytes []byte) error {
|
||||
return m.FromJSON(bytes)
|
||||
}
|
||||
|
||||
// MarshalJSON @implements json.Marshaler
|
||||
func (m *Map) MarshalJSON() ([]byte, error) {
|
||||
return m.ToJSON()
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
// Copyright (c) 2015, Emir Pasic. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package maps provides an abstract Map interface.
|
||||
//
|
||||
// In computer science, an associative array, map, symbol table, or dictionary is an abstract data type composed of a collection of (key, value) pairs, such that each possible key appears just once in the collection.
|
||||
//
|
||||
// Operations associated with this data type allow:
|
||||
// - the addition of a pair to the collection
|
||||
// - the removal of a pair from the collection
|
||||
// - the modification of an existing pair
|
||||
// - the lookup of a value associated with a particular key
|
||||
//
|
||||
// Reference: https://en.wikipedia.org/wiki/Associative_array
|
||||
package maps
|
||||
|
||||
import "github.com/emirpasic/gods/containers"
|
||||
|
||||
// Map interface that all maps implement
|
||||
type Map interface {
|
||||
Put(key interface{}, value interface{})
|
||||
Get(key interface{}) (value interface{}, found bool)
|
||||
Remove(key interface{})
|
||||
Keys() []interface{}
|
||||
|
||||
containers.Container
|
||||
// Empty() bool
|
||||
// Size() int
|
||||
// Clear()
|
||||
// Values() []interface{}
|
||||
// String() string
|
||||
}
|
||||
|
||||
// BidiMap interface that all bidirectional maps implement (extends the Map interface)
|
||||
type BidiMap interface {
|
||||
GetKey(value interface{}) (key interface{}, found bool)
|
||||
|
||||
Map
|
||||
}
|
|
@ -1,251 +0,0 @@
|
|||
// Copyright (c) 2015, Emir Pasic. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package utils
|
||||
|
||||
import "time"
|
||||
|
||||
// Comparator will make type assertion (see IntComparator for example),
|
||||
// which will panic if a or b are not of the asserted type.
|
||||
//
|
||||
// Should return a number:
|
||||
// negative , if a < b
|
||||
// zero , if a == b
|
||||
// positive , if a > b
|
||||
type Comparator func(a, b interface{}) int
|
||||
|
||||
// StringComparator provides a fast comparison on strings
|
||||
func StringComparator(a, b interface{}) int {
|
||||
s1 := a.(string)
|
||||
s2 := b.(string)
|
||||
min := len(s2)
|
||||
if len(s1) < len(s2) {
|
||||
min = len(s1)
|
||||
}
|
||||
diff := 0
|
||||
for i := 0; i < min && diff == 0; i++ {
|
||||
diff = int(s1[i]) - int(s2[i])
|
||||
}
|
||||
if diff == 0 {
|
||||
diff = len(s1) - len(s2)
|
||||
}
|
||||
if diff < 0 {
|
||||
return -1
|
||||
}
|
||||
if diff > 0 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// IntComparator provides a basic comparison on int
|
||||
func IntComparator(a, b interface{}) int {
|
||||
aAsserted := a.(int)
|
||||
bAsserted := b.(int)
|
||||
switch {
|
||||
case aAsserted > bAsserted:
|
||||
return 1
|
||||
case aAsserted < bAsserted:
|
||||
return -1
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// Int8Comparator provides a basic comparison on int8
|
||||
func Int8Comparator(a, b interface{}) int {
|
||||
aAsserted := a.(int8)
|
||||
bAsserted := b.(int8)
|
||||
switch {
|
||||
case aAsserted > bAsserted:
|
||||
return 1
|
||||
case aAsserted < bAsserted:
|
||||
return -1
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// Int16Comparator provides a basic comparison on int16
|
||||
func Int16Comparator(a, b interface{}) int {
|
||||
aAsserted := a.(int16)
|
||||
bAsserted := b.(int16)
|
||||
switch {
|
||||
case aAsserted > bAsserted:
|
||||
return 1
|
||||
case aAsserted < bAsserted:
|
||||
return -1
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// Int32Comparator provides a basic comparison on int32
|
||||
func Int32Comparator(a, b interface{}) int {
|
||||
aAsserted := a.(int32)
|
||||
bAsserted := b.(int32)
|
||||
switch {
|
||||
case aAsserted > bAsserted:
|
||||
return 1
|
||||
case aAsserted < bAsserted:
|
||||
return -1
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// Int64Comparator provides a basic comparison on int64
|
||||
func Int64Comparator(a, b interface{}) int {
|
||||
aAsserted := a.(int64)
|
||||
bAsserted := b.(int64)
|
||||
switch {
|
||||
case aAsserted > bAsserted:
|
||||
return 1
|
||||
case aAsserted < bAsserted:
|
||||
return -1
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// UIntComparator provides a basic comparison on uint
|
||||
func UIntComparator(a, b interface{}) int {
|
||||
aAsserted := a.(uint)
|
||||
bAsserted := b.(uint)
|
||||
switch {
|
||||
case aAsserted > bAsserted:
|
||||
return 1
|
||||
case aAsserted < bAsserted:
|
||||
return -1
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// UInt8Comparator provides a basic comparison on uint8
|
||||
func UInt8Comparator(a, b interface{}) int {
|
||||
aAsserted := a.(uint8)
|
||||
bAsserted := b.(uint8)
|
||||
switch {
|
||||
case aAsserted > bAsserted:
|
||||
return 1
|
||||
case aAsserted < bAsserted:
|
||||
return -1
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// UInt16Comparator provides a basic comparison on uint16
|
||||
func UInt16Comparator(a, b interface{}) int {
|
||||
aAsserted := a.(uint16)
|
||||
bAsserted := b.(uint16)
|
||||
switch {
|
||||
case aAsserted > bAsserted:
|
||||
return 1
|
||||
case aAsserted < bAsserted:
|
||||
return -1
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// UInt32Comparator provides a basic comparison on uint32
|
||||
func UInt32Comparator(a, b interface{}) int {
|
||||
aAsserted := a.(uint32)
|
||||
bAsserted := b.(uint32)
|
||||
switch {
|
||||
case aAsserted > bAsserted:
|
||||
return 1
|
||||
case aAsserted < bAsserted:
|
||||
return -1
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// UInt64Comparator provides a basic comparison on uint64
|
||||
func UInt64Comparator(a, b interface{}) int {
|
||||
aAsserted := a.(uint64)
|
||||
bAsserted := b.(uint64)
|
||||
switch {
|
||||
case aAsserted > bAsserted:
|
||||
return 1
|
||||
case aAsserted < bAsserted:
|
||||
return -1
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// Float32Comparator provides a basic comparison on float32
|
||||
func Float32Comparator(a, b interface{}) int {
|
||||
aAsserted := a.(float32)
|
||||
bAsserted := b.(float32)
|
||||
switch {
|
||||
case aAsserted > bAsserted:
|
||||
return 1
|
||||
case aAsserted < bAsserted:
|
||||
return -1
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// Float64Comparator provides a basic comparison on float64
|
||||
func Float64Comparator(a, b interface{}) int {
|
||||
aAsserted := a.(float64)
|
||||
bAsserted := b.(float64)
|
||||
switch {
|
||||
case aAsserted > bAsserted:
|
||||
return 1
|
||||
case aAsserted < bAsserted:
|
||||
return -1
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// ByteComparator provides a basic comparison on byte
|
||||
func ByteComparator(a, b interface{}) int {
|
||||
aAsserted := a.(byte)
|
||||
bAsserted := b.(byte)
|
||||
switch {
|
||||
case aAsserted > bAsserted:
|
||||
return 1
|
||||
case aAsserted < bAsserted:
|
||||
return -1
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// RuneComparator provides a basic comparison on rune
|
||||
func RuneComparator(a, b interface{}) int {
|
||||
aAsserted := a.(rune)
|
||||
bAsserted := b.(rune)
|
||||
switch {
|
||||
case aAsserted > bAsserted:
|
||||
return 1
|
||||
case aAsserted < bAsserted:
|
||||
return -1
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// TimeComparator provides a basic comparison on time.Time
|
||||
func TimeComparator(a, b interface{}) int {
|
||||
aAsserted := a.(time.Time)
|
||||
bAsserted := b.(time.Time)
|
||||
|
||||
switch {
|
||||
case aAsserted.After(bAsserted):
|
||||
return 1
|
||||
case aAsserted.Before(bAsserted):
|
||||
return -1
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
// Copyright (c) 2015, Emir Pasic. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package utils
|
||||
|
||||
import "sort"
|
||||
|
||||
// Sort sorts values (in-place) with respect to the given comparator.
|
||||
//
|
||||
// Uses Go's sort (hybrid of quicksort for large and then insertion sort for smaller slices).
|
||||
func Sort(values []interface{}, comparator Comparator) {
|
||||
sort.Sort(sortable{values, comparator})
|
||||
}
|
||||
|
||||
type sortable struct {
|
||||
values []interface{}
|
||||
comparator Comparator
|
||||
}
|
||||
|
||||
func (s sortable) Len() int {
|
||||
return len(s.values)
|
||||
}
|
||||
func (s sortable) Swap(i, j int) {
|
||||
s.values[i], s.values[j] = s.values[j], s.values[i]
|
||||
}
|
||||
func (s sortable) Less(i, j int) bool {
|
||||
return s.comparator(s.values[i], s.values[j]) < 0
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
// Copyright (c) 2015, Emir Pasic. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package utils provides common utility functions.
|
||||
//
|
||||
// Provided functionalities:
|
||||
// - sorting
|
||||
// - comparators
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// ToString converts a value to string.
|
||||
func ToString(value interface{}) string {
|
||||
switch value := value.(type) {
|
||||
case string:
|
||||
return value
|
||||
case int8:
|
||||
return strconv.FormatInt(int64(value), 10)
|
||||
case int16:
|
||||
return strconv.FormatInt(int64(value), 10)
|
||||
case int32:
|
||||
return strconv.FormatInt(int64(value), 10)
|
||||
case int64:
|
||||
return strconv.FormatInt(value, 10)
|
||||
case uint8:
|
||||
return strconv.FormatUint(uint64(value), 10)
|
||||
case uint16:
|
||||
return strconv.FormatUint(uint64(value), 10)
|
||||
case uint32:
|
||||
return strconv.FormatUint(uint64(value), 10)
|
||||
case uint64:
|
||||
return strconv.FormatUint(value, 10)
|
||||
case float32:
|
||||
return strconv.FormatFloat(float64(value), 'g', -1, 64)
|
||||
case float64:
|
||||
return strconv.FormatFloat(value, 'g', -1, 64)
|
||||
case bool:
|
||||
return strconv.FormatBool(value)
|
||||
default:
|
||||
return fmt.Sprintf("%+v", value)
|
||||
}
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Juan Batiz-Benet
|
||||
|
||||
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.
|
|
@ -1,79 +0,0 @@
|
|||
# go-log
|
||||
|
||||
[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io)
|
||||
[![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/)
|
||||
[![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23ipfs)
|
||||
[![standard-readme compliant](https://img.shields.io/badge/standard--readme-OK-green.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme)
|
||||
[![GoDoc](https://godoc.org/github.com/ipfs/go-log?status.svg)](https://godoc.org/github.com/ipfs/go-log)
|
||||
[![CircleCI](https://img.shields.io/circleci/build/github/ipfs/go-log?style=flat-square)](https://circleci.com/gh/ipfs/go-log)
|
||||
|
||||
<!---[![Coverage Status](https://coveralls.io/repos/github/ipfs/go-log/badge.svg?branch=master)](https://coveralls.io/github/ipfs/go-log?branch=master)--->
|
||||
|
||||
|
||||
> The logging library used by go-ipfs
|
||||
|
||||
It currently uses a modified version of [go-logging](https://github.com/whyrusleeping/go-logging) to implement the standard printf-style log output.
|
||||
|
||||
## Install
|
||||
|
||||
```sh
|
||||
go get github.com/ipfs/go-log
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
Once the package is imported under the name `logging`, an instance of `EventLogger` can be created like so:
|
||||
|
||||
```go
|
||||
var log = logging.Logger("subsystem name")
|
||||
```
|
||||
|
||||
It can then be used to emit log messages, either plain printf-style messages at six standard levels or structured messages using `Start`, `StartFromParentState`, `Finish` and `FinishWithErr` methods.
|
||||
|
||||
## Example
|
||||
|
||||
```go
|
||||
func (s *Session) GetBlock(ctx context.Context, c *cid.Cid) (blk blocks.Block, err error) {
|
||||
|
||||
// Starts Span called "Session.GetBlock", associates with `ctx`
|
||||
ctx = log.Start(ctx, "Session.GetBlock")
|
||||
|
||||
// defer so `blk` and `err` can be evaluated after call
|
||||
defer func() {
|
||||
// tag span associated with `ctx`
|
||||
log.SetTags(ctx, map[string]interface{}{
|
||||
"cid": c,
|
||||
"block", blk,
|
||||
})
|
||||
// if err is non-nil tag the span with an error
|
||||
log.FinishWithErr(ctx, err)
|
||||
}()
|
||||
|
||||
if shouldStartSomething() {
|
||||
// log message on span associated with `ctx`
|
||||
log.LogKV(ctx, "startSomething", true)
|
||||
}
|
||||
...
|
||||
}
|
||||
```
|
||||
## Tracing
|
||||
|
||||
`go-log` wraps the [opentracing-go](https://github.com/opentracing/opentracing-go) methods - `StartSpan`, `Finish`, `LogKV`, and `SetTag`.
|
||||
|
||||
`go-log` implements its own tracer - `loggabletracer` - based on the [basictracer-go](https://github.com/opentracing/basictracer-go) implementation. If there is an active [`WriterGroup`](https://github.com/ipfs/go-log/blob/master/writer/option.go) the `loggabletracer` will [record](https://github.com/ipfs/go-log/blob/master/tracer/recorder.go) span data to the `WriterGroup`. An example of this can be seen in the [`log tail`](https://github.com/ipfs/go-ipfs/blob/master/core/commands/log.go) command of `go-ipfs`.
|
||||
|
||||
Third party tracers may be used by calling `opentracing.SetGlobalTracer()` with your desired tracing implementation. An example of this can be seen using the [`go-jaeger-plugin`](https://github.com/ipfs/go-jaeger-plugin) and the `go-ipfs` [tracer plugin](https://github.com/ipfs/go-ipfs/blob/master/plugin/tracer.go)
|
||||
|
||||
## Contribute
|
||||
|
||||
Feel free to join in. All welcome. Open an [issue](https://github.com/ipfs/go-log/issues)!
|
||||
|
||||
This repository falls under the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md).
|
||||
|
||||
### Want to hack on IPFS?
|
||||
|
||||
[![](https://cdn.rawgit.com/jbenet/contribute-ipfs-gif/master/img/contribute.gif)](https://github.com/ipfs/community/blob/master/contributing.md)
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
|
@ -1,38 +0,0 @@
|
|||
package log
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
)
|
||||
|
||||
type key int
|
||||
|
||||
const metadataKey key = 0
|
||||
|
||||
// ContextWithLoggable returns a derived context which contains the provided
|
||||
// Loggable. Any Events logged with the derived context will include the
|
||||
// provided Loggable.
|
||||
func ContextWithLoggable(ctx context.Context, l Loggable) context.Context {
|
||||
existing, err := MetadataFromContext(ctx)
|
||||
if err != nil {
|
||||
// context does not contain meta. just set the new metadata
|
||||
child := context.WithValue(ctx, metadataKey, Metadata(l.Loggable()))
|
||||
return child
|
||||
}
|
||||
|
||||
merged := DeepMerge(existing, l.Loggable())
|
||||
child := context.WithValue(ctx, metadataKey, merged)
|
||||
return child
|
||||
}
|
||||
|
||||
// MetadataFromContext extracts Matadata from a given context's value.
|
||||
func MetadataFromContext(ctx context.Context) (Metadata, error) {
|
||||
value := ctx.Value(metadataKey)
|
||||
if value != nil {
|
||||
metadata, ok := value.(Metadata)
|
||||
if ok {
|
||||
return metadata, nil
|
||||
}
|
||||
}
|
||||
return nil, errors.New("context contains no metadata")
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
package log
|
||||
|
||||
type entry struct {
|
||||
loggables []Loggable
|
||||
system string
|
||||
event string
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
package log
|
||||
|
||||
import (
|
||||
log2 "github.com/ipfs/go-log/v2"
|
||||
)
|
||||
|
||||
// LogLevel represents a log severity level. Use the package variables as an
|
||||
// enum.
|
||||
type LogLevel = log2.LogLevel
|
||||
|
||||
var (
|
||||
LevelDebug = log2.LevelDebug
|
||||
LevelInfo = log2.LevelInfo
|
||||
LevelWarn = log2.LevelWarn
|
||||
LevelError = log2.LevelError
|
||||
LevelDPanic = log2.LevelDPanic
|
||||
LevelPanic = log2.LevelPanic
|
||||
LevelFatal = log2.LevelFatal
|
||||
)
|
||||
|
||||
// LevelFromString parses a string-based level and returns the corresponding
|
||||
// LogLevel.
|
||||
//
|
||||
// Supported strings are: DEBUG, INFO, WARN, ERROR, DPANIC, PANIC, FATAL, and
|
||||
// their lower-case forms.
|
||||
//
|
||||
// The returned LogLevel must be discarded if error is not nil.
|
||||
func LevelFromString(level string) (LogLevel, error) {
|
||||
return log2.LevelFromString(level)
|
||||
}
|
|
@ -1,420 +0,0 @@
|
|||
// Package log is the logging library used by IPFS
|
||||
// (https://github.com/ipfs/go-ipfs). It uses a modified version of
|
||||
// https://godoc.org/github.com/whyrusleeping/go-logging .
|
||||
package log
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"path"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
log2 "github.com/ipfs/go-log/v2"
|
||||
writer "github.com/ipfs/go-log/writer"
|
||||
|
||||
opentrace "github.com/opentracing/opentracing-go"
|
||||
otExt "github.com/opentracing/opentracing-go/ext"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var log = Logger("eventlog")
|
||||
|
||||
// StandardLogger provides API compatibility with standard printf loggers
|
||||
// eg. go-logging
|
||||
type StandardLogger interface {
|
||||
log2.StandardLogger
|
||||
// Deprecated use Warn
|
||||
Warning(args ...interface{})
|
||||
// Deprecated use Warnf
|
||||
Warningf(format string, args ...interface{})
|
||||
}
|
||||
|
||||
// EventLogger extends the StandardLogger interface to allow for log items
|
||||
// containing structured metadata
|
||||
type EventLogger interface {
|
||||
StandardLogger
|
||||
|
||||
// Event merges structured data from the provided inputs into a single
|
||||
// machine-readable log event.
|
||||
//
|
||||
// If the context contains metadata, a copy of this is used as the base
|
||||
// metadata accumulator.
|
||||
//
|
||||
// If one or more loggable objects are provided, these are deep-merged into base blob.
|
||||
//
|
||||
// Next, the event name is added to the blob under the key "event". If
|
||||
// the key "event" already exists, it will be over-written.
|
||||
//
|
||||
// Finally the timestamp and package name are added to the accumulator and
|
||||
// the metadata is logged.
|
||||
// DEPRECATED
|
||||
// Deprecated: Stop using go-log for event logging
|
||||
Event(ctx context.Context, event string, m ...Loggable)
|
||||
|
||||
// DEPRECATED
|
||||
// Deprecated: Stop using go-log for event logging
|
||||
EventBegin(ctx context.Context, event string, m ...Loggable) *EventInProgress
|
||||
|
||||
// Start starts an opentracing span with `name`, using
|
||||
// any Span found within `ctx` as a ChildOfRef. If no such parent could be
|
||||
// found, Start creates a root (parentless) Span.
|
||||
//
|
||||
// The return value is a context.Context object built around the
|
||||
// returned Span.
|
||||
//
|
||||
// Example usage:
|
||||
//
|
||||
// SomeFunction(ctx context.Context, ...) {
|
||||
// ctx := log.Start(ctx, "SomeFunction")
|
||||
// defer log.Finish(ctx)
|
||||
// ...
|
||||
// }
|
||||
// Deprecated: Stop using go-log for event logging
|
||||
Start(ctx context.Context, name string) context.Context
|
||||
|
||||
// StartFromParentState starts an opentracing span with `name`, using
|
||||
// any Span found within `ctx` as a ChildOfRef. If no such parent could be
|
||||
// found, StartSpanFromParentState creates a root (parentless) Span.
|
||||
//
|
||||
// StartFromParentState will attempt to deserialize a SpanContext from `parent`,
|
||||
// using any Span found within to continue the trace
|
||||
//
|
||||
// The return value is a context.Context object built around the
|
||||
// returned Span.
|
||||
//
|
||||
// An error is returned when `parent` cannot be deserialized to a SpanContext
|
||||
//
|
||||
// Example usage:
|
||||
//
|
||||
// SomeFunction(ctx context.Context, bParent []byte) {
|
||||
// ctx := log.StartFromParentState(ctx, "SomeFunction", bParent)
|
||||
// defer log.Finish(ctx)
|
||||
// ...
|
||||
// }
|
||||
// Deprecated: Stop using go-log for event logging
|
||||
StartFromParentState(ctx context.Context, name string, parent []byte) (context.Context, error)
|
||||
|
||||
// Finish completes the span associated with `ctx`.
|
||||
//
|
||||
// Finish() must be the last call made to any span instance, and to do
|
||||
// otherwise leads to undefined behavior.
|
||||
// Finish will do its best to notify (log) when used in correctly
|
||||
// .e.g called twice, or called on a spanless `ctx`
|
||||
// Deprecated: Stop using go-log for event logging
|
||||
Finish(ctx context.Context)
|
||||
|
||||
// FinishWithErr completes the span associated with `ctx` and also calls
|
||||
// SetErr if `err` is non-nil
|
||||
//
|
||||
// FinishWithErr() must be the last call made to any span instance, and to do
|
||||
// otherwise leads to undefined behavior.
|
||||
// FinishWithErr will do its best to notify (log) when used in correctly
|
||||
// .e.g called twice, or called on a spanless `ctx`
|
||||
// Deprecated: Stop using go-log for event logging
|
||||
FinishWithErr(ctx context.Context, err error)
|
||||
|
||||
// SetErr tags the span associated with `ctx` to reflect an error occured, and
|
||||
// logs the value `err` under key `error`.
|
||||
// Deprecated: Stop using go-log for event logging
|
||||
SetErr(ctx context.Context, err error)
|
||||
|
||||
// LogKV records key:value logging data about an event stored in `ctx`
|
||||
// Eexample:
|
||||
// log.LogKV(
|
||||
// "error", "resolve failure",
|
||||
// "type", "cache timeout",
|
||||
// "waited.millis", 1500)
|
||||
// Deprecated: Stop using go-log for event logging
|
||||
LogKV(ctx context.Context, alternatingKeyValues ...interface{})
|
||||
|
||||
// SetTag tags key `k` and value `v` on the span associated with `ctx`
|
||||
// Deprecated: Stop using go-log for event logging
|
||||
SetTag(ctx context.Context, key string, value interface{})
|
||||
|
||||
// SetTags tags keys from the `tags` maps on the span associated with `ctx`
|
||||
// Example:
|
||||
// log.SetTags(ctx, map[string]{
|
||||
// "type": bizStruct,
|
||||
// "request": req,
|
||||
// })
|
||||
// Deprecated: Stop using go-log for event logging
|
||||
SetTags(ctx context.Context, tags map[string]interface{})
|
||||
|
||||
// SerializeContext takes the SpanContext instance stored in `ctx` and Seralizes
|
||||
// it to bytes. An error is returned if the `ctx` cannot be serialized to
|
||||
// a bytes array
|
||||
// Deprecated: Stop using go-log for event logging
|
||||
SerializeContext(ctx context.Context) ([]byte, error)
|
||||
}
|
||||
|
||||
var _ EventLogger = Logger("test-logger")
|
||||
|
||||
// Logger retrieves an event logger by name
|
||||
func Logger(system string) *ZapEventLogger {
|
||||
if len(system) == 0 {
|
||||
setuplog := Logger("setup-logger")
|
||||
setuplog.Error("Missing name parameter")
|
||||
system = "undefined"
|
||||
}
|
||||
logger := log2.Logger(system)
|
||||
return &ZapEventLogger{system: system, SugaredLogger: logger.SugaredLogger}
|
||||
}
|
||||
|
||||
// ZapEventLogger implements the EventLogger and wraps a go-logging Logger
|
||||
type ZapEventLogger struct {
|
||||
zap.SugaredLogger
|
||||
|
||||
system string
|
||||
// TODO add log-level
|
||||
}
|
||||
|
||||
// Deprecated: use Warn
|
||||
func (el *ZapEventLogger) Warning(args ...interface{}) {
|
||||
el.Warn(args...)
|
||||
}
|
||||
|
||||
// Deprecated: use Warnf
|
||||
func (el *ZapEventLogger) Warningf(format string, args ...interface{}) {
|
||||
el.Warnf(format, args...)
|
||||
}
|
||||
|
||||
// Deprecated: Stop using go-log for event logging
|
||||
func (el *ZapEventLogger) Start(ctx context.Context, operationName string) context.Context {
|
||||
span, ctx := opentrace.StartSpanFromContext(ctx, operationName)
|
||||
span.SetTag("system", el.system)
|
||||
return ctx
|
||||
}
|
||||
|
||||
// Deprecated: Stop using go-log for event logging
|
||||
func (el *ZapEventLogger) StartFromParentState(ctx context.Context, operationName string, parent []byte) (context.Context, error) {
|
||||
sc, err := deserializeContext(parent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//TODO RPCServerOption is probably not the best tag, as this is likely from a peer
|
||||
span, ctx := opentrace.StartSpanFromContext(ctx, operationName, otExt.RPCServerOption(sc))
|
||||
span.SetTag("system", el.system)
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
// Deprecated: Stop using go-log for event logging
|
||||
func (el *ZapEventLogger) SerializeContext(ctx context.Context) ([]byte, error) {
|
||||
gTracer := opentrace.GlobalTracer()
|
||||
b := make([]byte, 0)
|
||||
carrier := bytes.NewBuffer(b)
|
||||
span := opentrace.SpanFromContext(ctx)
|
||||
if err := gTracer.Inject(span.Context(), opentrace.Binary, carrier); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return carrier.Bytes(), nil
|
||||
}
|
||||
|
||||
// Deprecated: Stop using go-log for event logging
|
||||
func (el *ZapEventLogger) LogKV(ctx context.Context, alternatingKeyValues ...interface{}) {
|
||||
span := opentrace.SpanFromContext(ctx)
|
||||
if span == nil {
|
||||
_, file, line, _ := runtime.Caller(1)
|
||||
log.Errorf("LogKV with no Span in context called on %s:%d", path.Base(file), line)
|
||||
return
|
||||
}
|
||||
span.LogKV(alternatingKeyValues...)
|
||||
}
|
||||
|
||||
// Deprecated: Stop using go-log for event logging
|
||||
func (el *ZapEventLogger) SetTag(ctx context.Context, k string, v interface{}) {
|
||||
span := opentrace.SpanFromContext(ctx)
|
||||
if span == nil {
|
||||
_, file, line, _ := runtime.Caller(1)
|
||||
log.Errorf("SetTag with no Span in context called on %s:%d", path.Base(file), line)
|
||||
return
|
||||
}
|
||||
span.SetTag(k, v)
|
||||
}
|
||||
|
||||
// Deprecated: Stop using go-log for event logging
|
||||
func (el *ZapEventLogger) SetTags(ctx context.Context, tags map[string]interface{}) {
|
||||
span := opentrace.SpanFromContext(ctx)
|
||||
if span == nil {
|
||||
_, file, line, _ := runtime.Caller(1)
|
||||
log.Errorf("SetTags with no Span in context called on %s:%d", path.Base(file), line)
|
||||
return
|
||||
}
|
||||
for k, v := range tags {
|
||||
span.SetTag(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
func (el *ZapEventLogger) setErr(ctx context.Context, err error, skip int) {
|
||||
span := opentrace.SpanFromContext(ctx)
|
||||
if span == nil {
|
||||
_, file, line, _ := runtime.Caller(skip)
|
||||
log.Errorf("SetErr with no Span in context called on %s:%d", path.Base(file), line)
|
||||
return
|
||||
}
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
otExt.Error.Set(span, true)
|
||||
span.LogKV("error", err.Error())
|
||||
}
|
||||
|
||||
// Deprecated: Stop using go-log for event logging
|
||||
func (el *ZapEventLogger) SetErr(ctx context.Context, err error) {
|
||||
el.setErr(ctx, err, 1)
|
||||
}
|
||||
|
||||
// Deprecated: Stop using go-log for event logging
|
||||
func (el *ZapEventLogger) Finish(ctx context.Context) {
|
||||
span := opentrace.SpanFromContext(ctx)
|
||||
if span == nil {
|
||||
_, file, line, _ := runtime.Caller(1)
|
||||
log.Errorf("Finish with no Span in context called on %s:%d", path.Base(file), line)
|
||||
return
|
||||
}
|
||||
span.Finish()
|
||||
}
|
||||
|
||||
// Deprecated: Stop using go-log for event logging
|
||||
func (el *ZapEventLogger) FinishWithErr(ctx context.Context, err error) {
|
||||
el.setErr(ctx, err, 2)
|
||||
el.Finish(ctx)
|
||||
}
|
||||
|
||||
func deserializeContext(bCtx []byte) (opentrace.SpanContext, error) {
|
||||
gTracer := opentrace.GlobalTracer()
|
||||
carrier := bytes.NewReader(bCtx)
|
||||
spanContext, err := gTracer.Extract(opentrace.Binary, carrier)
|
||||
if err != nil {
|
||||
log.Warning("Failed to deserialize context %s", err)
|
||||
return nil, err
|
||||
}
|
||||
return spanContext, nil
|
||||
}
|
||||
|
||||
// Deprecated: Stop using go-log for event logging
|
||||
func (el *ZapEventLogger) EventBegin(ctx context.Context, event string, metadata ...Loggable) *EventInProgress {
|
||||
ctx = el.Start(ctx, event)
|
||||
|
||||
for _, m := range metadata {
|
||||
for l, v := range m.Loggable() {
|
||||
el.LogKV(ctx, l, v)
|
||||
}
|
||||
}
|
||||
|
||||
eip := &EventInProgress{}
|
||||
eip.doneFunc = func(additional []Loggable) {
|
||||
// anything added during the operation
|
||||
// e.g. deprecated methods event.Append(...) or event.SetError(...)
|
||||
for _, m := range eip.loggables {
|
||||
for l, v := range m.Loggable() {
|
||||
el.LogKV(ctx, l, v)
|
||||
}
|
||||
}
|
||||
el.Finish(ctx)
|
||||
}
|
||||
return eip
|
||||
}
|
||||
|
||||
// Deprecated: Stop using go-log for event logging
|
||||
func (el *ZapEventLogger) Event(ctx context.Context, event string, metadata ...Loggable) {
|
||||
|
||||
// short circuit if theres nothing to write to
|
||||
if !writer.WriterGroup.Active() {
|
||||
return
|
||||
}
|
||||
|
||||
// Collect loggables for later logging
|
||||
var loggables []Loggable
|
||||
|
||||
// get any existing metadata from the context
|
||||
existing, err := MetadataFromContext(ctx)
|
||||
if err != nil {
|
||||
existing = Metadata{}
|
||||
}
|
||||
loggables = append(loggables, existing)
|
||||
loggables = append(loggables, metadata...)
|
||||
|
||||
e := entry{
|
||||
loggables: loggables,
|
||||
system: el.system,
|
||||
event: event,
|
||||
}
|
||||
|
||||
accum := Metadata{}
|
||||
for _, loggable := range e.loggables {
|
||||
accum = DeepMerge(accum, loggable.Loggable())
|
||||
}
|
||||
|
||||
// apply final attributes to reserved keys
|
||||
// TODO accum["level"] = level
|
||||
accum["event"] = e.event
|
||||
accum["system"] = e.system
|
||||
accum["time"] = FormatRFC3339(time.Now())
|
||||
|
||||
var buf bytes.Buffer
|
||||
encoder := json.NewEncoder(&buf)
|
||||
encoder.SetEscapeHTML(false)
|
||||
err = encoder.Encode(accum)
|
||||
if err != nil {
|
||||
el.Errorf("ERROR FORMATTING EVENT ENTRY: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
_, _ = writer.WriterGroup.Write(buf.Bytes())
|
||||
}
|
||||
|
||||
// DEPRECATED
|
||||
// EventInProgress represent and event which is happening
|
||||
// Deprecated: Stop using go-log for event logging
|
||||
type EventInProgress struct {
|
||||
loggables []Loggable
|
||||
doneFunc func([]Loggable)
|
||||
}
|
||||
|
||||
// DEPRECATED use `LogKV` or `SetTag`
|
||||
// Append adds loggables to be included in the call to Done
|
||||
func (eip *EventInProgress) Append(l Loggable) {
|
||||
eip.loggables = append(eip.loggables, l)
|
||||
}
|
||||
|
||||
// DEPRECATED use `SetError(ctx, error)`
|
||||
// SetError includes the provided error
|
||||
func (eip *EventInProgress) SetError(err error) {
|
||||
eip.loggables = append(eip.loggables, LoggableMap{
|
||||
"error": err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
// Done creates a new Event entry that includes the duration and appended
|
||||
// loggables.
|
||||
// Deprecated: Stop using go-log for event logging
|
||||
func (eip *EventInProgress) Done() {
|
||||
eip.doneFunc(eip.loggables) // create final event with extra data
|
||||
}
|
||||
|
||||
// DEPRECATED use `FinishWithErr`
|
||||
// DoneWithErr creates a new Event entry that includes the duration and appended
|
||||
// loggables. DoneWithErr accepts an error, if err is non-nil, it is set on
|
||||
// the EventInProgress. Otherwise the logic is the same as the `Done()` method
|
||||
func (eip *EventInProgress) DoneWithErr(err error) {
|
||||
if err != nil {
|
||||
eip.SetError(err)
|
||||
}
|
||||
eip.doneFunc(eip.loggables)
|
||||
}
|
||||
|
||||
// Close is an alias for done
|
||||
// Deprecated: Stop using go-log for event logging
|
||||
func (eip *EventInProgress) Close() error {
|
||||
eip.Done()
|
||||
return nil
|
||||
}
|
||||
|
||||
// FormatRFC3339 returns the given time in UTC with RFC3999Nano format.
|
||||
func FormatRFC3339(t time.Time) string {
|
||||
return t.UTC().Format(time.RFC3339Nano)
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
package log
|
||||
|
||||
// Loggable describes objects that can be marshalled into Metadata for logging
|
||||
type Loggable interface {
|
||||
Loggable() map[string]interface{}
|
||||
}
|
||||
|
||||
// LoggableMap is just a generic map keyed by string. It
|
||||
// implements the Loggable interface.
|
||||
type LoggableMap map[string]interface{}
|
||||
|
||||
// Loggable implements the Loggable interface for LoggableMap
|
||||
func (l LoggableMap) Loggable() map[string]interface{} {
|
||||
return l
|
||||
}
|
||||
|
||||
// LoggableF converts a func into a Loggable
|
||||
type LoggableF func() map[string]interface{}
|
||||
|
||||
// Loggable implements the Loggable interface by running
|
||||
// the LoggableF function.
|
||||
func (l LoggableF) Loggable() map[string]interface{} {
|
||||
return l()
|
||||
}
|
||||
|
||||
// Deferred returns a LoggableF where the execution of the
|
||||
// provided function is deferred.
|
||||
func Deferred(key string, f func() string) Loggable {
|
||||
function := func() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
key: f(),
|
||||
}
|
||||
}
|
||||
return LoggableF(function)
|
||||
}
|
||||
|
||||
// Pair returns a Loggable where key is paired to Loggable.
|
||||
func Pair(key string, l Loggable) Loggable {
|
||||
return LoggableMap{
|
||||
key: l,
|
||||
}
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
package log
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// Metadata is a convenience type for generic maps
|
||||
type Metadata map[string]interface{}
|
||||
|
||||
// DeepMerge merges the second Metadata parameter into the first.
|
||||
// Nested Metadata are merged recursively. Primitives are over-written.
|
||||
func DeepMerge(b, a Metadata) Metadata {
|
||||
out := Metadata{}
|
||||
for k, v := range b {
|
||||
out[k] = v
|
||||
}
|
||||
for k, v := range a {
|
||||
|
||||
maybe, err := Metadatify(v)
|
||||
if err != nil {
|
||||
// if the new value is not meta. just overwrite the dest vaue
|
||||
if out[k] != nil {
|
||||
log.Debugf("Overwriting key: %s, old: %s, new: %s", k, out[k], v)
|
||||
}
|
||||
out[k] = v
|
||||
continue
|
||||
}
|
||||
|
||||
// it is meta. What about dest?
|
||||
outv, exists := out[k]
|
||||
if !exists {
|
||||
// the new value is meta, but there's no dest value. just write it
|
||||
out[k] = v
|
||||
continue
|
||||
}
|
||||
|
||||
outMetadataValue, err := Metadatify(outv)
|
||||
if err != nil {
|
||||
// the new value is meta and there's a dest value, but the dest
|
||||
// value isn't meta. just overwrite
|
||||
out[k] = v
|
||||
continue
|
||||
}
|
||||
|
||||
// both are meta. merge them.
|
||||
out[k] = DeepMerge(outMetadataValue, maybe)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Loggable implements the Loggable interface.
|
||||
func (m Metadata) Loggable() map[string]interface{} {
|
||||
// NB: method defined on value to avoid de-referencing nil Metadata
|
||||
return m
|
||||
}
|
||||
|
||||
// JsonString returns the marshaled JSON string for the metadata.
|
||||
func (m Metadata) JsonString() (string, error) {
|
||||
// NB: method defined on value
|
||||
b, err := json.Marshal(m)
|
||||
return string(b), err
|
||||
}
|
||||
|
||||
// Metadatify converts maps into Metadata.
|
||||
func Metadatify(i interface{}) (Metadata, error) {
|
||||
value := reflect.ValueOf(i)
|
||||
if value.Kind() == reflect.Map {
|
||||
m := map[string]interface{}{}
|
||||
for _, k := range value.MapKeys() {
|
||||
m[k.String()] = value.MapIndex(k).Interface()
|
||||
}
|
||||
return Metadata(m), nil
|
||||
}
|
||||
return nil, errors.New("is not a map")
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
package log
|
||||
|
||||
import (
|
||||
tracer "github.com/ipfs/go-log/tracer"
|
||||
lwriter "github.com/ipfs/go-log/writer"
|
||||
"os"
|
||||
|
||||
opentrace "github.com/opentracing/opentracing-go"
|
||||
|
||||
log2 "github.com/ipfs/go-log/v2"
|
||||
)
|
||||
|
||||
func init() {
|
||||
SetupLogging()
|
||||
}
|
||||
|
||||
// Logging environment variables
|
||||
const (
|
||||
envTracingFile = "GOLOG_TRACING_FILE" // /path/to/file
|
||||
)
|
||||
|
||||
func SetupLogging() {
|
||||
// We're importing V2. Given that we setup logging on init, we should be
|
||||
// fine skipping the rest of the initialization.
|
||||
|
||||
// TracerPlugins are instantiated after this, so use loggable tracer
|
||||
// by default, if a TracerPlugin is added it will override this
|
||||
lgblRecorder := tracer.NewLoggableRecorder()
|
||||
lgblTracer := tracer.New(lgblRecorder)
|
||||
opentrace.SetGlobalTracer(lgblTracer)
|
||||
|
||||
if tracingfp := os.Getenv(envTracingFile); len(tracingfp) > 0 {
|
||||
f, err := os.Create(tracingfp)
|
||||
if err != nil {
|
||||
log.Error("failed to create tracing file: %s", tracingfp)
|
||||
} else {
|
||||
lwriter.WriterGroup.AddWriter(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SetDebugLogging calls SetAllLoggers with logging.DEBUG
|
||||
func SetDebugLogging() {
|
||||
log2.SetDebugLogging()
|
||||
}
|
||||
|
||||
// SetAllLoggers changes the logging level of all loggers to lvl
|
||||
func SetAllLoggers(lvl LogLevel) {
|
||||
log2.SetAllLoggers(lvl)
|
||||
}
|
||||
|
||||
// SetLogLevel changes the log level of a specific subsystem
|
||||
// name=="*" changes all subsystems
|
||||
func SetLogLevel(name, level string) error {
|
||||
return log2.SetLogLevel(name, level)
|
||||
}
|
||||
|
||||
// SetLogLevelRegex sets all loggers to level `l` that match expression `e`.
|
||||
// An error is returned if `e` fails to compile.
|
||||
func SetLogLevelRegex(e, l string) error {
|
||||
return log2.SetLogLevelRegex(e, l)
|
||||
}
|
||||
|
||||
// GetSubsystems returns a slice containing the
|
||||
// names of the current loggers
|
||||
func GetSubsystems() []string {
|
||||
return log2.GetSubsystems()
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
{
|
||||
"bugs": {
|
||||
"url": "https://github.com/ipfs/go-log"
|
||||
},
|
||||
"gx": {
|
||||
"dvcsimport": "github.com/ipfs/go-log"
|
||||
},
|
||||
"gxDependencies": [
|
||||
{
|
||||
"author": "whyrusleeping",
|
||||
"hash": "QmcaSwFc5RBg8yCq54QURwEU4nwjfCpjbpmaAm4VbdGLKv",
|
||||
"name": "go-logging",
|
||||
"version": "0.0.0"
|
||||
},
|
||||
{
|
||||
"author": "frist",
|
||||
"hash": "QmWLWmRVSiagqP15jczsGME1qpob6HDbtbHAY2he9W5iUo",
|
||||
"name": "opentracing-go",
|
||||
"version": "0.0.3"
|
||||
},
|
||||
{
|
||||
"author": "mattn",
|
||||
"hash": "QmTsHcKgTQ4VeYZd8eKYpTXeLW7KNwkRD9wjnrwsV2sToq",
|
||||
"name": "go-colorable",
|
||||
"version": "0.2.0"
|
||||
},
|
||||
{
|
||||
"author": "whyrusleeping",
|
||||
"hash": "QmddjPSGZb3ieihSseFeCfVRpZzcqczPNsD2DvarSwnjJB",
|
||||
"name": "gogo-protobuf",
|
||||
"version": "1.2.1"
|
||||
}
|
||||
],
|
||||
"gxVersion": "0.12.1",
|
||||
"language": "go",
|
||||
"license": "",
|
||||
"name": "go-log",
|
||||
"releaseCmd": "git commit -a -m \"gx publish $VERSION\"",
|
||||
"version": "1.5.9"
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 The OpenTracing Authors
|
||||
|
||||
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.
|
|
@ -1,42 +0,0 @@
|
|||
package loggabletracer
|
||||
|
||||
// SpanContext holds the basic Span metadata.
|
||||
type SpanContext struct {
|
||||
// A probabilistically unique identifier for a [multi-span] trace.
|
||||
TraceID uint64
|
||||
|
||||
// A probabilistically unique identifier for a span.
|
||||
SpanID uint64
|
||||
|
||||
// Whether the trace is sampled.
|
||||
Sampled bool
|
||||
|
||||
// The span's associated baggage.
|
||||
Baggage map[string]string // initialized on first use
|
||||
}
|
||||
|
||||
// ForeachBaggageItem belongs to the opentracing.SpanContext interface
|
||||
func (c SpanContext) ForeachBaggageItem(handler func(k, v string) bool) {
|
||||
for k, v := range c.Baggage {
|
||||
if !handler(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithBaggageItem returns an entirely new loggabletracer SpanContext with the
|
||||
// given key:value baggage pair set.
|
||||
func (c SpanContext) WithBaggageItem(key, val string) SpanContext {
|
||||
var newBaggage map[string]string
|
||||
if c.Baggage == nil {
|
||||
newBaggage = map[string]string{key: val}
|
||||
} else {
|
||||
newBaggage = make(map[string]string, len(c.Baggage)+1)
|
||||
for k, v := range c.Baggage {
|
||||
newBaggage[k] = v
|
||||
}
|
||||
newBaggage[key] = val
|
||||
}
|
||||
// Use positional parameters so the compiler will help catch new fields.
|
||||
return SpanContext{c.TraceID, c.SpanID, c.Sampled, newBaggage}
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
package loggabletracer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const debugGoroutineIDTag = "_initial_goroutine"
|
||||
|
||||
type errAssertionFailed struct {
|
||||
span *spanImpl
|
||||
msg string
|
||||
}
|
||||
|
||||
// Error implements the error interface.
|
||||
func (err *errAssertionFailed) Error() string {
|
||||
return fmt.Sprintf("%s:\n%+v", err.msg, err.span)
|
||||
}
|
||||
|
||||
func (s *spanImpl) Lock() {
|
||||
s.Mutex.Lock()
|
||||
s.maybeAssertSanityLocked()
|
||||
}
|
||||
|
||||
func (s *spanImpl) maybeAssertSanityLocked() {
|
||||
if s.tracer == nil {
|
||||
s.Mutex.Unlock()
|
||||
panic(&errAssertionFailed{span: s, msg: "span used after call to Finish()"})
|
||||
}
|
||||
if s.tracer.options.DebugAssertSingleGoroutine {
|
||||
startID := curGoroutineID()
|
||||
curID, ok := s.raw.Tags[debugGoroutineIDTag].(uint64)
|
||||
if !ok {
|
||||
// This is likely invoked in the context of the SetTag which sets
|
||||
// debugGoroutineTag.
|
||||
return
|
||||
}
|
||||
if startID != curID {
|
||||
s.Mutex.Unlock()
|
||||
panic(&errAssertionFailed{
|
||||
span: s,
|
||||
msg: fmt.Sprintf("span started on goroutine %d, but now running on %d", startID, curID),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var goroutineSpace = []byte("goroutine ")
|
||||
var littleBuf = sync.Pool{
|
||||
New: func() interface{} {
|
||||
buf := make([]byte, 64)
|
||||
return &buf
|
||||
},
|
||||
}
|
||||
|
||||
// Credit to @bradfitz:
|
||||
// https://github.com/golang/net/blob/master/http2/gotrack.go#L51
|
||||
func curGoroutineID() uint64 {
|
||||
bp := littleBuf.Get().(*[]byte)
|
||||
defer littleBuf.Put(bp)
|
||||
b := *bp
|
||||
b = b[:runtime.Stack(b, false)]
|
||||
// Parse the 4707 out of "goroutine 4707 ["
|
||||
b = bytes.TrimPrefix(b, goroutineSpace)
|
||||
i := bytes.IndexByte(b, ' ')
|
||||
if i < 0 {
|
||||
panic(fmt.Sprintf("No space found in %q", b))
|
||||
}
|
||||
b = b[:i]
|
||||
n, err := strconv.ParseUint(string(b), 10, 64)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Failed to parse goroutine ID out of %q: %v", b, err))
|
||||
}
|
||||
return n
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
package loggabletracer
|
||||
|
||||
import "github.com/opentracing/opentracing-go"
|
||||
|
||||
// A SpanEvent is emitted when a mutating command is called on a Span.
|
||||
type SpanEvent interface{}
|
||||
|
||||
// EventCreate is emitted when a Span is created.
|
||||
type EventCreate struct{ OperationName string }
|
||||
|
||||
// EventTag is received when SetTag is called.
|
||||
type EventTag struct {
|
||||
Key string
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
// EventBaggage is received when SetBaggageItem is called.
|
||||
type EventBaggage struct {
|
||||
Key, Value string
|
||||
}
|
||||
|
||||
// EventLogFields is received when LogFields or LogKV is called.
|
||||
type EventLogFields opentracing.LogRecord
|
||||
|
||||
// EventLog is received when Log (or one of its derivatives) is called.
|
||||
//
|
||||
// DEPRECATED
|
||||
type EventLog opentracing.LogData
|
||||
|
||||
// EventFinish is received when Finish is called.
|
||||
type EventFinish RawSpan
|
||||
|
||||
func (s *spanImpl) onCreate(opName string) {
|
||||
if s.event != nil {
|
||||
s.event(EventCreate{OperationName: opName})
|
||||
}
|
||||
}
|
||||
func (s *spanImpl) onTag(key string, value interface{}) {
|
||||
if s.event != nil {
|
||||
s.event(EventTag{Key: key, Value: value})
|
||||
}
|
||||
}
|
||||
func (s *spanImpl) onLog(ld opentracing.LogData) {
|
||||
if s.event != nil {
|
||||
s.event(EventLog(ld))
|
||||
}
|
||||
}
|
||||
func (s *spanImpl) onLogFields(lr opentracing.LogRecord) {
|
||||
if s.event != nil {
|
||||
s.event(EventLogFields(lr))
|
||||
}
|
||||
}
|
||||
func (s *spanImpl) onBaggage(key, value string) {
|
||||
if s.event != nil {
|
||||
s.event(EventBaggage{Key: key, Value: value})
|
||||
}
|
||||
}
|
||||
func (s *spanImpl) onFinish(sp RawSpan) {
|
||||
if s.event != nil {
|
||||
s.event(EventFinish(sp))
|
||||
}
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
package loggabletracer
|
||||
|
||||
import opentracing "github.com/opentracing/opentracing-go"
|
||||
|
||||
type accessorPropagator struct {
|
||||
tracer *LoggableTracer
|
||||
}
|
||||
|
||||
// DelegatingCarrier is a flexible carrier interface which can be implemented
|
||||
// by types which have a means of storing the trace metadata and already know
|
||||
// how to serialize themselves (for example, protocol buffers).
|
||||
type DelegatingCarrier interface {
|
||||
SetState(traceID, spanID uint64, sampled bool)
|
||||
State() (traceID, spanID uint64, sampled bool)
|
||||
SetBaggageItem(key, value string)
|
||||
GetBaggage(func(key, value string))
|
||||
}
|
||||
|
||||
func (p *accessorPropagator) Inject(
|
||||
spanContext opentracing.SpanContext,
|
||||
carrier interface{},
|
||||
) error {
|
||||
dc, ok := carrier.(DelegatingCarrier)
|
||||
if !ok || dc == nil {
|
||||
return opentracing.ErrInvalidCarrier
|
||||
}
|
||||
sc, ok := spanContext.(SpanContext)
|
||||
if !ok {
|
||||
return opentracing.ErrInvalidSpanContext
|
||||
}
|
||||
dc.SetState(sc.TraceID, sc.SpanID, sc.Sampled)
|
||||
for k, v := range sc.Baggage {
|
||||
dc.SetBaggageItem(k, v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *accessorPropagator) Extract(
|
||||
carrier interface{},
|
||||
) (opentracing.SpanContext, error) {
|
||||
dc, ok := carrier.(DelegatingCarrier)
|
||||
if !ok || dc == nil {
|
||||
return nil, opentracing.ErrInvalidCarrier
|
||||
}
|
||||
|
||||
traceID, spanID, sampled := dc.State()
|
||||
sc := SpanContext{
|
||||
TraceID: traceID,
|
||||
SpanID: spanID,
|
||||
Sampled: sampled,
|
||||
Baggage: nil,
|
||||
}
|
||||
dc.GetBaggage(func(k, v string) {
|
||||
if sc.Baggage == nil {
|
||||
sc.Baggage = map[string]string{}
|
||||
}
|
||||
sc.Baggage[k] = v
|
||||
})
|
||||
|
||||
return sc, nil
|
||||
}
|
|
@ -1,178 +0,0 @@
|
|||
package loggabletracer
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/ipfs/go-log/tracer/wire"
|
||||
opentracing "github.com/opentracing/opentracing-go"
|
||||
)
|
||||
|
||||
type textMapPropagator struct {
|
||||
}
|
||||
type binaryPropagator struct {
|
||||
}
|
||||
|
||||
const (
|
||||
prefixTracerState = "ot-tracer-"
|
||||
prefixBaggage = "ot-baggage-"
|
||||
|
||||
tracerStateFieldCount = 3
|
||||
fieldNameTraceID = prefixTracerState + "traceid"
|
||||
fieldNameSpanID = prefixTracerState + "spanid"
|
||||
fieldNameSampled = prefixTracerState + "sampled"
|
||||
)
|
||||
|
||||
func (p *textMapPropagator) Inject(
|
||||
spanContext opentracing.SpanContext,
|
||||
opaqueCarrier interface{},
|
||||
) error {
|
||||
sc, ok := spanContext.(SpanContext)
|
||||
if !ok {
|
||||
return opentracing.ErrInvalidSpanContext
|
||||
}
|
||||
carrier, ok := opaqueCarrier.(opentracing.TextMapWriter)
|
||||
if !ok {
|
||||
return opentracing.ErrInvalidCarrier
|
||||
}
|
||||
carrier.Set(fieldNameTraceID, strconv.FormatUint(sc.TraceID, 16))
|
||||
carrier.Set(fieldNameSpanID, strconv.FormatUint(sc.SpanID, 16))
|
||||
carrier.Set(fieldNameSampled, strconv.FormatBool(sc.Sampled))
|
||||
|
||||
for k, v := range sc.Baggage {
|
||||
carrier.Set(prefixBaggage+k, v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *textMapPropagator) Extract(
|
||||
opaqueCarrier interface{},
|
||||
) (opentracing.SpanContext, error) {
|
||||
carrier, ok := opaqueCarrier.(opentracing.TextMapReader)
|
||||
if !ok {
|
||||
return nil, opentracing.ErrInvalidCarrier
|
||||
}
|
||||
requiredFieldCount := 0
|
||||
var traceID, spanID uint64
|
||||
var sampled bool
|
||||
var err error
|
||||
decodedBaggage := make(map[string]string)
|
||||
err = carrier.ForeachKey(func(k, v string) error {
|
||||
switch strings.ToLower(k) {
|
||||
case fieldNameTraceID:
|
||||
traceID, err = strconv.ParseUint(v, 16, 64)
|
||||
if err != nil {
|
||||
return opentracing.ErrSpanContextCorrupted
|
||||
}
|
||||
case fieldNameSpanID:
|
||||
spanID, err = strconv.ParseUint(v, 16, 64)
|
||||
if err != nil {
|
||||
return opentracing.ErrSpanContextCorrupted
|
||||
}
|
||||
case fieldNameSampled:
|
||||
sampled, err = strconv.ParseBool(v)
|
||||
if err != nil {
|
||||
return opentracing.ErrSpanContextCorrupted
|
||||
}
|
||||
default:
|
||||
lowercaseK := strings.ToLower(k)
|
||||
if strings.HasPrefix(lowercaseK, prefixBaggage) {
|
||||
decodedBaggage[strings.TrimPrefix(lowercaseK, prefixBaggage)] = v
|
||||
}
|
||||
// Balance off the requiredFieldCount++ just below...
|
||||
requiredFieldCount--
|
||||
}
|
||||
requiredFieldCount++
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if requiredFieldCount < tracerStateFieldCount {
|
||||
if requiredFieldCount == 0 {
|
||||
return nil, opentracing.ErrSpanContextNotFound
|
||||
}
|
||||
return nil, opentracing.ErrSpanContextCorrupted
|
||||
}
|
||||
|
||||
return SpanContext{
|
||||
TraceID: traceID,
|
||||
SpanID: spanID,
|
||||
Sampled: sampled,
|
||||
Baggage: decodedBaggage,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *binaryPropagator) Inject(
|
||||
spanContext opentracing.SpanContext,
|
||||
opaqueCarrier interface{},
|
||||
) error {
|
||||
sc, ok := spanContext.(SpanContext)
|
||||
if !ok {
|
||||
return opentracing.ErrInvalidSpanContext
|
||||
}
|
||||
carrier, ok := opaqueCarrier.(io.Writer)
|
||||
if !ok {
|
||||
return opentracing.ErrInvalidCarrier
|
||||
}
|
||||
|
||||
state := wire.TracerState{}
|
||||
state.TraceId = sc.TraceID
|
||||
state.SpanId = sc.SpanID
|
||||
state.Sampled = sc.Sampled
|
||||
state.BaggageItems = sc.Baggage
|
||||
|
||||
b, err := proto.Marshal(&state)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Write the length of the marshalled binary to the writer.
|
||||
length := uint32(len(b))
|
||||
if err := binary.Write(carrier, binary.BigEndian, &length); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = carrier.Write(b)
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *binaryPropagator) Extract(
|
||||
opaqueCarrier interface{},
|
||||
) (opentracing.SpanContext, error) {
|
||||
carrier, ok := opaqueCarrier.(io.Reader)
|
||||
if !ok {
|
||||
return nil, opentracing.ErrInvalidCarrier
|
||||
}
|
||||
|
||||
// Read the length of marshalled binary. io.ReadAll isn't that performant
|
||||
// since it keeps resizing the underlying buffer as it encounters more bytes
|
||||
// to read. By reading the length, we can allocate a fixed sized buf and read
|
||||
// the exact amount of bytes into it.
|
||||
var length uint32
|
||||
if err := binary.Read(carrier, binary.BigEndian, &length); err != nil {
|
||||
return nil, opentracing.ErrSpanContextCorrupted
|
||||
}
|
||||
buf := make([]byte, length)
|
||||
if n, err := carrier.Read(buf); err != nil {
|
||||
if n > 0 {
|
||||
return nil, opentracing.ErrSpanContextCorrupted
|
||||
}
|
||||
return nil, opentracing.ErrSpanContextNotFound
|
||||
}
|
||||
|
||||
ctx := wire.TracerState{}
|
||||
if err := proto.Unmarshal(buf, &ctx); err != nil {
|
||||
return nil, opentracing.ErrSpanContextCorrupted
|
||||
}
|
||||
|
||||
return SpanContext{
|
||||
TraceID: ctx.TraceId,
|
||||
SpanID: ctx.SpanId,
|
||||
Sampled: ctx.Sampled,
|
||||
Baggage: ctx.BaggageItems,
|
||||
}, nil
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
package loggabletracer
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
opentracing "github.com/opentracing/opentracing-go"
|
||||
)
|
||||
|
||||
// RawSpan encapsulates all state associated with a (finished) Span.
|
||||
type RawSpan struct {
|
||||
// Those recording the RawSpan should also record the contents of its
|
||||
// SpanContext.
|
||||
Context SpanContext
|
||||
|
||||
// The SpanID of this SpanContext's first intra-trace reference (i.e.,
|
||||
// "parent"), or 0 if there is no parent.
|
||||
ParentSpanID uint64
|
||||
|
||||
// The name of the "operation" this span is an instance of. (Called a "span
|
||||
// name" in some implementations)
|
||||
Operation string
|
||||
|
||||
// We store <start, duration> rather than <start, end> so that only
|
||||
// one of the timestamps has global clock uncertainty issues.
|
||||
Start time.Time
|
||||
Duration time.Duration
|
||||
|
||||
// Essentially an extension mechanism. Can be used for many purposes,
|
||||
// not to be enumerated here.
|
||||
Tags opentracing.Tags
|
||||
|
||||
// The span's "microlog".
|
||||
Logs []opentracing.LogRecord
|
||||
}
|
|
@ -1,103 +0,0 @@
|
|||
package loggabletracer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
writer "github.com/ipfs/go-log/writer"
|
||||
opentrace "github.com/opentracing/opentracing-go"
|
||||
)
|
||||
|
||||
// A SpanRecorder handles all of the `RawSpan` data generated via an
|
||||
// associated `Tracer` (see `NewStandardTracer`) instance. It also names
|
||||
// the containing process and provides access to a straightforward tag map.
|
||||
type SpanRecorder interface {
|
||||
// Implementations must determine whether and where to store `span`.
|
||||
RecordSpan(span RawSpan)
|
||||
}
|
||||
|
||||
type LoggableSpanRecorder struct{}
|
||||
|
||||
// NewLoggableRecorder creates new LoggableSpanRecorder
|
||||
func NewLoggableRecorder() *LoggableSpanRecorder {
|
||||
return new(LoggableSpanRecorder)
|
||||
}
|
||||
|
||||
// Loggable Representation of a span, treated as an event log
|
||||
type LoggableSpan struct {
|
||||
TraceID uint64 `json:"TraceID"`
|
||||
SpanID uint64 `json:"SpanID"`
|
||||
ParentSpanID uint64 `json:"ParentSpanID"`
|
||||
Operation string `json:"Operation"`
|
||||
Start time.Time `json:"Start"`
|
||||
Duration time.Duration `json:"Duration"`
|
||||
Tags opentrace.Tags `json:"Tags"`
|
||||
Logs []SpanLog `json:"Logs"`
|
||||
}
|
||||
|
||||
type SpanLog struct {
|
||||
Timestamp time.Time `json:"Timestamp"`
|
||||
Field []SpanField `json:"Fields"`
|
||||
}
|
||||
|
||||
type SpanField struct {
|
||||
Key string `json:"Key"`
|
||||
Value string `json:"Value"`
|
||||
}
|
||||
|
||||
// RecordSpan implements the respective method of SpanRecorder.
|
||||
func (r *LoggableSpanRecorder) RecordSpan(span RawSpan) {
|
||||
// short circuit if theres nothing to write to
|
||||
if !writer.WriterGroup.Active() {
|
||||
return
|
||||
}
|
||||
|
||||
sl := make([]SpanLog, len(span.Logs))
|
||||
for i := range span.Logs {
|
||||
sl[i].Timestamp = span.Logs[i].Timestamp
|
||||
sf := make([]SpanField, len(span.Logs[i].Fields))
|
||||
sl[i].Field = sf
|
||||
for j := range span.Logs[i].Fields {
|
||||
sf[j].Key = span.Logs[i].Fields[j].Key()
|
||||
sf[j].Value = fmt.Sprint(span.Logs[i].Fields[j].Value())
|
||||
}
|
||||
}
|
||||
|
||||
tags := make(map[string]interface{}, len(span.Tags))
|
||||
for k, v := range span.Tags {
|
||||
switch vt := v.(type) {
|
||||
case bool, string, int, int8, int16, int32, int64, uint, uint8, uint16, uint64:
|
||||
tags[k] = v
|
||||
case []byte:
|
||||
base64.StdEncoding.EncodeToString(vt)
|
||||
default:
|
||||
tags[k] = fmt.Sprint(v)
|
||||
}
|
||||
}
|
||||
|
||||
spanlog := &LoggableSpan{
|
||||
TraceID: span.Context.TraceID,
|
||||
SpanID: span.Context.SpanID,
|
||||
ParentSpanID: span.ParentSpanID,
|
||||
Operation: span.Operation,
|
||||
Start: span.Start,
|
||||
Duration: span.Duration,
|
||||
Tags: tags,
|
||||
Logs: sl,
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
encoder := json.NewEncoder(&buf)
|
||||
encoder.SetEscapeHTML(false)
|
||||
err := encoder.Encode(spanlog)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "ERROR FORMATTING SPAN ENTRY: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
_, _ = writer.WriterGroup.Write(buf.Bytes())
|
||||
}
|
|
@ -1,274 +0,0 @@
|
|||
package loggabletracer
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
opentracing "github.com/opentracing/opentracing-go"
|
||||
"github.com/opentracing/opentracing-go/ext"
|
||||
"github.com/opentracing/opentracing-go/log"
|
||||
)
|
||||
|
||||
// Span provides access to the essential details of the span, for use
|
||||
// by loggabletracer consumers. These methods may only be called prior
|
||||
// to (*opentracing.Span).Finish().
|
||||
type Span interface {
|
||||
opentracing.Span
|
||||
|
||||
// Operation names the work done by this span instance
|
||||
Operation() string
|
||||
|
||||
// Start indicates when the span began
|
||||
Start() time.Time
|
||||
}
|
||||
|
||||
// Implements the `Span` interface. Created via LoggableTracer (see
|
||||
// `loggabletracer.New()`).
|
||||
type spanImpl struct {
|
||||
tracer *LoggableTracer
|
||||
event func(SpanEvent)
|
||||
sync.Mutex // protects the fields below
|
||||
raw RawSpan
|
||||
// The number of logs dropped because of MaxLogsPerSpan.
|
||||
numDroppedLogs int
|
||||
}
|
||||
|
||||
var spanPool = &sync.Pool{New: func() interface{} {
|
||||
return &spanImpl{}
|
||||
}}
|
||||
|
||||
func (s *spanImpl) reset() {
|
||||
s.tracer, s.event = nil, nil
|
||||
// Note: Would like to do the following, but then the consumer of RawSpan
|
||||
// (the recorder) needs to make sure that they're not holding on to the
|
||||
// baggage or logs when they return (i.e. they need to copy if they care):
|
||||
//
|
||||
// logs, baggage := s.raw.Logs[:0], s.raw.Baggage
|
||||
// for k := range baggage {
|
||||
// delete(baggage, k)
|
||||
// }
|
||||
// s.raw.Logs, s.raw.Baggage = logs, baggage
|
||||
//
|
||||
// That's likely too much to ask for. But there is some magic we should
|
||||
// be able to do with `runtime.SetFinalizer` to reclaim that memory into
|
||||
// a buffer pool when GC considers them unreachable, which should ease
|
||||
// some of the load. Hard to say how quickly that would be in practice
|
||||
// though.
|
||||
s.raw = RawSpan{
|
||||
Context: SpanContext{},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *spanImpl) SetOperationName(operationName string) opentracing.Span {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
s.raw.Operation = operationName
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *spanImpl) trim() bool {
|
||||
return !s.raw.Context.Sampled && s.tracer.options.TrimUnsampledSpans
|
||||
}
|
||||
|
||||
func (s *spanImpl) SetTag(key string, value interface{}) opentracing.Span {
|
||||
defer s.onTag(key, value)
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
if key == string(ext.SamplingPriority) {
|
||||
if v, ok := value.(uint16); ok {
|
||||
s.raw.Context.Sampled = v != 0
|
||||
return s
|
||||
}
|
||||
}
|
||||
if s.trim() {
|
||||
return s
|
||||
}
|
||||
|
||||
if s.raw.Tags == nil {
|
||||
s.raw.Tags = opentracing.Tags{}
|
||||
}
|
||||
s.raw.Tags[key] = value
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *spanImpl) LogKV(keyValues ...interface{}) {
|
||||
fields, err := log.InterleavedKVToFields(keyValues...)
|
||||
if err != nil {
|
||||
s.LogFields(log.Error(err), log.String("function", "LogKV"))
|
||||
return
|
||||
}
|
||||
s.LogFields(fields...)
|
||||
}
|
||||
|
||||
func (s *spanImpl) appendLog(lr opentracing.LogRecord) {
|
||||
maxLogs := s.tracer.options.MaxLogsPerSpan
|
||||
if maxLogs == 0 || len(s.raw.Logs) < maxLogs {
|
||||
s.raw.Logs = append(s.raw.Logs, lr)
|
||||
return
|
||||
}
|
||||
|
||||
// We have too many logs. We don't touch the first numOld logs; we treat the
|
||||
// rest as a circular buffer and overwrite the oldest log among those.
|
||||
numOld := (maxLogs - 1) / 2
|
||||
numNew := maxLogs - numOld
|
||||
s.raw.Logs[numOld+s.numDroppedLogs%numNew] = lr
|
||||
s.numDroppedLogs++
|
||||
}
|
||||
|
||||
func (s *spanImpl) LogFields(fields ...log.Field) {
|
||||
lr := opentracing.LogRecord{
|
||||
Fields: fields,
|
||||
}
|
||||
defer s.onLogFields(lr)
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
if s.trim() || s.tracer.options.DropAllLogs {
|
||||
return
|
||||
}
|
||||
if lr.Timestamp.IsZero() {
|
||||
lr.Timestamp = time.Now()
|
||||
}
|
||||
s.appendLog(lr)
|
||||
}
|
||||
|
||||
func (s *spanImpl) LogEvent(event string) {
|
||||
s.Log(opentracing.LogData{
|
||||
Event: event,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *spanImpl) LogEventWithPayload(event string, payload interface{}) {
|
||||
s.Log(opentracing.LogData{
|
||||
Event: event,
|
||||
Payload: payload,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *spanImpl) Log(ld opentracing.LogData) {
|
||||
defer s.onLog(ld)
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
if s.trim() || s.tracer.options.DropAllLogs {
|
||||
return
|
||||
}
|
||||
|
||||
if ld.Timestamp.IsZero() {
|
||||
ld.Timestamp = time.Now()
|
||||
}
|
||||
|
||||
s.appendLog(ld.ToLogRecord())
|
||||
}
|
||||
|
||||
func (s *spanImpl) Finish() {
|
||||
s.FinishWithOptions(opentracing.FinishOptions{})
|
||||
}
|
||||
|
||||
// rotateLogBuffer rotates the records in the buffer: records 0 to pos-1 move at
|
||||
// the end (i.e. pos circular left shifts).
|
||||
func rotateLogBuffer(buf []opentracing.LogRecord, pos int) {
|
||||
// This algorithm is described in:
|
||||
// http://www.cplusplus.com/reference/algorithm/rotate
|
||||
for first, middle, next := 0, pos, pos; first != middle; {
|
||||
buf[first], buf[next] = buf[next], buf[first]
|
||||
first++
|
||||
next++
|
||||
if next == len(buf) {
|
||||
next = middle
|
||||
} else if first == middle {
|
||||
middle = next
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *spanImpl) FinishWithOptions(opts opentracing.FinishOptions) {
|
||||
finishTime := opts.FinishTime
|
||||
if finishTime.IsZero() {
|
||||
finishTime = time.Now()
|
||||
}
|
||||
duration := finishTime.Sub(s.raw.Start)
|
||||
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
for _, lr := range opts.LogRecords {
|
||||
s.appendLog(lr)
|
||||
}
|
||||
for _, ld := range opts.BulkLogData {
|
||||
s.appendLog(ld.ToLogRecord())
|
||||
}
|
||||
|
||||
if s.numDroppedLogs > 0 {
|
||||
// We dropped some log events, which means that we used part of Logs as a
|
||||
// circular buffer (see appendLog). De-circularize it.
|
||||
numOld := (len(s.raw.Logs) - 1) / 2
|
||||
numNew := len(s.raw.Logs) - numOld
|
||||
rotateLogBuffer(s.raw.Logs[numOld:], s.numDroppedLogs%numNew)
|
||||
|
||||
// Replace the log in the middle (the oldest "new" log) with information
|
||||
// about the dropped logs. This means that we are effectively dropping one
|
||||
// more "new" log.
|
||||
numDropped := s.numDroppedLogs + 1
|
||||
s.raw.Logs[numOld] = opentracing.LogRecord{
|
||||
// Keep the timestamp of the last dropped event.
|
||||
Timestamp: s.raw.Logs[numOld].Timestamp,
|
||||
Fields: []log.Field{
|
||||
log.String("event", "dropped Span logs"),
|
||||
log.Int("dropped_log_count", numDropped),
|
||||
log.String("component", "loggabletracer"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
s.raw.Duration = duration
|
||||
|
||||
s.onFinish(s.raw)
|
||||
s.tracer.options.Recorder.RecordSpan(s.raw)
|
||||
|
||||
// Last chance to get options before the span is possibly reset.
|
||||
poolEnabled := s.tracer.options.EnableSpanPool
|
||||
if s.tracer.options.DebugAssertUseAfterFinish {
|
||||
// This makes it much more likely to catch a panic on any subsequent
|
||||
// operation since s.tracer is accessed on every call to `Lock`.
|
||||
// We don't call `reset()` here to preserve the logs in the Span
|
||||
// which are printed when the assertion triggers.
|
||||
s.tracer = nil
|
||||
}
|
||||
|
||||
if poolEnabled {
|
||||
spanPool.Put(s)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *spanImpl) Tracer() opentracing.Tracer {
|
||||
return s.tracer
|
||||
}
|
||||
|
||||
func (s *spanImpl) Context() opentracing.SpanContext {
|
||||
return s.raw.Context
|
||||
}
|
||||
|
||||
func (s *spanImpl) SetBaggageItem(key, val string) opentracing.Span {
|
||||
s.onBaggage(key, val)
|
||||
if s.trim() {
|
||||
return s
|
||||
}
|
||||
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
s.raw.Context = s.raw.Context.WithBaggageItem(key, val)
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *spanImpl) BaggageItem(key string) string {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
return s.raw.Context.Baggage[key]
|
||||
}
|
||||
|
||||
func (s *spanImpl) Operation() string {
|
||||
return s.raw.Operation
|
||||
}
|
||||
|
||||
func (s *spanImpl) Start() time.Time {
|
||||
return s.raw.Start
|
||||
}
|
|
@ -1,280 +0,0 @@
|
|||
package loggabletracer
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
writer "github.com/ipfs/go-log/writer"
|
||||
opentracing "github.com/opentracing/opentracing-go"
|
||||
)
|
||||
|
||||
// Tracer extends the opentracing.Tracer interface with methods to
|
||||
// probe implementation state, for use by loggabletracer consumers.
|
||||
type Tracer interface {
|
||||
opentracing.Tracer
|
||||
|
||||
// Options gets the Options used in New() or NewWithOptions().
|
||||
Options() Options
|
||||
}
|
||||
|
||||
// Options allows creating a customized Tracer via NewWithOptions. The object
|
||||
// must not be updated when there is an active tracer using it.
|
||||
type Options struct {
|
||||
// ShouldSample is a function which is called when creating a new Span and
|
||||
// determines whether that Span is sampled. The randomized TraceID is supplied
|
||||
// to allow deterministic sampling decisions to be made across different nodes.
|
||||
// For example,
|
||||
//
|
||||
// func(traceID uint64) { return traceID % 64 == 0 }
|
||||
//
|
||||
// samples every 64th trace on average.
|
||||
ShouldSample func(traceID uint64) bool
|
||||
// TrimUnsampledSpans turns potentially expensive operations on unsampled
|
||||
// Spans into no-ops. More precisely, tags and log events are silently
|
||||
// discarded. If NewSpanEventListener is set, the callbacks will still fire.
|
||||
TrimUnsampledSpans bool
|
||||
// Recorder receives Spans which have been finished.
|
||||
Recorder SpanRecorder
|
||||
// NewSpanEventListener can be used to enhance the tracer by effectively
|
||||
// attaching external code to trace events. See NetTraceIntegrator for a
|
||||
// practical example, and event.go for the list of possible events.
|
||||
NewSpanEventListener func() func(SpanEvent)
|
||||
// DropAllLogs turns log events on all Spans into no-ops.
|
||||
// If NewSpanEventListener is set, the callbacks will still fire.
|
||||
DropAllLogs bool
|
||||
// MaxLogsPerSpan limits the number of Logs in a span (if set to a nonzero
|
||||
// value). If a span has more logs than this value, logs are dropped as
|
||||
// necessary (and replaced with a log describing how many were dropped).
|
||||
//
|
||||
// About half of the MaxLogPerSpan logs kept are the oldest logs, and about
|
||||
// half are the newest logs.
|
||||
//
|
||||
// If NewSpanEventListener is set, the callbacks will still fire for all log
|
||||
// events. This value is ignored if DropAllLogs is true.
|
||||
MaxLogsPerSpan int
|
||||
// DebugAssertSingleGoroutine internally records the ID of the goroutine
|
||||
// creating each Span and verifies that no operation is carried out on
|
||||
// it on a different goroutine.
|
||||
// Provided strictly for development purposes.
|
||||
// Passing Spans between goroutine without proper synchronization often
|
||||
// results in use-after-Finish() errors. For a simple example, consider the
|
||||
// following pseudocode:
|
||||
//
|
||||
// func (s *Server) Handle(req http.Request) error {
|
||||
// sp := s.StartSpan("server")
|
||||
// defer sp.Finish()
|
||||
// wait := s.queueProcessing(opentracing.ContextWithSpan(context.Background(), sp), req)
|
||||
// select {
|
||||
// case resp := <-wait:
|
||||
// return resp.Error
|
||||
// case <-time.After(10*time.Second):
|
||||
// sp.LogEvent("timed out waiting for processing")
|
||||
// return ErrTimedOut
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// This looks reasonable at first, but a request which spends more than ten
|
||||
// seconds in the queue is abandoned by the main goroutine and its trace
|
||||
// finished, leading to use-after-finish when the request is finally
|
||||
// processed. Note also that even joining on to a finished Span via
|
||||
// StartSpanWithOptions constitutes an illegal operation.
|
||||
//
|
||||
// Code bases which do not require (or decide they do not want) Spans to
|
||||
// be passed across goroutine boundaries can run with this flag enabled in
|
||||
// tests to increase their chances of spotting wrong-doers.
|
||||
DebugAssertSingleGoroutine bool
|
||||
// DebugAssertUseAfterFinish is provided strictly for development purposes.
|
||||
// When set, it attempts to exacerbate issues emanating from use of Spans
|
||||
// after calling Finish by running additional assertions.
|
||||
DebugAssertUseAfterFinish bool
|
||||
// EnableSpanPool enables the use of a pool, so that the tracer reuses spans
|
||||
// after Finish has been called on it. Adds a slight performance gain as it
|
||||
// reduces allocations. However, if you have any use-after-finish race
|
||||
// conditions the code may panic.
|
||||
EnableSpanPool bool
|
||||
}
|
||||
|
||||
// DefaultOptions returns an Options object with a 1 in 64 sampling rate and
|
||||
// all options disabled. A Recorder needs to be set manually before using the
|
||||
// returned object with a Tracer.
|
||||
func DefaultOptions() Options {
|
||||
return Options{
|
||||
ShouldSample: func(traceID uint64) bool { return traceID%64 == 0 },
|
||||
MaxLogsPerSpan: 100,
|
||||
}
|
||||
}
|
||||
|
||||
// NewWithOptions creates a customized Tracer.
|
||||
func NewWithOptions(opts Options) opentracing.Tracer {
|
||||
rval := &LoggableTracer{options: opts}
|
||||
rval.accessorPropagator = &accessorPropagator{rval}
|
||||
return rval
|
||||
}
|
||||
|
||||
// New creates and returns a standard Tracer which defers completed Spans to
|
||||
// `recorder`.
|
||||
// Spans created by this Tracer support the ext.SamplingPriority tag: Setting
|
||||
// ext.SamplingPriority causes the Span to be Sampled from that point on.
|
||||
func New(recorder SpanRecorder) opentracing.Tracer {
|
||||
opts := DefaultOptions()
|
||||
opts.Recorder = recorder
|
||||
return NewWithOptions(opts)
|
||||
}
|
||||
|
||||
// Implements the `Tracer` interface.
|
||||
type LoggableTracer struct {
|
||||
options Options
|
||||
textPropagator *textMapPropagator
|
||||
binaryPropagator *binaryPropagator
|
||||
accessorPropagator *accessorPropagator
|
||||
}
|
||||
|
||||
func (t *LoggableTracer) StartSpan(
|
||||
operationName string,
|
||||
opts ...opentracing.StartSpanOption,
|
||||
) opentracing.Span {
|
||||
|
||||
if !writer.WriterGroup.Active() {
|
||||
return opentracing.NoopTracer.StartSpan(opentracing.NoopTracer{}, operationName)
|
||||
}
|
||||
|
||||
sso := opentracing.StartSpanOptions{}
|
||||
for _, o := range opts {
|
||||
o.Apply(&sso)
|
||||
}
|
||||
return t.StartSpanWithOptions(operationName, sso)
|
||||
}
|
||||
|
||||
func (t *LoggableTracer) getSpan() *spanImpl {
|
||||
if t.options.EnableSpanPool {
|
||||
sp := spanPool.Get().(*spanImpl)
|
||||
sp.reset()
|
||||
return sp
|
||||
}
|
||||
return &spanImpl{}
|
||||
}
|
||||
|
||||
func (t *LoggableTracer) StartSpanWithOptions(
|
||||
operationName string,
|
||||
opts opentracing.StartSpanOptions,
|
||||
) opentracing.Span {
|
||||
if !writer.WriterGroup.Active() {
|
||||
return opentracing.NoopTracer.StartSpan(opentracing.NoopTracer{}, operationName)
|
||||
}
|
||||
// Start time.
|
||||
startTime := opts.StartTime
|
||||
if startTime.IsZero() {
|
||||
startTime = time.Now()
|
||||
}
|
||||
|
||||
// Tags.
|
||||
tags := opts.Tags
|
||||
|
||||
// Build the new span. This is the only allocation: We'll return this as
|
||||
// an opentracing.Span.
|
||||
sp := t.getSpan()
|
||||
// Look for a parent in the list of References.
|
||||
//
|
||||
// TODO: would be nice if loggabletracer did something with all
|
||||
// References, not just the first one.
|
||||
ReferencesLoop:
|
||||
for _, ref := range opts.References {
|
||||
switch ref.Type {
|
||||
case opentracing.ChildOfRef,
|
||||
opentracing.FollowsFromRef:
|
||||
|
||||
refCtx, ok := ref.ReferencedContext.(SpanContext)
|
||||
if !ok {
|
||||
// Could be a noopSpanContext
|
||||
// Ignore that parent.
|
||||
continue
|
||||
}
|
||||
sp.raw.Context.TraceID = refCtx.TraceID
|
||||
sp.raw.Context.SpanID = randomID()
|
||||
sp.raw.Context.Sampled = refCtx.Sampled
|
||||
sp.raw.ParentSpanID = refCtx.SpanID
|
||||
|
||||
if l := len(refCtx.Baggage); l > 0 {
|
||||
sp.raw.Context.Baggage = make(map[string]string, l)
|
||||
for k, v := range refCtx.Baggage {
|
||||
sp.raw.Context.Baggage[k] = v
|
||||
}
|
||||
}
|
||||
break ReferencesLoop
|
||||
}
|
||||
}
|
||||
if sp.raw.Context.TraceID == 0 {
|
||||
// No parent Span found; allocate new trace and span ids and determine
|
||||
// the Sampled status.
|
||||
sp.raw.Context.TraceID, sp.raw.Context.SpanID = randomID2()
|
||||
sp.raw.Context.Sampled = t.options.ShouldSample(sp.raw.Context.TraceID)
|
||||
}
|
||||
|
||||
return t.startSpanInternal(
|
||||
sp,
|
||||
operationName,
|
||||
startTime,
|
||||
tags,
|
||||
)
|
||||
}
|
||||
|
||||
func (t *LoggableTracer) startSpanInternal(
|
||||
sp *spanImpl,
|
||||
operationName string,
|
||||
startTime time.Time,
|
||||
tags opentracing.Tags,
|
||||
) opentracing.Span {
|
||||
sp.tracer = t
|
||||
if t.options.NewSpanEventListener != nil {
|
||||
sp.event = t.options.NewSpanEventListener()
|
||||
}
|
||||
sp.raw.Operation = operationName
|
||||
sp.raw.Start = startTime
|
||||
sp.raw.Duration = -1
|
||||
sp.raw.Tags = tags
|
||||
if t.options.DebugAssertSingleGoroutine {
|
||||
sp.SetTag(debugGoroutineIDTag, curGoroutineID())
|
||||
}
|
||||
defer sp.onCreate(operationName)
|
||||
return sp
|
||||
}
|
||||
|
||||
type delegatorType struct{}
|
||||
|
||||
// Delegator is the format to use for DelegatingCarrier.
|
||||
var Delegator delegatorType
|
||||
|
||||
func (t *LoggableTracer) Inject(sc opentracing.SpanContext, format interface{}, carrier interface{}) error {
|
||||
if !writer.WriterGroup.Active() {
|
||||
return opentracing.NoopTracer.Inject(opentracing.NoopTracer{}, sc, format, carrier)
|
||||
}
|
||||
switch format {
|
||||
case opentracing.TextMap, opentracing.HTTPHeaders:
|
||||
return t.textPropagator.Inject(sc, carrier)
|
||||
case opentracing.Binary:
|
||||
return t.binaryPropagator.Inject(sc, carrier)
|
||||
}
|
||||
if _, ok := format.(delegatorType); ok {
|
||||
return t.accessorPropagator.Inject(sc, carrier)
|
||||
}
|
||||
return opentracing.ErrUnsupportedFormat
|
||||
}
|
||||
|
||||
func (t *LoggableTracer) Extract(format interface{}, carrier interface{}) (opentracing.SpanContext, error) {
|
||||
if !writer.WriterGroup.Active() {
|
||||
return opentracing.NoopTracer.Extract(opentracing.NoopTracer{}, format, carrier)
|
||||
}
|
||||
switch format {
|
||||
case opentracing.TextMap, opentracing.HTTPHeaders:
|
||||
return t.textPropagator.Extract(carrier)
|
||||
case opentracing.Binary:
|
||||
return t.binaryPropagator.Extract(carrier)
|
||||
}
|
||||
if _, ok := format.(delegatorType); ok {
|
||||
return t.accessorPropagator.Extract(carrier)
|
||||
}
|
||||
return nil, opentracing.ErrUnsupportedFormat
|
||||
}
|
||||
|
||||
func (t *LoggableTracer) Options() Options {
|
||||
return t.options
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
package loggabletracer
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
seededIDGen = rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
// The golang rand generators are *not* intrinsically thread-safe.
|
||||
seededIDLock sync.Mutex
|
||||
)
|
||||
|
||||
func randomID() uint64 {
|
||||
seededIDLock.Lock()
|
||||
defer seededIDLock.Unlock()
|
||||
return uint64(seededIDGen.Int63())
|
||||
}
|
||||
|
||||
func randomID2() (uint64, uint64) {
|
||||
seededIDLock.Lock()
|
||||
defer seededIDLock.Unlock()
|
||||
return uint64(seededIDGen.Int63()), uint64(seededIDGen.Int63())
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
pbgos := $(patsubst %.proto,%.pb.go,$(wildcard *.proto))
|
||||
|
||||
all: $(pbgos)
|
||||
|
||||
%.pb.go: %.proto
|
||||
protoc --gogofaster_out=. --proto_path=$(GOPATH)/src:. $<
|
|
@ -1,40 +0,0 @@
|
|||
package wire
|
||||
|
||||
// ProtobufCarrier is a DelegatingCarrier that uses protocol buffers as the
|
||||
// the underlying datastructure. The reason for implementing DelagatingCarrier
|
||||
// is to allow for end users to serialize the underlying protocol buffers using
|
||||
// jsonpb or any other serialization forms they want.
|
||||
type ProtobufCarrier TracerState
|
||||
|
||||
// SetState set's the tracer state.
|
||||
func (p *ProtobufCarrier) SetState(traceID, spanID uint64, sampled bool) {
|
||||
p.TraceId = traceID
|
||||
p.SpanId = spanID
|
||||
p.Sampled = sampled
|
||||
}
|
||||
|
||||
// State returns the tracer state.
|
||||
func (p *ProtobufCarrier) State() (traceID, spanID uint64, sampled bool) {
|
||||
traceID = p.TraceId
|
||||
spanID = p.SpanId
|
||||
sampled = p.Sampled
|
||||
return traceID, spanID, sampled
|
||||
}
|
||||
|
||||
// SetBaggageItem sets a baggage item.
|
||||
func (p *ProtobufCarrier) SetBaggageItem(key, value string) {
|
||||
if p.BaggageItems == nil {
|
||||
p.BaggageItems = map[string]string{key: value}
|
||||
return
|
||||
}
|
||||
|
||||
p.BaggageItems[key] = value
|
||||
}
|
||||
|
||||
// GetBaggage iterates over each baggage item and executes the callback with
|
||||
// the key:value pair.
|
||||
func (p *ProtobufCarrier) GetBaggage(f func(k, v string)) {
|
||||
for k, v := range p.BaggageItems {
|
||||
f(k, v)
|
||||
}
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
package wire
|
||||
|
||||
//go:generate protoc --gogofaster_out=$GOPATH/src/github.com/ipfs/go-log/tracer/wire wire.proto
|
||||
|
||||
// Run `go get github.com/gogo/protobuf/protoc-gen-gogofaster` to install the
|
||||
// gogofaster generator binary.
|
|
@ -1,528 +0,0 @@
|
|||
// Code generated by protoc-gen-gogo. DO NOT EDIT.
|
||||
// source: wire.proto
|
||||
|
||||
package wire
|
||||
|
||||
import (
|
||||
encoding_binary "encoding/binary"
|
||||
fmt "fmt"
|
||||
proto "github.com/gogo/protobuf/proto"
|
||||
io "io"
|
||||
math "math"
|
||||
math_bits "math/bits"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
type TracerState struct {
|
||||
TraceId uint64 `protobuf:"fixed64,1,opt,name=trace_id,json=traceId,proto3" json:"trace_id,omitempty"`
|
||||
SpanId uint64 `protobuf:"fixed64,2,opt,name=span_id,json=spanId,proto3" json:"span_id,omitempty"`
|
||||
Sampled bool `protobuf:"varint,3,opt,name=sampled,proto3" json:"sampled,omitempty"`
|
||||
BaggageItems map[string]string `protobuf:"bytes,4,rep,name=baggage_items,json=baggageItems,proto3" json:"baggage_items,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||
}
|
||||
|
||||
func (m *TracerState) Reset() { *m = TracerState{} }
|
||||
func (m *TracerState) String() string { return proto.CompactTextString(m) }
|
||||
func (*TracerState) ProtoMessage() {}
|
||||
func (*TracerState) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_f2dcdddcdf68d8e0, []int{0}
|
||||
}
|
||||
func (m *TracerState) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
}
|
||||
func (m *TracerState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
if deterministic {
|
||||
return xxx_messageInfo_TracerState.Marshal(b, m, deterministic)
|
||||
} else {
|
||||
b = b[:cap(b)]
|
||||
n, err := m.MarshalToSizedBuffer(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b[:n], nil
|
||||
}
|
||||
}
|
||||
func (m *TracerState) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_TracerState.Merge(m, src)
|
||||
}
|
||||
func (m *TracerState) XXX_Size() int {
|
||||
return m.Size()
|
||||
}
|
||||
func (m *TracerState) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_TracerState.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_TracerState proto.InternalMessageInfo
|
||||
|
||||
func (m *TracerState) GetTraceId() uint64 {
|
||||
if m != nil {
|
||||
return m.TraceId
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *TracerState) GetSpanId() uint64 {
|
||||
if m != nil {
|
||||
return m.SpanId
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *TracerState) GetSampled() bool {
|
||||
if m != nil {
|
||||
return m.Sampled
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *TracerState) GetBaggageItems() map[string]string {
|
||||
if m != nil {
|
||||
return m.BaggageItems
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*TracerState)(nil), "loggabletracer.wire.TracerState")
|
||||
proto.RegisterMapType((map[string]string)(nil), "loggabletracer.wire.TracerState.BaggageItemsEntry")
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("wire.proto", fileDescriptor_f2dcdddcdf68d8e0) }
|
||||
|
||||
var fileDescriptor_f2dcdddcdf68d8e0 = []byte{
|
||||
// 250 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x2a, 0xcf, 0x2c, 0x4a,
|
||||
0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0xce, 0xc9, 0x4f, 0x4f, 0x4f, 0x4c, 0xca, 0x49,
|
||||
0x2d, 0x29, 0x4a, 0x4c, 0x4e, 0x2d, 0xd2, 0x03, 0x49, 0x29, 0x7d, 0x65, 0xe4, 0xe2, 0x0e, 0x01,
|
||||
0xf3, 0x83, 0x4b, 0x12, 0x4b, 0x52, 0x85, 0x24, 0xb9, 0x38, 0xc0, 0xd2, 0xf1, 0x99, 0x29, 0x12,
|
||||
0x8c, 0x0a, 0x8c, 0x1a, 0x6c, 0x41, 0xec, 0x60, 0xbe, 0x67, 0x8a, 0x90, 0x38, 0x17, 0x7b, 0x71,
|
||||
0x41, 0x62, 0x1e, 0x48, 0x86, 0x09, 0x2c, 0xc3, 0x06, 0xe2, 0x7a, 0xa6, 0x08, 0x49, 0x70, 0xb1,
|
||||
0x17, 0x27, 0xe6, 0x16, 0xe4, 0xa4, 0xa6, 0x48, 0x30, 0x2b, 0x30, 0x6a, 0x70, 0x04, 0xc1, 0xb8,
|
||||
0x42, 0xe1, 0x5c, 0xbc, 0x49, 0x89, 0xe9, 0xe9, 0x89, 0xe9, 0xa9, 0xf1, 0x99, 0x25, 0xa9, 0xb9,
|
||||
0xc5, 0x12, 0x2c, 0x0a, 0xcc, 0x1a, 0xdc, 0x46, 0x46, 0x7a, 0x58, 0x9c, 0xa2, 0x87, 0xe4, 0x0c,
|
||||
0x3d, 0x27, 0x88, 0x2e, 0x4f, 0x90, 0x26, 0xd7, 0xbc, 0x92, 0xa2, 0xca, 0x20, 0x9e, 0x24, 0x24,
|
||||
0x21, 0x29, 0x7b, 0x2e, 0x41, 0x0c, 0x25, 0x42, 0x02, 0x5c, 0xcc, 0xd9, 0xa9, 0x95, 0x60, 0x67,
|
||||
0x73, 0x06, 0x81, 0x98, 0x42, 0x22, 0x5c, 0xac, 0x65, 0x89, 0x39, 0xa5, 0xa9, 0x60, 0x07, 0x73,
|
||||
0x06, 0x41, 0x38, 0x56, 0x4c, 0x16, 0x8c, 0x4e, 0x72, 0x27, 0x1e, 0xc9, 0x31, 0x5e, 0x78, 0x24,
|
||||
0xc7, 0xf8, 0xe0, 0x91, 0x1c, 0xe3, 0x84, 0xc7, 0x72, 0x0c, 0x17, 0x1e, 0xcb, 0x31, 0xdc, 0x78,
|
||||
0x2c, 0xc7, 0x10, 0xc5, 0x02, 0x72, 0x4c, 0x12, 0x1b, 0x38, 0xcc, 0x8c, 0x01, 0x01, 0x00, 0x00,
|
||||
0xff, 0xff, 0xe4, 0x48, 0xf4, 0xf8, 0x41, 0x01, 0x00, 0x00,
|
||||
}
|
||||
|
||||
func (m *TracerState) Marshal() (dAtA []byte, err error) {
|
||||
size := m.Size()
|
||||
dAtA = make([]byte, size)
|
||||
n, err := m.MarshalToSizedBuffer(dAtA[:size])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dAtA[:n], nil
|
||||
}
|
||||
|
||||
func (m *TracerState) MarshalTo(dAtA []byte) (int, error) {
|
||||
size := m.Size()
|
||||
return m.MarshalToSizedBuffer(dAtA[:size])
|
||||
}
|
||||
|
||||
func (m *TracerState) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
i := len(dAtA)
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if len(m.BaggageItems) > 0 {
|
||||
for k := range m.BaggageItems {
|
||||
v := m.BaggageItems[k]
|
||||
baseI := i
|
||||
i -= len(v)
|
||||
copy(dAtA[i:], v)
|
||||
i = encodeVarintWire(dAtA, i, uint64(len(v)))
|
||||
i--
|
||||
dAtA[i] = 0x12
|
||||
i -= len(k)
|
||||
copy(dAtA[i:], k)
|
||||
i = encodeVarintWire(dAtA, i, uint64(len(k)))
|
||||
i--
|
||||
dAtA[i] = 0xa
|
||||
i = encodeVarintWire(dAtA, i, uint64(baseI-i))
|
||||
i--
|
||||
dAtA[i] = 0x22
|
||||
}
|
||||
}
|
||||
if m.Sampled {
|
||||
i--
|
||||
if m.Sampled {
|
||||
dAtA[i] = 1
|
||||
} else {
|
||||
dAtA[i] = 0
|
||||
}
|
||||
i--
|
||||
dAtA[i] = 0x18
|
||||
}
|
||||
if m.SpanId != 0 {
|
||||
i -= 8
|
||||
encoding_binary.LittleEndian.PutUint64(dAtA[i:], uint64(m.SpanId))
|
||||
i--
|
||||
dAtA[i] = 0x11
|
||||
}
|
||||
if m.TraceId != 0 {
|
||||
i -= 8
|
||||
encoding_binary.LittleEndian.PutUint64(dAtA[i:], uint64(m.TraceId))
|
||||
i--
|
||||
dAtA[i] = 0x9
|
||||
}
|
||||
return len(dAtA) - i, nil
|
||||
}
|
||||
|
||||
func encodeVarintWire(dAtA []byte, offset int, v uint64) int {
|
||||
offset -= sovWire(v)
|
||||
base := offset
|
||||
for v >= 1<<7 {
|
||||
dAtA[offset] = uint8(v&0x7f | 0x80)
|
||||
v >>= 7
|
||||
offset++
|
||||
}
|
||||
dAtA[offset] = uint8(v)
|
||||
return base
|
||||
}
|
||||
func (m *TracerState) Size() (n int) {
|
||||
if m == nil {
|
||||
return 0
|
||||
}
|
||||
var l int
|
||||
_ = l
|
||||
if m.TraceId != 0 {
|
||||
n += 9
|
||||
}
|
||||
if m.SpanId != 0 {
|
||||
n += 9
|
||||
}
|
||||
if m.Sampled {
|
||||
n += 2
|
||||
}
|
||||
if len(m.BaggageItems) > 0 {
|
||||
for k, v := range m.BaggageItems {
|
||||
_ = k
|
||||
_ = v
|
||||
mapEntrySize := 1 + len(k) + sovWire(uint64(len(k))) + 1 + len(v) + sovWire(uint64(len(v)))
|
||||
n += mapEntrySize + 1 + sovWire(uint64(mapEntrySize))
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func sovWire(x uint64) (n int) {
|
||||
return (math_bits.Len64(x|1) + 6) / 7
|
||||
}
|
||||
func sozWire(x uint64) (n int) {
|
||||
return sovWire(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
||||
}
|
||||
func (m *TracerState) Unmarshal(dAtA []byte) error {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
for iNdEx < l {
|
||||
preIndex := iNdEx
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowWire
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
fieldNum := int32(wire >> 3)
|
||||
wireType := int(wire & 0x7)
|
||||
if wireType == 4 {
|
||||
return fmt.Errorf("proto: TracerState: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: TracerState: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||
}
|
||||
switch fieldNum {
|
||||
case 1:
|
||||
if wireType != 1 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field TraceId", wireType)
|
||||
}
|
||||
m.TraceId = 0
|
||||
if (iNdEx + 8) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.TraceId = uint64(encoding_binary.LittleEndian.Uint64(dAtA[iNdEx:]))
|
||||
iNdEx += 8
|
||||
case 2:
|
||||
if wireType != 1 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field SpanId", wireType)
|
||||
}
|
||||
m.SpanId = 0
|
||||
if (iNdEx + 8) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.SpanId = uint64(encoding_binary.LittleEndian.Uint64(dAtA[iNdEx:]))
|
||||
iNdEx += 8
|
||||
case 3:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Sampled", wireType)
|
||||
}
|
||||
var v int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowWire
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
v |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
m.Sampled = bool(v != 0)
|
||||
case 4:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field BaggageItems", wireType)
|
||||
}
|
||||
var msglen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowWire
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
msglen |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if msglen < 0 {
|
||||
return ErrInvalidLengthWire
|
||||
}
|
||||
postIndex := iNdEx + msglen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthWire
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
if m.BaggageItems == nil {
|
||||
m.BaggageItems = make(map[string]string)
|
||||
}
|
||||
var mapkey string
|
||||
var mapvalue string
|
||||
for iNdEx < postIndex {
|
||||
entryPreIndex := iNdEx
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowWire
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
fieldNum := int32(wire >> 3)
|
||||
if fieldNum == 1 {
|
||||
var stringLenmapkey uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowWire
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
stringLenmapkey |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
intStringLenmapkey := int(stringLenmapkey)
|
||||
if intStringLenmapkey < 0 {
|
||||
return ErrInvalidLengthWire
|
||||
}
|
||||
postStringIndexmapkey := iNdEx + intStringLenmapkey
|
||||
if postStringIndexmapkey < 0 {
|
||||
return ErrInvalidLengthWire
|
||||
}
|
||||
if postStringIndexmapkey > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
mapkey = string(dAtA[iNdEx:postStringIndexmapkey])
|
||||
iNdEx = postStringIndexmapkey
|
||||
} else if fieldNum == 2 {
|
||||
var stringLenmapvalue uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowWire
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
stringLenmapvalue |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
intStringLenmapvalue := int(stringLenmapvalue)
|
||||
if intStringLenmapvalue < 0 {
|
||||
return ErrInvalidLengthWire
|
||||
}
|
||||
postStringIndexmapvalue := iNdEx + intStringLenmapvalue
|
||||
if postStringIndexmapvalue < 0 {
|
||||
return ErrInvalidLengthWire
|
||||
}
|
||||
if postStringIndexmapvalue > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue])
|
||||
iNdEx = postStringIndexmapvalue
|
||||
} else {
|
||||
iNdEx = entryPreIndex
|
||||
skippy, err := skipWire(dAtA[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
return ErrInvalidLengthWire
|
||||
}
|
||||
if (iNdEx + skippy) > postIndex {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
m.BaggageItems[mapkey] = mapvalue
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipWire(dAtA[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
return ErrInvalidLengthWire
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func skipWire(dAtA []byte) (n int, err error) {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
depth := 0
|
||||
for iNdEx < l {
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowWire
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
wireType := int(wire & 0x7)
|
||||
switch wireType {
|
||||
case 0:
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowWire
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx++
|
||||
if dAtA[iNdEx-1] < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
case 1:
|
||||
iNdEx += 8
|
||||
case 2:
|
||||
var length int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowWire
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
length |= (int(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if length < 0 {
|
||||
return 0, ErrInvalidLengthWire
|
||||
}
|
||||
iNdEx += length
|
||||
case 3:
|
||||
depth++
|
||||
case 4:
|
||||
if depth == 0 {
|
||||
return 0, ErrUnexpectedEndOfGroupWire
|
||||
}
|
||||
depth--
|
||||
case 5:
|
||||
iNdEx += 4
|
||||
default:
|
||||
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
|
||||
}
|
||||
if iNdEx < 0 {
|
||||
return 0, ErrInvalidLengthWire
|
||||
}
|
||||
if depth == 0 {
|
||||
return iNdEx, nil
|
||||
}
|
||||
}
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
var (
|
||||
ErrInvalidLengthWire = fmt.Errorf("proto: negative length found during unmarshaling")
|
||||
ErrIntOverflowWire = fmt.Errorf("proto: integer overflow")
|
||||
ErrUnexpectedEndOfGroupWire = fmt.Errorf("proto: unexpected end of group")
|
||||
)
|
|
@ -1,10 +0,0 @@
|
|||
syntax = "proto3";
|
||||
package loggabletracer.wire;
|
||||
option go_package = "wire";
|
||||
|
||||
message TracerState {
|
||||
fixed64 trace_id = 1;
|
||||
fixed64 span_id = 2;
|
||||
bool sampled = 3;
|
||||
map<string, string> baggage_items = 4;
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
package log
|
||||
|
||||
// WriterGroup is the global writer group for logs to output to
|
||||
var WriterGroup = NewMirrorWriter()
|
|
@ -1,251 +0,0 @@
|
|||
package log
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// MaxWriterBuffer specifies how big the writer buffer can get before
|
||||
// killing the writer.
|
||||
var MaxWriterBuffer = 512 * 1024
|
||||
|
||||
// MirrorWriter implements a WriteCloser which syncs incoming bytes to multiple
|
||||
// [buffered] WriteClosers. They can be added with AddWriter().
|
||||
type MirrorWriter struct {
|
||||
active uint32
|
||||
|
||||
// channel for incoming writers
|
||||
writerAdd chan *writerAdd
|
||||
|
||||
// slices of writer/sync-channel pairs
|
||||
writers []*bufWriter
|
||||
|
||||
// synchronization channel for incoming writes
|
||||
msgSync chan []byte
|
||||
}
|
||||
|
||||
// NewMirrorWriter initializes and returns a MirrorWriter.
|
||||
func NewMirrorWriter() *MirrorWriter {
|
||||
mw := &MirrorWriter{
|
||||
msgSync: make(chan []byte, 64), // sufficiently large buffer to avoid callers waiting
|
||||
writerAdd: make(chan *writerAdd),
|
||||
}
|
||||
|
||||
go mw.logRoutine()
|
||||
|
||||
return mw
|
||||
}
|
||||
|
||||
// Write broadcasts the written bytes to all Writers.
|
||||
func (mw *MirrorWriter) Write(b []byte) (int, error) {
|
||||
mycopy := make([]byte, len(b))
|
||||
copy(mycopy, b)
|
||||
mw.msgSync <- mycopy
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
// Close closes the MirrorWriter
|
||||
func (mw *MirrorWriter) Close() error {
|
||||
// it is up to the caller to ensure that write is not called during or
|
||||
// after close is called.
|
||||
close(mw.msgSync)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mw *MirrorWriter) doClose() {
|
||||
for _, w := range mw.writers {
|
||||
w.writer.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func (mw *MirrorWriter) logRoutine() {
|
||||
// rebind to avoid races on nilling out struct fields
|
||||
msgSync := mw.msgSync
|
||||
writerAdd := mw.writerAdd
|
||||
|
||||
defer mw.doClose()
|
||||
|
||||
for {
|
||||
select {
|
||||
case b, ok := <-msgSync:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// write to all writers
|
||||
dropped := mw.broadcastMessage(b)
|
||||
|
||||
// consolidate the slice
|
||||
if dropped {
|
||||
mw.clearDeadWriters()
|
||||
}
|
||||
case wa := <-writerAdd:
|
||||
mw.writers = append(mw.writers, newBufWriter(wa.w))
|
||||
|
||||
atomic.StoreUint32(&mw.active, 1)
|
||||
close(wa.done)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// broadcastMessage sends the given message to every writer
|
||||
// if any writer is killed during the send, 'true' is returned
|
||||
func (mw *MirrorWriter) broadcastMessage(b []byte) bool {
|
||||
var dropped bool
|
||||
for i, w := range mw.writers {
|
||||
_, err := w.Write(b)
|
||||
if err != nil {
|
||||
mw.writers[i] = nil
|
||||
dropped = true
|
||||
}
|
||||
}
|
||||
return dropped
|
||||
}
|
||||
|
||||
func (mw *MirrorWriter) clearDeadWriters() {
|
||||
writers := mw.writers
|
||||
mw.writers = nil
|
||||
for _, w := range writers {
|
||||
if w != nil {
|
||||
mw.writers = append(mw.writers, w)
|
||||
}
|
||||
}
|
||||
if len(mw.writers) == 0 {
|
||||
atomic.StoreUint32(&mw.active, 0)
|
||||
}
|
||||
}
|
||||
|
||||
type writerAdd struct {
|
||||
w io.WriteCloser
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
// AddWriter attaches a new WriteCloser to this MirrorWriter.
|
||||
// The new writer will start getting any bytes written to the mirror.
|
||||
func (mw *MirrorWriter) AddWriter(w io.WriteCloser) {
|
||||
wa := &writerAdd{
|
||||
w: w,
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
mw.writerAdd <- wa
|
||||
<-wa.done
|
||||
}
|
||||
|
||||
// Active returns if there is at least one Writer
|
||||
// attached to this MirrorWriter
|
||||
func (mw *MirrorWriter) Active() (active bool) {
|
||||
return atomic.LoadUint32(&mw.active) == 1
|
||||
}
|
||||
|
||||
func newBufWriter(w io.WriteCloser) *bufWriter {
|
||||
bw := &bufWriter{
|
||||
writer: w,
|
||||
incoming: make(chan []byte, 1),
|
||||
}
|
||||
|
||||
go bw.loop()
|
||||
return bw
|
||||
}
|
||||
|
||||
// writes incoming messages to a buffer and when it fills
|
||||
// up, writes them to the writer
|
||||
type bufWriter struct {
|
||||
writer io.WriteCloser
|
||||
|
||||
incoming chan []byte
|
||||
|
||||
deathLock sync.Mutex
|
||||
dead bool
|
||||
}
|
||||
|
||||
var errDeadWriter = fmt.Errorf("writer is dead")
|
||||
|
||||
func (bw *bufWriter) Write(b []byte) (int, error) {
|
||||
bw.deathLock.Lock()
|
||||
dead := bw.dead
|
||||
bw.deathLock.Unlock()
|
||||
if dead {
|
||||
if bw.incoming != nil {
|
||||
close(bw.incoming)
|
||||
bw.incoming = nil
|
||||
}
|
||||
return 0, errDeadWriter
|
||||
}
|
||||
|
||||
bw.incoming <- b
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
func (bw *bufWriter) die() {
|
||||
bw.deathLock.Lock()
|
||||
bw.dead = true
|
||||
bw.writer.Close()
|
||||
bw.deathLock.Unlock()
|
||||
}
|
||||
|
||||
func (bw *bufWriter) loop() {
|
||||
bufsize := 0
|
||||
bufBase := make([][]byte, 0, 16) // some initial memory
|
||||
buffered := bufBase
|
||||
nextCh := make(chan []byte)
|
||||
|
||||
var nextMsg []byte
|
||||
|
||||
go func() {
|
||||
for b := range nextCh {
|
||||
_, err := bw.writer.Write(b)
|
||||
if err != nil {
|
||||
// TODO: need a way to notify there was an error here
|
||||
// wouldn't want to log here as it could casue an infinite loop
|
||||
bw.die()
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// collect and buffer messages
|
||||
incoming := bw.incoming
|
||||
for {
|
||||
if nextMsg == nil || nextCh == nil {
|
||||
// nextCh == nil implies we are 'dead' and draining the incoming channel
|
||||
// until the caller notices and closes it for us
|
||||
b, ok := <-incoming
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
nextMsg = b
|
||||
}
|
||||
|
||||
select {
|
||||
case b, ok := <-incoming:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
bufsize += len(b)
|
||||
buffered = append(buffered, b)
|
||||
if bufsize > MaxWriterBuffer {
|
||||
// if we have too many messages buffered, kill the writer
|
||||
bw.die()
|
||||
if nextCh != nil {
|
||||
close(nextCh)
|
||||
}
|
||||
nextCh = nil
|
||||
// explicity keep going here to drain incoming
|
||||
}
|
||||
case nextCh <- nextMsg:
|
||||
nextMsg = nil
|
||||
if len(buffered) > 0 {
|
||||
nextMsg = buffered[0]
|
||||
buffered = buffered[1:]
|
||||
bufsize -= len(nextMsg)
|
||||
}
|
||||
|
||||
if len(buffered) == 0 {
|
||||
// reset slice position
|
||||
buffered = bufBase[:0]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,7 +20,7 @@ import (
|
|||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
"github.com/libp2p/go-libp2p/core/protocol"
|
||||
|
||||
logging "github.com/ipfs/go-log"
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
)
|
||||
|
||||
// DefaultMaximumMessageSize is 1mb.
|
||||
|
@ -152,7 +152,6 @@ type PubSub struct {
|
|||
inboundStreamsMx sync.Mutex
|
||||
inboundStreams map[peer.ID]network.Stream
|
||||
|
||||
seenMessagesMx sync.Mutex
|
||||
seenMessages timecache.TimeCache
|
||||
seenMsgTTL time.Duration
|
||||
seenMsgStrategy timecache.Strategy
|
||||
|
@ -567,6 +566,7 @@ func (p *PubSub) processLoop(ctx context.Context) {
|
|||
}
|
||||
p.peers = nil
|
||||
p.topics = nil
|
||||
p.seenMessages.Done()
|
||||
}()
|
||||
|
||||
for {
|
||||
|
@ -985,22 +985,13 @@ func (p *PubSub) notifySubs(msg *Message) {
|
|||
|
||||
// seenMessage returns whether we already saw this message before
|
||||
func (p *PubSub) seenMessage(id string) bool {
|
||||
p.seenMessagesMx.Lock()
|
||||
defer p.seenMessagesMx.Unlock()
|
||||
return p.seenMessages.Has(id)
|
||||
}
|
||||
|
||||
// markSeen marks a message as seen such that seenMessage returns `true' for the given id
|
||||
// returns true if the message was freshly marked
|
||||
func (p *PubSub) markSeen(id string) bool {
|
||||
p.seenMessagesMx.Lock()
|
||||
defer p.seenMessagesMx.Unlock()
|
||||
if p.seenMessages.Has(id) {
|
||||
return false
|
||||
}
|
||||
|
||||
p.seenMessages.Add(id)
|
||||
return true
|
||||
return p.seenMessages.Add(id)
|
||||
}
|
||||
|
||||
// subscribedToMessage returns whether we are subscribed to one of the topics
|
||||
|
|
|
@ -1,71 +1,56 @@
|
|||
package timecache
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// FirstSeenCache is a thread-safe copy of https://github.com/whyrusleeping/timecache.
|
||||
// FirstSeenCache is a time cache that only marks the expiry of a message when first added.
|
||||
type FirstSeenCache struct {
|
||||
q *list.List
|
||||
m map[string]time.Time
|
||||
span time.Duration
|
||||
guard *sync.RWMutex
|
||||
lk sync.RWMutex
|
||||
m map[string]time.Time
|
||||
ttl time.Duration
|
||||
|
||||
done func()
|
||||
}
|
||||
|
||||
func newFirstSeenCache(span time.Duration) TimeCache {
|
||||
return &FirstSeenCache{
|
||||
q: list.New(),
|
||||
m: make(map[string]time.Time),
|
||||
span: span,
|
||||
guard: new(sync.RWMutex),
|
||||
var _ TimeCache = (*FirstSeenCache)(nil)
|
||||
|
||||
func newFirstSeenCache(ttl time.Duration) *FirstSeenCache {
|
||||
tc := &FirstSeenCache{
|
||||
m: make(map[string]time.Time),
|
||||
ttl: ttl,
|
||||
}
|
||||
|
||||
ctx, done := context.WithCancel(context.Background())
|
||||
tc.done = done
|
||||
go background(ctx, &tc.lk, tc.m)
|
||||
|
||||
return tc
|
||||
}
|
||||
|
||||
func (tc FirstSeenCache) Add(s string) {
|
||||
tc.guard.Lock()
|
||||
defer tc.guard.Unlock()
|
||||
func (tc *FirstSeenCache) Done() {
|
||||
tc.done()
|
||||
}
|
||||
|
||||
func (tc *FirstSeenCache) Has(s string) bool {
|
||||
tc.lk.RLock()
|
||||
defer tc.lk.RUnlock()
|
||||
|
||||
_, ok := tc.m[s]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (tc *FirstSeenCache) Add(s string) bool {
|
||||
tc.lk.Lock()
|
||||
defer tc.lk.Unlock()
|
||||
|
||||
_, ok := tc.m[s]
|
||||
if ok {
|
||||
panic("putting the same entry twice not supported")
|
||||
return false
|
||||
}
|
||||
|
||||
// TODO(#515): Do GC in the background
|
||||
tc.sweep()
|
||||
|
||||
tc.m[s] = time.Now()
|
||||
tc.q.PushFront(s)
|
||||
}
|
||||
|
||||
func (tc FirstSeenCache) sweep() {
|
||||
for {
|
||||
back := tc.q.Back()
|
||||
if back == nil {
|
||||
return
|
||||
}
|
||||
|
||||
v := back.Value.(string)
|
||||
t, ok := tc.m[v]
|
||||
if !ok {
|
||||
panic("inconsistent cache state")
|
||||
}
|
||||
|
||||
if time.Since(t) > tc.span {
|
||||
tc.q.Remove(back)
|
||||
delete(tc.m, v)
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (tc FirstSeenCache) Has(s string) bool {
|
||||
tc.guard.RLock()
|
||||
defer tc.guard.RUnlock()
|
||||
|
||||
ts, ok := tc.m[s]
|
||||
// Only consider the entry found if it was present in the cache AND hadn't already expired.
|
||||
return ok && time.Since(ts) <= tc.span
|
||||
tc.m[s] = time.Now().Add(tc.ttl)
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -1,84 +1,58 @@
|
|||
package timecache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/emirpasic/gods/maps/linkedhashmap"
|
||||
)
|
||||
|
||||
// LastSeenCache is a LRU cache that keeps entries for up to a specified time duration. After this duration has elapsed,
|
||||
// "old" entries will be purged from the cache.
|
||||
//
|
||||
// It's also a "sliding window" cache. Every time an unexpired entry is seen again, its timestamp slides forward. This
|
||||
// keeps frequently occurring entries cached and prevents them from being propagated, especially because of network
|
||||
// issues that might increase the number of duplicate messages in the network.
|
||||
//
|
||||
// Garbage collection of expired entries is event-driven, i.e. it only happens when there is a new entry added to the
|
||||
// cache. This should be ok - if existing entries are being looked up then the cache is not growing, and when a new one
|
||||
// appears that would grow the cache, garbage collection will attempt to reduce the pressure on the cache.
|
||||
//
|
||||
// This implementation is heavily inspired by https://github.com/whyrusleeping/timecache.
|
||||
// LastSeenCache is a time cache that extends the expiry of a seen message when added
|
||||
// or checked for presence with Has..
|
||||
type LastSeenCache struct {
|
||||
m *linkedhashmap.Map
|
||||
span time.Duration
|
||||
guard *sync.Mutex
|
||||
lk sync.Mutex
|
||||
m map[string]time.Time
|
||||
ttl time.Duration
|
||||
|
||||
done func()
|
||||
}
|
||||
|
||||
func newLastSeenCache(span time.Duration) TimeCache {
|
||||
return &LastSeenCache{
|
||||
m: linkedhashmap.New(),
|
||||
span: span,
|
||||
guard: new(sync.Mutex),
|
||||
var _ TimeCache = (*LastSeenCache)(nil)
|
||||
|
||||
func newLastSeenCache(ttl time.Duration) *LastSeenCache {
|
||||
tc := &LastSeenCache{
|
||||
m: make(map[string]time.Time),
|
||||
ttl: ttl,
|
||||
}
|
||||
|
||||
ctx, done := context.WithCancel(context.Background())
|
||||
tc.done = done
|
||||
go background(ctx, &tc.lk, tc.m)
|
||||
|
||||
return tc
|
||||
}
|
||||
|
||||
func (tc *LastSeenCache) Add(s string) {
|
||||
tc.guard.Lock()
|
||||
defer tc.guard.Unlock()
|
||||
|
||||
tc.add(s)
|
||||
|
||||
// Garbage collect expired entries
|
||||
// TODO(#515): Do GC in the background
|
||||
tc.gc()
|
||||
func (tc *LastSeenCache) Done() {
|
||||
tc.done()
|
||||
}
|
||||
|
||||
func (tc *LastSeenCache) add(s string) {
|
||||
// We don't need a lock here because this function is always called with the lock already acquired.
|
||||
func (tc *LastSeenCache) Add(s string) bool {
|
||||
tc.lk.Lock()
|
||||
defer tc.lk.Unlock()
|
||||
|
||||
// If an entry already exists, remove it and add a new one to the back of the list to maintain temporal ordering and
|
||||
// an accurate sliding window.
|
||||
tc.m.Remove(s)
|
||||
now := time.Now()
|
||||
tc.m.Put(s, &now)
|
||||
}
|
||||
_, ok := tc.m[s]
|
||||
tc.m[s] = time.Now().Add(tc.ttl)
|
||||
|
||||
func (tc *LastSeenCache) gc() {
|
||||
// We don't need a lock here because this function is always called with the lock already acquired.
|
||||
iter := tc.m.Iterator()
|
||||
for iter.Next() {
|
||||
key := iter.Key()
|
||||
ts := iter.Value().(*time.Time)
|
||||
// Exit if we've found an entry with an unexpired timestamp. Since we're iterating in order of insertion, all
|
||||
// entries hereafter will be unexpired.
|
||||
if time.Since(*ts) <= tc.span {
|
||||
return
|
||||
}
|
||||
tc.m.Remove(key)
|
||||
}
|
||||
return !ok
|
||||
}
|
||||
|
||||
func (tc *LastSeenCache) Has(s string) bool {
|
||||
tc.guard.Lock()
|
||||
defer tc.guard.Unlock()
|
||||
tc.lk.Lock()
|
||||
defer tc.lk.Unlock()
|
||||
|
||||
// If the entry exists and has not already expired, slide it forward.
|
||||
if ts, found := tc.m.Get(s); found {
|
||||
if t := ts.(*time.Time); time.Since(*t) <= tc.span {
|
||||
tc.add(s)
|
||||
return true
|
||||
}
|
||||
_, ok := tc.m[s]
|
||||
if ok {
|
||||
tc.m[s] = time.Now().Add(tc.ttl)
|
||||
}
|
||||
return false
|
||||
|
||||
return ok
|
||||
}
|
||||
|
|
|
@ -1,32 +1,52 @@
|
|||
package timecache
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"time"
|
||||
|
||||
logger "github.com/ipfs/go-log/v2"
|
||||
)
|
||||
|
||||
var log = logger.Logger("pubsub/timecache")
|
||||
|
||||
// Stategy is the TimeCache expiration strategy to use.
|
||||
type Strategy uint8
|
||||
|
||||
const (
|
||||
// Strategy_FirstSeen expires an entry from the time it was added.
|
||||
Strategy_FirstSeen Strategy = iota
|
||||
// Stategy_LastSeen expires an entry from the last time it was touched by an Add or Has.
|
||||
Strategy_LastSeen
|
||||
)
|
||||
|
||||
// TimeCache is a cahe of recently seen messages (by id).
|
||||
type TimeCache interface {
|
||||
Add(string)
|
||||
// Add adds an id into the cache, if it is not already there.
|
||||
// Returns true if the id was newly added to the cache.
|
||||
// Depending on the implementation strategy, it may or may not update the expiry of
|
||||
// an existing entry.
|
||||
Add(string) bool
|
||||
// Has checks the cache for the presence of an id.
|
||||
// Depending on the implementation strategy, it may or may not update the expiry of
|
||||
// an existing entry.
|
||||
Has(string) bool
|
||||
// Done signals that the user is done with this cache, which it may stop background threads
|
||||
// and relinquish resources.
|
||||
Done()
|
||||
}
|
||||
|
||||
// NewTimeCache defaults to the original ("first seen") cache implementation
|
||||
func NewTimeCache(span time.Duration) TimeCache {
|
||||
return NewTimeCacheWithStrategy(Strategy_FirstSeen, span)
|
||||
func NewTimeCache(ttl time.Duration) TimeCache {
|
||||
return NewTimeCacheWithStrategy(Strategy_FirstSeen, ttl)
|
||||
}
|
||||
|
||||
func NewTimeCacheWithStrategy(strategy Strategy, span time.Duration) TimeCache {
|
||||
func NewTimeCacheWithStrategy(strategy Strategy, ttl time.Duration) TimeCache {
|
||||
switch strategy {
|
||||
case Strategy_FirstSeen:
|
||||
return newFirstSeenCache(span)
|
||||
return newFirstSeenCache(ttl)
|
||||
case Strategy_LastSeen:
|
||||
return newLastSeenCache(span)
|
||||
return newLastSeenCache(ttl)
|
||||
default:
|
||||
// Default to the original time cache implementation
|
||||
return newFirstSeenCache(span)
|
||||
return newFirstSeenCache(ttl)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
package timecache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var backgroundSweepInterval = time.Minute
|
||||
|
||||
func background(ctx context.Context, lk sync.Locker, m map[string]time.Time) {
|
||||
ticker := time.NewTimer(backgroundSweepInterval)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case now := <-ticker.C:
|
||||
sweep(lk, m, now)
|
||||
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func sweep(lk sync.Locker, m map[string]time.Time, now time.Time) {
|
||||
lk.Lock()
|
||||
defer lk.Unlock()
|
||||
|
||||
for k, expiry := range m {
|
||||
if expiry.Before(now) {
|
||||
delete(m, k)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
coverage.txt
|
|
@ -1,20 +0,0 @@
|
|||
language: go
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- go: "1.13.x"
|
||||
- go: "1.14.x"
|
||||
- go: "tip"
|
||||
env:
|
||||
- LINT=true
|
||||
- COVERAGE=true
|
||||
|
||||
install:
|
||||
- if [ "$LINT" == true ]; then go get -u golang.org/x/lint/golint/... ; else echo 'skipping lint'; fi
|
||||
- go get -u github.com/stretchr/testify/...
|
||||
|
||||
script:
|
||||
- make test
|
||||
- go build ./...
|
||||
- if [ "$LINT" == true ]; then make lint ; else echo 'skipping lint'; fi
|
||||
- if [ "$COVERAGE" == true ]; then make cover && bash <(curl -s https://codecov.io/bash) ; else echo 'skipping coverage'; fi
|
|
@ -1,63 +0,0 @@
|
|||
Changes by Version
|
||||
==================
|
||||
|
||||
|
||||
1.2.0 (2020-07-01)
|
||||
-------------------
|
||||
|
||||
* Restore the ability to reset the current span in context to nil (#231) -- Yuri Shkuro
|
||||
* Use error.object per OpenTracing Semantic Conventions (#179) -- Rahman Syed
|
||||
* Convert nil pointer log field value to string "nil" (#230) -- Cyril Tovena
|
||||
* Add Go module support (#215) -- Zaba505
|
||||
* Make SetTag helper types in ext public (#229) -- Blake Edwards
|
||||
* Add log/fields helpers for keys from specification (#226) -- Dmitry Monakhov
|
||||
* Improve noop impementation (#223) -- chanxuehong
|
||||
* Add an extension to Tracer interface for custom go context creation (#220) -- Krzesimir Nowak
|
||||
* Fix typo in comments (#222) -- meteorlxy
|
||||
* Improve documentation for log.Object() to emphasize the requirement to pass immutable arguments (#219) -- 疯狂的小企鹅
|
||||
* [mock] Return ErrInvalidSpanContext if span context is not MockSpanContext (#216) -- Milad Irannejad
|
||||
|
||||
|
||||
1.1.0 (2019-03-23)
|
||||
-------------------
|
||||
|
||||
Notable changes:
|
||||
- The library is now released under Apache 2.0 license
|
||||
- Use Set() instead of Add() in HTTPHeadersCarrier is functionally a breaking change (fixes issue [#159](https://github.com/opentracing/opentracing-go/issues/159))
|
||||
- 'golang.org/x/net/context' is replaced with 'context' from the standard library
|
||||
|
||||
List of all changes:
|
||||
|
||||
- Export StartSpanFromContextWithTracer (#214) <Aaron Delaney>
|
||||
- Add IsGlobalTracerRegistered() to indicate if a tracer has been registered (#201) <Mike Goldsmith>
|
||||
- Use Set() instead of Add() in HTTPHeadersCarrier (#191) <jeremyxu2010>
|
||||
- Update license to Apache 2.0 (#181) <Andrea Kao>
|
||||
- Replace 'golang.org/x/net/context' with 'context' (#176) <Tony Ghita>
|
||||
- Port of Python opentracing/harness/api_check.py to Go (#146) <chris erway>
|
||||
- Fix race condition in MockSpan.Context() (#170) <Brad>
|
||||
- Add PeerHostIPv4.SetString() (#155) <NeoCN>
|
||||
- Add a Noop log field type to log to allow for optional fields (#150) <Matt Ho>
|
||||
|
||||
|
||||
1.0.2 (2017-04-26)
|
||||
-------------------
|
||||
|
||||
- Add more semantic tags (#139) <Rustam Zagirov>
|
||||
|
||||
|
||||
1.0.1 (2017-02-06)
|
||||
-------------------
|
||||
|
||||
- Correct spelling in comments <Ben Sigelman>
|
||||
- Address race in nextMockID() (#123) <bill fumerola>
|
||||
- log: avoid panic marshaling nil error (#131) <Anthony Voutas>
|
||||
- Deprecate InitGlobalTracer in favor of SetGlobalTracer (#128) <Yuri Shkuro>
|
||||
- Drop Go 1.5 that fails in Travis (#129) <Yuri Shkuro>
|
||||
- Add convenience methods Key() and Value() to log.Field <Ben Sigelman>
|
||||
- Add convenience methods to log.Field (2 years, 6 months ago) <Radu Berinde>
|
||||
|
||||
1.0.0 (2016-09-26)
|
||||
-------------------
|
||||
|
||||
- This release implements OpenTracing Specification 1.0 (https://opentracing.io/spec)
|
||||
|
|
@ -1,201 +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 2016 The OpenTracing 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.
|
|
@ -1,20 +0,0 @@
|
|||
.DEFAULT_GOAL := test-and-lint
|
||||
|
||||
.PHONY: test-and-lint
|
||||
test-and-lint: test lint
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
go test -v -cover -race ./...
|
||||
|
||||
.PHONY: cover
|
||||
cover:
|
||||
go test -v -coverprofile=coverage.txt -covermode=atomic -race ./...
|
||||
|
||||
.PHONY: lint
|
||||
lint:
|
||||
go fmt ./...
|
||||
golint ./...
|
||||
@# Run again with magic to exit non-zero if golint outputs anything.
|
||||
@! (golint ./... | read dummy)
|
||||
go vet ./...
|
|
@ -1,171 +0,0 @@
|
|||
[![Gitter chat](http://img.shields.io/badge/gitter-join%20chat%20%E2%86%92-brightgreen.svg)](https://gitter.im/opentracing/public) [![Build Status](https://travis-ci.org/opentracing/opentracing-go.svg?branch=master)](https://travis-ci.org/opentracing/opentracing-go) [![GoDoc](https://godoc.org/github.com/opentracing/opentracing-go?status.svg)](http://godoc.org/github.com/opentracing/opentracing-go)
|
||||
[![Sourcegraph Badge](https://sourcegraph.com/github.com/opentracing/opentracing-go/-/badge.svg)](https://sourcegraph.com/github.com/opentracing/opentracing-go?badge)
|
||||
|
||||
# OpenTracing API for Go
|
||||
|
||||
This package is a Go platform API for OpenTracing.
|
||||
|
||||
## Required Reading
|
||||
|
||||
In order to understand the Go platform API, one must first be familiar with the
|
||||
[OpenTracing project](https://opentracing.io) and
|
||||
[terminology](https://opentracing.io/specification/) more specifically.
|
||||
|
||||
## API overview for those adding instrumentation
|
||||
|
||||
Everyday consumers of this `opentracing` package really only need to worry
|
||||
about a couple of key abstractions: the `StartSpan` function, the `Span`
|
||||
interface, and binding a `Tracer` at `main()`-time. Here are code snippets
|
||||
demonstrating some important use cases.
|
||||
|
||||
#### Singleton initialization
|
||||
|
||||
The simplest starting point is `./default_tracer.go`. As early as possible, call
|
||||
|
||||
```go
|
||||
import "github.com/opentracing/opentracing-go"
|
||||
import ".../some_tracing_impl"
|
||||
|
||||
func main() {
|
||||
opentracing.SetGlobalTracer(
|
||||
// tracing impl specific:
|
||||
some_tracing_impl.New(...),
|
||||
)
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
#### Non-Singleton initialization
|
||||
|
||||
If you prefer direct control to singletons, manage ownership of the
|
||||
`opentracing.Tracer` implementation explicitly.
|
||||
|
||||
#### Creating a Span given an existing Go `context.Context`
|
||||
|
||||
If you use `context.Context` in your application, OpenTracing's Go library will
|
||||
happily rely on it for `Span` propagation. To start a new (blocking child)
|
||||
`Span`, you can use `StartSpanFromContext`.
|
||||
|
||||
```go
|
||||
func xyz(ctx context.Context, ...) {
|
||||
...
|
||||
span, ctx := opentracing.StartSpanFromContext(ctx, "operation_name")
|
||||
defer span.Finish()
|
||||
span.LogFields(
|
||||
log.String("event", "soft error"),
|
||||
log.String("type", "cache timeout"),
|
||||
log.Int("waited.millis", 1500))
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
#### Starting an empty trace by creating a "root span"
|
||||
|
||||
It's always possible to create a "root" `Span` with no parent or other causal
|
||||
reference.
|
||||
|
||||
```go
|
||||
func xyz() {
|
||||
...
|
||||
sp := opentracing.StartSpan("operation_name")
|
||||
defer sp.Finish()
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
#### Creating a (child) Span given an existing (parent) Span
|
||||
|
||||
```go
|
||||
func xyz(parentSpan opentracing.Span, ...) {
|
||||
...
|
||||
sp := opentracing.StartSpan(
|
||||
"operation_name",
|
||||
opentracing.ChildOf(parentSpan.Context()))
|
||||
defer sp.Finish()
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
#### Serializing to the wire
|
||||
|
||||
```go
|
||||
func makeSomeRequest(ctx context.Context) ... {
|
||||
if span := opentracing.SpanFromContext(ctx); span != nil {
|
||||
httpClient := &http.Client{}
|
||||
httpReq, _ := http.NewRequest("GET", "http://myservice/", nil)
|
||||
|
||||
// Transmit the span's TraceContext as HTTP headers on our
|
||||
// outbound request.
|
||||
opentracing.GlobalTracer().Inject(
|
||||
span.Context(),
|
||||
opentracing.HTTPHeaders,
|
||||
opentracing.HTTPHeadersCarrier(httpReq.Header))
|
||||
|
||||
resp, err := httpClient.Do(httpReq)
|
||||
...
|
||||
}
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
#### Deserializing from the wire
|
||||
|
||||
```go
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
|
||||
var serverSpan opentracing.Span
|
||||
appSpecificOperationName := ...
|
||||
wireContext, err := opentracing.GlobalTracer().Extract(
|
||||
opentracing.HTTPHeaders,
|
||||
opentracing.HTTPHeadersCarrier(req.Header))
|
||||
if err != nil {
|
||||
// Optionally record something about err here
|
||||
}
|
||||
|
||||
// Create the span referring to the RPC client if available.
|
||||
// If wireContext == nil, a root span will be created.
|
||||
serverSpan = opentracing.StartSpan(
|
||||
appSpecificOperationName,
|
||||
ext.RPCServerOption(wireContext))
|
||||
|
||||
defer serverSpan.Finish()
|
||||
|
||||
ctx := opentracing.ContextWithSpan(context.Background(), serverSpan)
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
#### Conditionally capture a field using `log.Noop`
|
||||
|
||||
In some situations, you may want to dynamically decide whether or not
|
||||
to log a field. For example, you may want to capture additional data,
|
||||
such as a customer ID, in non-production environments:
|
||||
|
||||
```go
|
||||
func Customer(order *Order) log.Field {
|
||||
if os.Getenv("ENVIRONMENT") == "dev" {
|
||||
return log.String("customer", order.Customer.ID)
|
||||
}
|
||||
return log.Noop()
|
||||
}
|
||||
```
|
||||
|
||||
#### Goroutine-safety
|
||||
|
||||
The entire public API is goroutine-safe and does not require external
|
||||
synchronization.
|
||||
|
||||
## API pointers for those implementing a tracing system
|
||||
|
||||
Tracing system implementors may be able to reuse or copy-paste-modify the `basictracer` package, found [here](https://github.com/opentracing/basictracer-go). In particular, see `basictracer.New(...)`.
|
||||
|
||||
## API compatibility
|
||||
|
||||
For the time being, "mild" backwards-incompatible changes may be made without changing the major version number. As OpenTracing and `opentracing-go` mature, backwards compatibility will become more of a priority.
|
||||
|
||||
## Tracer test suite
|
||||
|
||||
A test suite is available in the [harness](https://godoc.org/github.com/opentracing/opentracing-go/harness) package that can assist Tracer implementors to assert that their Tracer is working correctly.
|
||||
|
||||
## Licensing
|
||||
|
||||
[Apache 2.0 License](./LICENSE).
|
|
@ -1,24 +0,0 @@
|
|||
package opentracing
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
// TracerContextWithSpanExtension is an extension interface that the
|
||||
// implementation of the Tracer interface may want to implement. It
|
||||
// allows to have some control over the go context when the
|
||||
// ContextWithSpan is invoked.
|
||||
//
|
||||
// The primary purpose of this extension are adapters from opentracing
|
||||
// API to some other tracing API.
|
||||
type TracerContextWithSpanExtension interface {
|
||||
// ContextWithSpanHook gets called by the ContextWithSpan
|
||||
// function, when the Tracer implementation also implements
|
||||
// this interface. It allows to put extra information into the
|
||||
// context and make it available to the callers of the
|
||||
// ContextWithSpan.
|
||||
//
|
||||
// This hook is invoked before the ContextWithSpan function
|
||||
// actually puts the span into the context.
|
||||
ContextWithSpanHook(ctx context.Context, span Span) context.Context
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
package ext
|
||||
|
||||
import (
|
||||
"github.com/opentracing/opentracing-go"
|
||||
"github.com/opentracing/opentracing-go/log"
|
||||
)
|
||||
|
||||
// LogError sets the error=true tag on the Span and logs err as an "error" event.
|
||||
func LogError(span opentracing.Span, err error, fields ...log.Field) {
|
||||
Error.Set(span, true)
|
||||
ef := []log.Field{
|
||||
log.Event("error"),
|
||||
log.Error(err),
|
||||
}
|
||||
ef = append(ef, fields...)
|
||||
span.LogFields(ef...)
|
||||
}
|
|
@ -1,215 +0,0 @@
|
|||
package ext
|
||||
|
||||
import "github.com/opentracing/opentracing-go"
|
||||
|
||||
// These constants define common tag names recommended for better portability across
|
||||
// tracing systems and languages/platforms.
|
||||
//
|
||||
// The tag names are defined as typed strings, so that in addition to the usual use
|
||||
//
|
||||
// span.setTag(TagName, value)
|
||||
//
|
||||
// they also support value type validation via this additional syntax:
|
||||
//
|
||||
// TagName.Set(span, value)
|
||||
//
|
||||
var (
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// SpanKind (client/server or producer/consumer)
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
// SpanKind hints at relationship between spans, e.g. client/server
|
||||
SpanKind = spanKindTagName("span.kind")
|
||||
|
||||
// SpanKindRPCClient marks a span representing the client-side of an RPC
|
||||
// or other remote call
|
||||
SpanKindRPCClientEnum = SpanKindEnum("client")
|
||||
SpanKindRPCClient = opentracing.Tag{Key: string(SpanKind), Value: SpanKindRPCClientEnum}
|
||||
|
||||
// SpanKindRPCServer marks a span representing the server-side of an RPC
|
||||
// or other remote call
|
||||
SpanKindRPCServerEnum = SpanKindEnum("server")
|
||||
SpanKindRPCServer = opentracing.Tag{Key: string(SpanKind), Value: SpanKindRPCServerEnum}
|
||||
|
||||
// SpanKindProducer marks a span representing the producer-side of a
|
||||
// message bus
|
||||
SpanKindProducerEnum = SpanKindEnum("producer")
|
||||
SpanKindProducer = opentracing.Tag{Key: string(SpanKind), Value: SpanKindProducerEnum}
|
||||
|
||||
// SpanKindConsumer marks a span representing the consumer-side of a
|
||||
// message bus
|
||||
SpanKindConsumerEnum = SpanKindEnum("consumer")
|
||||
SpanKindConsumer = opentracing.Tag{Key: string(SpanKind), Value: SpanKindConsumerEnum}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Component name
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Component is a low-cardinality identifier of the module, library,
|
||||
// or package that is generating a span.
|
||||
Component = StringTagName("component")
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Sampling hint
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
// SamplingPriority determines the priority of sampling this Span.
|
||||
SamplingPriority = Uint16TagName("sampling.priority")
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Peer tags. These tags can be emitted by either client-side or
|
||||
// server-side to describe the other side/service in a peer-to-peer
|
||||
// communications, like an RPC call.
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
// PeerService records the service name of the peer.
|
||||
PeerService = StringTagName("peer.service")
|
||||
|
||||
// PeerAddress records the address name of the peer. This may be a "ip:port",
|
||||
// a bare "hostname", a FQDN or even a database DSN substring
|
||||
// like "mysql://username@127.0.0.1:3306/dbname"
|
||||
PeerAddress = StringTagName("peer.address")
|
||||
|
||||
// PeerHostname records the host name of the peer
|
||||
PeerHostname = StringTagName("peer.hostname")
|
||||
|
||||
// PeerHostIPv4 records IP v4 host address of the peer
|
||||
PeerHostIPv4 = IPv4TagName("peer.ipv4")
|
||||
|
||||
// PeerHostIPv6 records IP v6 host address of the peer
|
||||
PeerHostIPv6 = StringTagName("peer.ipv6")
|
||||
|
||||
// PeerPort records port number of the peer
|
||||
PeerPort = Uint16TagName("peer.port")
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// HTTP Tags
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
// HTTPUrl should be the URL of the request being handled in this segment
|
||||
// of the trace, in standard URI format. The protocol is optional.
|
||||
HTTPUrl = StringTagName("http.url")
|
||||
|
||||
// HTTPMethod is the HTTP method of the request, and is case-insensitive.
|
||||
HTTPMethod = StringTagName("http.method")
|
||||
|
||||
// HTTPStatusCode is the numeric HTTP status code (200, 404, etc) of the
|
||||
// HTTP response.
|
||||
HTTPStatusCode = Uint16TagName("http.status_code")
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// DB Tags
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
// DBInstance is database instance name.
|
||||
DBInstance = StringTagName("db.instance")
|
||||
|
||||
// DBStatement is a database statement for the given database type.
|
||||
// It can be a query or a prepared statement (i.e., before substitution).
|
||||
DBStatement = StringTagName("db.statement")
|
||||
|
||||
// DBType is a database type. For any SQL database, "sql".
|
||||
// For others, the lower-case database category, e.g. "redis"
|
||||
DBType = StringTagName("db.type")
|
||||
|
||||
// DBUser is a username for accessing database.
|
||||
DBUser = StringTagName("db.user")
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Message Bus Tag
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
// MessageBusDestination is an address at which messages can be exchanged
|
||||
MessageBusDestination = StringTagName("message_bus.destination")
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Error Tag
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Error indicates that operation represented by the span resulted in an error.
|
||||
Error = BoolTagName("error")
|
||||
)
|
||||
|
||||
// ---
|
||||
|
||||
// SpanKindEnum represents common span types
|
||||
type SpanKindEnum string
|
||||
|
||||
type spanKindTagName string
|
||||
|
||||
// Set adds a string tag to the `span`
|
||||
func (tag spanKindTagName) Set(span opentracing.Span, value SpanKindEnum) {
|
||||
span.SetTag(string(tag), value)
|
||||
}
|
||||
|
||||
type rpcServerOption struct {
|
||||
clientContext opentracing.SpanContext
|
||||
}
|
||||
|
||||
func (r rpcServerOption) Apply(o *opentracing.StartSpanOptions) {
|
||||
if r.clientContext != nil {
|
||||
opentracing.ChildOf(r.clientContext).Apply(o)
|
||||
}
|
||||
SpanKindRPCServer.Apply(o)
|
||||
}
|
||||
|
||||
// RPCServerOption returns a StartSpanOption appropriate for an RPC server span
|
||||
// with `client` representing the metadata for the remote peer Span if available.
|
||||
// In case client == nil, due to the client not being instrumented, this RPC
|
||||
// server span will be a root span.
|
||||
func RPCServerOption(client opentracing.SpanContext) opentracing.StartSpanOption {
|
||||
return rpcServerOption{client}
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
// StringTagName is a common tag name to be set to a string value
|
||||
type StringTagName string
|
||||
|
||||
// Set adds a string tag to the `span`
|
||||
func (tag StringTagName) Set(span opentracing.Span, value string) {
|
||||
span.SetTag(string(tag), value)
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
// Uint32TagName is a common tag name to be set to a uint32 value
|
||||
type Uint32TagName string
|
||||
|
||||
// Set adds a uint32 tag to the `span`
|
||||
func (tag Uint32TagName) Set(span opentracing.Span, value uint32) {
|
||||
span.SetTag(string(tag), value)
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
// Uint16TagName is a common tag name to be set to a uint16 value
|
||||
type Uint16TagName string
|
||||
|
||||
// Set adds a uint16 tag to the `span`
|
||||
func (tag Uint16TagName) Set(span opentracing.Span, value uint16) {
|
||||
span.SetTag(string(tag), value)
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
// BoolTagName is a common tag name to be set to a bool value
|
||||
type BoolTagName string
|
||||
|
||||
// Set adds a bool tag to the `span`
|
||||
func (tag BoolTagName) Set(span opentracing.Span, value bool) {
|
||||
span.SetTag(string(tag), value)
|
||||
}
|
||||
|
||||
// IPv4TagName is a common tag name to be set to an ipv4 value
|
||||
type IPv4TagName string
|
||||
|
||||
// Set adds IP v4 host address of the peer as an uint32 value to the `span`, keep this for backward and zipkin compatibility
|
||||
func (tag IPv4TagName) Set(span opentracing.Span, value uint32) {
|
||||
span.SetTag(string(tag), value)
|
||||
}
|
||||
|
||||
// SetString records IP v4 host address of the peer as a .-separated tuple to the `span`. E.g., "127.0.0.1"
|
||||
func (tag IPv4TagName) SetString(span opentracing.Span, value string) {
|
||||
span.SetTag(string(tag), value)
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
package opentracing
|
||||
|
||||
type registeredTracer struct {
|
||||
tracer Tracer
|
||||
isRegistered bool
|
||||
}
|
||||
|
||||
var (
|
||||
globalTracer = registeredTracer{NoopTracer{}, false}
|
||||
)
|
||||
|
||||
// SetGlobalTracer sets the [singleton] opentracing.Tracer returned by
|
||||
// GlobalTracer(). Those who use GlobalTracer (rather than directly manage an
|
||||
// opentracing.Tracer instance) should call SetGlobalTracer as early as
|
||||
// possible in main(), prior to calling the `StartSpan` global func below.
|
||||
// Prior to calling `SetGlobalTracer`, any Spans started via the `StartSpan`
|
||||
// (etc) globals are noops.
|
||||
func SetGlobalTracer(tracer Tracer) {
|
||||
globalTracer = registeredTracer{tracer, true}
|
||||
}
|
||||
|
||||
// GlobalTracer returns the global singleton `Tracer` implementation.
|
||||
// Before `SetGlobalTracer()` is called, the `GlobalTracer()` is a noop
|
||||
// implementation that drops all data handed to it.
|
||||
func GlobalTracer() Tracer {
|
||||
return globalTracer.tracer
|
||||
}
|
||||
|
||||
// StartSpan defers to `Tracer.StartSpan`. See `GlobalTracer()`.
|
||||
func StartSpan(operationName string, opts ...StartSpanOption) Span {
|
||||
return globalTracer.tracer.StartSpan(operationName, opts...)
|
||||
}
|
||||
|
||||
// InitGlobalTracer is deprecated. Please use SetGlobalTracer.
|
||||
func InitGlobalTracer(tracer Tracer) {
|
||||
SetGlobalTracer(tracer)
|
||||
}
|
||||
|
||||
// IsGlobalTracerRegistered returns a `bool` to indicate if a tracer has been globally registered
|
||||
func IsGlobalTracerRegistered() bool {
|
||||
return globalTracer.isRegistered
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
package opentracing
|
||||
|
||||
import "context"
|
||||
|
||||
type contextKey struct{}
|
||||
|
||||
var activeSpanKey = contextKey{}
|
||||
|
||||
// ContextWithSpan returns a new `context.Context` that holds a reference to
|
||||
// the span. If span is nil, a new context without an active span is returned.
|
||||
func ContextWithSpan(ctx context.Context, span Span) context.Context {
|
||||
if span != nil {
|
||||
if tracerWithHook, ok := span.Tracer().(TracerContextWithSpanExtension); ok {
|
||||
ctx = tracerWithHook.ContextWithSpanHook(ctx, span)
|
||||
}
|
||||
}
|
||||
return context.WithValue(ctx, activeSpanKey, span)
|
||||
}
|
||||
|
||||
// SpanFromContext returns the `Span` previously associated with `ctx`, or
|
||||
// `nil` if no such `Span` could be found.
|
||||
//
|
||||
// NOTE: context.Context != SpanContext: the former is Go's intra-process
|
||||
// context propagation mechanism, and the latter houses OpenTracing's per-Span
|
||||
// identity and baggage information.
|
||||
func SpanFromContext(ctx context.Context) Span {
|
||||
val := ctx.Value(activeSpanKey)
|
||||
if sp, ok := val.(Span); ok {
|
||||
return sp
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// StartSpanFromContext starts and returns a Span with `operationName`, using
|
||||
// any Span found within `ctx` as a ChildOfRef. If no such parent could be
|
||||
// found, StartSpanFromContext creates a root (parentless) Span.
|
||||
//
|
||||
// The second return value is a context.Context object built around the
|
||||
// returned Span.
|
||||
//
|
||||
// Example usage:
|
||||
//
|
||||
// SomeFunction(ctx context.Context, ...) {
|
||||
// sp, ctx := opentracing.StartSpanFromContext(ctx, "SomeFunction")
|
||||
// defer sp.Finish()
|
||||
// ...
|
||||
// }
|
||||
func StartSpanFromContext(ctx context.Context, operationName string, opts ...StartSpanOption) (Span, context.Context) {
|
||||
return StartSpanFromContextWithTracer(ctx, GlobalTracer(), operationName, opts...)
|
||||
}
|
||||
|
||||
// StartSpanFromContextWithTracer starts and returns a span with `operationName`
|
||||
// using a span found within the context as a ChildOfRef. If that doesn't exist
|
||||
// it creates a root span. It also returns a context.Context object built
|
||||
// around the returned span.
|
||||
//
|
||||
// It's behavior is identical to StartSpanFromContext except that it takes an explicit
|
||||
// tracer as opposed to using the global tracer.
|
||||
func StartSpanFromContextWithTracer(ctx context.Context, tracer Tracer, operationName string, opts ...StartSpanOption) (Span, context.Context) {
|
||||
if parentSpan := SpanFromContext(ctx); parentSpan != nil {
|
||||
opts = append(opts, ChildOf(parentSpan.Context()))
|
||||
}
|
||||
span := tracer.StartSpan(operationName, opts...)
|
||||
return span, ContextWithSpan(ctx, span)
|
||||
}
|
|
@ -1,282 +0,0 @@
|
|||
package log
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
)
|
||||
|
||||
type fieldType int
|
||||
|
||||
const (
|
||||
stringType fieldType = iota
|
||||
boolType
|
||||
intType
|
||||
int32Type
|
||||
uint32Type
|
||||
int64Type
|
||||
uint64Type
|
||||
float32Type
|
||||
float64Type
|
||||
errorType
|
||||
objectType
|
||||
lazyLoggerType
|
||||
noopType
|
||||
)
|
||||
|
||||
// Field instances are constructed via LogBool, LogString, and so on.
|
||||
// Tracing implementations may then handle them via the Field.Marshal
|
||||
// method.
|
||||
//
|
||||
// "heavily influenced by" (i.e., partially stolen from)
|
||||
// https://github.com/uber-go/zap
|
||||
type Field struct {
|
||||
key string
|
||||
fieldType fieldType
|
||||
numericVal int64
|
||||
stringVal string
|
||||
interfaceVal interface{}
|
||||
}
|
||||
|
||||
// String adds a string-valued key:value pair to a Span.LogFields() record
|
||||
func String(key, val string) Field {
|
||||
return Field{
|
||||
key: key,
|
||||
fieldType: stringType,
|
||||
stringVal: val,
|
||||
}
|
||||
}
|
||||
|
||||
// Bool adds a bool-valued key:value pair to a Span.LogFields() record
|
||||
func Bool(key string, val bool) Field {
|
||||
var numericVal int64
|
||||
if val {
|
||||
numericVal = 1
|
||||
}
|
||||
return Field{
|
||||
key: key,
|
||||
fieldType: boolType,
|
||||
numericVal: numericVal,
|
||||
}
|
||||
}
|
||||
|
||||
// Int adds an int-valued key:value pair to a Span.LogFields() record
|
||||
func Int(key string, val int) Field {
|
||||
return Field{
|
||||
key: key,
|
||||
fieldType: intType,
|
||||
numericVal: int64(val),
|
||||
}
|
||||
}
|
||||
|
||||
// Int32 adds an int32-valued key:value pair to a Span.LogFields() record
|
||||
func Int32(key string, val int32) Field {
|
||||
return Field{
|
||||
key: key,
|
||||
fieldType: int32Type,
|
||||
numericVal: int64(val),
|
||||
}
|
||||
}
|
||||
|
||||
// Int64 adds an int64-valued key:value pair to a Span.LogFields() record
|
||||
func Int64(key string, val int64) Field {
|
||||
return Field{
|
||||
key: key,
|
||||
fieldType: int64Type,
|
||||
numericVal: val,
|
||||
}
|
||||
}
|
||||
|
||||
// Uint32 adds a uint32-valued key:value pair to a Span.LogFields() record
|
||||
func Uint32(key string, val uint32) Field {
|
||||
return Field{
|
||||
key: key,
|
||||
fieldType: uint32Type,
|
||||
numericVal: int64(val),
|
||||
}
|
||||
}
|
||||
|
||||
// Uint64 adds a uint64-valued key:value pair to a Span.LogFields() record
|
||||
func Uint64(key string, val uint64) Field {
|
||||
return Field{
|
||||
key: key,
|
||||
fieldType: uint64Type,
|
||||
numericVal: int64(val),
|
||||
}
|
||||
}
|
||||
|
||||
// Float32 adds a float32-valued key:value pair to a Span.LogFields() record
|
||||
func Float32(key string, val float32) Field {
|
||||
return Field{
|
||||
key: key,
|
||||
fieldType: float32Type,
|
||||
numericVal: int64(math.Float32bits(val)),
|
||||
}
|
||||
}
|
||||
|
||||
// Float64 adds a float64-valued key:value pair to a Span.LogFields() record
|
||||
func Float64(key string, val float64) Field {
|
||||
return Field{
|
||||
key: key,
|
||||
fieldType: float64Type,
|
||||
numericVal: int64(math.Float64bits(val)),
|
||||
}
|
||||
}
|
||||
|
||||
// Error adds an error with the key "error.object" to a Span.LogFields() record
|
||||
func Error(err error) Field {
|
||||
return Field{
|
||||
key: "error.object",
|
||||
fieldType: errorType,
|
||||
interfaceVal: err,
|
||||
}
|
||||
}
|
||||
|
||||
// Object adds an object-valued key:value pair to a Span.LogFields() record
|
||||
// Please pass in an immutable object, otherwise there may be concurrency issues.
|
||||
// Such as passing in the map, log.Object may result in "fatal error: concurrent map iteration and map write".
|
||||
// Because span is sent asynchronously, it is possible that this map will also be modified.
|
||||
func Object(key string, obj interface{}) Field {
|
||||
return Field{
|
||||
key: key,
|
||||
fieldType: objectType,
|
||||
interfaceVal: obj,
|
||||
}
|
||||
}
|
||||
|
||||
// Event creates a string-valued Field for span logs with key="event" and value=val.
|
||||
func Event(val string) Field {
|
||||
return String("event", val)
|
||||
}
|
||||
|
||||
// Message creates a string-valued Field for span logs with key="message" and value=val.
|
||||
func Message(val string) Field {
|
||||
return String("message", val)
|
||||
}
|
||||
|
||||
// LazyLogger allows for user-defined, late-bound logging of arbitrary data
|
||||
type LazyLogger func(fv Encoder)
|
||||
|
||||
// Lazy adds a LazyLogger to a Span.LogFields() record; the tracing
|
||||
// implementation will call the LazyLogger function at an indefinite time in
|
||||
// the future (after Lazy() returns).
|
||||
func Lazy(ll LazyLogger) Field {
|
||||
return Field{
|
||||
fieldType: lazyLoggerType,
|
||||
interfaceVal: ll,
|
||||
}
|
||||
}
|
||||
|
||||
// Noop creates a no-op log field that should be ignored by the tracer.
|
||||
// It can be used to capture optional fields, for example those that should
|
||||
// only be logged in non-production environment:
|
||||
//
|
||||
// func customerField(order *Order) log.Field {
|
||||
// if os.Getenv("ENVIRONMENT") == "dev" {
|
||||
// return log.String("customer", order.Customer.ID)
|
||||
// }
|
||||
// return log.Noop()
|
||||
// }
|
||||
//
|
||||
// span.LogFields(log.String("event", "purchase"), customerField(order))
|
||||
//
|
||||
func Noop() Field {
|
||||
return Field{
|
||||
fieldType: noopType,
|
||||
}
|
||||
}
|
||||
|
||||
// Encoder allows access to the contents of a Field (via a call to
|
||||
// Field.Marshal).
|
||||
//
|
||||
// Tracer implementations typically provide an implementation of Encoder;
|
||||
// OpenTracing callers typically do not need to concern themselves with it.
|
||||
type Encoder interface {
|
||||
EmitString(key, value string)
|
||||
EmitBool(key string, value bool)
|
||||
EmitInt(key string, value int)
|
||||
EmitInt32(key string, value int32)
|
||||
EmitInt64(key string, value int64)
|
||||
EmitUint32(key string, value uint32)
|
||||
EmitUint64(key string, value uint64)
|
||||
EmitFloat32(key string, value float32)
|
||||
EmitFloat64(key string, value float64)
|
||||
EmitObject(key string, value interface{})
|
||||
EmitLazyLogger(value LazyLogger)
|
||||
}
|
||||
|
||||
// Marshal passes a Field instance through to the appropriate
|
||||
// field-type-specific method of an Encoder.
|
||||
func (lf Field) Marshal(visitor Encoder) {
|
||||
switch lf.fieldType {
|
||||
case stringType:
|
||||
visitor.EmitString(lf.key, lf.stringVal)
|
||||
case boolType:
|
||||
visitor.EmitBool(lf.key, lf.numericVal != 0)
|
||||
case intType:
|
||||
visitor.EmitInt(lf.key, int(lf.numericVal))
|
||||
case int32Type:
|
||||
visitor.EmitInt32(lf.key, int32(lf.numericVal))
|
||||
case int64Type:
|
||||
visitor.EmitInt64(lf.key, int64(lf.numericVal))
|
||||
case uint32Type:
|
||||
visitor.EmitUint32(lf.key, uint32(lf.numericVal))
|
||||
case uint64Type:
|
||||
visitor.EmitUint64(lf.key, uint64(lf.numericVal))
|
||||
case float32Type:
|
||||
visitor.EmitFloat32(lf.key, math.Float32frombits(uint32(lf.numericVal)))
|
||||
case float64Type:
|
||||
visitor.EmitFloat64(lf.key, math.Float64frombits(uint64(lf.numericVal)))
|
||||
case errorType:
|
||||
if err, ok := lf.interfaceVal.(error); ok {
|
||||
visitor.EmitString(lf.key, err.Error())
|
||||
} else {
|
||||
visitor.EmitString(lf.key, "<nil>")
|
||||
}
|
||||
case objectType:
|
||||
visitor.EmitObject(lf.key, lf.interfaceVal)
|
||||
case lazyLoggerType:
|
||||
visitor.EmitLazyLogger(lf.interfaceVal.(LazyLogger))
|
||||
case noopType:
|
||||
// intentionally left blank
|
||||
}
|
||||
}
|
||||
|
||||
// Key returns the field's key.
|
||||
func (lf Field) Key() string {
|
||||
return lf.key
|
||||
}
|
||||
|
||||
// Value returns the field's value as interface{}.
|
||||
func (lf Field) Value() interface{} {
|
||||
switch lf.fieldType {
|
||||
case stringType:
|
||||
return lf.stringVal
|
||||
case boolType:
|
||||
return lf.numericVal != 0
|
||||
case intType:
|
||||
return int(lf.numericVal)
|
||||
case int32Type:
|
||||
return int32(lf.numericVal)
|
||||
case int64Type:
|
||||
return int64(lf.numericVal)
|
||||
case uint32Type:
|
||||
return uint32(lf.numericVal)
|
||||
case uint64Type:
|
||||
return uint64(lf.numericVal)
|
||||
case float32Type:
|
||||
return math.Float32frombits(uint32(lf.numericVal))
|
||||
case float64Type:
|
||||
return math.Float64frombits(uint64(lf.numericVal))
|
||||
case errorType, objectType, lazyLoggerType:
|
||||
return lf.interfaceVal
|
||||
case noopType:
|
||||
return nil
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// String returns a string representation of the key and value.
|
||||
func (lf Field) String() string {
|
||||
return fmt.Sprint(lf.key, ":", lf.Value())
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
package log
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// InterleavedKVToFields converts keyValues a la Span.LogKV() to a Field slice
|
||||
// a la Span.LogFields().
|
||||
func InterleavedKVToFields(keyValues ...interface{}) ([]Field, error) {
|
||||
if len(keyValues)%2 != 0 {
|
||||
return nil, fmt.Errorf("non-even keyValues len: %d", len(keyValues))
|
||||
}
|
||||
fields := make([]Field, len(keyValues)/2)
|
||||
for i := 0; i*2 < len(keyValues); i++ {
|
||||
key, ok := keyValues[i*2].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf(
|
||||
"non-string key (pair #%d): %T",
|
||||
i, keyValues[i*2])
|
||||
}
|
||||
switch typedVal := keyValues[i*2+1].(type) {
|
||||
case bool:
|
||||
fields[i] = Bool(key, typedVal)
|
||||
case string:
|
||||
fields[i] = String(key, typedVal)
|
||||
case int:
|
||||
fields[i] = Int(key, typedVal)
|
||||
case int8:
|
||||
fields[i] = Int32(key, int32(typedVal))
|
||||
case int16:
|
||||
fields[i] = Int32(key, int32(typedVal))
|
||||
case int32:
|
||||
fields[i] = Int32(key, typedVal)
|
||||
case int64:
|
||||
fields[i] = Int64(key, typedVal)
|
||||
case uint:
|
||||
fields[i] = Uint64(key, uint64(typedVal))
|
||||
case uint64:
|
||||
fields[i] = Uint64(key, typedVal)
|
||||
case uint8:
|
||||
fields[i] = Uint32(key, uint32(typedVal))
|
||||
case uint16:
|
||||
fields[i] = Uint32(key, uint32(typedVal))
|
||||
case uint32:
|
||||
fields[i] = Uint32(key, typedVal)
|
||||
case float32:
|
||||
fields[i] = Float32(key, typedVal)
|
||||
case float64:
|
||||
fields[i] = Float64(key, typedVal)
|
||||
default:
|
||||
if typedVal == nil || (reflect.ValueOf(typedVal).Kind() == reflect.Ptr && reflect.ValueOf(typedVal).IsNil()) {
|
||||
fields[i] = String(key, "nil")
|
||||
continue
|
||||
}
|
||||
// When in doubt, coerce to a string
|
||||
fields[i] = String(key, fmt.Sprint(typedVal))
|
||||
}
|
||||
}
|
||||
return fields, nil
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
package opentracing
|
||||
|
||||
import "github.com/opentracing/opentracing-go/log"
|
||||
|
||||
// A NoopTracer is a trivial, minimum overhead implementation of Tracer
|
||||
// for which all operations are no-ops.
|
||||
//
|
||||
// The primary use of this implementation is in libraries, such as RPC
|
||||
// frameworks, that make tracing an optional feature controlled by the
|
||||
// end user. A no-op implementation allows said libraries to use it
|
||||
// as the default Tracer and to write instrumentation that does
|
||||
// not need to keep checking if the tracer instance is nil.
|
||||
//
|
||||
// For the same reason, the NoopTracer is the default "global" tracer
|
||||
// (see GlobalTracer and SetGlobalTracer functions).
|
||||
//
|
||||
// WARNING: NoopTracer does not support baggage propagation.
|
||||
type NoopTracer struct{}
|
||||
|
||||
type noopSpan struct{}
|
||||
type noopSpanContext struct{}
|
||||
|
||||
var (
|
||||
defaultNoopSpanContext SpanContext = noopSpanContext{}
|
||||
defaultNoopSpan Span = noopSpan{}
|
||||
defaultNoopTracer Tracer = NoopTracer{}
|
||||
)
|
||||
|
||||
const (
|
||||
emptyString = ""
|
||||
)
|
||||
|
||||
// noopSpanContext:
|
||||
func (n noopSpanContext) ForeachBaggageItem(handler func(k, v string) bool) {}
|
||||
|
||||
// noopSpan:
|
||||
func (n noopSpan) Context() SpanContext { return defaultNoopSpanContext }
|
||||
func (n noopSpan) SetBaggageItem(key, val string) Span { return n }
|
||||
func (n noopSpan) BaggageItem(key string) string { return emptyString }
|
||||
func (n noopSpan) SetTag(key string, value interface{}) Span { return n }
|
||||
func (n noopSpan) LogFields(fields ...log.Field) {}
|
||||
func (n noopSpan) LogKV(keyVals ...interface{}) {}
|
||||
func (n noopSpan) Finish() {}
|
||||
func (n noopSpan) FinishWithOptions(opts FinishOptions) {}
|
||||
func (n noopSpan) SetOperationName(operationName string) Span { return n }
|
||||
func (n noopSpan) Tracer() Tracer { return defaultNoopTracer }
|
||||
func (n noopSpan) LogEvent(event string) {}
|
||||
func (n noopSpan) LogEventWithPayload(event string, payload interface{}) {}
|
||||
func (n noopSpan) Log(data LogData) {}
|
||||
|
||||
// StartSpan belongs to the Tracer interface.
|
||||
func (n NoopTracer) StartSpan(operationName string, opts ...StartSpanOption) Span {
|
||||
return defaultNoopSpan
|
||||
}
|
||||
|
||||
// Inject belongs to the Tracer interface.
|
||||
func (n NoopTracer) Inject(sp SpanContext, format interface{}, carrier interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Extract belongs to the Tracer interface.
|
||||
func (n NoopTracer) Extract(format interface{}, carrier interface{}) (SpanContext, error) {
|
||||
return nil, ErrSpanContextNotFound
|
||||
}
|
|
@ -1,176 +0,0 @@
|
|||
package opentracing
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// CORE PROPAGATION INTERFACES:
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
var (
|
||||
// ErrUnsupportedFormat occurs when the `format` passed to Tracer.Inject() or
|
||||
// Tracer.Extract() is not recognized by the Tracer implementation.
|
||||
ErrUnsupportedFormat = errors.New("opentracing: Unknown or unsupported Inject/Extract format")
|
||||
|
||||
// ErrSpanContextNotFound occurs when the `carrier` passed to
|
||||
// Tracer.Extract() is valid and uncorrupted but has insufficient
|
||||
// information to extract a SpanContext.
|
||||
ErrSpanContextNotFound = errors.New("opentracing: SpanContext not found in Extract carrier")
|
||||
|
||||
// ErrInvalidSpanContext errors occur when Tracer.Inject() is asked to
|
||||
// operate on a SpanContext which it is not prepared to handle (for
|
||||
// example, since it was created by a different tracer implementation).
|
||||
ErrInvalidSpanContext = errors.New("opentracing: SpanContext type incompatible with tracer")
|
||||
|
||||
// ErrInvalidCarrier errors occur when Tracer.Inject() or Tracer.Extract()
|
||||
// implementations expect a different type of `carrier` than they are
|
||||
// given.
|
||||
ErrInvalidCarrier = errors.New("opentracing: Invalid Inject/Extract carrier")
|
||||
|
||||
// ErrSpanContextCorrupted occurs when the `carrier` passed to
|
||||
// Tracer.Extract() is of the expected type but is corrupted.
|
||||
ErrSpanContextCorrupted = errors.New("opentracing: SpanContext data corrupted in Extract carrier")
|
||||
)
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// BUILTIN PROPAGATION FORMATS:
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// BuiltinFormat is used to demarcate the values within package `opentracing`
|
||||
// that are intended for use with the Tracer.Inject() and Tracer.Extract()
|
||||
// methods.
|
||||
type BuiltinFormat byte
|
||||
|
||||
const (
|
||||
// Binary represents SpanContexts as opaque binary data.
|
||||
//
|
||||
// For Tracer.Inject(): the carrier must be an `io.Writer`.
|
||||
//
|
||||
// For Tracer.Extract(): the carrier must be an `io.Reader`.
|
||||
Binary BuiltinFormat = iota
|
||||
|
||||
// TextMap represents SpanContexts as key:value string pairs.
|
||||
//
|
||||
// Unlike HTTPHeaders, the TextMap format does not restrict the key or
|
||||
// value character sets in any way.
|
||||
//
|
||||
// For Tracer.Inject(): the carrier must be a `TextMapWriter`.
|
||||
//
|
||||
// For Tracer.Extract(): the carrier must be a `TextMapReader`.
|
||||
TextMap
|
||||
|
||||
// HTTPHeaders represents SpanContexts as HTTP header string pairs.
|
||||
//
|
||||
// Unlike TextMap, the HTTPHeaders format requires that the keys and values
|
||||
// be valid as HTTP headers as-is (i.e., character casing may be unstable
|
||||
// and special characters are disallowed in keys, values should be
|
||||
// URL-escaped, etc).
|
||||
//
|
||||
// For Tracer.Inject(): the carrier must be a `TextMapWriter`.
|
||||
//
|
||||
// For Tracer.Extract(): the carrier must be a `TextMapReader`.
|
||||
//
|
||||
// See HTTPHeadersCarrier for an implementation of both TextMapWriter
|
||||
// and TextMapReader that defers to an http.Header instance for storage.
|
||||
// For example, Inject():
|
||||
//
|
||||
// carrier := opentracing.HTTPHeadersCarrier(httpReq.Header)
|
||||
// err := span.Tracer().Inject(
|
||||
// span.Context(), opentracing.HTTPHeaders, carrier)
|
||||
//
|
||||
// Or Extract():
|
||||
//
|
||||
// carrier := opentracing.HTTPHeadersCarrier(httpReq.Header)
|
||||
// clientContext, err := tracer.Extract(
|
||||
// opentracing.HTTPHeaders, carrier)
|
||||
//
|
||||
HTTPHeaders
|
||||
)
|
||||
|
||||
// TextMapWriter is the Inject() carrier for the TextMap builtin format. With
|
||||
// it, the caller can encode a SpanContext for propagation as entries in a map
|
||||
// of unicode strings.
|
||||
type TextMapWriter interface {
|
||||
// Set a key:value pair to the carrier. Multiple calls to Set() for the
|
||||
// same key leads to undefined behavior.
|
||||
//
|
||||
// NOTE: The backing store for the TextMapWriter may contain data unrelated
|
||||
// to SpanContext. As such, Inject() and Extract() implementations that
|
||||
// call the TextMapWriter and TextMapReader interfaces must agree on a
|
||||
// prefix or other convention to distinguish their own key:value pairs.
|
||||
Set(key, val string)
|
||||
}
|
||||
|
||||
// TextMapReader is the Extract() carrier for the TextMap builtin format. With it,
|
||||
// the caller can decode a propagated SpanContext as entries in a map of
|
||||
// unicode strings.
|
||||
type TextMapReader interface {
|
||||
// ForeachKey returns TextMap contents via repeated calls to the `handler`
|
||||
// function. If any call to `handler` returns a non-nil error, ForeachKey
|
||||
// terminates and returns that error.
|
||||
//
|
||||
// NOTE: The backing store for the TextMapReader may contain data unrelated
|
||||
// to SpanContext. As such, Inject() and Extract() implementations that
|
||||
// call the TextMapWriter and TextMapReader interfaces must agree on a
|
||||
// prefix or other convention to distinguish their own key:value pairs.
|
||||
//
|
||||
// The "foreach" callback pattern reduces unnecessary copying in some cases
|
||||
// and also allows implementations to hold locks while the map is read.
|
||||
ForeachKey(handler func(key, val string) error) error
|
||||
}
|
||||
|
||||
// TextMapCarrier allows the use of regular map[string]string
|
||||
// as both TextMapWriter and TextMapReader.
|
||||
type TextMapCarrier map[string]string
|
||||
|
||||
// ForeachKey conforms to the TextMapReader interface.
|
||||
func (c TextMapCarrier) ForeachKey(handler func(key, val string) error) error {
|
||||
for k, v := range c {
|
||||
if err := handler(k, v); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Set implements Set() of opentracing.TextMapWriter
|
||||
func (c TextMapCarrier) Set(key, val string) {
|
||||
c[key] = val
|
||||
}
|
||||
|
||||
// HTTPHeadersCarrier satisfies both TextMapWriter and TextMapReader.
|
||||
//
|
||||
// Example usage for server side:
|
||||
//
|
||||
// carrier := opentracing.HTTPHeadersCarrier(httpReq.Header)
|
||||
// clientContext, err := tracer.Extract(opentracing.HTTPHeaders, carrier)
|
||||
//
|
||||
// Example usage for client side:
|
||||
//
|
||||
// carrier := opentracing.HTTPHeadersCarrier(httpReq.Header)
|
||||
// err := tracer.Inject(
|
||||
// span.Context(),
|
||||
// opentracing.HTTPHeaders,
|
||||
// carrier)
|
||||
//
|
||||
type HTTPHeadersCarrier http.Header
|
||||
|
||||
// Set conforms to the TextMapWriter interface.
|
||||
func (c HTTPHeadersCarrier) Set(key, val string) {
|
||||
h := http.Header(c)
|
||||
h.Set(key, val)
|
||||
}
|
||||
|
||||
// ForeachKey conforms to the TextMapReader interface.
|
||||
func (c HTTPHeadersCarrier) ForeachKey(handler func(key, val string) error) error {
|
||||
for k, vals := range c {
|
||||
for _, v := range vals {
|
||||
if err := handler(k, v); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -1,189 +0,0 @@
|
|||
package opentracing
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/opentracing/opentracing-go/log"
|
||||
)
|
||||
|
||||
// SpanContext represents Span state that must propagate to descendant Spans and across process
|
||||
// boundaries (e.g., a <trace_id, span_id, sampled> tuple).
|
||||
type SpanContext interface {
|
||||
// ForeachBaggageItem grants access to all baggage items stored in the
|
||||
// SpanContext.
|
||||
// The handler function will be called for each baggage key/value pair.
|
||||
// The ordering of items is not guaranteed.
|
||||
//
|
||||
// The bool return value indicates if the handler wants to continue iterating
|
||||
// through the rest of the baggage items; for example if the handler is trying to
|
||||
// find some baggage item by pattern matching the name, it can return false
|
||||
// as soon as the item is found to stop further iterations.
|
||||
ForeachBaggageItem(handler func(k, v string) bool)
|
||||
}
|
||||
|
||||
// Span represents an active, un-finished span in the OpenTracing system.
|
||||
//
|
||||
// Spans are created by the Tracer interface.
|
||||
type Span interface {
|
||||
// Sets the end timestamp and finalizes Span state.
|
||||
//
|
||||
// With the exception of calls to Context() (which are always allowed),
|
||||
// Finish() must be the last call made to any span instance, and to do
|
||||
// otherwise leads to undefined behavior.
|
||||
Finish()
|
||||
// FinishWithOptions is like Finish() but with explicit control over
|
||||
// timestamps and log data.
|
||||
FinishWithOptions(opts FinishOptions)
|
||||
|
||||
// Context() yields the SpanContext for this Span. Note that the return
|
||||
// value of Context() is still valid after a call to Span.Finish(), as is
|
||||
// a call to Span.Context() after a call to Span.Finish().
|
||||
Context() SpanContext
|
||||
|
||||
// Sets or changes the operation name.
|
||||
//
|
||||
// Returns a reference to this Span for chaining.
|
||||
SetOperationName(operationName string) Span
|
||||
|
||||
// Adds a tag to the span.
|
||||
//
|
||||
// If there is a pre-existing tag set for `key`, it is overwritten.
|
||||
//
|
||||
// Tag values can be numeric types, strings, or bools. The behavior of
|
||||
// other tag value types is undefined at the OpenTracing level. If a
|
||||
// tracing system does not know how to handle a particular value type, it
|
||||
// may ignore the tag, but shall not panic.
|
||||
//
|
||||
// Returns a reference to this Span for chaining.
|
||||
SetTag(key string, value interface{}) Span
|
||||
|
||||
// LogFields is an efficient and type-checked way to record key:value
|
||||
// logging data about a Span, though the programming interface is a little
|
||||
// more verbose than LogKV(). Here's an example:
|
||||
//
|
||||
// span.LogFields(
|
||||
// log.String("event", "soft error"),
|
||||
// log.String("type", "cache timeout"),
|
||||
// log.Int("waited.millis", 1500))
|
||||
//
|
||||
// Also see Span.FinishWithOptions() and FinishOptions.BulkLogData.
|
||||
LogFields(fields ...log.Field)
|
||||
|
||||
// LogKV is a concise, readable way to record key:value logging data about
|
||||
// a Span, though unfortunately this also makes it less efficient and less
|
||||
// type-safe than LogFields(). Here's an example:
|
||||
//
|
||||
// span.LogKV(
|
||||
// "event", "soft error",
|
||||
// "type", "cache timeout",
|
||||
// "waited.millis", 1500)
|
||||
//
|
||||
// For LogKV (as opposed to LogFields()), the parameters must appear as
|
||||
// key-value pairs, like
|
||||
//
|
||||
// span.LogKV(key1, val1, key2, val2, key3, val3, ...)
|
||||
//
|
||||
// The keys must all be strings. The values may be strings, numeric types,
|
||||
// bools, Go error instances, or arbitrary structs.
|
||||
//
|
||||
// (Note to implementors: consider the log.InterleavedKVToFields() helper)
|
||||
LogKV(alternatingKeyValues ...interface{})
|
||||
|
||||
// SetBaggageItem sets a key:value pair on this Span and its SpanContext
|
||||
// that also propagates to descendants of this Span.
|
||||
//
|
||||
// SetBaggageItem() enables powerful functionality given a full-stack
|
||||
// opentracing integration (e.g., arbitrary application data from a mobile
|
||||
// app can make it, transparently, all the way into the depths of a storage
|
||||
// system), and with it some powerful costs: use this feature with care.
|
||||
//
|
||||
// IMPORTANT NOTE #1: SetBaggageItem() will only propagate baggage items to
|
||||
// *future* causal descendants of the associated Span.
|
||||
//
|
||||
// IMPORTANT NOTE #2: Use this thoughtfully and with care. Every key and
|
||||
// value is copied into every local *and remote* child of the associated
|
||||
// Span, and that can add up to a lot of network and cpu overhead.
|
||||
//
|
||||
// Returns a reference to this Span for chaining.
|
||||
SetBaggageItem(restrictedKey, value string) Span
|
||||
|
||||
// Gets the value for a baggage item given its key. Returns the empty string
|
||||
// if the value isn't found in this Span.
|
||||
BaggageItem(restrictedKey string) string
|
||||
|
||||
// Provides access to the Tracer that created this Span.
|
||||
Tracer() Tracer
|
||||
|
||||
// Deprecated: use LogFields or LogKV
|
||||
LogEvent(event string)
|
||||
// Deprecated: use LogFields or LogKV
|
||||
LogEventWithPayload(event string, payload interface{})
|
||||
// Deprecated: use LogFields or LogKV
|
||||
Log(data LogData)
|
||||
}
|
||||
|
||||
// LogRecord is data associated with a single Span log. Every LogRecord
|
||||
// instance must specify at least one Field.
|
||||
type LogRecord struct {
|
||||
Timestamp time.Time
|
||||
Fields []log.Field
|
||||
}
|
||||
|
||||
// FinishOptions allows Span.FinishWithOptions callers to override the finish
|
||||
// timestamp and provide log data via a bulk interface.
|
||||
type FinishOptions struct {
|
||||
// FinishTime overrides the Span's finish time, or implicitly becomes
|
||||
// time.Now() if FinishTime.IsZero().
|
||||
//
|
||||
// FinishTime must resolve to a timestamp that's >= the Span's StartTime
|
||||
// (per StartSpanOptions).
|
||||
FinishTime time.Time
|
||||
|
||||
// LogRecords allows the caller to specify the contents of many LogFields()
|
||||
// calls with a single slice. May be nil.
|
||||
//
|
||||
// None of the LogRecord.Timestamp values may be .IsZero() (i.e., they must
|
||||
// be set explicitly). Also, they must be >= the Span's start timestamp and
|
||||
// <= the FinishTime (or time.Now() if FinishTime.IsZero()). Otherwise the
|
||||
// behavior of FinishWithOptions() is undefined.
|
||||
//
|
||||
// If specified, the caller hands off ownership of LogRecords at
|
||||
// FinishWithOptions() invocation time.
|
||||
//
|
||||
// If specified, the (deprecated) BulkLogData must be nil or empty.
|
||||
LogRecords []LogRecord
|
||||
|
||||
// BulkLogData is DEPRECATED.
|
||||
BulkLogData []LogData
|
||||
}
|
||||
|
||||
// LogData is DEPRECATED
|
||||
type LogData struct {
|
||||
Timestamp time.Time
|
||||
Event string
|
||||
Payload interface{}
|
||||
}
|
||||
|
||||
// ToLogRecord converts a deprecated LogData to a non-deprecated LogRecord
|
||||
func (ld *LogData) ToLogRecord() LogRecord {
|
||||
var literalTimestamp time.Time
|
||||
if ld.Timestamp.IsZero() {
|
||||
literalTimestamp = time.Now()
|
||||
} else {
|
||||
literalTimestamp = ld.Timestamp
|
||||
}
|
||||
rval := LogRecord{
|
||||
Timestamp: literalTimestamp,
|
||||
}
|
||||
if ld.Payload == nil {
|
||||
rval.Fields = []log.Field{
|
||||
log.String("event", ld.Event),
|
||||
}
|
||||
} else {
|
||||
rval.Fields = []log.Field{
|
||||
log.String("event", ld.Event),
|
||||
log.Object("payload", ld.Payload),
|
||||
}
|
||||
}
|
||||
return rval
|
||||
}
|
|
@ -1,304 +0,0 @@
|
|||
package opentracing
|
||||
|
||||
import "time"
|
||||
|
||||
// Tracer is a simple, thin interface for Span creation and SpanContext
|
||||
// propagation.
|
||||
type Tracer interface {
|
||||
|
||||
// Create, start, and return a new Span with the given `operationName` and
|
||||
// incorporate the given StartSpanOption `opts`. (Note that `opts` borrows
|
||||
// from the "functional options" pattern, per
|
||||
// http://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis)
|
||||
//
|
||||
// A Span with no SpanReference options (e.g., opentracing.ChildOf() or
|
||||
// opentracing.FollowsFrom()) becomes the root of its own trace.
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// var tracer opentracing.Tracer = ...
|
||||
//
|
||||
// // The root-span case:
|
||||
// sp := tracer.StartSpan("GetFeed")
|
||||
//
|
||||
// // The vanilla child span case:
|
||||
// sp := tracer.StartSpan(
|
||||
// "GetFeed",
|
||||
// opentracing.ChildOf(parentSpan.Context()))
|
||||
//
|
||||
// // All the bells and whistles:
|
||||
// sp := tracer.StartSpan(
|
||||
// "GetFeed",
|
||||
// opentracing.ChildOf(parentSpan.Context()),
|
||||
// opentracing.Tag{"user_agent", loggedReq.UserAgent},
|
||||
// opentracing.StartTime(loggedReq.Timestamp),
|
||||
// )
|
||||
//
|
||||
StartSpan(operationName string, opts ...StartSpanOption) Span
|
||||
|
||||
// Inject() takes the `sm` SpanContext instance and injects it for
|
||||
// propagation within `carrier`. The actual type of `carrier` depends on
|
||||
// the value of `format`.
|
||||
//
|
||||
// OpenTracing defines a common set of `format` values (see BuiltinFormat),
|
||||
// and each has an expected carrier type.
|
||||
//
|
||||
// Other packages may declare their own `format` values, much like the keys
|
||||
// used by `context.Context` (see https://godoc.org/context#WithValue).
|
||||
//
|
||||
// Example usage (sans error handling):
|
||||
//
|
||||
// carrier := opentracing.HTTPHeadersCarrier(httpReq.Header)
|
||||
// err := tracer.Inject(
|
||||
// span.Context(),
|
||||
// opentracing.HTTPHeaders,
|
||||
// carrier)
|
||||
//
|
||||
// NOTE: All opentracing.Tracer implementations MUST support all
|
||||
// BuiltinFormats.
|
||||
//
|
||||
// Implementations may return opentracing.ErrUnsupportedFormat if `format`
|
||||
// is not supported by (or not known by) the implementation.
|
||||
//
|
||||
// Implementations may return opentracing.ErrInvalidCarrier or any other
|
||||
// implementation-specific error if the format is supported but injection
|
||||
// fails anyway.
|
||||
//
|
||||
// See Tracer.Extract().
|
||||
Inject(sm SpanContext, format interface{}, carrier interface{}) error
|
||||
|
||||
// Extract() returns a SpanContext instance given `format` and `carrier`.
|
||||
//
|
||||
// OpenTracing defines a common set of `format` values (see BuiltinFormat),
|
||||
// and each has an expected carrier type.
|
||||
//
|
||||
// Other packages may declare their own `format` values, much like the keys
|
||||
// used by `context.Context` (see
|
||||
// https://godoc.org/golang.org/x/net/context#WithValue).
|
||||
//
|
||||
// Example usage (with StartSpan):
|
||||
//
|
||||
//
|
||||
// carrier := opentracing.HTTPHeadersCarrier(httpReq.Header)
|
||||
// clientContext, err := tracer.Extract(opentracing.HTTPHeaders, carrier)
|
||||
//
|
||||
// // ... assuming the ultimate goal here is to resume the trace with a
|
||||
// // server-side Span:
|
||||
// var serverSpan opentracing.Span
|
||||
// if err == nil {
|
||||
// span = tracer.StartSpan(
|
||||
// rpcMethodName, ext.RPCServerOption(clientContext))
|
||||
// } else {
|
||||
// span = tracer.StartSpan(rpcMethodName)
|
||||
// }
|
||||
//
|
||||
//
|
||||
// NOTE: All opentracing.Tracer implementations MUST support all
|
||||
// BuiltinFormats.
|
||||
//
|
||||
// Return values:
|
||||
// - A successful Extract returns a SpanContext instance and a nil error
|
||||
// - If there was simply no SpanContext to extract in `carrier`, Extract()
|
||||
// returns (nil, opentracing.ErrSpanContextNotFound)
|
||||
// - If `format` is unsupported or unrecognized, Extract() returns (nil,
|
||||
// opentracing.ErrUnsupportedFormat)
|
||||
// - If there are more fundamental problems with the `carrier` object,
|
||||
// Extract() may return opentracing.ErrInvalidCarrier,
|
||||
// opentracing.ErrSpanContextCorrupted, or implementation-specific
|
||||
// errors.
|
||||
//
|
||||
// See Tracer.Inject().
|
||||
Extract(format interface{}, carrier interface{}) (SpanContext, error)
|
||||
}
|
||||
|
||||
// StartSpanOptions allows Tracer.StartSpan() callers and implementors a
|
||||
// mechanism to override the start timestamp, specify Span References, and make
|
||||
// a single Tag or multiple Tags available at Span start time.
|
||||
//
|
||||
// StartSpan() callers should look at the StartSpanOption interface and
|
||||
// implementations available in this package.
|
||||
//
|
||||
// Tracer implementations can convert a slice of `StartSpanOption` instances
|
||||
// into a `StartSpanOptions` struct like so:
|
||||
//
|
||||
// func StartSpan(opName string, opts ...opentracing.StartSpanOption) {
|
||||
// sso := opentracing.StartSpanOptions{}
|
||||
// for _, o := range opts {
|
||||
// o.Apply(&sso)
|
||||
// }
|
||||
// ...
|
||||
// }
|
||||
//
|
||||
type StartSpanOptions struct {
|
||||
// Zero or more causal references to other Spans (via their SpanContext).
|
||||
// If empty, start a "root" Span (i.e., start a new trace).
|
||||
References []SpanReference
|
||||
|
||||
// StartTime overrides the Span's start time, or implicitly becomes
|
||||
// time.Now() if StartTime.IsZero().
|
||||
StartTime time.Time
|
||||
|
||||
// Tags may have zero or more entries; the restrictions on map values are
|
||||
// identical to those for Span.SetTag(). May be nil.
|
||||
//
|
||||
// If specified, the caller hands off ownership of Tags at
|
||||
// StartSpan() invocation time.
|
||||
Tags map[string]interface{}
|
||||
}
|
||||
|
||||
// StartSpanOption instances (zero or more) may be passed to Tracer.StartSpan.
|
||||
//
|
||||
// StartSpanOption borrows from the "functional options" pattern, per
|
||||
// http://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis
|
||||
type StartSpanOption interface {
|
||||
Apply(*StartSpanOptions)
|
||||
}
|
||||
|
||||
// SpanReferenceType is an enum type describing different categories of
|
||||
// relationships between two Spans. If Span-2 refers to Span-1, the
|
||||
// SpanReferenceType describes Span-1 from Span-2's perspective. For example,
|
||||
// ChildOfRef means that Span-1 created Span-2.
|
||||
//
|
||||
// NOTE: Span-1 and Span-2 do *not* necessarily depend on each other for
|
||||
// completion; e.g., Span-2 may be part of a background job enqueued by Span-1,
|
||||
// or Span-2 may be sitting in a distributed queue behind Span-1.
|
||||
type SpanReferenceType int
|
||||
|
||||
const (
|
||||
// ChildOfRef refers to a parent Span that caused *and* somehow depends
|
||||
// upon the new child Span. Often (but not always), the parent Span cannot
|
||||
// finish until the child Span does.
|
||||
//
|
||||
// An timing diagram for a ChildOfRef that's blocked on the new Span:
|
||||
//
|
||||
// [-Parent Span---------]
|
||||
// [-Child Span----]
|
||||
//
|
||||
// See http://opentracing.io/spec/
|
||||
//
|
||||
// See opentracing.ChildOf()
|
||||
ChildOfRef SpanReferenceType = iota
|
||||
|
||||
// FollowsFromRef refers to a parent Span that does not depend in any way
|
||||
// on the result of the new child Span. For instance, one might use
|
||||
// FollowsFromRefs to describe pipeline stages separated by queues,
|
||||
// or a fire-and-forget cache insert at the tail end of a web request.
|
||||
//
|
||||
// A FollowsFromRef Span is part of the same logical trace as the new Span:
|
||||
// i.e., the new Span is somehow caused by the work of its FollowsFromRef.
|
||||
//
|
||||
// All of the following could be valid timing diagrams for children that
|
||||
// "FollowFrom" a parent.
|
||||
//
|
||||
// [-Parent Span-] [-Child Span-]
|
||||
//
|
||||
//
|
||||
// [-Parent Span--]
|
||||
// [-Child Span-]
|
||||
//
|
||||
//
|
||||
// [-Parent Span-]
|
||||
// [-Child Span-]
|
||||
//
|
||||
// See http://opentracing.io/spec/
|
||||
//
|
||||
// See opentracing.FollowsFrom()
|
||||
FollowsFromRef
|
||||
)
|
||||
|
||||
// SpanReference is a StartSpanOption that pairs a SpanReferenceType and a
|
||||
// referenced SpanContext. See the SpanReferenceType documentation for
|
||||
// supported relationships. If SpanReference is created with
|
||||
// ReferencedContext==nil, it has no effect. Thus it allows for a more concise
|
||||
// syntax for starting spans:
|
||||
//
|
||||
// sc, _ := tracer.Extract(someFormat, someCarrier)
|
||||
// span := tracer.StartSpan("operation", opentracing.ChildOf(sc))
|
||||
//
|
||||
// The `ChildOf(sc)` option above will not panic if sc == nil, it will just
|
||||
// not add the parent span reference to the options.
|
||||
type SpanReference struct {
|
||||
Type SpanReferenceType
|
||||
ReferencedContext SpanContext
|
||||
}
|
||||
|
||||
// Apply satisfies the StartSpanOption interface.
|
||||
func (r SpanReference) Apply(o *StartSpanOptions) {
|
||||
if r.ReferencedContext != nil {
|
||||
o.References = append(o.References, r)
|
||||
}
|
||||
}
|
||||
|
||||
// ChildOf returns a StartSpanOption pointing to a dependent parent span.
|
||||
// If sc == nil, the option has no effect.
|
||||
//
|
||||
// See ChildOfRef, SpanReference
|
||||
func ChildOf(sc SpanContext) SpanReference {
|
||||
return SpanReference{
|
||||
Type: ChildOfRef,
|
||||
ReferencedContext: sc,
|
||||
}
|
||||
}
|
||||
|
||||
// FollowsFrom returns a StartSpanOption pointing to a parent Span that caused
|
||||
// the child Span but does not directly depend on its result in any way.
|
||||
// If sc == nil, the option has no effect.
|
||||
//
|
||||
// See FollowsFromRef, SpanReference
|
||||
func FollowsFrom(sc SpanContext) SpanReference {
|
||||
return SpanReference{
|
||||
Type: FollowsFromRef,
|
||||
ReferencedContext: sc,
|
||||
}
|
||||
}
|
||||
|
||||
// StartTime is a StartSpanOption that sets an explicit start timestamp for the
|
||||
// new Span.
|
||||
type StartTime time.Time
|
||||
|
||||
// Apply satisfies the StartSpanOption interface.
|
||||
func (t StartTime) Apply(o *StartSpanOptions) {
|
||||
o.StartTime = time.Time(t)
|
||||
}
|
||||
|
||||
// Tags are a generic map from an arbitrary string key to an opaque value type.
|
||||
// The underlying tracing system is responsible for interpreting and
|
||||
// serializing the values.
|
||||
type Tags map[string]interface{}
|
||||
|
||||
// Apply satisfies the StartSpanOption interface.
|
||||
func (t Tags) Apply(o *StartSpanOptions) {
|
||||
if o.Tags == nil {
|
||||
o.Tags = make(map[string]interface{})
|
||||
}
|
||||
for k, v := range t {
|
||||
o.Tags[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// Tag may be passed as a StartSpanOption to add a tag to new spans,
|
||||
// or its Set method may be used to apply the tag to an existing Span,
|
||||
// for example:
|
||||
//
|
||||
// tracer.StartSpan("opName", Tag{"Key", value})
|
||||
//
|
||||
// or
|
||||
//
|
||||
// Tag{"key", value}.Set(span)
|
||||
type Tag struct {
|
||||
Key string
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
// Apply satisfies the StartSpanOption interface.
|
||||
func (t Tag) Apply(o *StartSpanOptions) {
|
||||
if o.Tags == nil {
|
||||
o.Tags = make(map[string]interface{})
|
||||
}
|
||||
o.Tags[t.Key] = t.Value
|
||||
}
|
||||
|
||||
// Set applies the tag to an existing Span.
|
||||
func (t Tag) Set(s Span) {
|
||||
s.SetTag(t.Key, t.Value)
|
||||
}
|
|
@ -187,14 +187,6 @@ github.com/edsrzf/mmap-go
|
|||
## explicit; go 1.14
|
||||
github.com/elastic/gosigar
|
||||
github.com/elastic/gosigar/sys/windows
|
||||
# github.com/emirpasic/gods v1.18.1
|
||||
## explicit; go 1.2
|
||||
github.com/emirpasic/gods/containers
|
||||
github.com/emirpasic/gods/lists
|
||||
github.com/emirpasic/gods/lists/doublylinkedlist
|
||||
github.com/emirpasic/gods/maps
|
||||
github.com/emirpasic/gods/maps/linkedhashmap
|
||||
github.com/emirpasic/gods/utils
|
||||
# github.com/ethereum/go-ethereum v1.10.26 => github.com/status-im/go-ethereum v1.10.25-status.4
|
||||
## explicit; go 1.17
|
||||
github.com/ethereum/go-ethereum
|
||||
|
@ -411,12 +403,6 @@ github.com/imdario/mergo
|
|||
# github.com/ipfs/go-cid v0.3.2
|
||||
## explicit; go 1.18
|
||||
github.com/ipfs/go-cid
|
||||
# github.com/ipfs/go-log v1.0.5
|
||||
## explicit; go 1.12
|
||||
github.com/ipfs/go-log
|
||||
github.com/ipfs/go-log/tracer
|
||||
github.com/ipfs/go-log/tracer/wire
|
||||
github.com/ipfs/go-log/writer
|
||||
# github.com/ipfs/go-log/v2 v2.5.1
|
||||
## explicit; go 1.16
|
||||
github.com/ipfs/go-log/v2
|
||||
|
@ -544,7 +530,7 @@ github.com/libp2p/go-libp2p/p2p/transport/webtransport
|
|||
# github.com/libp2p/go-libp2p-asn-util v0.2.0
|
||||
## explicit; go 1.17
|
||||
github.com/libp2p/go-libp2p-asn-util
|
||||
# github.com/libp2p/go-libp2p-pubsub v0.9.0
|
||||
# github.com/libp2p/go-libp2p-pubsub v0.9.1
|
||||
## explicit; go 1.19
|
||||
github.com/libp2p/go-libp2p-pubsub
|
||||
github.com/libp2p/go-libp2p-pubsub/pb
|
||||
|
@ -693,11 +679,6 @@ github.com/onsi/ginkgo/v2/types
|
|||
# github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417
|
||||
## explicit
|
||||
github.com/opencontainers/runtime-spec/specs-go
|
||||
# github.com/opentracing/opentracing-go v1.2.0
|
||||
## explicit; go 1.14
|
||||
github.com/opentracing/opentracing-go
|
||||
github.com/opentracing/opentracing-go/ext
|
||||
github.com/opentracing/opentracing-go/log
|
||||
# github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58
|
||||
## explicit; go 1.16
|
||||
github.com/pbnjay/memory
|
||||
|
@ -985,7 +966,7 @@ github.com/vacp2p/mvds/transport
|
|||
github.com/waku-org/go-discover/discover
|
||||
github.com/waku-org/go-discover/discover/v4wire
|
||||
github.com/waku-org/go-discover/discover/v5wire
|
||||
# github.com/waku-org/go-waku v0.5.2-0.20230223234511-57f6110074d0
|
||||
# github.com/waku-org/go-waku v0.5.2-0.20230224151428-d6c87f346b72
|
||||
## explicit; go 1.19
|
||||
github.com/waku-org/go-waku/logging
|
||||
github.com/waku-org/go-waku/waku/persistence
|
||||
|
|
Loading…
Reference in New Issue